a7e714c2fae4a76661b19f7a9fcfa0e1f9412be1
2 # Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
8 from mathutils
import *
9 from gpu_extras
.batch
import batch_for_shader
12 "name":"Skate Rift model compiler",
13 "author": "Harry Godden (hgn)",
20 "category":"Import/Export",
23 class mdl_vert(Structure
): # 48 bytes. Quite large. Could compress
24 #{ # the normals and uvs to i16s. Not an
25 _pack_
= 1 # real issue, yet.
26 _fields_
= [("co",c_float
*3),
30 ("weights",c_uint16
*4),
34 class mdl_submesh(Structure
):
37 _fields_
= [("indice_start",c_uint32
),
38 ("indice_count",c_uint32
),
39 ("vertex_start",c_uint32
),
40 ("vertex_count",c_uint32
),
41 ("bbx",(c_float
*3)*2),
42 ("material_id",c_uint32
)] # index into the material array
45 class mdl_material(Structure
):
48 _fields_
= [("pstr_name",c_uint32
)]
51 class mdl_node(Structure
):
54 _fields_
= [("co",c_float
*3),
57 ("sub_uid",c_uint32
), # dont use
58 ("submesh_start",c_uint32
),
59 ("submesh_count",c_uint32
),
60 ("classtype",c_uint32
),
63 ("pstr_name",c_uint32
)]
66 class mdl_header(Structure
):
69 _fields_
= [("identifier",c_uint32
),
71 ("file_length",c_uint32
),
74 ("node_count",c_uint32
),
75 ("node_offset",c_uint32
),
77 ("submesh_count",c_uint32
),
78 ("submesh_offset",c_uint32
),
80 ("material_count",c_uint32
),
81 ("material_offset",c_uint32
),
83 ("anim_count",c_uint32
),
84 ("anim_offset",c_uint32
),
86 ("entdata_size",c_uint32
),
87 ("entdata_offset",c_uint32
),
89 ("strings_size",c_uint32
),
90 ("strings_offset",c_uint32
),
92 ("keyframe_count",c_uint32
),
93 ("keyframe_offset",c_uint32
),
95 ("vertex_count",c_uint32
),
96 ("vertex_offset",c_uint32
),
98 ("indice_count",c_uint32
),
99 ("indice_offset",c_uint32
),]
102 class mdl_animation(Structure
):
105 _fields_
= [("pstr_name",c_uint32
),
111 class mdl_keyframe(Structure
):
114 _fields_
= [("co",c_float
*3),
120 # ==========================================
122 # ctypes _fields_ defines the data which is filled in by:
123 # def encode_obj( _, node, node_def ):
125 # gizmos get drawn into the viewport via:
127 # def editor_interface( object ):
130 class classtype_gate(Structure
):
133 _fields_
= [("target",c_uint32
),
136 def encode_obj(_
, node
,node_def
):
140 obj
= node_def
['obj']
142 if obj
.cv_data
.target
!= None:
143 _
.target
= obj
.cv_data
.target
.cv_data
.uid
145 if obj
.type == 'MESH':
147 _
.dims
[0] = obj
.data
.cv_data
.v0
[0]
148 _
.dims
[1] = obj
.data
.cv_data
.v0
[1]
149 _
.dims
[2] = obj
.data
.cv_data
.v0
[2]
153 _
.dims
[0] = obj
.cv_data
.v0
[0]
154 _
.dims
[1] = obj
.cv_data
.v0
[1]
155 _
.dims
[2] = obj
.cv_data
.v0
[2]
160 class classtype_spawn(Structure
):
163 _fields_
= [("pstr_alias",c_uint32
)]
165 def encode_obj(_
, node
,node_def
):
168 _
.pstr_alias
= encoder_process_pstr( node_def
['obj'].cv_data
.strp
)
172 class classtype_water(Structure
):
175 _fields_
= [("temp",c_uint32
)]
177 def encode_obj(_
, node
,node_def
):
184 class classtype_route_node(Structure
):
187 _fields_
= [("target",c_uint32
),
188 ("target1",c_uint32
)]
190 def encode_obj(_
, node
,node_def
):
193 obj
= node_def
['obj']
195 if obj
.cv_data
.target
!= None:
196 _
.target
= obj
.cv_data
.target
.cv_data
.uid
197 if obj
.cv_data
.target1
!= None:
198 _
.target1
= obj
.cv_data
.target1
.cv_data
.uid
202 class classtype_route(Structure
):
205 _fields_
= [("id_start",c_uint32
),
206 ("colour",c_float
*3)]
208 def encode_obj(_
, node
,node_def
):
211 obj
= node_def
['obj']
213 _
.colour
[0] = obj
.cv_data
.colour
[0]
214 _
.colour
[1] = obj
.cv_data
.colour
[1]
215 _
.colour
[2] = obj
.cv_data
.colour
[2]
217 if obj
.cv_data
.target
!= None:
218 _
.id_start
= obj
.cv_data
.target
.cv_data
.uid
222 class classtype_skin(Structure
):
225 _fields_
= [("skeleton",c_uint32
)]
227 def encode_obj(_
, node
,node_def
):
231 armature_def
= node_def
['linked_armature']
232 _
.skeleton
= armature_def
['obj'].cv_data
.uid
236 class classtype_skeleton(Structure
):
239 _fields_
= [("channels",c_uint32
),
240 ("ik_count",c_uint32
),
241 ("collider_count",c_uint32
),
242 ("anim_start",c_uint32
),
243 ("anim_count",c_uint32
)]
245 def encode_obj(_
, node
,node_def
):
249 _
.channels
= len( node_def
['bones'] )
250 _
.ik_count
= node_def
['ik_count']
251 _
.collider_count
= node_def
['collider_count']
252 _
.anim_start
= node_def
['anim_start']
253 _
.anim_count
= node_def
['anim_count']
257 class classtype_bone(Structure
):
260 _fields_
= [("deform",c_uint32
),
261 ("ik_target",c_uint32
),
262 ("ik_pole",c_uint32
),
263 ("collider",c_uint32
),
264 ("use_limits",c_uint32
),
265 ("angle_limits",(c_float
*3)*2),
266 ("hitbox",(c_float
*3)*2)]
268 def encode_obj(_
, node
,node_def
):
272 armature_def
= node_def
['linked_armature']
273 obj
= node_def
['bone']
275 _
.deform
= node_def
['deform']
277 if 'ik_target' in node_def
:
279 _
.ik_target
= armature_def
['bones'].index( node_def
['ik_target'] )
280 _
.ik_pole
= armature_def
['bones'].index( node_def
['ik_pole'] )
285 if obj
.cv_data
.collider
:
288 _
.hitbox
[0][0] = obj
.cv_data
.v0
[0]
289 _
.hitbox
[0][1] = obj
.cv_data
.v0
[2]
290 _
.hitbox
[0][2] = -obj
.cv_data
.v1
[1]
291 _
.hitbox
[1][0] = obj
.cv_data
.v1
[0]
292 _
.hitbox
[1][1] = obj
.cv_data
.v1
[2]
293 _
.hitbox
[1][2] = -obj
.cv_data
.v0
[1]
299 _
.angle_limits
[0][0] = obj
.cv_data
.mins
[0]
300 _
.angle_limits
[0][1] = obj
.cv_data
.mins
[2]
301 _
.angle_limits
[0][2] = -obj
.cv_data
.maxs
[1]
302 _
.angle_limits
[1][0] = obj
.cv_data
.maxs
[0]
303 _
.angle_limits
[1][1] = obj
.cv_data
.maxs
[2]
304 _
.angle_limits
[1][2] = -obj
.cv_data
.mins
[1]
312 class classtype_achievement_box(Structure
):
315 _fields_
= [("pstr_name",c_uint32
),
316 ("trigger",c_uint32
)]
318 def encode_obj(_
, node
,node_def
):
324 class classtype_audio(Structure
):
327 _fields_
= [("pstr_file",c_uint32
),
331 def encode_obj(_
, node
,node_def
):
335 obj
= node_def
['obj']
337 _
.pstr_file
= encoder_process_pstr( obj
.cv_data
.strp
)
338 _
.flags
= obj
.cv_data
.intp
339 _
.volume
= obj
.cv_data
.fltp
343 def editor_interface(yada
):
349 def draw_scene_helpers(yada
):
356 # Current encoder state
369 # The actual file header
371 'header': mdl_header(),
373 # Compiled data chunks (each can be read optionally by the client)
377 #1---------------------------------
378 'node': [], # Metadata 'chunk'
382 'entdata': bytearray(), # variable width
383 'strings': bytearray(), # .
384 #2---------------------------------
385 'keyframe': [], # Animations
386 #3---------------------------------
387 'vertex': [], # Mesh data
391 # All objects of the model in their final heirachy
397 # Allows us to reuse definitions
401 'material_cache': {},
404 g_encoder
['header'].identifier
= 0xABCD0000
405 g_encoder
['header'].version
= 1
407 # Add fake NoneID material
409 none_material
= c_uint32(1234)
410 none_material
.name
= ""
411 encoder_process_material( none_material
)
426 root
.pstr_name
= encoder_process_pstr('')
427 root
.submesh_start
= 0
428 root
.submesh_count
= 0
431 root
.parent
= 0xffffffff
433 g_encoder
['data']['node'] += [root
]
437 # fill with 0x00 until a multiple of align. Returns how many bytes it added
439 def bytearray_align_to( buffer, align
, offset
=0 ):
443 while ((len(buffer)+offset
) % align
) != 0:
445 buffer.extend( b
'\0' )
452 # Add a string to the string buffer except if it already exists there then we
453 # just return its ID.
455 def encoder_process_pstr( s
):
459 cache
= g_encoder
['string_cache']
464 cache
[s
] = len( g_encoder
['data']['strings'] )
466 buffer = g_encoder
['data']['strings']
467 buffer.extend( s
.encode('utf-8') )
468 buffer.extend( b
'\0' )
470 bytearray_align_to( buffer, 4 )
474 # Add a material to the material buffer. Returns 0 (None ID) if invalid
476 def encoder_process_material( mat
):
483 cache
= g_encoder
['material_cache']
484 buffer = g_encoder
['data']['material']
486 if mat
.name
in cache
:
487 return cache
[mat
.name
]
489 cache
[mat
.name
] = len( buffer )
491 dest
= mdl_material()
492 dest
.pstr_name
= encoder_process_pstr( mat
.name
)
495 return cache
[mat
.name
]
498 # Create a tree structure containing all the objects in the collection
500 def encoder_build_scene_graph( collection
):
504 print( " creating scene graph" )
508 graph
= g_encoder
['scene_graph']
509 graph_lookup
= g_encoder
['graph_lookup']
512 graph
["children"] = []
514 graph
["parent"] = None
519 uid
= g_encoder
['uid_count']
520 g_encoder
['uid_count'] += 1
524 for obj
in collection
.all_objects
:
526 if obj
.parent
: continue
528 def _extend( p
, n
, d
):
533 tree
["children"] = []
539 # Descend into amature
541 if n
.type == 'ARMATURE':
543 tree
["bones"] = [None] # None is the root transform
545 tree
["collider_count"] = 0
547 # Here also collects some information about constraints, ik and
548 # counts colliders for the armature.
550 def _extendb( p
, n
, d
):
556 btree
["linked_armature"] = tree
557 btree
["uid"] = _new_uid()
558 btree
["children"] = []
561 tree
["bones"] += [n
.name
]
565 _extendb( btree
, c
, d
+1 )
568 for c
in tree
['obj'].pose
.bones
[n
.name
].constraints
:
572 btree
["ik_target"] = c
.subtarget
573 btree
["ik_pole"] = c
.pole_subtarget
574 tree
["ik_count"] += 1
578 if n
.cv_data
.collider
:
579 tree
['collider_count'] += 1
581 btree
['deform'] = n
.use_deform
582 p
['children'] += [btree
]
585 for b
in n
.data
.bones
:
587 _extendb( tree
, b
, d
+1 )
591 # Recurse into children of this object
593 for obj1
in n
.children
:
596 for c1
in obj1
.users_collection
:
600 _extend( tree
, obj1
, d
+1 )
606 p
["children"] += [tree
]
607 graph_lookup
[n
] = tree
611 _extend( graph
, obj
, 1 )
617 # Kind of a useless thing i made but it looks cool and adds complexity!!1
619 def encoder_graph_iterator( root
):
621 for c
in root
['children']:
624 yield from encoder_graph_iterator(c
)
629 # Push a vertex into the model file, or return a cached index (c_uint32)
631 def encoder_vertex_push( vertex_reference
, co
,norm
,uv
,colour
,groups
,weights
):
634 buffer = g_encoder
['data']['vertex']
637 m
= float(10**TOLERENCE
)
639 # Would be nice to know if this can be done faster than it currently runs,
642 key
= (int(co
[0]*m
+0.5),
650 colour
[0]*m
+0.5, # these guys are already quantized
654 weights
[0]*m
+0.5, # v
663 if key
in vertex_reference
:
664 return vertex_reference
[key
]
667 index
= c_uint32( len(vertex_reference
) )
668 vertex_reference
[key
] = index
679 v
.colour
[0] = colour
[0]
680 v
.colour
[1] = colour
[1]
681 v
.colour
[2] = colour
[2]
682 v
.colour
[3] = colour
[3]
683 v
.weights
[0] = weights
[0]
684 v
.weights
[1] = weights
[1]
685 v
.weights
[2] = weights
[2]
686 v
.weights
[3] = weights
[3]
687 v
.groups
[0] = groups
[0]
688 v
.groups
[1] = groups
[1]
689 v
.groups
[2] = groups
[2]
690 v
.groups
[3] = groups
[3]
698 # Compile a mesh (or use one from the cache) onto node, based on node_def
701 def encoder_compile_mesh( node
, node_def
):
705 graph
= g_encoder
['scene_graph']
706 graph_lookup
= g_encoder
['graph_lookup']
707 mesh_cache
= g_encoder
['mesh_cache']
708 obj
= node_def
['obj']
712 # Check for modifiers that typically change the data per-instance
713 # there is no well defined rule for the choices here, its just what i've
714 # needed while producing the game.
716 # It may be possible to detect these cases automatically.
718 for mod
in obj
.modifiers
:
720 if mod
.type == 'DATA_TRANSFER' or mod
.type == 'SHRINKWRAP' or \
721 mod
.type == 'BOOLEAN' or mod
.type == 'CURVE' or \
724 can_use_cache
= False
727 if mod
.type == 'ARMATURE':
728 armature_def
= graph_lookup
[mod
.object]
730 # Check the cache first
732 if can_use_cache
and (obj
.data
.name
in mesh_cache
):
734 ref
= mesh_cache
[obj
.data
.name
]
735 node
.submesh_start
= ref
.submesh_start
736 node
.submesh_count
= ref
.submesh_count
740 # Compile a whole new mesh
742 node
.submesh_start
= len( g_encoder
['data']['submesh'] )
743 node
.submesh_count
= 0
745 default_mat
= c_uint32(12345)
746 default_mat
.name
= ""
748 dgraph
= bpy
.context
.evaluated_depsgraph_get()
749 data
= obj
.evaluated_get(dgraph
).data
750 data
.calc_loop_triangles()
751 data
.calc_normals_split()
753 # Mesh is split into submeshes based on their material
755 mat_list
= data
.materials
if len(data
.materials
) > 0 else [default_mat
]
756 for material_id
, mat
in enumerate(mat_list
):
761 sm
.indice_start
= len( g_encoder
['data']['indice'] )
762 sm
.vertex_start
= len( g_encoder
['data']['vertex'] )
765 sm
.material_id
= encoder_process_material( mat
)
769 sm
.bbx
[0][i
] = 999999
770 sm
.bbx
[1][i
] = -999999
773 # Keep a reference to very very very similar vertices
775 vertex_reference
= {}
777 # Write the vertex / indice data
779 for tri_index
, tri
in enumerate(data
.loop_triangles
):
781 if tri
.material_index
!= material_id
:
786 vert
= data
.vertices
[tri
.vertices
[j
]]
788 vi
= data
.loops
[li
].vertex_index
790 # Gather vertex information
793 norm
= data
.loops
[li
].normal
795 colour
= (255,255,255,255)
802 uv
= data
.uv_layers
.active
.data
[li
].uv
806 if data
.vertex_colors
:
808 colour
= data
.vertex_colors
.active
.data
[li
].color
809 colour
= (int(colour
[0]*255.0),\
810 int(colour
[1]*255.0),\
811 int(colour
[2]*255.0),\
812 int(colour
[3]*255.0))
815 # Weight groups: truncates to the 3 with the most influence. The
816 # fourth bone ID is never used by the shader so it is
821 src_groups
= [_
for _
in data
.vertices
[vi
].groups \
822 if obj
.vertex_groups
[_
.group
].name
in \
823 armature_def
['bones']]
825 weight_groups
= sorted( src_groups
, key
= \
826 lambda a
: a
.weight
, reverse
=True )
830 if len(weight_groups
) > ml
:
832 g
= weight_groups
[ml
]
833 name
= obj
.vertex_groups
[g
.group
].name
837 groups
[ml
] = armature_def
['bones'].index(name
)
842 if len(weight_groups
) > 0:
844 inv_norm
= (1.0/tot
) * 65535.0
847 weights
[ml
] = int( weights
[ml
] * inv_norm
)
848 weights
[ml
] = min( weights
[ml
], 65535 )
849 weights
[ml
] = max( weights
[ml
], 0 )
853 # Add vertex and expand bound box
855 index
= encoder_vertex_push( vertex_reference
, co
, \
861 g_encoder
['data']['indice'] += [index
]
865 # How many unique verts did we add in total
867 sm
.vertex_count
= len(g_encoder
['data']['vertex']) - sm
.vertex_start
868 sm
.indice_count
= len(g_encoder
['data']['indice']) - sm
.indice_start
870 # Make sure bounding box isn't -inf -> inf if no vertices
872 if sm
.vertex_count
== 0:
878 for j
in range(sm
.vertex_count
):
880 vert
= g_encoder
['data']['vertex'][ sm
.vertex_start
+ j
]
884 sm
.bbx
[0][i
] = min( sm
.bbx
[0][i
], vert
.co
[i
] )
885 sm
.bbx
[1][i
] = max( sm
.bbx
[1][i
], vert
.co
[i
] )
890 # Add submesh to encoder
892 g_encoder
['data']['submesh'] += [sm
]
893 node
.submesh_count
+= 1
897 # Save a reference to this node since we want to reuse the submesh indices
899 g_encoder
['mesh_cache'][obj
.data
.name
] = node
903 def encoder_compile_ent_as( name
, node
, node_def
):
907 if name
== 'classtype_none':
913 elif name
not in globals():
915 print( "Classtype '" +name
+ "' is unknown!" )
919 buffer = g_encoder
['data']['entdata']
920 node
.offset
= len(buffer)
922 cl
= globals()[ name
]
924 inst
.encode_obj( node
, node_def
)
926 buffer.extend( bytearray(inst
) )
927 bytearray_align_to( buffer, 4 )
930 # Compiles animation data into model and gives us some extra node_def entries
932 def encoder_compile_armature( node
, node_def
):
936 entdata
= g_encoder
['data']['entdata']
937 animdata
= g_encoder
['data']['anim']
938 keyframedata
= g_encoder
['data']['keyframe']
939 mesh_cache
= g_encoder
['mesh_cache']
940 obj
= node_def
['obj']
941 bones
= node_def
['bones']
944 node_def
['anim_start'] = len(animdata
)
945 node_def
['anim_count'] = 0
949 if obj
.animation_data
:
951 # So we can restore later
953 previous_frame
= bpy
.context
.scene
.frame_current
954 previous_action
= obj
.animation_data
.action
955 POSE_OR_REST_CACHE
= obj
.data
.pose_position
956 obj
.data
.pose_position
= 'POSE'
958 for NLALayer
in obj
.animation_data
.nla_tracks
:
960 for NLAStrip
in NLALayer
.strips
:
964 for a
in bpy
.data
.actions
:
966 if a
.name
== NLAStrip
.name
:
968 obj
.animation_data
.action
= a
973 # Clip to NLA settings
975 anim_start
= int(NLAStrip
.action_frame_start
)
976 anim_end
= int(NLAStrip
.action_frame_end
)
980 anim
= mdl_animation()
981 anim
.pstr_name
= encoder_process_pstr( NLAStrip
.action
.name
)
983 anim
.offset
= len(keyframedata
)
984 anim
.length
= anim_end
-anim_start
986 # Export the keyframes
987 for frame
in range(anim_start
,anim_end
):
989 bpy
.context
.scene
.frame_set(frame
)
991 for bone_name
in bones
:
993 for pb
in obj
.pose
.bones
:
995 if pb
.name
!= bone_name
: continue
997 rb
= obj
.data
.bones
[ bone_name
]
999 # relative bone matrix
1000 if rb
.parent
is not None:
1002 offset_mtx
= rb
.parent
.matrix_local
1003 offset_mtx
= offset_mtx
.inverted_safe() @ \
1006 inv_parent
= pb
.parent
.matrix
@ offset_mtx
1007 inv_parent
.invert_safe()
1008 fpm
= inv_parent
@ pb
.matrix
1012 bone_mtx
= rb
.matrix
.to_4x4()
1013 local_inv
= rb
.matrix_local
.inverted_safe()
1014 fpm
= bone_mtx
@ local_inv
@ pb
.matrix
1017 loc
, rot
, sca
= fpm
.decompose()
1020 final_pos
= Vector(( loc
[0], loc
[2], -loc
[1] ))
1023 lc_m
= pb
.matrix_channel
.to_3x3()
1024 if pb
.parent
is not None:
1026 smtx
= pb
.parent
.matrix_channel
.to_3x3()
1027 lc_m
= smtx
.inverted() @ lc_m
1029 rq
= lc_m
.to_quaternion()
1032 kf
.co
[0] = final_pos
[0]
1033 kf
.co
[1] = final_pos
[1]
1034 kf
.co
[2] = final_pos
[2]
1046 keyframedata
+= [kf
]
1052 # Add to animation buffer
1055 node_def
['anim_count'] += 1
1059 status_name
= F
" " + " |"*(node_def
['depth']-1)
1060 print( F
"{status_name} | *anim: {NLAStrip.action.name}" )
1064 # Restore context to how it was before
1066 bpy
.context
.scene
.frame_set( previous_frame
)
1067 obj
.animation_data
.action
= previous_action
1068 obj
.data
.pose_position
= POSE_OR_REST_CACHE
1072 # We are trying to compile this node_def
1074 def encoder_process_definition( node_def
):
1078 # data sources for object/bone are taken differently
1080 if 'obj' in node_def
:
1082 obj
= node_def
['obj']
1084 obj_co
= obj
.location
1086 if obj_type
== 'ARMATURE':
1087 obj_classtype
= 'classtype_skeleton'
1090 obj_classtype
= obj
.cv_data
.classtype
1092 # Check for armature deform
1094 for mod
in obj
.modifiers
:
1096 if mod
.type == 'ARMATURE':
1098 obj_classtype
= 'classtype_skin'
1100 # Make sure to freeze armature in rest while we collect
1101 # vertex information
1103 armature_def
= g_encoder
['graph_lookup'][mod
.object]
1104 POSE_OR_REST_CACHE
= armature_def
['obj'].data
.pose_position
1105 armature_def
['obj'].data
.pose_position
= 'REST'
1106 node_def
['linked_armature'] = armature_def
1113 elif 'bone' in node_def
:
1115 obj
= node_def
['bone']
1117 obj_co
= obj
.head_local
1118 obj_classtype
= 'classtype_bone'
1124 node
.pstr_name
= encoder_process_pstr( obj
.name
)
1126 if node_def
["parent"]:
1127 node
.parent
= node_def
["parent"]["uid"]
1131 node
.co
[0] = obj_co
[0]
1132 node
.co
[1] = obj_co
[2]
1133 node
.co
[2] = -obj_co
[1]
1135 # Convert rotation quat to our space type
1137 quat
= obj
.matrix_local
.to_quaternion()
1140 node
.q
[2] = -quat
[2]
1143 # Bone scale is just a vector to the tail
1145 if obj_type
== 'BONE':
1147 node
.s
[0] = obj
.tail_local
[0] - node
.co
[0]
1148 node
.s
[1] = obj
.tail_local
[2] - node
.co
[1]
1149 node
.s
[2] = -obj
.tail_local
[1] - node
.co
[2]
1153 node
.s
[0] = obj
.scale
[0]
1154 node
.s
[1] = obj
.scale
[2]
1155 node
.s
[2] = obj
.scale
[1]
1160 tot_uid
= g_encoder
['uid_count']-1
1161 obj_uid
= node_def
['uid']
1162 obj_depth
= node_def
['depth']-1
1164 status_id
= F
" [{obj_uid: 3}/{tot_uid}]" + " |"*obj_depth
1165 status_name
= status_id
+ F
" L {obj.name}"
1167 if obj_classtype
!= 'classtype_none': status_type
= obj_classtype
1168 else: status_type
= obj_type
1170 status_parent
= F
"{node.parent: 3}"
1173 if obj_classtype
== 'classtype_skin':
1174 status_armref
= F
" [armature -> {armature_def['obj'].cv_data.uid}]"
1176 print(F
"{status_name:<32} {status_type:<22} {status_parent} {status_armref}")
1178 # Process mesh if needed
1180 if obj_type
== 'MESH':
1182 encoder_compile_mesh( node
, node_def
)
1184 elif obj_type
== 'ARMATURE':
1186 encoder_compile_armature( node
, node_def
)
1189 encoder_compile_ent_as( obj_classtype
, node
, node_def
)
1191 # Make sure to reset the armature we just mucked about with
1193 if obj_classtype
== 'classtype_skin':
1194 armature_def
['obj'].data
.pose_position
= POSE_OR_REST_CACHE
1196 g_encoder
['data']['node'] += [node
]
1199 # The post processing step or the pre processing to the writing step
1201 def encoder_write_to_file( path
):
1205 # Compile down to a byte array
1207 header
= g_encoder
['header']
1208 file_pos
= sizeof(header
)
1209 file_data
= bytearray()
1210 print( " Compositing data arrays" )
1212 for array_name
in g_encoder
['data']:
1214 file_pos
+= bytearray_align_to( file_data
, 16, sizeof(header
) )
1215 arr
= g_encoder
['data'][array_name
]
1217 setattr( header
, array_name
+ "_offset", file_pos
)
1219 print( F
" {array_name:<16} @{file_pos:> 8X}[{len(arr)}]" )
1221 if isinstance( arr
, bytearray
):
1223 setattr( header
, array_name
+ "_size", len(arr
) )
1225 file_data
.extend( arr
)
1226 file_pos
+= len(arr
)
1230 setattr( header
, array_name
+ "_count", len(arr
) )
1234 bbytes
= bytearray(item
)
1235 file_data
.extend( bbytes
)
1236 file_pos
+= sizeof(item
)
1241 # This imperitive for this field to be santized in the future!
1243 header
.file_length
= file_pos
1245 print( " Writing file" )
1246 # Write header and data chunk to file
1248 fp
= open( path
, "wb" )
1249 fp
.write( bytearray( header
) )
1250 fp
.write( file_data
)
1254 # Main compiler, uses string as the identifier for the collection
1256 def write_model(collection_name
):
1259 print( F
"Model graph | Create mode '{collection_name}'" )
1261 collection
= bpy
.data
.collections
[collection_name
]
1264 encoder_build_scene_graph( collection
)
1268 print( " Comping objects" )
1269 it
= encoder_graph_iterator( g_encoder
['scene_graph'] )
1271 encoder_process_definition( node_def
)
1276 path
= F
"/home/harry/Documents/carve/models_src/{collection_name}.mdl"
1277 encoder_write_to_file( path
)
1279 print( F
"Completed {collection_name}.mdl" )
1284 # ------------------------------------------------------------------------------
1286 cv_view_draw_handler
= None
1287 cv_view_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
1290 global cv_view_shader
1291 cv_view_shader
.bind()
1292 gpu
.state
.depth_mask_set(False)
1293 gpu
.state
.line_width_set(2.0)
1294 gpu
.state
.face_culling_set('BACK')
1295 gpu
.state
.depth_test_set('LESS')
1296 gpu
.state
.blend_set('NONE')
1301 #def drawbezier(p0,h0,p1,h1,c0,c1):
1302 # nonlocal verts, colours
1306 # colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
1309 # colours += [(1.0,1.0,1,1),(1,1,1,1)]
1312 # for i in range(10):
1318 # p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
1319 # verts += [(last[0],last[1],last[2])]
1320 # verts += [(p[0],p[1],p[2])]
1321 # colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
1326 def drawbhandle(obj
, direction
, colour
):
1327 nonlocal verts
, colours
1329 h0
= obj
.matrix_world
@ Vector((0,direction
,0))
1332 colours
+= [colour
,colour
]
1334 def drawbezier(p0
,h0
,p1
,h1
,c0
,c1
):
1335 nonlocal verts
, colours
1344 p
=ttt
*p1
+(3*tt
-3*ttt
)*h1
+(3*ttt
-6*tt
+3*t
)*h0
+(3*tt
-ttt
-3*t
+1)*p0
1345 verts
+= [(last
[0],last
[1],last
[2])]
1346 verts
+= [(p
[0],p
[1],p
[2])]
1347 colours
+= [c0
*a0
+c1
*(1-a0
),c0
*a0
+c1
*(1-a0
)]
1350 def drawsbpath(o0
,o1
,c0
,c1
,s0
,s1
):
1351 nonlocal course_count
1353 offs
= ((course_count
% 2)*2-1) * course_count
* 0.02
1355 p0
= o0
.matrix_world
@ Vector((offs
, 0,0))
1356 h0
= o0
.matrix_world
@ Vector((offs
, s0
,0))
1357 p1
= o1
.matrix_world
@ Vector((offs
, 0,0))
1358 h1
= o1
.matrix_world
@ Vector((offs
,-s1
,0))
1359 drawbezier(p0
,h0
,p1
,h1
,c0
,c1
)
1361 def drawbpath(o0
,o1
,c0
,c1
):
1362 drawsbpath(o0
,o1
,c0
,c1
,1.0,1.0)
1364 def drawbline(p0
,p1
,c0
,c1
):
1365 nonlocal verts
, colours
1369 for obj
in bpy
.context
.collection
.objects
:
1370 if obj
.type == 'ARMATURE':
1371 for bone
in obj
.data
.bones
:
1372 if bone
.cv_data
.collider
and obj
.data
.pose_position
== 'REST':
1378 vs
[0]=obj
.matrix_world
@Vector((c
[0]+a
[0],c
[1]+a
[1],c
[2]+a
[2]))
1379 vs
[1]=obj
.matrix_world
@Vector((c
[0]+a
[0],c
[1]+b
[1],c
[2]+a
[2]))
1380 vs
[2]=obj
.matrix_world
@Vector((c
[0]+b
[0],c
[1]+b
[1],c
[2]+a
[2]))
1381 vs
[3]=obj
.matrix_world
@Vector((c
[0]+b
[0],c
[1]+a
[1],c
[2]+a
[2]))
1382 vs
[4]=obj
.matrix_world
@Vector((c
[0]+a
[0],c
[1]+a
[1],c
[2]+b
[2]))
1383 vs
[5]=obj
.matrix_world
@Vector((c
[0]+a
[0],c
[1]+b
[1],c
[2]+b
[2]))
1384 vs
[6]=obj
.matrix_world
@Vector((c
[0]+b
[0],c
[1]+b
[1],c
[2]+b
[2]))
1385 vs
[7]=obj
.matrix_world
@Vector((c
[0]+b
[0],c
[1]+a
[1],c
[2]+b
[2]))
1387 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1388 (0,4),(1,5),(2,6),(3,7)]
1393 verts
+= [(v0
[0],v0
[1],v0
[2])]
1394 verts
+= [(v1
[0],v1
[1],v1
[2])]
1395 colours
+= [(0.5,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)]
1397 center
=obj
.matrix_world
@c
1399 def _angle_lim( major
, minor
, amin
, amax
, colour
):
1400 nonlocal verts
, colours
1408 a0
= amin
*(1.0-t0
)+amax
*t0
1409 a1
= amin
*(1.0-t1
)+amax
*t1
1411 p0
= c
+ major
*f
*math
.cos(a0
) + minor
*f
*math
.sin(a0
)
1412 p1
= c
+ major
*f
*math
.cos(a1
) + minor
*f
*math
.sin(a1
)
1414 p0
=obj
.matrix_world
@ p0
1415 p1
=obj
.matrix_world
@ p1
1417 colours
+= [colour
,colour
]
1421 colours
+= [colour
,colour
]
1424 colours
+= [colour
,colour
]
1426 verts
+= [c
+major
*1.2*f
,c
+major
*f
*0.8]
1427 colours
+= [colour
,colour
]
1429 if bone
.cv_data
.con0
:
1430 _angle_lim( Vector((0,1,0)),Vector((0,0,1)), \
1431 bone
.cv_data
.mins
[0], bone
.cv_data
.maxs
[0], \
1433 _angle_lim( Vector((0,0,1)),Vector((1,0,0)), \
1434 bone
.cv_data
.mins
[1], bone
.cv_data
.maxs
[1], \
1436 _angle_lim( Vector((1,0,0)),Vector((0,1,0)), \
1437 bone
.cv_data
.mins
[2], bone
.cv_data
.maxs
[2], \
1441 if obj
.cv_data
.classtype
== 'classtype_gate':
1442 if obj
.type == 'MESH':
1443 dims
= obj
.data
.cv_data
.v0
1445 dims
= obj
.cv_data
.v0
1448 c
= Vector((0,0,dims
[2]))
1450 vs
[0] = obj
.matrix_world
@ Vector((-dims
[0],0.0,-dims
[1]+dims
[2]))
1451 vs
[1] = obj
.matrix_world
@ Vector((-dims
[0],0.0, dims
[1]+dims
[2]))
1452 vs
[2] = obj
.matrix_world
@ Vector(( dims
[0],0.0, dims
[1]+dims
[2]))
1453 vs
[3] = obj
.matrix_world
@ Vector(( dims
[0],0.0,-dims
[1]+dims
[2]))
1454 vs
[4] = obj
.matrix_world
@ (c
+Vector((-1,0,-2)))
1455 vs
[5] = obj
.matrix_world
@ (c
+Vector((-1,0, 2)))
1456 vs
[6] = obj
.matrix_world
@ (c
+Vector(( 1,0, 2)))
1457 vs
[7] = obj
.matrix_world
@ (c
+Vector((-1,0, 0)))
1458 vs
[8] = obj
.matrix_world
@ (c
+Vector(( 1,0, 0)))
1460 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
1465 verts
+= [(v0
[0],v0
[1],v0
[2])]
1466 verts
+= [(v1
[0],v1
[1],v1
[2])]
1467 colours
+= [(1,1,0,1),(1,1,0,1)]
1469 sw
= (0.4,0.4,0.4,0.2)
1470 if obj
.cv_data
.target
!= None:
1471 drawbline( obj
.location
, obj
.cv_data
.target
.location
, sw
,sw
)
1473 elif obj
.cv_data
.classtype
== 'classtype_route_node':
1474 sw
= Vector((0.4,0.4,0.4,0.2))
1475 sw2
= Vector((1.5,0.2,0.2,0.0))
1476 if obj
.cv_data
.target
!= None:
1477 drawbpath( obj
, obj
.cv_data
.target
, sw
, sw
)
1478 if obj
.cv_data
.target1
!= None:
1479 drawbpath( obj
, obj
.cv_data
.target1
, sw
, sw
)
1481 drawbhandle( obj
, 1.0, (0.8,0.8,0.8,1.0) )
1482 drawbhandle( obj
, -1.0, (0.4,0.4,0.4,1.0) )
1484 p1
= obj
.location
+ \
1485 obj
.matrix_world
.to_quaternion() @ Vector((0,0,-6+1.5))
1486 drawbline( obj
.location
, p1
, sw
,sw2
)
1488 elif obj
.cv_data
.classtype
== 'classtype_achievement_box':
1489 a
= Vector((-1,-1,-1))
1493 vs
[0] = obj
.matrix_world
@ Vector((a
[0], a
[1], a
[2]))
1494 vs
[1] = obj
.matrix_world
@ Vector((a
[0], b
[1], a
[2]))
1495 vs
[2] = obj
.matrix_world
@ Vector((b
[0], b
[1], a
[2]))
1496 vs
[3] = obj
.matrix_world
@ Vector((b
[0], a
[1], a
[2]))
1497 vs
[4] = obj
.matrix_world
@ Vector((a
[0], a
[1], b
[2]))
1498 vs
[5] = obj
.matrix_world
@ Vector((a
[0], b
[1], b
[2]))
1499 vs
[6] = obj
.matrix_world
@ Vector((b
[0], b
[1], b
[2]))
1500 vs
[7] = obj
.matrix_world
@ Vector((b
[0], a
[1], b
[2]))
1502 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1503 (0,4),(1,5),(2,6),(3,7)]
1508 verts
+= [(v0
[0],v0
[1],v0
[2])]
1509 verts
+= [(v1
[0],v1
[1],v1
[2])]
1510 colours
+= [(0,1,0,1),(0,1,0,1)]
1512 if obj
.cv_data
.target
!= None:
1514 vs
[0] = obj
.location
1515 vs
[1] = obj
.cv_data
.target
.location
1520 verts
+= [(v0
[0],v0
[1],v0
[2])]
1521 verts
+= [(v1
[0],v1
[1],v1
[2])]
1522 colours
+= [(0,1,1,1),(0,1,1,1)]
1525 elif obj
.cv_data
.classtype
== 'classtype_block':
1526 a
= obj
.data
.cv_data
.v0
1527 b
= obj
.data
.cv_data
.v1
1530 vs
[0] = obj
.matrix_world
@ Vector((a
[0], a
[1], a
[2]))
1531 vs
[1] = obj
.matrix_world
@ Vector((a
[0], b
[1], a
[2]))
1532 vs
[2] = obj
.matrix_world
@ Vector((b
[0], b
[1], a
[2]))
1533 vs
[3] = obj
.matrix_world
@ Vector((b
[0], a
[1], a
[2]))
1534 vs
[4] = obj
.matrix_world
@ Vector((a
[0], a
[1], b
[2]))
1535 vs
[5] = obj
.matrix_world
@ Vector((a
[0], b
[1], b
[2]))
1536 vs
[6] = obj
.matrix_world
@ Vector((b
[0], b
[1], b
[2]))
1537 vs
[7] = obj
.matrix_world
@ Vector((b
[0], a
[1], b
[2]))
1539 indices
= [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1540 (0,4),(1,5),(2,6),(3,7)]
1545 verts
+= [(v0
[0],v0
[1],v0
[2])]
1546 verts
+= [(v1
[0],v1
[1],v1
[2])]
1547 colours
+= [(1,1,0,1),(1,1,0,1)]
1549 elif obj
.cv_data
.classtype
== 'classtype_capsule':
1550 h
= obj
.data
.cv_data
.v0
[0]
1551 r
= obj
.data
.cv_data
.v0
[1]
1554 vs
[0] = obj
.matrix_world
@ Vector((0.0,0.0, h
*0.5 ))
1555 vs
[1] = obj
.matrix_world
@ Vector((0.0,0.0,-h
*0.5 ))
1556 vs
[2] = obj
.matrix_world
@ Vector(( r
,0.0, h
*0.5-r
))
1557 vs
[3] = obj
.matrix_world
@ Vector(( -r
,0.0, h
*0.5-r
))
1558 vs
[4] = obj
.matrix_world
@ Vector(( r
,0.0,-h
*0.5+r
))
1559 vs
[5] = obj
.matrix_world
@ Vector(( -r
,0.0,-h
*0.5+r
))
1560 vs
[6] = obj
.matrix_world
@ Vector((0.0, r
, h
*0.5-r
))
1561 vs
[7] = obj
.matrix_world
@ Vector((0.0,-r
, h
*0.5-r
))
1562 vs
[8] = obj
.matrix_world
@ Vector((0.0, r
,-h
*0.5+r
))
1563 vs
[9] = obj
.matrix_world
@ Vector((0.0,-r
,-h
*0.5+r
))
1565 indices
= [(0,1),(2,3),(4,5),(6,7),(8,9)]
1570 verts
+= [(v0
[0],v0
[1],v0
[2])]
1571 verts
+= [(v1
[0],v1
[1],v1
[2])]
1572 colours
+= [(0.5,1,0,1),(0.5,1,0,1)]
1574 elif obj
.cv_data
.classtype
== 'classtype_spawn':
1576 vs
[0] = obj
.matrix_world
@ Vector((0,0,0))
1577 vs
[1] = obj
.matrix_world
@ Vector((0,2,0))
1578 vs
[2] = obj
.matrix_world
@ Vector((0.5,1,0))
1579 vs
[3] = obj
.matrix_world
@ Vector((-0.5,1,0))
1580 indices
= [(0,1),(1,2),(1,3)]
1584 verts
+= [(v0
[0],v0
[1],v0
[2])]
1585 verts
+= [(v1
[0],v1
[1],v1
[2])]
1586 colours
+= [(0,1,1,1),(0,1,1,1)]
1588 elif obj
.cv_data
.classtype
== 'classtype_route':
1590 vs
[0] = obj
.location
1591 vs
[1] = obj
.cv_data
.target
.location
1596 verts
+= [(v0
[0],v0
[1],v0
[2])]
1597 verts
+= [(v1
[0],v1
[1],v1
[2])]
1598 colours
+= [(0,1,1,1),(0,1,1,1)]
1602 stack
[0] = obj
.cv_data
.target
1604 loop_complete
= False
1607 if stack_i
[si
-1] == 2:
1611 if si
== 0: # Loop failed to complete
1616 targets
= [None,None]
1617 targets
[0] = node
.cv_data
.target
1619 if node
.cv_data
.classtype
== 'classtype_route_node':
1620 targets
[1] = node
.cv_data
.target1
1622 nextnode
= targets
[stack_i
[si
-1]]
1625 if nextnode
!= None: # branch
1626 if nextnode
== stack
[0]: # Loop completed
1627 loop_complete
= True
1631 for sj
in range(si
):
1632 if stack
[sj
] == nextnode
: # invalidated path
1638 stack
[si
] = nextnode
1643 cc
= Vector((obj
.cv_data
.colour
[0],\
1644 obj
.cv_data
.colour
[1],\
1645 obj
.cv_data
.colour
[2],\
1648 for sj
in range(si
):
1651 if stack
[sj
].cv_data
.classtype
== 'classtype_gate' and \
1652 stack
[sk
].cv_data
.classtype
== 'classtype_gate':
1653 dist
= (stack
[sj
].location
-stack
[sk
].location
).magnitude
1654 drawsbpath( stack
[sj
], stack
[sk
], cc
*0.4, cc
, dist
, dist
)
1657 drawbpath( stack
[sj
], stack
[sk
], cc
, cc
)
1661 elif obj
.cv_data
.classtype
== 'classtype_car_path':
1662 v0
= obj
.matrix_world
.to_quaternion() @ Vector((0,1,0))
1663 c0
= Vector((v0
.x
*0.5+0.5, v0
.y
*0.5+0.5, 0.0, 1.0))
1664 drawbhandle( obj
, 1.0, (0.9,0.9,0.9,1.0) )
1666 if obj
.cv_data
.target
!= None:
1667 v1
= obj
.cv_data
.target
.matrix_world
.to_quaternion()@Vector((0,1,0))
1668 c1
= Vector((v1
.x
*0.5+0.5, v1
.y
*0.5+0.5, 0.0, 1.0))
1670 drawbhandle( obj
.cv_data
.target
, -1.0, (0.5,0.5,0.5,1.0) )
1671 drawbpath( obj
, obj
.cv_data
.target
, c0
, c1
)
1673 if obj
.cv_data
.target1
!= None:
1674 v1
= obj
.cv_data
.target1
.matrix_world
.to_quaternion()@Vector((0,1,0))
1675 c1
= Vector((v1
.x
*0.5+0.5, v1
.y
*0.5+0.5, 0.0, 1.0))
1677 drawbhandle( obj
.cv_data
.target1
, -1.0, (0.5,0.5,0.5,1.0) )
1678 drawbpath( obj
, obj
.cv_data
.target1
, c0
, c1
)
1680 lines
= batch_for_shader(\
1681 cv_view_shader
, 'LINES', \
1682 { "pos":verts
, "color":colours
})
1684 lines
.draw( cv_view_shader
)
1686 def cv_poll_target(scene
, obj
):
1687 if obj
== bpy
.context
.active_object
:
1689 if obj
.cv_data
.classtype
== 'classtype_none':
1693 class CV_MESH_SETTINGS(bpy
.types
.PropertyGroup
):
1694 v0
: bpy
.props
.FloatVectorProperty(name
="v0",size
=3)
1695 v1
: bpy
.props
.FloatVectorProperty(name
="v1",size
=3)
1696 v2
: bpy
.props
.FloatVectorProperty(name
="v2",size
=3)
1697 v3
: bpy
.props
.FloatVectorProperty(name
="v3",size
=3)
1699 class CV_OBJ_SETTINGS(bpy
.types
.PropertyGroup
):
1700 uid
: bpy
.props
.IntProperty( name
="" )
1702 strp
: bpy
.props
.StringProperty( name
="strp" )
1703 intp
: bpy
.props
.IntProperty( name
="intp" )
1704 fltp
: bpy
.props
.FloatProperty( name
="fltp" )
1706 target
: bpy
.props
.PointerProperty( type=bpy
.types
.Object
, name
="target", \
1707 poll
=cv_poll_target
)
1708 target1
: bpy
.props
.PointerProperty( type=bpy
.types
.Object
, name
="target1", \
1709 poll
=cv_poll_target
)
1711 colour
: bpy
.props
.FloatVectorProperty(name
="colour",subtype
='COLOR',\
1714 classtype
: bpy
.props
.EnumProperty(
1717 ('classtype_none', "classtype_none", "", 0),
1718 ('classtype_gate', "classtype_gate", "", 1),
1719 ('classtype_block', "classtype_block", "", 2),
1720 ('classtype_spawn', "classtype_spawn", "", 3),
1721 ('classtype_water', "classtype_water", "", 4),
1722 ('classtype_car_path', "classtype_car_path", "", 5),
1723 ('classtype_INSTANCE', "","", 6 ),
1724 ('classtype_capsule', "classtype_capsule", "", 7 ),
1725 ('classtype_route_node', "classtype_route_node", "", 8 ),
1726 ('classtype_route', "classtype_route", "", 9 ),
1727 ('classtype_bone',"classtype_bone","",10),
1728 ('classtype_SKELETON', "","", 11 ),
1729 ('classtype_SKIN',"","",12),
1730 ('classtype_achievement_box',"classtype_achievement_box","",13),
1731 ('classtype_audio',"classtype_audio","",14),
1734 class CV_BONE_SETTINGS(bpy
.types
.PropertyGroup
):
1735 collider
: bpy
.props
.BoolProperty(name
="Collider",default
=False)
1736 v0
: bpy
.props
.FloatVectorProperty(name
="v0",size
=3)
1737 v1
: bpy
.props
.FloatVectorProperty(name
="v1",size
=3)
1739 con0
: bpy
.props
.BoolProperty(name
="Constriant 0",default
=False)
1740 mins
: bpy
.props
.FloatVectorProperty(name
="mins",size
=3)
1741 maxs
: bpy
.props
.FloatVectorProperty(name
="maxs",size
=3)
1743 class CV_BONE_PANEL(bpy
.types
.Panel
):
1744 bl_label
="Bone Config"
1745 bl_idname
="SCENE_PT_cv_bone"
1746 bl_space_type
='PROPERTIES'
1747 bl_region_type
='WINDOW'
1750 def draw(_
,context
):
1751 active_object
= context
.active_object
1752 if active_object
== None: return
1754 bone
= active_object
.data
.bones
.active
1755 if bone
== None: return
1757 _
.layout
.prop( bone
.cv_data
, "collider" )
1758 _
.layout
.prop( bone
.cv_data
, "v0" )
1759 _
.layout
.prop( bone
.cv_data
, "v1" )
1761 _
.layout
.label( text
="Angle Limits" )
1762 _
.layout
.prop( bone
.cv_data
, "con0" )
1763 _
.layout
.prop( bone
.cv_data
, "mins" )
1764 _
.layout
.prop( bone
.cv_data
, "maxs" )
1766 class CV_SCENE_SETTINGS(bpy
.types
.PropertyGroup
):
1767 use_hidden
: bpy
.props
.BoolProperty( name
="use hidden", default
=False )
1769 class CV_OBJ_PANEL(bpy
.types
.Panel
):
1770 bl_label
="Entity Config"
1771 bl_idname
="SCENE_PT_cv_entity"
1772 bl_space_type
='PROPERTIES'
1773 bl_region_type
='WINDOW'
1776 def draw(_
,context
):
1777 active_object
= bpy
.context
.active_object
1778 if active_object
== None: return
1779 if active_object
.type == 'ARMATURE':
1781 row
= _
.layout
.row()
1783 row
.label( text
="This object has the intrinsic classtype of skeleton" )
1787 _
.layout
.prop( active_object
.cv_data
, "classtype" )
1789 if active_object
.cv_data
.classtype
== 'classtype_gate':
1790 _
.layout
.prop( active_object
.cv_data
, "target" )
1792 mesh
= active_object
.data
1793 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1794 _
.layout
.prop( mesh
.cv_data
, "v0" )
1796 elif active_object
.cv_data
.classtype
== 'classtype_car_path' or \
1797 active_object
.cv_data
.classtype
== 'classtype_route_node':
1798 _
.layout
.prop( active_object
.cv_data
, "target" )
1799 _
.layout
.prop( active_object
.cv_data
, "target1" )
1801 elif active_object
.cv_data
.classtype
== 'classtype_route':
1802 _
.layout
.prop( active_object
.cv_data
, "target" )
1803 _
.layout
.prop( active_object
.cv_data
, "colour" )
1805 elif active_object
.cv_data
.classtype
== 'classtype_block':
1806 mesh
= active_object
.data
1808 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1809 _
.layout
.prop( mesh
.cv_data
, "v0" )
1810 _
.layout
.prop( mesh
.cv_data
, "v1" )
1811 _
.layout
.prop( mesh
.cv_data
, "v2" )
1812 _
.layout
.prop( mesh
.cv_data
, "v3" )
1813 elif active_object
.cv_data
.classtype
== 'classtype_capsule':
1814 mesh
= active_object
.data
1815 _
.layout
.label( text
=F
"(i) Data is stored in {mesh.name}" )
1816 _
.layout
.prop( mesh
.cv_data
, "v0" )
1817 elif active_object
.cv_data
.classtype
== 'classtype_achievement_box':
1818 _
.layout
.prop( active_object
.cv_data
, "strp" )
1819 _
.layout
.prop( active_object
.cv_data
, "target" )
1820 elif active_object
.cv_data
.classtype
== 'classtype_audio':
1821 _
.layout
.prop( active_object
.cv_data
, "strp" )
1822 _
.layout
.prop( active_object
.cv_data
, "intp" )
1823 _
.layout
.prop( active_object
.cv_data
, "fltp" )
1825 class CV_INTERFACE(bpy
.types
.Panel
):
1826 bl_idname
= "VIEW3D_PT_carve"
1828 bl_space_type
= 'VIEW_3D'
1829 bl_region_type
= 'UI'
1830 bl_category
= "Carve"
1832 def draw(_
, context
):
1834 layout
.prop( context
.scene
.cv_data
, "use_hidden")
1835 layout
.operator( "carve.compile_all" )
1838 view_layer
= bpy
.context
.view_layer
1839 for col
in view_layer
.layer_collection
.children
["export"].children
:
1840 if not col
.hide_viewport
or bpy
.context
.scene
.cv_data
.use_hidden
:
1841 write_model( col
.name
)
1843 class CV_COMPILE(bpy
.types
.Operator
):
1844 bl_idname
="carve.compile_all"
1845 bl_label
="Compile All"
1847 def execute(_
,context
):
1849 #cProfile.runctx("test_compile()",globals(),locals(),sort=1)
1850 #for col in bpy.data.collections["export"].children:
1851 # write_model( col.name )
1855 classes
= [CV_OBJ_SETTINGS
,CV_OBJ_PANEL
,CV_COMPILE
,CV_INTERFACE
,\
1856 CV_MESH_SETTINGS
, CV_SCENE_SETTINGS
, CV_BONE_SETTINGS
,\
1860 global cv_view_draw_handler
1863 bpy
.utils
.register_class(c
)
1865 bpy
.types
.Object
.cv_data
= bpy
.props
.PointerProperty(type=CV_OBJ_SETTINGS
)
1866 bpy
.types
.Mesh
.cv_data
= bpy
.props
.PointerProperty(type=CV_MESH_SETTINGS
)
1867 bpy
.types
.Scene
.cv_data
= bpy
.props
.PointerProperty(type=CV_SCENE_SETTINGS
)
1868 bpy
.types
.Bone
.cv_data
= bpy
.props
.PointerProperty(type=CV_BONE_SETTINGS
)
1870 cv_view_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
1871 cv_draw
,(),'WINDOW','POST_VIEW')
1874 global cv_view_draw_handler
1877 bpy
.utils
.unregister_class(c
)
1879 bpy
.types
.SpaceView3D
.draw_handler_remove(cv_view_draw_handler
,'WINDOW')