16d1f381e201f2c9625c335690a4404134d38150
3 from mathutils
import *
4 from gpu_extras
.batch
import batch_for_shader
7 "name":"Carve exporter",
8 "author": "Harry Godden (hgn)",
15 "category":"Import/Export",
18 class mdl_vert(Structure
):
20 _fields_
= [("co",c_float
*3),
25 class mdl_submesh(Structure
):
27 _fields_
= [("indice_start",c_uint32
),
28 ("indice_count",c_uint32
),
29 ("vertex_start",c_uint32
),
30 ("vertex_count",c_uint32
),
31 ("bbx",(c_float
*3)*2),
32 ("material_id",c_uint32
)] # index into the material array
34 class mdl_material(Structure
):
36 _fields_
= [("pstr_name",c_uint32
)]
38 class mdl_node(Structure
):
40 _fields_
= [("co",c_float
*3),
43 ("submesh_start",c_uint32
),
44 ("submesh_count",c_uint32
),
45 ("classtype",c_uint32
),
47 ("pstr_name",c_uint32
)]
49 class mdl_header(Structure
):
51 _fields_
= [("identifier",c_uint32
),
53 ("file_length",c_uint32
),
54 ("vertex_count",c_uint32
),
55 ("vertex_offset",c_uint32
),
57 ("indice_count",c_uint32
),
58 ("indice_offset",c_uint32
),
60 ("submesh_count",c_uint32
),
61 ("submesh_offset",c_uint32
),
63 ("material_count",c_uint32
),
64 ("material_offset",c_uint32
),
66 ("node_count",c_uint32
),
67 ("node_offset",c_uint32
),
69 ("strings_offset",c_uint32
),
70 ("entdata_offset",c_uint32
)
74 # ==========================================
76 class classtype_gate(Structure
):
78 _fields_
= [("target",c_uint32
)]
80 class classtype_block(Structure
):
82 _fields_
= [("bbx",(c_float
*3)*2)]
84 class classtype_spawn(Structure
):
86 _fields_
= [("temp",c_uint32
)]
88 class classtype_water(Structure
):
90 _fields_
= [("temp",c_uint32
)]
93 # ==============================================================================
95 def write_model(name
):
96 print( F
"Create mode {name}" )
98 collection
= bpy
.data
.collections
[name
]
100 header
= mdl_header()
101 header
.identifier
= 0xABCD0000
103 header
.vertex_count
= 0
104 header
.indice_count
= 0
105 header
.submesh_count
= 0
106 header
.node_count
= 0
107 header
.material_count
= 0
108 header
.file_length
= 0
124 def emplace_string( s
):
125 nonlocal string_cache
, strings_buffer
127 if s
in string_cache
:
128 return string_cache
[s
]
130 string_cache
[s
] = len( strings_buffer
)
131 strings_buffer
+= (s
+'\0').encode('utf-8')
132 return string_cache
[s
]
134 def emplace_material( mat
):
135 nonlocal material_cache
, material_buffer
137 if mat
.name
in material_cache
:
138 return material_cache
[mat
.name
]
140 material_cache
[mat
.name
] = header
.material_count
141 dest
= mdl_material()
142 dest
.pstr_name
= emplace_string( mat
.name
)
143 material_buffer
+= [dest
]
145 header
.material_count
+= 1
146 return material_cache
[mat
.name
]
148 # Create root or empty node and materials
150 none_material
= c_uint32(69)
151 none_material
.name
= ""
152 emplace_material( none_material
)
165 root
.pstr_name
= emplace_string('')
166 root
.submesh_start
= 0
167 root
.submesh_count
= 0
170 node_buffer
+= [root
]
174 print( " assigning ids" )
175 header
.node_count
= 1
176 for obj
in collection
.all_objects
:
177 obj
.cv_data
.uid
= header
.node_count
178 header
.node_count
+= 1
180 print( " compiling data" )
181 for obj
in collection
.all_objects
:
182 print( F
" [{obj.cv_data.uid}/{header.node_count-1}] {obj.name}" )
185 node
.co
[0] = obj
.location
[0]
186 node
.co
[1] = obj
.location
[2]
187 node
.co
[2] = -obj
.location
[1]
189 # Convert rotation quat to our space type
190 quat
= obj
.matrix_world
.to_quaternion()
196 node
.s
[0] = obj
.scale
[0]
197 node
.s
[1] = obj
.scale
[2]
198 node
.s
[2] = obj
.scale
[1]
199 node
.pstr_name
= emplace_string( obj
.name
)
201 # Process entity data
203 node
.offset
= entdata_length
204 classtype
= obj
.cv_data
.classtype
206 if classtype
== 'k_classtype_none':
210 elif classtype
== 'k_classtype_gate':
212 entdata_length
+= sizeof( classtype_gate
)
214 gate
= classtype_gate()
216 if obj
.cv_data
.target
!= None:
217 gate
.target
= obj
.cv_data
.target
.cv_data
.uid
219 entdata_buffer
+= [gate
]
221 elif classtype
== 'k_classtype_block':
223 entdata_length
+= sizeof( classtype_block
)
225 source
= obj
.data
.cv_data
227 block
= classtype_block()
228 block
.bbx
[0][0] = source
.v0
[0]
229 block
.bbx
[0][1] = source
.v0
[2]
230 block
.bbx
[0][2] = -source
.v0
[1]
231 block
.bbx
[1][0] = source
.v1
[0]
232 block
.bbx
[1][1] = source
.v1
[2]
233 block
.bbx
[1][2] = -source
.v1
[1]
234 entdata_buffer
+= [block
]
236 elif classtype
== 'k_classtype_spawn':
239 elif classtype
== 'k_classtype_water':
244 node
.submesh_start
= header
.submesh_count
245 node
.submesh_count
= 0
247 if obj
.type == 'MESH':
248 default_mat
= c_uint32(69)
249 default_mat
.name
= ""
251 if obj
.data
.name
in mesh_cache
:
252 ref
= mesh_cache
[obj
.data
.name
]
253 node
.submesh_start
= ref
.submesh_start
254 node
.submesh_count
= ref
.submesh_count
255 node_buffer
+= [node
]
258 dgraph
= bpy
.context
.evaluated_depsgraph_get()
259 data
= obj
.evaluated_get(dgraph
).data
260 data
.calc_loop_triangles()
261 data
.calc_normals_split()
263 mat_list
= data
.materials
if len(data
.materials
) > 0 else [default_mat
]
264 for material_id
, mat
in enumerate(mat_list
):
268 sm
.indice_start
= header
.indice_count
269 sm
.vertex_start
= header
.vertex_count
272 sm
.material_id
= emplace_material( mat
)
275 sm
.bbx
[0][i
] = 999999
276 sm
.bbx
[1][i
] = -999999
280 # Write the vertex / indice data
282 for tri_index
, tri
in enumerate(data
.loop_triangles
):
283 if tri
.material_index
!= material_id
:
287 vert
= data
.vertices
[tri
.vertices
[j
]]
291 norm
= data
.loops
[li
].normal
295 uv
= data
.uv_layers
.active
.data
[li
].uv
296 if data
.vertex_colors
:
297 colour
= data
.vertex_colors
.active
.data
[li
].color
299 key
= (round(co
[0],4),round(co
[1],4),round(co
[2],4),\
300 round(norm
[0],4),round(norm
[1],4),round(norm
[2],4),\
301 round(uv
[0],4),round(uv
[1],4),\
302 round(colour
[0],4),round(colour
[1],4),\
303 round(colour
[2],4),round(colour
[3],4))
306 indice_buffer
+= [boffa
[key
]]
308 index
= c_uint32(sm
.vertex_count
)
312 indice_buffer
+= [index
]
323 v
.colour
[0] = colour
[0]
324 v
.colour
[1] = colour
[1]
325 v
.colour
[2] = colour
[2]
326 v
.colour
[3] = colour
[3]
330 sm
.bbx
[0][i
] = min( sm
.bbx
[0][i
], v
.co
[i
] )
331 sm
.bbx
[1][i
] = max( sm
.bbx
[1][i
], v
.co
[i
] )
335 if sm
.vertex_count
== 0:
340 submesh_buffer
+= [sm
]
341 node
.submesh_count
+= 1
342 header
.submesh_count
+= 1
343 header
.vertex_count
+= sm
.vertex_count
344 header
.indice_count
+= sm
.indice_count
346 mesh_cache
[obj
.data
.name
] = node
347 node_buffer
+= [node
]
351 print( "Writing data" )
352 fpos
= sizeof(header
)
354 header
.node_offset
= fpos
355 fpos
+= sizeof(mdl_node
)*header
.node_count
357 header
.submesh_offset
= fpos
358 fpos
+= sizeof(mdl_submesh
)*header
.submesh_count
360 header
.material_offset
= fpos
361 fpos
+= sizeof(mdl_material
)*header
.material_count
363 header
.entdata_offset
= fpos
364 fpos
+= entdata_length
366 header
.vertex_offset
= fpos
367 fpos
+= sizeof(mdl_vert
)*header
.vertex_count
369 header
.indice_offset
= fpos
370 fpos
+= sizeof(c_uint32
)*header
.indice_count
372 header
.strings_offset
= fpos
373 fpos
+= len(strings_buffer
)
375 header
.file_length
= fpos
377 fp
= open(F
"/home/harry/Documents/carve/models/{name}.mdl", "wb")
378 fp
.write( bytearray( header
) )
380 for node
in node_buffer
:
381 fp
.write( bytearray(node
) )
382 for sm
in submesh_buffer
:
383 fp
.write( bytearray(sm
) )
384 for mat
in material_buffer
:
385 fp
.write( bytearray(mat
) )
386 for ed
in entdata_buffer
:
387 fp
.write( bytearray(ed
) )
388 for v
in vertex_buffer
:
389 fp
.write( bytearray(v
) )
390 for i
in indice_buffer
:
391 fp
.write( bytearray(i
) )
392 fp
.write( strings_buffer
)
395 print( F
"Completed {name}.mdl" )
398 # ------------------------------------------------------------------------------
400 cv_view_draw_handler
= None
401 cv_view_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
404 global cv_view_shader
405 cv_view_shader
.bind()
406 gpu
.state
.depth_mask_set(False)
407 gpu
.state
.line_width_set(2.0)
408 gpu
.state
.face_culling_set('BACK')
409 gpu
.state
.depth_test_set('NONE')
410 gpu
.state
.blend_set('ADDITIVE')
415 for obj
in bpy
.context
.collection
.objects
:
416 if obj
.cv_data
.classtype
== 'k_classtype_gate':
417 if obj
.cv_data
.target
!= None:
419 p1
= obj
.cv_data
.target
.location
420 verts
+= [(p0
[0],p0
[1],p0
[2])]
421 verts
+= [(p1
[0],p1
[1],p1
[2])]
422 colours
+= [(0,1,0,1.0),(1,0,0,1.0)]
423 elif obj
.cv_data
.classtype
== 'k_classtype_block':
424 a
= obj
.data
.cv_data
.v0
425 b
= obj
.data
.cv_data
.v1
428 vs
[0] = obj
.matrix_world
@ Vector((a
[0], a
[1], a
[2]))
429 vs
[1] = obj
.matrix_world
@ Vector((a
[0], b
[1], a
[2]))
430 vs
[2] = obj
.matrix_world
@ Vector((b
[0], b
[1], a
[2]))
431 vs
[3] = obj
.matrix_world
@ Vector((b
[0], a
[1], a
[2]))
432 vs
[4] = obj
.matrix_world
@ Vector((a
[0], a
[1], b
[2]))
433 vs
[5] = obj
.matrix_world
@ Vector((a
[0], b
[1], b
[2]))
434 vs
[6] = obj
.matrix_world
@ Vector((b
[0], b
[1], b
[2]))
435 vs
[7] = obj
.matrix_world
@ Vector((b
[0], a
[1], b
[2]))
437 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
438 (0,4),(1,5),(2,6),(3,7)]
443 verts
+= [(v0
[0],v0
[1],v0
[2])]
444 verts
+= [(v1
[0],v1
[1],v1
[2])]
445 colours
+= [(1,1,0,1),(1,1,0,1)]
447 lines
= batch_for_shader(\
448 cv_view_shader
, 'LINES', \
449 { "pos":verts
, "color":colours
})
451 lines
.draw( cv_view_shader
)
453 def cv_poll_target(scene
, obj
):
454 if obj
== bpy
.context
.active_object
:
456 if obj
.cv_data
.classtype
== 'k_classtype_none':
460 class CV_MESH_SETTINGS(bpy
.types
.PropertyGroup
):
461 v0
: bpy
.props
.FloatVectorProperty(name
="v0",size
=3)
462 v1
: bpy
.props
.FloatVectorProperty(name
="v1",size
=3)
463 v2
: bpy
.props
.FloatVectorProperty(name
="v2",size
=3)
464 v3
: bpy
.props
.FloatVectorProperty(name
="v3",size
=3)
466 class CV_OBJ_SETTINGS(bpy
.types
.PropertyGroup
):
467 uid
: bpy
.props
.IntProperty( name
="" )
469 target
: bpy
.props
.PointerProperty( type=bpy
.types
.Object
, name
="target", \
470 poll
=cv_poll_target
)
472 classtype
: bpy
.props
.EnumProperty(
475 ('k_classtype_none', "k_classtype_none", "", 0),
476 ('k_classtype_gate', "k_classtype_gate", "", 1),
477 ('k_classtype_block', "k_classtype_block", "", 2),
478 ('k_classtype_spawn', "k_classtype_spawn", "", 3),
479 ('k_classtype_water', "k_classtype_water", "", 4)
482 class CV_OBJ_PANEL(bpy
.types
.Panel
):
483 bl_label
="Entity Config"
484 bl_idname
="SCENE_PT_cv_entity"
485 bl_space_type
='PROPERTIES'
486 bl_region_type
='WINDOW'
490 active_object
= bpy
.context
.active_object
491 if active_object
== None: return
492 _
.layout
.prop( active_object
.cv_data
, "classtype" )
494 if active_object
.cv_data
.classtype
== 'k_classtype_gate':
495 _
.layout
.prop( active_object
.cv_data
, "target" )
496 elif active_object
.cv_data
.classtype
== 'k_classtype_block':
497 mesh
= active_object
.data
499 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
500 _
.layout
.prop( mesh
.cv_data
, "v0" )
501 _
.layout
.prop( mesh
.cv_data
, "v1" )
502 _
.layout
.prop( mesh
.cv_data
, "v2" )
503 _
.layout
.prop( mesh
.cv_data
, "v3" )
505 class CV_INTERFACE(bpy
.types
.Panel
):
506 bl_idname
= "VIEW3D_PT_carve"
508 bl_space_type
= 'VIEW_3D'
509 bl_region_type
= 'UI'
510 bl_category
= "Carve"
512 def draw(_
, context
):
514 layout
.operator( "carve.compile_all" )
516 class CV_COMPILE(bpy
.types
.Operator
):
517 bl_idname
="carve.compile_all"
518 bl_label
="Compile All"
520 def execute(_
,context
):
521 for col
in bpy
.data
.collections
["export"].children
:
522 write_model( col
.name
)
526 classes
= [CV_OBJ_SETTINGS
,CV_OBJ_PANEL
,CV_COMPILE
,CV_INTERFACE
,\
530 global cv_view_draw_handler
533 bpy
.utils
.register_class(c
)
535 bpy
.types
.Object
.cv_data
= bpy
.props
.PointerProperty(type=CV_OBJ_SETTINGS
)
536 bpy
.types
.Mesh
.cv_data
= bpy
.props
.PointerProperty(type=CV_MESH_SETTINGS
)
538 cv_view_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
539 cv_draw
,(),'WINDOW','POST_VIEW')
542 global cv_view_draw_handler
545 bpy
.utils
.unregister_class(c
)
547 bpy
.types
.SpaceView3D
.draw_handler_remove(cv_view_draw_handler
,'WINDOW')