3f2dd793d04ff12d7f8e9091a0189a9d3530cb79
4 from mathutils
import *
5 from gpu_extras
.batch
import batch_for_shader
8 "name":"Carve exporter",
9 "author": "Harry Godden (hgn)",
16 "category":"Import/Export",
19 class mdl_vert(Structure
):
21 _fields_
= [("co",c_float
*3),
25 ("weights",c_uint16
*4),
28 class mdl_submesh(Structure
):
30 _fields_
= [("indice_start",c_uint32
),
31 ("indice_count",c_uint32
),
32 ("vertex_start",c_uint32
),
33 ("vertex_count",c_uint32
),
34 ("bbx",(c_float
*3)*2),
35 ("material_id",c_uint32
)] # index into the material array
37 class mdl_material(Structure
):
39 _fields_
= [("pstr_name",c_uint32
)]
41 class mdl_node(Structure
):
43 _fields_
= [("co",c_float
*3),
46 ("submesh_start",c_uint32
),
47 ("submesh_count",c_uint32
),
48 ("classtype",c_uint32
),
51 ("pstr_name",c_uint32
)]
53 class mdl_header(Structure
):
55 _fields_
= [("identifier",c_uint32
),
57 ("file_length",c_uint32
),
58 ("vertex_count",c_uint32
),
59 ("vertex_offset",c_uint32
),
61 ("indice_count",c_uint32
),
62 ("indice_offset",c_uint32
),
64 ("submesh_count",c_uint32
),
65 ("submesh_offset",c_uint32
),
67 ("material_count",c_uint32
),
68 ("material_offset",c_uint32
),
70 ("node_count",c_uint32
),
71 ("node_offset",c_uint32
),
73 ("anim_count",c_uint32
),
74 ("anim_offset",c_uint32
),
76 ("strings_offset",c_uint32
),
77 ("entdata_offset",c_uint32
),
78 ("animdata_offset",c_uint32
)
81 class mdl_animation(Structure
):
83 _fields_
= [("pstr_name",c_uint32
),
88 class mdl_keyframe(Structure
):
90 _fields_
= [("co",c_float
*3),
95 # ==========================================
97 class classtype_gate(Structure
):
99 _fields_
= [("target",c_uint32
),
102 class classtype_block(Structure
):
104 _fields_
= [("bbx",(c_float
*3)*2)]
106 class classtype_spawn(Structure
):
108 _fields_
= [("temp",c_uint32
)]
110 class classtype_water(Structure
):
112 _fields_
= [("temp",c_uint32
)]
114 class classtype_car_path(Structure
):
116 _fields_
= [("target",c_uint32
),
117 ("target1",c_uint32
)]
119 class classtype_instance(Structure
):
121 _fields_
= [("pstr_file",c_uint32
)]
123 class classtype_capsule(Structure
):
125 _fields_
= [("height",c_float
),
128 class classtype_route_node(Structure
):
130 _fields_
= [("target",c_uint32
),
131 ("target1",c_uint32
)]
133 class classtype_route(Structure
):
135 _fields_
= [("pstr_name",c_uint32
),
136 ("id_start",c_uint32
),
137 ("colour",c_float
*3)]
139 class classtype_skin(Structure
):
141 _fields_
= [("skeleton",c_uint32
)]
143 class classtype_skeleton(Structure
):
145 _fields_
= [("channels",c_uint32
),
146 ("ik_count",c_uint32
),
147 ("anim_start",c_uint32
),
148 ("anim_count",c_uint32
)]
150 class classtype_bone(Structure
):
152 _fields_
= [("deform",c_uint32
)]
154 class classtype_ik_bone(Structure
):
156 _fields_
= [("deform",c_uint32
),
161 # ==============================================================================
163 def write_model(collection_name
):
164 print( F
"Model graph | Create mode '{collection_name}'" )
166 header
= mdl_header()
167 header
.identifier
= 0xABCD0000
169 header
.vertex_count
= 0
170 header
.indice_count
= 0
171 header
.submesh_count
= 0
172 header
.node_count
= 0
173 header
.material_count
= 0
174 header
.file_length
= 0
194 def emplace_string( s
):
195 nonlocal string_cache
, strings_buffer
197 if s
in string_cache
:
198 return string_cache
[s
]
200 string_cache
[s
] = len( strings_buffer
)
201 strings_buffer
+= (s
+'\0').encode('utf-8')
202 return string_cache
[s
]
204 def emplace_material( mat
):
205 nonlocal material_cache
, material_buffer
207 if mat
.name
in material_cache
:
208 return material_cache
[mat
.name
]
210 material_cache
[mat
.name
] = header
.material_count
211 dest
= mdl_material()
212 dest
.pstr_name
= emplace_string( mat
.name
)
213 material_buffer
+= [dest
]
215 header
.material_count
+= 1
216 return material_cache
[mat
.name
]
218 # Create root or empty node and materials
219 # this is to designate id 0 as 'NULL'
221 none_material
= c_uint32(69)
222 none_material
.name
= ""
223 emplace_material( none_material
)
236 root
.pstr_name
= emplace_string('')
237 root
.submesh_start
= 0
238 root
.submesh_count
= 0
241 node_buffer
+= [root
]
245 print( " assigning ids" )
246 collection
= bpy
.data
.collections
[collection_name
]
249 # ==========================================
251 header
.node_count
= 0
254 uid
= header
.node_count
255 header
.node_count
+= 1
258 print( " creating scene graph" )
262 graph
["children"] = []
263 graph
["uid"] = _uid()
264 graph
["parent"] = None
266 graph_lookup
= {} # object can lookup its graph def here
268 for obj
in collection
.all_objects
:
271 def _extend( p
, n
, d
):
275 tree
["children"] = []
281 if n
.type == 'ARMATURE':
282 tree
["bones"] = [None] # None is the root transform
285 def _extendb( p
, n
, d
):
290 btree
["uid"] = _uid()
291 btree
["children"] = []
294 tree
["bones"] += [n
.name
]
297 _extendb( btree
, c
, d
+1 )
299 for c
in tree
['obj'].pose
.bones
[n
.name
].constraints
:
301 btree
["target"] = c
.subtarget
302 btree
["pole"] = c
.pole_subtarget
303 tree
["ik_count"] += 1
305 btree
['deform'] = n
.use_deform
306 p
['children'] += [btree
]
308 for b
in n
.data
.bones
:
310 _extendb( tree
, b
, d
+1 )
312 for obj1
in n
.children
:
313 _extend( tree
, obj1
, d
+1 )
315 p
["children"] += [tree
]
316 graph_lookup
[n
] = tree
318 _extend( graph
, obj
, 1 )
322 for c
in p
['children']:
324 yield from _graph_iter(c
)
326 it
= _graph_iter(graph
)
328 root
.parent
= 0xffffffff
331 # ==============================================
332 it
= _graph_iter(graph
)
333 print( " compiling data" )
335 if 'obj' in node_def
:
336 obj
= node_def
['obj']
339 elif 'bone' in node_def
:
340 obj
= node_def
['bone']
342 objco
= obj
.head_local
344 depth
= node_def
['depth']
345 uid
= node_def
['uid']
348 node
.co
[0] = objco
[0]
349 node
.co
[1] = objco
[2]
350 node
.co
[2] = -objco
[1]
352 # Convert rotation quat to our space type
353 quat
= obj
.matrix_local
.to_quaternion()
360 node
.s
[0] = obj
.tail_local
[0] - node
.co
[0]
361 node
.s
[1] = obj
.tail_local
[2] - node
.co
[1]
362 node
.s
[2] = -obj
.tail_local
[1] - node
.co
[2]
364 node
.s
[0] = obj
.scale
[0]
365 node
.s
[1] = obj
.scale
[2]
366 node
.s
[2] = obj
.scale
[1]
368 node
.pstr_name
= emplace_string( obj
.name
)
370 if node_def
["parent"]:
371 node
.parent
= node_def
["parent"]["uid"]
374 if 'target' in node_def
:
375 classtype
= 'k_classtype_ik_bone'
377 classtype
= 'k_classtype_bone'
378 elif objt
== 'ARMATURE':
379 classtype
= 'k_classtype_skeleton'
381 classtype
= obj
.cv_data
.classtype
384 # =================================================================
387 # Dont use the cache if we have modifiers that affect the normals
395 for mod
in obj
.modifiers
:
396 if mod
.type == 'DATA_TRANSFER' or mod
.type == 'SHRINKWRAP':
397 can_use_cache
= False
399 if mod
.type == 'ARMATURE':
400 classtype
= 'k_classtype_skin'
401 armature_def
= graph_lookup
[mod
.object]
402 armature_def
['obj'].data
.pose_position
= 'REST'
404 if can_use_cache
and obj
.data
.name
in mesh_cache
:
405 ref
= mesh_cache
[obj
.data
.name
]
406 node
.submesh_start
= ref
.submesh_start
407 node
.submesh_count
= ref
.submesh_count
411 node
.submesh_start
= header
.submesh_count
412 node
.submesh_count
= 0
414 default_mat
= c_uint32(69)
415 default_mat
.name
= ""
417 dgraph
= bpy
.context
.evaluated_depsgraph_get()
418 data
= obj
.evaluated_get(dgraph
).data
419 data
.calc_loop_triangles()
420 data
.calc_normals_split()
422 mat_list
= data
.materials
if len(data
.materials
) > 0 else [default_mat
]
423 for material_id
, mat
in enumerate(mat_list
):
427 sm
.indice_start
= header
.indice_count
428 sm
.vertex_start
= header
.vertex_count
431 sm
.material_id
= emplace_material( mat
)
434 sm
.bbx
[0][i
] = 999999
435 sm
.bbx
[1][i
] = -999999
439 # Write the vertex / indice data
441 for tri_index
, tri
in enumerate(data
.loop_triangles
):
442 if tri
.material_index
!= material_id
:
446 vert
= data
.vertices
[tri
.vertices
[j
]]
448 vi
= data
.loops
[li
].vertex_index
451 norm
= data
.loops
[li
].normal
453 colour
= (255,255,255,255)
458 uv
= data
.uv_layers
.active
.data
[li
].uv
460 if data
.vertex_colors
:
461 colour
= data
.vertex_colors
.active
.data
[li
].color
462 colour
= (int(colour
[0]*255.0),\
463 int(colour
[1]*255.0),\
464 int(colour
[2]*255.0),\
465 int(colour
[3]*255.0))
470 weight_groups
= sorted( data
.vertices
[vi
].groups
, key
= \
471 lambda a
: a
.weight
, reverse
=True )
474 if len(weight_groups
) > ml
:
475 g
= weight_groups
[ml
]
476 name
= obj
.vertex_groups
[g
.group
].name
480 groups
[ml
] = armature_def
['bones'].index(name
)
483 if len(weight_groups
) > 0:
484 inv_norm
= (1.0/tot
) * 65535.0
486 weights
[ml
] = int( weights
[ml
] * inv_norm
)
487 weights
[ml
] = min( weights
[ml
], 65535 )
488 weights
[ml
] = max( weights
[ml
], 0 )
491 m
= float(10**TOLERENCE
)
493 key
= (int(co
[0]*m
+0.5),\
515 indice_buffer
+= [boffa
[key
]]
517 index
= c_uint32(sm
.vertex_count
)
521 indice_buffer
+= [index
]
532 v
.colour
[0] = colour
[0]
533 v
.colour
[1] = colour
[1]
534 v
.colour
[2] = colour
[2]
535 v
.colour
[3] = colour
[3]
536 v
.weights
[0] = weights
[0]
537 v
.weights
[1] = weights
[1]
538 v
.weights
[2] = weights
[2]
539 v
.weights
[3] = weights
[3]
540 v
.groups
[0] = groups
[0]
541 v
.groups
[1] = groups
[1]
542 v
.groups
[2] = groups
[2]
543 v
.groups
[3] = groups
[3]
548 sm
.bbx
[0][i
] = min( sm
.bbx
[0][i
], v
.co
[i
] )
549 sm
.bbx
[1][i
] = max( sm
.bbx
[1][i
], v
.co
[i
] )
553 if sm
.vertex_count
== 0:
558 submesh_buffer
+= [sm
]
559 node
.submesh_count
+= 1
560 header
.submesh_count
+= 1
561 header
.vertex_count
+= sm
.vertex_count
562 header
.indice_count
+= sm
.indice_count
564 mesh_cache
[obj
.data
.name
] = node
566 # Process entity data
567 # ==================================================================
568 node
.offset
= entdata_length
570 if classtype
!= 'k_classtype_none':
575 s000
= F
" [{uid: 3}/{header.node_count-1}]" + " |"*(depth
-1)
576 s001
= F
" L {obj.name}"
579 s004
= F
"{node.parent: 3}"
582 if classtype
== 'k_classtype_skin':
583 armature_def
['obj'].data
.pose_position
= 'POSE'
584 s005
= F
" [armature -> {armature_def['obj'].cv_data.uid}]"
586 scmp
= F
"{s002:<32} {s003:<22} {s004} {s005}"
589 if classtype
== 'k_classtype_INSTANCE' or \
590 classtype
== 'k_classtype_BONE' or \
591 classtype
== 'k_classtype_SKELETON' or \
592 classtype
== 'k_classtype_SKIN':
593 print( "ERROR: user classtype cannot be _INSTANCE or _BONE" )
597 elif classtype
== 'k_classtype_skin':
600 armature
= armature_def
['obj']
601 entdata_length
+= sizeof( classtype_skin
)
603 skin
= classtype_skin()
604 skin
.skeleton
= armature
.cv_data
.uid
605 entdata_buffer
+= [skin
]
607 elif classtype
== 'k_classtype_skeleton':
609 entdata_length
+= sizeof( classtype_skeleton
)
610 skeleton
= classtype_skeleton()
612 armature_def
= graph_lookup
[obj
]
614 bones
= armature_def
['bones']
615 skeleton
.channels
= len(bones
)
616 skeleton
.ik_count
= armature_def
["ik_count"]
618 if armature
.animation_data
:
619 previous_frame
= bpy
.context
.scene
.frame_current
620 previous_action
= armature
.animation_data
.action
622 skeleton
.anim_start
= len(anim_buffer
)
623 skeleton
.anim_count
= 0
625 for NLALayer
in obj
.animation_data
.nla_tracks
:
626 for NLAStrip
in NLALayer
.strips
:
628 for a
in bpy
.data
.actions
:
629 if a
.name
== NLAStrip
.name
:
630 armature
.animation_data
.action
= a
633 anim_start
= int(NLAStrip
.action_frame_start
)
634 anim_end
= int(NLAStrip
.action_frame_end
)
637 anim
= mdl_animation()
638 anim
.pstr_name
= emplace_string( NLAStrip
.action
.name
)
640 anim
.offset
= animdata_length
641 anim
.length
= anim_end
-anim_start
643 # Export the fucking keyframes
644 for frame
in range(anim_start
,anim_end
):
645 bpy
.context
.scene
.frame_set(frame
)
647 for bone_name
in bones
:
648 for pb
in armature
.pose
.bones
:
649 if pb
.name
== bone_name
:
650 rb
= armature
.data
.bones
[ bone_name
]
652 loc
, rot
, sca
= pb
.matrix_basis
.decompose()
656 final_pos
= Vector(( vp
[0], vp
[2], -vp
[1] ))
659 lc_m
= pb
.matrix_channel
.to_3x3()
660 if pb
.parent
is not None:
661 smtx
= pb
.parent
.matrix_channel
.to_3x3()
662 lc_m
= smtx
.inverted() @ lc_m
663 rq
= lc_m
.to_quaternion()
666 kf
.co
[0] = final_pos
[0]
667 kf
.co
[1] = final_pos
[1]
668 kf
.co
[2] = final_pos
[2]
680 animdata_buffer
+= [kf
]
681 animdata_length
+= sizeof(mdl_keyframe
)
684 anim_buffer
+= [anim
]
685 skeleton
.anim_count
+= 1
687 s000
= F
" [{uid: 3}/{header.node_count-1}]" + " |"*(depth
-1)
688 print( F
"{s000} | *anim: {NLAStrip.action.name}" )
690 bpy
.context
.scene
.frame_set( previous_frame
)
691 armature
.animation_data
.action
= previous_action
693 entdata_buffer
+= [skeleton
]
695 elif classtype
== 'k_classtype_bone':
697 entdata_length
+= sizeof( classtype_bone
)
699 bone
= classtype_bone()
700 bone
.deform
= node_def
['deform']
701 entdata_buffer
+= [bone
]
703 elif classtype
== 'k_classtype_ik_bone':
705 entdata_length
+= sizeof( classtype_ik_bone
)
707 ikbone
= classtype_ik_bone()
708 ikbone
.target
= armature_def
['bones'].index( node_def
['target'] )
709 ikbone
.pole
= armature_def
['bones'].index( node_def
['pole'] )
710 ikbone
.deform
= node_def
['deform']
712 entdata_buffer
+= [ikbone
]
714 elif classtype
== 'k_classtype_gate':
716 entdata_length
+= sizeof( classtype_gate
)
718 gate
= classtype_gate()
720 if obj
.cv_data
.target
!= None:
721 gate
.target
= obj
.cv_data
.target
.cv_data
.uid
723 if obj
.type == 'MESH':
724 gate
.dims
[0] = obj
.data
.cv_data
.v0
[0]
725 gate
.dims
[1] = obj
.data
.cv_data
.v0
[1]
726 gate
.dims
[2] = obj
.data
.cv_data
.v0
[2]
728 gate
.dims
[0] = obj
.cv_data
.v0
[0]
729 gate
.dims
[1] = obj
.cv_data
.v0
[1]
730 gate
.dims
[2] = obj
.cv_data
.v0
[2]
732 entdata_buffer
+= [gate
]
734 elif classtype
== 'k_classtype_block':
736 entdata_length
+= sizeof( classtype_block
)
738 source
= obj
.data
.cv_data
740 block
= classtype_block()
741 block
.bbx
[0][0] = source
.v0
[0]
742 block
.bbx
[0][1] = source
.v0
[2]
743 block
.bbx
[0][2] = -source
.v1
[1]
745 block
.bbx
[1][0] = source
.v1
[0]
746 block
.bbx
[1][1] = source
.v1
[2]
747 block
.bbx
[1][2] = -source
.v0
[1]
748 entdata_buffer
+= [block
]
750 elif classtype
== 'k_classtype_spawn':
753 elif classtype
== 'k_classtype_water':
756 elif classtype
== 'k_classtype_car_path':
758 entdata_length
+= sizeof( classtype_car_path
)
760 pn
= classtype_car_path()
764 if obj
.cv_data
.target
!= None:
765 pn
.target
= obj
.cv_data
.target
.cv_data
.uid
766 if obj
.cv_data
.target1
!= None:
767 pn
.target1
= obj
.cv_data
.target1
.cv_data
.uid
769 entdata_buffer
+= [pn
]
771 elif obj
.is_instancer
:
772 target
= obj
.instance_collection
775 entdata_length
+= sizeof( classtype_instance
)
777 inst
= classtype_instance()
778 inst
.pstr_file
= emplace_string( F
"models/{target.name}.mdl" )
779 entdata_buffer
+= [inst
]
781 elif classtype
== 'k_classtype_capsule':
784 elif classtype
== 'k_classtype_route_node':
786 entdata_length
+= sizeof( classtype_route_node
)
788 rn
= classtype_route_node()
789 if obj
.cv_data
.target
!= None:
790 rn
.target
= obj
.cv_data
.target
.cv_data
.uid
791 if obj
.cv_data
.target1
!= None:
792 rn
.target1
= obj
.cv_data
.target1
.cv_data
.uid
794 entdata_buffer
+= [rn
]
796 elif classtype
== 'k_classtype_route':
798 entdata_length
+= sizeof( classtype_route
)
799 r
= classtype_route()
800 r
.pstr_name
= emplace_string("not-implemented")
801 r
.colour
[0] = obj
.cv_data
.colour
[0]
802 r
.colour
[1] = obj
.cv_data
.colour
[1]
803 r
.colour
[2] = obj
.cv_data
.colour
[2]
805 if obj
.cv_data
.target
!= None:
806 r
.id_start
= obj
.cv_data
.target
.cv_data
.uid
808 entdata_buffer
+= [r
]
810 # classtype == 'k_classtype_none':
815 node_buffer
+= [node
]
819 header
.anim_count
= len(anim_buffer
)
821 print( "Writing data" )
822 fpos
= sizeof(header
)
824 print( F
"Nodes: {header.node_count}" )
825 header
.node_offset
= fpos
826 fpos
+= sizeof(mdl_node
)*header
.node_count
828 print( F
"Submeshes: {header.submesh_count}" )
829 header
.submesh_offset
= fpos
830 fpos
+= sizeof(mdl_submesh
)*header
.submesh_count
832 print( F
"Materials: {header.material_count}" )
833 header
.material_offset
= fpos
834 fpos
+= sizeof(mdl_material
)*header
.material_count
836 print( F
"Animation count: {header.anim_count}" )
837 header
.anim_offset
= fpos
838 fpos
+= sizeof(mdl_animation
)*header
.anim_count
840 print( F
"Entdata length: {entdata_length}" )
841 header
.entdata_offset
= fpos
842 fpos
+= entdata_length
844 print( F
"Vertex count: {header.vertex_count}" )
845 header
.vertex_offset
= fpos
846 fpos
+= sizeof(mdl_vert
)*header
.vertex_count
848 print( F
"Indice count: {header.indice_count}" )
849 header
.indice_offset
= fpos
850 fpos
+= sizeof(c_uint32
)*header
.indice_count
852 print( F
"Keyframe count: {animdata_length}" )
853 header
.animdata_offset
= fpos
854 fpos
+= animdata_length
856 print( F
"Strings length: {len(strings_buffer)}" )
857 header
.strings_offset
= fpos
858 fpos
+= len(strings_buffer
)
860 header
.file_length
= fpos
862 path
= F
"/home/harry/Documents/carve/models_src/{collection_name}.mdl"
863 fp
= open( path
, "wb" )
865 fp
.write( bytearray( header
) )
867 for node
in node_buffer
:
868 fp
.write( bytearray(node
) )
869 for sm
in submesh_buffer
:
870 fp
.write( bytearray(sm
) )
871 for mat
in material_buffer
:
872 fp
.write( bytearray(mat
) )
873 for a
in anim_buffer
:
874 fp
.write( bytearray(a
) )
875 for ed
in entdata_buffer
:
876 fp
.write( bytearray(ed
) )
877 for v
in vertex_buffer
:
878 fp
.write( bytearray(v
) )
879 for i
in indice_buffer
:
880 fp
.write( bytearray(i
) )
881 for kf
in animdata_buffer
:
882 fp
.write( bytearray(kf
) )
884 fp
.write( strings_buffer
)
887 print( F
"Completed {collection_name}.mdl" )
890 # ------------------------------------------------------------------------------
892 cv_view_draw_handler
= None
893 cv_view_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
896 global cv_view_shader
897 cv_view_shader
.bind()
898 gpu
.state
.depth_mask_set(False)
899 gpu
.state
.line_width_set(2.0)
900 gpu
.state
.face_culling_set('BACK')
901 gpu
.state
.depth_test_set('LESS')
902 gpu
.state
.blend_set('NONE')
907 #def drawbezier(p0,h0,p1,h1,c0,c1):
908 # nonlocal verts, colours
912 # colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
915 # colours += [(1.0,1.0,1,1),(1,1,1,1)]
918 # for i in range(10):
924 # p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
925 # verts += [(last[0],last[1],last[2])]
926 # verts += [(p[0],p[1],p[2])]
927 # colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
932 def drawbhandle(obj
, direction
, colour
):
933 nonlocal verts
, colours
935 h0
= obj
.matrix_world
@ Vector((0,direction
,0))
938 colours
+= [colour
,colour
]
940 def drawbezier(p0
,h0
,p1
,h1
,c0
,c1
):
941 nonlocal verts
, colours
950 p
=ttt
*p1
+(3*tt
-3*ttt
)*h1
+(3*ttt
-6*tt
+3*t
)*h0
+(3*tt
-ttt
-3*t
+1)*p0
951 verts
+= [(last
[0],last
[1],last
[2])]
952 verts
+= [(p
[0],p
[1],p
[2])]
953 colours
+= [c0
*a0
+c1
*(1-a0
),c0
*a0
+c1
*(1-a0
)]
956 def drawsbpath(o0
,o1
,c0
,c1
,s0
,s1
):
957 nonlocal course_count
959 offs
= ((course_count
% 2)*2-1) * course_count
* 0.02
961 p0
= o0
.matrix_world
@ Vector((offs
, 0,0))
962 h0
= o0
.matrix_world
@ Vector((offs
, s0
,0))
963 p1
= o1
.matrix_world
@ Vector((offs
, 0,0))
964 h1
= o1
.matrix_world
@ Vector((offs
,-s1
,0))
965 drawbezier(p0
,h0
,p1
,h1
,c0
,c1
)
967 def drawbpath(o0
,o1
,c0
,c1
):
968 drawsbpath(o0
,o1
,c0
,c1
,1.0,1.0)
970 def drawbline(p0
,p1
,c0
,c1
):
971 nonlocal verts
, colours
975 for obj
in bpy
.context
.collection
.objects
:
977 if obj
.cv_data
.classtype
== 'k_classtype_gate':
978 if obj
.type == 'MESH':
979 dims
= obj
.data
.cv_data
.v0
981 dims
= obj
.cv_data
.v0
984 c
= Vector((0,0,dims
[2]))
986 vs
[0] = obj
.matrix_world
@ Vector((-dims
[0],0.0,-dims
[1]+dims
[2]))
987 vs
[1] = obj
.matrix_world
@ Vector((-dims
[0],0.0, dims
[1]+dims
[2]))
988 vs
[2] = obj
.matrix_world
@ Vector(( dims
[0],0.0, dims
[1]+dims
[2]))
989 vs
[3] = obj
.matrix_world
@ Vector(( dims
[0],0.0,-dims
[1]+dims
[2]))
990 vs
[4] = obj
.matrix_world
@ (c
+Vector((-1,0,-2)))
991 vs
[5] = obj
.matrix_world
@ (c
+Vector((-1,0, 2)))
992 vs
[6] = obj
.matrix_world
@ (c
+Vector(( 1,0, 2)))
993 vs
[7] = obj
.matrix_world
@ (c
+Vector((-1,0, 0)))
994 vs
[8] = obj
.matrix_world
@ (c
+Vector(( 1,0, 0)))
996 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
1001 verts
+= [(v0
[0],v0
[1],v0
[2])]
1002 verts
+= [(v1
[0],v1
[1],v1
[2])]
1003 colours
+= [(1,1,0,1),(1,1,0,1)]
1005 sw
= (0.4,0.4,0.4,0.2)
1006 if obj
.cv_data
.target
!= None:
1007 drawbline( obj
.location
, obj
.cv_data
.target
.location
, sw
,sw
)
1009 elif obj
.cv_data
.classtype
== 'k_classtype_route_node':
1010 sw
= Vector((0.4,0.4,0.4,0.2))
1011 sw2
= Vector((1.5,0.2,0.2,0.0))
1012 if obj
.cv_data
.target
!= None:
1013 drawbpath( obj
, obj
.cv_data
.target
, sw
, sw
)
1014 if obj
.cv_data
.target1
!= None:
1015 drawbpath( obj
, obj
.cv_data
.target1
, sw
, sw
)
1017 drawbhandle( obj
, 1.0, (0.8,0.8,0.8,1.0) )
1018 drawbhandle( obj
, -1.0, (0.4,0.4,0.4,1.0) )
1020 p1
= obj
.location
+ \
1021 obj
.matrix_world
.to_quaternion() @ Vector((0,0,-6+1.5))
1022 drawbline( obj
.location
, p1
, sw
,sw2
)
1025 elif obj
.cv_data
.classtype
== 'k_classtype_block':
1026 a
= obj
.data
.cv_data
.v0
1027 b
= obj
.data
.cv_data
.v1
1030 vs
[0] = obj
.matrix_world
@ Vector((a
[0], a
[1], a
[2]))
1031 vs
[1] = obj
.matrix_world
@ Vector((a
[0], b
[1], a
[2]))
1032 vs
[2] = obj
.matrix_world
@ Vector((b
[0], b
[1], a
[2]))
1033 vs
[3] = obj
.matrix_world
@ Vector((b
[0], a
[1], a
[2]))
1034 vs
[4] = obj
.matrix_world
@ Vector((a
[0], a
[1], b
[2]))
1035 vs
[5] = obj
.matrix_world
@ Vector((a
[0], b
[1], b
[2]))
1036 vs
[6] = obj
.matrix_world
@ Vector((b
[0], b
[1], b
[2]))
1037 vs
[7] = obj
.matrix_world
@ Vector((b
[0], a
[1], b
[2]))
1039 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1040 (0,4),(1,5),(2,6),(3,7)]
1045 verts
+= [(v0
[0],v0
[1],v0
[2])]
1046 verts
+= [(v1
[0],v1
[1],v1
[2])]
1047 colours
+= [(1,1,0,1),(1,1,0,1)]
1049 elif obj
.cv_data
.classtype
== 'k_classtype_capsule':
1050 h
= obj
.data
.cv_data
.v0
[0]
1051 r
= obj
.data
.cv_data
.v0
[1]
1054 vs
[0] = obj
.matrix_world
@ Vector((0.0,0.0, h
*0.5 ))
1055 vs
[1] = obj
.matrix_world
@ Vector((0.0,0.0,-h
*0.5 ))
1056 vs
[2] = obj
.matrix_world
@ Vector(( r
,0.0, h
*0.5-r
))
1057 vs
[3] = obj
.matrix_world
@ Vector(( -r
,0.0, h
*0.5-r
))
1058 vs
[4] = obj
.matrix_world
@ Vector(( r
,0.0,-h
*0.5+r
))
1059 vs
[5] = obj
.matrix_world
@ Vector(( -r
,0.0,-h
*0.5+r
))
1060 vs
[6] = obj
.matrix_world
@ Vector((0.0, r
, h
*0.5-r
))
1061 vs
[7] = obj
.matrix_world
@ Vector((0.0,-r
, h
*0.5-r
))
1062 vs
[8] = obj
.matrix_world
@ Vector((0.0, r
,-h
*0.5+r
))
1063 vs
[9] = obj
.matrix_world
@ Vector((0.0,-r
,-h
*0.5+r
))
1065 indices
= [(0,1),(2,3),(4,5),(6,7),(8,9)]
1070 verts
+= [(v0
[0],v0
[1],v0
[2])]
1071 verts
+= [(v1
[0],v1
[1],v1
[2])]
1072 colours
+= [(0.5,1,0,1),(0.5,1,0,1)]
1074 elif obj
.cv_data
.classtype
== 'k_classtype_spawn':
1076 vs
[0] = obj
.matrix_world
@ Vector((0,0,0))
1077 vs
[1] = obj
.matrix_world
@ Vector((0,2,0))
1078 vs
[2] = obj
.matrix_world
@ Vector((0.5,1,0))
1079 vs
[3] = obj
.matrix_world
@ Vector((-0.5,1,0))
1080 indices
= [(0,1),(1,2),(1,3)]
1084 verts
+= [(v0
[0],v0
[1],v0
[2])]
1085 verts
+= [(v1
[0],v1
[1],v1
[2])]
1086 colours
+= [(0,1,1,1),(0,1,1,1)]
1088 elif obj
.cv_data
.classtype
== 'k_classtype_route':
1090 vs
[0] = obj
.location
1091 vs
[1] = obj
.cv_data
.target
.location
1096 verts
+= [(v0
[0],v0
[1],v0
[2])]
1097 verts
+= [(v1
[0],v1
[1],v1
[2])]
1098 colours
+= [(0,1,1,1),(0,1,1,1)]
1102 stack
[0] = obj
.cv_data
.target
1104 loop_complete
= False
1107 if stack_i
[si
-1] == 2:
1111 if si
== 0: # Loop failed to complete
1116 targets
= [None,None]
1117 targets
[0] = node
.cv_data
.target
1119 if node
.cv_data
.classtype
== 'k_classtype_route_node':
1120 targets
[1] = node
.cv_data
.target1
1122 nextnode
= targets
[stack_i
[si
-1]]
1125 if nextnode
!= None: # branch
1126 if nextnode
== stack
[0]: # Loop completed
1127 loop_complete
= True
1131 for sj
in range(si
):
1132 if stack
[sj
] == nextnode
: # invalidated path
1138 stack
[si
] = nextnode
1143 cc
= Vector((obj
.cv_data
.colour
[0],\
1144 obj
.cv_data
.colour
[1],\
1145 obj
.cv_data
.colour
[2],\
1148 for sj
in range(si
):
1151 if stack
[sj
].cv_data
.classtype
== 'k_classtype_gate' and \
1152 stack
[sk
].cv_data
.classtype
== 'k_classtype_gate':
1153 dist
= (stack
[sj
].location
-stack
[sk
].location
).magnitude
1154 drawsbpath( stack
[sj
], stack
[sk
], cc
*0.4, cc
, dist
, dist
)
1157 drawbpath( stack
[sj
], stack
[sk
], cc
, cc
)
1161 elif obj
.cv_data
.classtype
== 'k_classtype_car_path':
1162 v0
= obj
.matrix_world
.to_quaternion() @ Vector((0,1,0))
1163 c0
= Vector((v0
.x
*0.5+0.5, v0
.y
*0.5+0.5, 0.0, 1.0))
1164 drawbhandle( obj
, 1.0, (0.9,0.9,0.9,1.0) )
1166 if obj
.cv_data
.target
!= None:
1167 v1
= obj
.cv_data
.target
.matrix_world
.to_quaternion()@Vector((0,1,0))
1168 c1
= Vector((v1
.x
*0.5+0.5, v1
.y
*0.5+0.5, 0.0, 1.0))
1170 drawbhandle( obj
.cv_data
.target
, -1.0, (0.5,0.5,0.5,1.0) )
1171 drawbpath( obj
, obj
.cv_data
.target
, c0
, c1
)
1173 if obj
.cv_data
.target1
!= None:
1174 v1
= obj
.cv_data
.target1
.matrix_world
.to_quaternion()@Vector((0,1,0))
1175 c1
= Vector((v1
.x
*0.5+0.5, v1
.y
*0.5+0.5, 0.0, 1.0))
1177 drawbhandle( obj
.cv_data
.target1
, -1.0, (0.5,0.5,0.5,1.0) )
1178 drawbpath( obj
, obj
.cv_data
.target1
, c0
, c1
)
1180 lines
= batch_for_shader(\
1181 cv_view_shader
, 'LINES', \
1182 { "pos":verts
, "color":colours
})
1184 lines
.draw( cv_view_shader
)
1186 def cv_poll_target(scene
, obj
):
1187 if obj
== bpy
.context
.active_object
:
1189 if obj
.cv_data
.classtype
== 'k_classtype_none':
1193 class CV_MESH_SETTINGS(bpy
.types
.PropertyGroup
):
1194 v0
: bpy
.props
.FloatVectorProperty(name
="v0",size
=3)
1195 v1
: bpy
.props
.FloatVectorProperty(name
="v1",size
=3)
1196 v2
: bpy
.props
.FloatVectorProperty(name
="v2",size
=3)
1197 v3
: bpy
.props
.FloatVectorProperty(name
="v3",size
=3)
1199 class CV_OBJ_SETTINGS(bpy
.types
.PropertyGroup
):
1200 uid
: bpy
.props
.IntProperty( name
="" )
1202 target
: bpy
.props
.PointerProperty( type=bpy
.types
.Object
, name
="target", \
1203 poll
=cv_poll_target
)
1204 target1
: bpy
.props
.PointerProperty( type=bpy
.types
.Object
, name
="target1", \
1205 poll
=cv_poll_target
)
1207 colour
: bpy
.props
.FloatVectorProperty(name
="colour",subtype
='COLOR',\
1210 classtype
: bpy
.props
.EnumProperty(
1213 ('k_classtype_none', "k_classtype_none", "", 0),
1214 ('k_classtype_gate', "k_classtype_gate", "", 1),
1215 ('k_classtype_block', "k_classtype_block", "", 2),
1216 ('k_classtype_spawn', "k_classtype_spawn", "", 3),
1217 ('k_classtype_water', "k_classtype_water", "", 4),
1218 ('k_classtype_car_path', "k_classtype_car_path", "", 5),
1219 ('k_classtype_INSTANCE', "","", 6 ),
1220 ('k_classtype_capsule', "k_classtype_capsule", "", 7 ),
1221 ('k_classtype_route_node', "k_classtype_route_node", "", 8 ),
1222 ('k_classtype_route', "k_classtype_route", "", 9 ),
1223 ('k_classtype_bone',"k_classtype_bone","",10),
1224 ('k_classtype_SKELETON', "","", 11 ),
1225 ('k_classtype_SKIN',"","",12)
1228 class CV_SCENE_SETTINGS(bpy
.types
.PropertyGroup
):
1229 use_hidden
: bpy
.props
.BoolProperty( name
="use hidden", default
=False )
1231 class CV_OBJ_PANEL(bpy
.types
.Panel
):
1232 bl_label
="Entity Config"
1233 bl_idname
="SCENE_PT_cv_entity"
1234 bl_space_type
='PROPERTIES'
1235 bl_region_type
='WINDOW'
1238 def draw(_
,context
):
1239 active_object
= bpy
.context
.active_object
1240 if active_object
== None: return
1241 _
.layout
.prop( active_object
.cv_data
, "classtype" )
1243 if active_object
.cv_data
.classtype
== 'k_classtype_gate':
1244 _
.layout
.prop( active_object
.cv_data
, "target" )
1246 mesh
= active_object
.data
1247 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1248 _
.layout
.prop( mesh
.cv_data
, "v0" )
1250 elif active_object
.cv_data
.classtype
== 'k_classtype_car_path' or \
1251 active_object
.cv_data
.classtype
== 'k_classtype_route_node':
1252 _
.layout
.prop( active_object
.cv_data
, "target" )
1253 _
.layout
.prop( active_object
.cv_data
, "target1" )
1255 elif active_object
.cv_data
.classtype
== 'k_classtype_route':
1256 _
.layout
.prop( active_object
.cv_data
, "target" )
1257 _
.layout
.prop( active_object
.cv_data
, "colour" )
1259 elif active_object
.cv_data
.classtype
== 'k_classtype_block':
1260 mesh
= active_object
.data
1262 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1263 _
.layout
.prop( mesh
.cv_data
, "v0" )
1264 _
.layout
.prop( mesh
.cv_data
, "v1" )
1265 _
.layout
.prop( mesh
.cv_data
, "v2" )
1266 _
.layout
.prop( mesh
.cv_data
, "v3" )
1267 elif active_object
.cv_data
.classtype
== 'k_classtype_capsule':
1268 mesh
= active_object
.data
1269 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1270 _
.layout
.prop( mesh
.cv_data
, "v0" )
1272 class CV_INTERFACE(bpy
.types
.Panel
):
1273 bl_idname
= "VIEW3D_PT_carve"
1275 bl_space_type
= 'VIEW_3D'
1276 bl_region_type
= 'UI'
1277 bl_category
= "Carve"
1279 def draw(_
, context
):
1281 layout
.prop( context
.scene
.cv_data
, "use_hidden")
1282 layout
.operator( "carve.compile_all" )
1285 view_layer
= bpy
.context
.view_layer
1286 for col
in view_layer
.layer_collection
.children
["export"].children
:
1287 if not col
.hide_viewport
or bpy
.context
.scene
.cv_data
.use_hidden
:
1288 write_model( col
.name
)
1290 class CV_COMPILE(bpy
.types
.Operator
):
1291 bl_idname
="carve.compile_all"
1292 bl_label
="Compile All"
1294 def execute(_
,context
):
1296 #cProfile.runctx("test_compile()",globals(),locals(),sort=1)
1297 #for col in bpy.data.collections["export"].children:
1298 # write_model( col.name )
1302 classes
= [CV_OBJ_SETTINGS
,CV_OBJ_PANEL
,CV_COMPILE
,CV_INTERFACE
,\
1303 CV_MESH_SETTINGS
, CV_SCENE_SETTINGS
]
1306 global cv_view_draw_handler
1309 bpy
.utils
.register_class(c
)
1311 bpy
.types
.Object
.cv_data
= bpy
.props
.PointerProperty(type=CV_OBJ_SETTINGS
)
1312 bpy
.types
.Mesh
.cv_data
= bpy
.props
.PointerProperty(type=CV_MESH_SETTINGS
)
1313 bpy
.types
.Scene
.cv_data
= bpy
.props
.PointerProperty(type=CV_SCENE_SETTINGS
)
1315 cv_view_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
1316 cv_draw
,(),'WINDOW','POST_VIEW')
1319 global cv_view_draw_handler
1322 bpy
.utils
.unregister_class(c
)
1324 bpy
.types
.SpaceView3D
.draw_handler_remove(cv_view_draw_handler
,'WINDOW')