3cff63d7eed48bb2023e8dc64e788423af102aff
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
1 #
2 # Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 #
4
5 import bpy, math, gpu
6 import cProfile
7 from ctypes import *
8 from mathutils import *
9 from gpu_extras.batch import batch_for_shader
10
11 bl_info = {
12 "name":"Carve exporter",
13 "author": "Harry Godden (hgn)",
14 "version": (0,1),
15 "blender":(3,1,0),
16 "location":"Export",
17 "descriptin":"",
18 "warning":"",
19 "wiki_url":"",
20 "category":"Import/Export",
21 }
22
23 class mdl_vert(Structure):
24 _pack_ = 1
25 _fields_ = [("co",c_float*3),
26 ("norm",c_float*3),
27 ("uv",c_float*2),
28 ("colour",c_uint8*4),
29 ("weights",c_uint16*4),
30 ("groups",c_uint8*4)]
31
32 class mdl_submesh(Structure):
33 _pack_ = 1
34 _fields_ = [("indice_start",c_uint32),
35 ("indice_count",c_uint32),
36 ("vertex_start",c_uint32),
37 ("vertex_count",c_uint32),
38 ("bbx",(c_float*3)*2),
39 ("material_id",c_uint32)] # index into the material array
40
41 class mdl_material(Structure):
42 _pack_ = 1
43 _fields_ = [("pstr_name",c_uint32)]
44
45 class mdl_node(Structure):
46 _pack_ = 1
47 _fields_ = [("co",c_float*3),
48 ( "q",c_float*4),
49 ( "s",c_float*3),
50 ("submesh_start",c_uint32),
51 ("submesh_count",c_uint32),
52 ("classtype",c_uint32),
53 ("offset",c_uint32),
54 ("parent",c_uint32),
55 ("pstr_name",c_uint32)]
56
57 class mdl_header(Structure):
58 _pack_ = 1
59 _fields_ = [("identifier",c_uint32),
60 ("version",c_uint32),
61 ("file_length",c_uint32),
62 ("vertex_count",c_uint32),
63 ("vertex_offset",c_uint32),
64
65 ("indice_count",c_uint32),
66 ("indice_offset",c_uint32),
67
68 ("submesh_count",c_uint32),
69 ("submesh_offset",c_uint32),
70
71 ("material_count",c_uint32),
72 ("material_offset",c_uint32),
73
74 ("node_count",c_uint32),
75 ("node_offset",c_uint32),
76
77 ("anim_count",c_uint32),
78 ("anim_offset",c_uint32),
79
80 ("strings_offset",c_uint32),
81 ("entdata_offset",c_uint32),
82 ("animdata_offset",c_uint32)
83 ]
84
85 class mdl_animation(Structure):
86 _pack_ = 1
87 _fields_ = [("pstr_name",c_uint32),
88 ("length",c_uint32),
89 ("rate",c_float),
90 ("offset",c_uint32)]
91
92 class mdl_keyframe(Structure):
93 _pack_ = 1
94 _fields_ = [("co",c_float*3),
95 ("q",c_float*4),
96 ("s",c_float*3)]
97
98 # Entity types
99 # ==========================================
100
101 class classtype_gate(Structure):
102 _pack_ = 1
103 _fields_ = [("target",c_uint32),
104 ("dims",c_float*3)]
105
106 class classtype_block(Structure):
107 _pack_ = 1
108 _fields_ = [("bbx",(c_float*3)*2)]
109
110 class classtype_spawn(Structure):
111 _pack_ = 1
112 _fields_ = [("temp",c_uint32)]
113
114 class classtype_water(Structure):
115 _pack_ = 1
116 _fields_ = [("temp",c_uint32)]
117
118 class classtype_car_path(Structure):
119 _pack_ = 1
120 _fields_ = [("target",c_uint32),
121 ("target1",c_uint32)]
122
123 class classtype_instance(Structure):
124 _pack_ = 1
125 _fields_ = [("pstr_file",c_uint32)]
126
127 class classtype_capsule(Structure):
128 _pack_ = 1
129 _fields_ = [("height",c_float),
130 ("radius",c_float)]
131
132 class classtype_route_node(Structure):
133 _pack_ = 1
134 _fields_ = [("target",c_uint32),
135 ("target1",c_uint32)]
136
137 class classtype_route(Structure):
138 _pack_ = 1
139 _fields_ = [("id_start",c_uint32),
140 ("colour",c_float*3)]
141
142 class classtype_skin(Structure):
143 _pack_ = 1
144 _fields_ = [("skeleton",c_uint32)]
145
146 class classtype_skeleton(Structure):
147 _pack_ = 1
148 _fields_ = [("channels",c_uint32),
149 ("ik_count",c_uint32),
150 ("collider_count",c_uint32),
151 ("anim_start",c_uint32),
152 ("anim_count",c_uint32)]
153
154 class classtype_bone(Structure):
155 _pack_ = 1
156 _fields_ = [("deform",c_uint32),
157 ("ik_target",c_uint32),
158 ("ik_pole",c_uint32),
159 ("collider",c_uint32),
160 ("use_limits",c_uint32),
161 ("angle_limits",(c_float*3)*2),
162 ("hitbox",(c_float*3)*2)]
163
164 class classtype_achievement_box(Structure):
165 _pack_ = 1
166 _fields_ = [("pstr_name",c_uint32)]
167
168 # Exporter
169 # ==============================================================================
170
171 def write_model(collection_name):
172 print( F"Model graph | Create mode '{collection_name}'" )
173
174 header = mdl_header()
175 header.identifier = 0xABCD0000
176 header.version = 0
177 header.vertex_count = 0
178 header.indice_count = 0
179 header.submesh_count = 0
180 header.node_count = 0
181 header.material_count = 0
182 header.file_length = 0
183
184 mesh_cache = {}
185 string_cache = {}
186 material_cache = {}
187
188 strings_buffer = b''
189
190 material_buffer = []
191 submesh_buffer = []
192 vertex_buffer = []
193 indice_buffer = []
194 node_buffer = []
195 entdata_buffer = []
196 entdata_length = 0
197
198 anim_buffer = []
199 animdata_length = 0
200 animdata_buffer = []
201
202 def emplace_string( s ):
203 nonlocal string_cache, strings_buffer
204
205 if s in string_cache:
206 return string_cache[s]
207
208 string_cache[s] = len( strings_buffer )
209 strings_buffer += (s+'\0').encode('utf-8')
210 return string_cache[s]
211
212 def emplace_material( mat ):
213 nonlocal material_cache, material_buffer
214
215 if mat == None:
216 return 0
217
218 if mat.name in material_cache:
219 return material_cache[mat.name]
220
221 material_cache[mat.name] = header.material_count
222 dest = mdl_material()
223 dest.pstr_name = emplace_string( mat.name )
224 material_buffer += [dest]
225
226 header.material_count += 1
227 return material_cache[mat.name]
228
229 # Create root or empty node and materials
230 # this is to designate id 0 as 'NULL'
231 #
232 none_material = c_uint32(69)
233 none_material.name = ""
234 emplace_material( none_material )
235
236 root = mdl_node()
237 root.co[0] = 0
238 root.co[1] = 0
239 root.co[2] = 0
240 root.q[0] = 0
241 root.q[1] = 0
242 root.q[2] = 0
243 root.q[3] = 1
244 root.s[0] = 1
245 root.s[1] = 1
246 root.s[2] = 1
247 root.pstr_name = emplace_string('')
248 root.submesh_start = 0
249 root.submesh_count = 0
250 root.offset = 0
251 root.classtype = 0
252 node_buffer += [root]
253
254 # Do exporting
255 #
256 print( " assigning ids" )
257 collection = bpy.data.collections[collection_name]
258
259 # Scene graph
260 # ==========================================
261
262 header.node_count = 0
263 def _uid():
264 nonlocal header
265 uid = header.node_count
266 header.node_count += 1
267 return uid
268
269 print( " creating scene graph" )
270 graph = {}
271 graph["obj"] = None
272 graph["depth"] = 0
273 graph["children"] = []
274 graph["uid"] = _uid()
275 graph["parent"] = None
276
277 graph_lookup = {} # object can lookup its graph def here
278
279 for obj in collection.all_objects:
280 if not obj.parent:
281
282 def _extend( p, n, d ):
283 uid = _uid()
284 tree = {}
285 tree["uid"] = uid
286 tree["children"] = []
287 tree["depth"] = d
288 tree["obj"] = n
289 tree["parent"] = p
290 n.cv_data.uid = uid
291
292 if n.type == 'ARMATURE':
293 tree["bones"] = [None] # None is the root transform
294 tree["ik_count"] = 0
295 tree["collider_count"] = 0
296
297 def _extendb( p, n, d ):
298 nonlocal tree
299
300 btree = {}
301 btree["bone"] = n
302 btree["uid"] = _uid()
303 btree["children"] = []
304 btree["depth"] = d
305 btree["parent"] = p
306 tree["bones"] += [n.name]
307
308 for c in n.children:
309 _extendb( btree, c, d+1 )
310
311 for c in tree['obj'].pose.bones[n.name].constraints:
312 if c.type == 'IK':
313 btree["target"] = c.subtarget
314 btree["pole"] = c.pole_subtarget
315 tree["ik_count"] += 1
316
317 if n.cv_data.collider:
318 tree['collider_count'] += 1
319
320 btree['deform'] = n.use_deform
321 p['children'] += [btree]
322
323 for b in n.data.bones:
324 if not b.parent:
325 _extendb( tree, b, d+1 )
326
327 for obj1 in n.children:
328 nonlocal collection
329 for c1 in obj1.users_collection:
330 if c1 == collection:
331 _extend( tree, obj1, d+1 )
332 break
333
334 p["children"] += [tree]
335 graph_lookup[n] = tree
336
337 _extend( graph, obj, 1 )
338
339
340 def _graph_iter(p):
341 for c in p['children']:
342 yield c
343 yield from _graph_iter(c)
344
345 it = _graph_iter(graph)
346
347 root.parent = 0xffffffff
348
349 # Compile
350 # ==============================================
351 it = _graph_iter(graph)
352 print( " compiling data" )
353 for node_def in it:
354 if 'obj' in node_def:
355 obj = node_def['obj']
356 objt = obj.type
357 objco = obj.location
358 elif 'bone' in node_def:
359 obj = node_def['bone']
360 objt = 'BONE'
361 objco = obj.head_local
362
363 depth = node_def['depth']
364 uid = node_def['uid']
365
366 node = mdl_node()
367 node.co[0] = objco[0]
368 node.co[1] = objco[2]
369 node.co[2] = -objco[1]
370
371 # Convert rotation quat to our space type
372 quat = obj.matrix_local.to_quaternion()
373 node.q[0] = quat[1]
374 node.q[1] = quat[3]
375 node.q[2] = -quat[2]
376 node.q[3] = quat[0]
377
378 if objt == 'BONE':
379 node.s[0] = obj.tail_local[0] - node.co[0]
380 node.s[1] = obj.tail_local[2] - node.co[1]
381 node.s[2] = -obj.tail_local[1] - node.co[2]
382 else:
383 node.s[0] = obj.scale[0]
384 node.s[1] = obj.scale[2]
385 node.s[2] = obj.scale[1]
386
387 node.pstr_name = emplace_string( obj.name )
388
389 if node_def["parent"]:
390 node.parent = node_def["parent"]["uid"]
391
392 if objt == 'BONE':
393 classtype = 'k_classtype_bone'
394 elif objt == 'ARMATURE':
395 classtype = 'k_classtype_skeleton'
396 else:
397 classtype = obj.cv_data.classtype
398
399 # Process type: MESH
400 # =================================================================
401 #
402
403 # Dont use the cache if we have modifiers that affect the normals
404 #
405 compile_mesh = False
406 if objt == 'MESH':
407 armature_def = None
408 compile_mesh = True
409 can_use_cache = True
410
411 for mod in obj.modifiers:
412 if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
413 mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
414 mod.type == 'ARRAY':
415 can_use_cache = False
416
417 if mod.type == 'ARMATURE':
418 classtype = 'k_classtype_skin'
419 armature_def = graph_lookup[mod.object]
420 POSE_OR_REST_CACHE = armature_def['obj'].data.pose_position
421
422 armature_def['obj'].data.pose_position = 'REST'
423
424 if can_use_cache and obj.data.name in mesh_cache:
425 ref = mesh_cache[obj.data.name]
426 node.submesh_start = ref.submesh_start
427 node.submesh_count = ref.submesh_count
428 compile_mesh = False
429
430 if compile_mesh:
431 node.submesh_start = header.submesh_count
432 node.submesh_count = 0
433
434 default_mat = c_uint32(69)
435 default_mat.name = ""
436
437 dgraph = bpy.context.evaluated_depsgraph_get()
438 data = obj.evaluated_get(dgraph).data
439 data.calc_loop_triangles()
440 data.calc_normals_split()
441
442 mat_list = data.materials if len(data.materials) > 0 else [default_mat]
443 for material_id, mat in enumerate(mat_list):
444 mref = {}
445
446 sm = mdl_submesh()
447 sm.indice_start = header.indice_count
448 sm.vertex_start = header.vertex_count
449 sm.vertex_count = 0
450 sm.indice_count = 0
451 sm.material_id = emplace_material( mat )
452
453 for i in range(3):
454 sm.bbx[0][i] = 999999
455 sm.bbx[1][i] = -999999
456
457 boffa = {}
458
459 # Write the vertex / indice data
460 #
461 for tri_index, tri in enumerate(data.loop_triangles):
462 if tri.material_index != material_id:
463 continue
464
465 for j in range(3):
466 vert = data.vertices[tri.vertices[j]]
467 li = tri.loops[j]
468 vi = data.loops[li].vertex_index
469
470 co = vert.co
471 norm = data.loops[li].normal
472 uv = (0,0)
473 colour = (255,255,255,255)
474 groups = [0,0,0,0]
475 weights = [0,0,0,0]
476
477 if data.uv_layers:
478 uv = data.uv_layers.active.data[li].uv
479
480 if data.vertex_colors:
481 colour = data.vertex_colors.active.data[li].color
482 colour = (int(colour[0]*255.0),\
483 int(colour[1]*255.0),\
484 int(colour[2]*255.0),\
485 int(colour[3]*255.0))
486
487 # WEight groups
488 #
489 if armature_def:
490 src_groups = [_ for _ in data.vertices[vi].groups \
491 if obj.vertex_groups[_.group].name in \
492 armature_def['bones']]
493
494 weight_groups = sorted( src_groups, key = \
495 lambda a: a.weight, reverse=True )
496 tot = 0.0
497 for ml in range(3):
498 if len(weight_groups) > ml:
499 g = weight_groups[ml]
500 name = obj.vertex_groups[g.group].name
501 weight = g.weight
502
503 weights[ml] = weight
504 groups[ml] = armature_def['bones'].index(name)
505 tot += weight
506
507 if len(weight_groups) > 0:
508 inv_norm = (1.0/tot) * 65535.0
509 for ml in range(3):
510 weights[ml] = int( weights[ml] * inv_norm )
511 weights[ml] = min( weights[ml], 65535 )
512 weights[ml] = max( weights[ml], 0 )
513
514 TOLERENCE = 4
515 m = float(10**TOLERENCE)
516
517 key = (int(co[0]*m+0.5),\
518 int(co[1]*m+0.5),\
519 int(co[2]*m+0.5),\
520 int(norm[0]*m+0.5),\
521 int(norm[1]*m+0.5),\
522 int(norm[2]*m+0.5),\
523 int(uv[0]*m+0.5),\
524 int(uv[1]*m+0.5),\
525 colour[0],\
526 colour[1],\
527 colour[2],\
528 colour[3],\
529 weights[0],\
530 weights[1],\
531 weights[2],\
532 weights[3],\
533 groups[0],\
534 groups[1],\
535 groups[2],\
536 groups[3])
537
538 if key in boffa:
539 indice_buffer += [boffa[key]]
540 else:
541 index = c_uint32(sm.vertex_count)
542 sm.vertex_count += 1
543
544 boffa[key] = index
545 indice_buffer += [index]
546
547 v = mdl_vert()
548 v.co[0] = co[0]
549 v.co[1] = co[2]
550 v.co[2] = -co[1]
551 v.norm[0] = norm[0]
552 v.norm[1] = norm[2]
553 v.norm[2] = -norm[1]
554 v.uv[0] = uv[0]
555 v.uv[1] = uv[1]
556 v.colour[0] = colour[0]
557 v.colour[1] = colour[1]
558 v.colour[2] = colour[2]
559 v.colour[3] = colour[3]
560 v.weights[0] = weights[0]
561 v.weights[1] = weights[1]
562 v.weights[2] = weights[2]
563 v.weights[3] = weights[3]
564 v.groups[0] = groups[0]
565 v.groups[1] = groups[1]
566 v.groups[2] = groups[2]
567 v.groups[3] = groups[3]
568
569 vertex_buffer += [v]
570
571 for i in range(3):
572 sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
573 sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
574
575 sm.indice_count += 1
576
577 if sm.vertex_count == 0:
578 for j in range(2):
579 for i in range(3):
580 sm.bbx[j][i] = 0
581
582 submesh_buffer += [sm]
583 node.submesh_count += 1
584 header.submesh_count += 1
585 header.vertex_count += sm.vertex_count
586 header.indice_count += sm.indice_count
587
588 mesh_cache[obj.data.name] = node
589
590 # Process entity data
591 # ==================================================================
592 node.offset = entdata_length
593
594 if classtype != 'k_classtype_none':
595 disptype = classtype
596 else:
597 disptype = objt
598
599 s000 = F" [{uid: 3}/{header.node_count-1}]" + " |"*(depth-1)
600 s001 = F" L {obj.name}"
601 s002 = s000+s001
602 s003 = F"{disptype}"
603 s004 = F"{node.parent: 3}"
604 s005 = ""
605
606 if classtype == 'k_classtype_skin':
607 armature_def['obj'].data.pose_position = POSE_OR_REST_CACHE
608 s005 = F" [armature -> {armature_def['obj'].cv_data.uid}]"
609
610 scmp = F"{s002:<32} {s003:<22} {s004} {s005}"
611 print( scmp )
612
613 if classtype == 'k_classtype_INSTANCE' or \
614 classtype == 'k_classtype_BONE' or \
615 classtype == 'k_classtype_SKELETON' or \
616 classtype == 'k_classtype_SKIN':
617 print( "ERROR: user classtype cannot be _INSTANCE or _BONE" )
618 node.classtype = 0
619 node.offset = 0
620
621 elif classtype == 'k_classtype_skin':
622 node.classtype = 12
623
624 armature = armature_def['obj']
625 entdata_length += sizeof( classtype_skin )
626
627 skin = classtype_skin()
628 skin.skeleton = armature.cv_data.uid
629 entdata_buffer += [skin]
630
631 elif classtype == 'k_classtype_skeleton':
632 node.classtype = 11
633 entdata_length += sizeof( classtype_skeleton )
634 skeleton = classtype_skeleton()
635
636 armature_def = graph_lookup[obj]
637 armature = obj
638 bones = armature_def['bones']
639 skeleton.channels = len(bones)
640 skeleton.ik_count = armature_def["ik_count"]
641 skeleton.collider_count = armature_def["collider_count"]
642
643 if armature.animation_data:
644 previous_frame = bpy.context.scene.frame_current
645 previous_action = armature.animation_data.action
646
647 skeleton.anim_start = len(anim_buffer)
648 skeleton.anim_count = 0
649
650 for NLALayer in obj.animation_data.nla_tracks:
651 for NLAStrip in NLALayer.strips:
652 # Use action
653 for a in bpy.data.actions:
654 if a.name == NLAStrip.name:
655 armature.animation_data.action = a
656 break
657
658 anim_start = int(NLAStrip.action_frame_start)
659 anim_end = int(NLAStrip.action_frame_end)
660
661 # export strips
662 anim = mdl_animation()
663 anim.pstr_name = emplace_string( NLAStrip.action.name )
664 anim.rate = 30.0
665 anim.offset = animdata_length
666 anim.length = anim_end-anim_start
667
668 # Export the fucking keyframes
669 for frame in range(anim_start,anim_end):
670 bpy.context.scene.frame_set(frame)
671
672 for bone_name in bones:
673 for pb in armature.pose.bones:
674 if pb.name == bone_name:
675 rb = armature.data.bones[ bone_name ]
676
677 # relative bone matrix
678 if rb.parent is not None:
679 offset_mtx = rb.parent.matrix_local
680 offset_mtx = offset_mtx.inverted_safe() @ \
681 rb.matrix_local
682
683 inv_parent = pb.parent.matrix @ offset_mtx
684 inv_parent.invert_safe()
685 fpm = inv_parent @ pb.matrix
686 else:
687 bone_mtx = rb.matrix.to_4x4()
688 local_inv = rb.matrix_local.inverted_safe()
689 fpm = bone_mtx @ local_inv @ pb.matrix
690
691 loc, rot, sca = fpm.decompose()
692
693 # local position
694 final_pos = Vector(( loc[0], loc[2], -loc[1] ))
695
696 # rotation
697 lc_m = pb.matrix_channel.to_3x3()
698 if pb.parent is not None:
699 smtx = pb.parent.matrix_channel.to_3x3()
700 lc_m = smtx.inverted() @ lc_m
701 rq = lc_m.to_quaternion()
702
703 kf = mdl_keyframe()
704 kf.co[0] = final_pos[0]
705 kf.co[1] = final_pos[1]
706 kf.co[2] = final_pos[2]
707
708 kf.q[0] = rq[1]
709 kf.q[1] = rq[3]
710 kf.q[2] = -rq[2]
711 kf.q[3] = rq[0]
712
713 # scale
714 kf.s[0] = sca[0]
715 kf.s[1] = sca[2]
716 kf.s[2] = sca[1]
717
718 animdata_buffer += [kf]
719 animdata_length += sizeof(mdl_keyframe)
720 break
721
722 anim_buffer += [anim]
723 skeleton.anim_count += 1
724
725 s000 = F" [{uid: 3}/{header.node_count-1}]" + " |"*(depth-1)
726 print( F"{s000} | *anim: {NLAStrip.action.name}" )
727
728 bpy.context.scene.frame_set( previous_frame )
729 armature.animation_data.action = previous_action
730
731 entdata_buffer += [skeleton]
732
733 elif classtype == 'k_classtype_bone':
734 node.classtype = 10
735 entdata_length += sizeof( classtype_bone )
736
737 bone = classtype_bone()
738 bone.deform = node_def['deform']
739
740 if 'target' in node_def:
741 bone.ik_target = armature_def['bones'].index( node_def['target'] )
742 bone.ik_pole = armature_def['bones'].index( node_def['pole'] )
743 else:
744 bone.ik_target = 0
745 bone.ik_pole = 0
746
747 bone.collider = 1 if obj.cv_data.collider else 0
748 if obj.cv_data.collider:
749 bone.hitbox[0][0] = obj.cv_data.v0[0]
750 bone.hitbox[0][1] = obj.cv_data.v0[2]
751 bone.hitbox[0][2] = -obj.cv_data.v1[1]
752 bone.hitbox[1][0] = obj.cv_data.v1[0]
753 bone.hitbox[1][1] = obj.cv_data.v1[2]
754 bone.hitbox[1][2] = -obj.cv_data.v0[1]
755 else:
756 bone.hitbox[0][0] = 0.0
757 bone.hitbox[0][1] = 0.0
758 bone.hitbox[0][2] = 0.0
759 bone.hitbox[1][0] = 0.0
760 bone.hitbox[1][1] = 0.0
761 bone.hitbox[1][2] = 0.0
762
763 if obj.cv_data.con0:
764 bone.use_limits = 1
765 bone.angle_limits[0][0] = obj.cv_data.mins[0]
766 bone.angle_limits[0][1] = obj.cv_data.mins[2]
767 bone.angle_limits[0][2] = -obj.cv_data.maxs[1]
768 bone.angle_limits[1][0] = obj.cv_data.maxs[0]
769 bone.angle_limits[1][1] = obj.cv_data.maxs[2]
770 bone.angle_limits[1][2] = -obj.cv_data.mins[1]
771 else:
772 bone.use_limits = 0
773 bone.angle_limits[0][0] = 0.0
774 bone.angle_limits[0][1] = 0.0
775 bone.angle_limits[0][2] = 0.0
776 bone.angle_limits[1][0] = 0.0
777 bone.angle_limits[1][1] = 0.0
778 bone.angle_limits[1][2] = 0.0
779
780 bone.deform = node_def['deform']
781 entdata_buffer += [bone]
782
783 elif classtype == 'k_classtype_gate':
784 node.classtype = 1
785 entdata_length += sizeof( classtype_gate )
786
787 gate = classtype_gate()
788 gate.target = 0
789 if obj.cv_data.target != None:
790 gate.target = obj.cv_data.target.cv_data.uid
791
792 if obj.type == 'MESH':
793 gate.dims[0] = obj.data.cv_data.v0[0]
794 gate.dims[1] = obj.data.cv_data.v0[1]
795 gate.dims[2] = obj.data.cv_data.v0[2]
796 else:
797 gate.dims[0] = obj.cv_data.v0[0]
798 gate.dims[1] = obj.cv_data.v0[1]
799 gate.dims[2] = obj.cv_data.v0[2]
800
801 entdata_buffer += [gate]
802
803 elif classtype == 'k_classtype_block':
804 node.classtype = 2
805 entdata_length += sizeof( classtype_block )
806
807 source = obj.data.cv_data
808
809 block = classtype_block()
810 block.bbx[0][0] = source.v0[0]
811 block.bbx[0][1] = source.v0[2]
812 block.bbx[0][2] = -source.v1[1]
813
814 block.bbx[1][0] = source.v1[0]
815 block.bbx[1][1] = source.v1[2]
816 block.bbx[1][2] = -source.v0[1]
817 entdata_buffer += [block]
818
819 elif classtype == 'k_classtype_achievement_box':
820 node.classtype = 13
821
822 entdata_length += sizeof( classtype_achievement_box )
823 ach = classtype_achievement_box()
824 ach.pstr_name = emplace_string( obj.cv_data.strp )
825 entdata_buffer += [ach]
826
827 elif classtype == 'k_classtype_spawn':
828 node.classtype = 3
829
830 elif classtype == 'k_classtype_water':
831 node.classtype = 4
832
833 elif classtype == 'k_classtype_car_path':
834 node.classtype = 5
835 entdata_length += sizeof( classtype_car_path )
836
837 pn = classtype_car_path()
838 pn.target = 0
839 pn.target1 = 0
840
841 if obj.cv_data.target != None:
842 pn.target = obj.cv_data.target.cv_data.uid
843 if obj.cv_data.target1 != None:
844 pn.target1 = obj.cv_data.target1.cv_data.uid
845
846 entdata_buffer += [pn]
847
848 elif obj.is_instancer:
849 target = obj.instance_collection
850
851 node.classtype = 6
852 entdata_length += sizeof( classtype_instance )
853
854 inst = classtype_instance()
855 inst.pstr_file = emplace_string( F"models/{target.name}.mdl" )
856 entdata_buffer += [inst]
857
858 elif classtype == 'k_classtype_capsule':
859 node.classtype = 7
860
861 elif classtype == 'k_classtype_route_node':
862 node.classtype = 8
863 entdata_length += sizeof( classtype_route_node )
864
865 rn = classtype_route_node()
866 if obj.cv_data.target != None:
867 rn.target = obj.cv_data.target.cv_data.uid
868 if obj.cv_data.target1 != None:
869 rn.target1 = obj.cv_data.target1.cv_data.uid
870
871 entdata_buffer += [rn]
872
873 elif classtype == 'k_classtype_route':
874 node.classtype = 9
875 entdata_length += sizeof( classtype_route )
876 r = classtype_route()
877 r.colour[0] = obj.cv_data.colour[0]
878 r.colour[1] = obj.cv_data.colour[1]
879 r.colour[2] = obj.cv_data.colour[2]
880
881 if obj.cv_data.target != None:
882 r.id_start = obj.cv_data.target.cv_data.uid
883
884 entdata_buffer += [r]
885
886 # classtype == 'k_classtype_none':
887 else:
888 node.classtype = 0
889 node.offset = 0
890
891 node_buffer += [node]
892
893 # Write data arrays
894 #
895 header.anim_count = len(anim_buffer)
896
897 print( "Writing data" )
898 fpos = sizeof(header)
899
900 print( F"Nodes: {header.node_count}" )
901 header.node_offset = fpos
902 fpos += sizeof(mdl_node)*header.node_count
903
904 print( F"Submeshes: {header.submesh_count}" )
905 header.submesh_offset = fpos
906 fpos += sizeof(mdl_submesh)*header.submesh_count
907
908 print( F"Materials: {header.material_count}" )
909 header.material_offset = fpos
910 fpos += sizeof(mdl_material)*header.material_count
911
912 print( F"Animation count: {header.anim_count}" )
913 header.anim_offset = fpos
914 fpos += sizeof(mdl_animation)*header.anim_count
915
916 print( F"Entdata length: {entdata_length}" )
917 header.entdata_offset = fpos
918 fpos += entdata_length
919
920 print( F"Vertex count: {header.vertex_count}" )
921 header.vertex_offset = fpos
922 fpos += sizeof(mdl_vert)*header.vertex_count
923
924 print( F"Indice count: {header.indice_count}" )
925 header.indice_offset = fpos
926 fpos += sizeof(c_uint32)*header.indice_count
927
928 print( F"Keyframe count: {animdata_length}" )
929 header.animdata_offset = fpos
930 fpos += animdata_length
931
932 print( F"Strings length: {len(strings_buffer)}" )
933 header.strings_offset = fpos
934 fpos += len(strings_buffer)
935
936 header.file_length = fpos
937
938 path = F"/home/harry/Documents/carve/models_src/{collection_name}.mdl"
939 fp = open( path, "wb" )
940
941 fp.write( bytearray( header ) )
942
943 for node in node_buffer:
944 fp.write( bytearray(node) )
945 for sm in submesh_buffer:
946 fp.write( bytearray(sm) )
947 for mat in material_buffer:
948 fp.write( bytearray(mat) )
949 for a in anim_buffer:
950 fp.write( bytearray(a) )
951 for ed in entdata_buffer:
952 fp.write( bytearray(ed) )
953 for v in vertex_buffer:
954 fp.write( bytearray(v) )
955 for i in indice_buffer:
956 fp.write( bytearray(i) )
957 for kf in animdata_buffer:
958 fp.write( bytearray(kf) )
959
960 fp.write( strings_buffer )
961 fp.close()
962
963 print( F"Completed {collection_name}.mdl" )
964
965 # Clicky clicky GUI
966 # ------------------------------------------------------------------------------
967
968 cv_view_draw_handler = None
969 cv_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
970
971 def cv_draw():
972 global cv_view_shader
973 cv_view_shader.bind()
974 gpu.state.depth_mask_set(False)
975 gpu.state.line_width_set(2.0)
976 gpu.state.face_culling_set('BACK')
977 gpu.state.depth_test_set('LESS')
978 gpu.state.blend_set('NONE')
979
980 verts = []
981 colours = []
982
983 #def drawbezier(p0,h0,p1,h1,c0,c1):
984 # nonlocal verts, colours
985
986 # verts += [p0]
987 # verts += [h0]
988 # colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
989 # verts += [p1]
990 # verts += [h1]
991 # colours += [(1.0,1.0,1,1),(1,1,1,1)]
992 #
993 # last = p0
994 # for i in range(10):
995 # t = (i+1)/10
996 # a0 = 1-t
997
998 # tt = t*t
999 # ttt = tt*t
1000 # p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
1001 # verts += [(last[0],last[1],last[2])]
1002 # verts += [(p[0],p[1],p[2])]
1003 # colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
1004 # last = p
1005
1006 course_count = 0
1007
1008 def drawbhandle(obj, direction, colour):
1009 nonlocal verts, colours
1010 p0 = obj.location
1011 h0 = obj.matrix_world @ Vector((0,direction,0))
1012 verts += [p0]
1013 verts += [h0]
1014 colours += [colour,colour]
1015
1016 def drawbezier(p0,h0,p1,h1,c0,c1):
1017 nonlocal verts, colours
1018
1019 last = p0
1020 for i in range(10):
1021 t = (i+1)/10
1022 a0 = 1-t
1023
1024 tt = t*t
1025 ttt = tt*t
1026 p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
1027 verts += [(last[0],last[1],last[2])]
1028 verts += [(p[0],p[1],p[2])]
1029 colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
1030 last = p
1031
1032 def drawsbpath(o0,o1,c0,c1,s0,s1):
1033 nonlocal course_count
1034
1035 offs = ((course_count % 2)*2-1) * course_count * 0.02
1036
1037 p0 = o0.matrix_world @ Vector((offs, 0,0))
1038 h0 = o0.matrix_world @ Vector((offs, s0,0))
1039 p1 = o1.matrix_world @ Vector((offs, 0,0))
1040 h1 = o1.matrix_world @ Vector((offs,-s1,0))
1041 drawbezier(p0,h0,p1,h1,c0,c1)
1042
1043 def drawbpath(o0,o1,c0,c1):
1044 drawsbpath(o0,o1,c0,c1,1.0,1.0)
1045
1046 def drawbline(p0,p1,c0,c1):
1047 nonlocal verts, colours
1048 verts += [p0,p1]
1049 colours += [c0,c1]
1050
1051 for obj in bpy.context.collection.objects:
1052 if obj.type == 'ARMATURE':
1053 for bone in obj.data.bones:
1054 if bone.cv_data.collider and obj.data.pose_position == 'REST':
1055 c = bone.head_local
1056 a = bone.cv_data.v0
1057 b = bone.cv_data.v1
1058
1059 vs = [None]*8
1060 vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
1061 vs[1]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+a[2]))
1062 vs[2]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+a[2]))
1063 vs[3]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+a[2]))
1064 vs[4]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+b[2]))
1065 vs[5]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+b[2]))
1066 vs[6]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+b[2]))
1067 vs[7]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+b[2]))
1068
1069 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1070 (0,4),(1,5),(2,6),(3,7)]
1071
1072 for l in indices:
1073 v0 = vs[l[0]]
1074 v1 = vs[l[1]]
1075 verts += [(v0[0],v0[1],v0[2])]
1076 verts += [(v1[0],v1[1],v1[2])]
1077 colours += [(0.5,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)]
1078
1079 center=obj.matrix_world@c
1080
1081 def _angle_lim( major, minor, amin, amax, colour ):
1082 nonlocal verts, colours
1083 f = 0.05
1084 ay = major*f
1085 ax = minor*f
1086
1087 for x in range(16):
1088 t0 = x/16
1089 t1 = (x+1)/16
1090 a0 = amin*(1.0-t0)+amax*t0
1091 a1 = amin*(1.0-t1)+amax*t1
1092
1093 p0 = c + major*f*math.cos(a0) + minor*f*math.sin(a0)
1094 p1 = c + major*f*math.cos(a1) + minor*f*math.sin(a1)
1095
1096 p0=obj.matrix_world @ p0
1097 p1=obj.matrix_world @ p1
1098 verts += [p0,p1]
1099 colours += [colour,colour]
1100
1101 if x == 0:
1102 verts += [p0,c]
1103 colours += [colour,colour]
1104 if x == 15:
1105 verts += [p1,c]
1106 colours += [colour,colour]
1107
1108 verts += [c+major*1.2*f,c+major*f*0.8]
1109 colours += [colour,colour]
1110
1111 if bone.cv_data.con0:
1112 _angle_lim( Vector((0,1,0)),Vector((0,0,1)), \
1113 bone.cv_data.mins[0], bone.cv_data.maxs[0], \
1114 (1,0,0,1))
1115 _angle_lim( Vector((0,0,1)),Vector((1,0,0)), \
1116 bone.cv_data.mins[1], bone.cv_data.maxs[1], \
1117 (0,1,0,1))
1118 _angle_lim( Vector((1,0,0)),Vector((0,1,0)), \
1119 bone.cv_data.mins[2], bone.cv_data.maxs[2], \
1120 (0,0,1,1))
1121
1122
1123 if obj.cv_data.classtype == 'k_classtype_gate':
1124 if obj.type == 'MESH':
1125 dims = obj.data.cv_data.v0
1126 else:
1127 dims = obj.cv_data.v0
1128
1129 vs = [None]*9
1130 c = Vector((0,0,dims[2]))
1131
1132 vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
1133 vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
1134 vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
1135 vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
1136 vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
1137 vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
1138 vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
1139 vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
1140 vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
1141
1142 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
1143
1144 for l in indices:
1145 v0 = vs[l[0]]
1146 v1 = vs[l[1]]
1147 verts += [(v0[0],v0[1],v0[2])]
1148 verts += [(v1[0],v1[1],v1[2])]
1149 colours += [(1,1,0,1),(1,1,0,1)]
1150
1151 sw = (0.4,0.4,0.4,0.2)
1152 if obj.cv_data.target != None:
1153 drawbline( obj.location, obj.cv_data.target.location, sw,sw )
1154
1155 elif obj.cv_data.classtype == 'k_classtype_route_node':
1156 sw = Vector((0.4,0.4,0.4,0.2))
1157 sw2 = Vector((1.5,0.2,0.2,0.0))
1158 if obj.cv_data.target != None:
1159 drawbpath( obj, obj.cv_data.target, sw, sw )
1160 if obj.cv_data.target1 != None:
1161 drawbpath( obj, obj.cv_data.target1, sw, sw )
1162
1163 drawbhandle( obj, 1.0, (0.8,0.8,0.8,1.0) )
1164 drawbhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
1165
1166 p1 = obj.location+ \
1167 obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
1168 drawbline( obj.location, p1, sw,sw2 )
1169
1170 elif obj.cv_data.classtype == 'k_classtype_achievement_box':
1171 a = Vector((-1,-1,-1))
1172 b = Vector((1,1,1))
1173
1174 vs = [None]*8
1175 vs[0] = obj.matrix_world @ Vector((a[0], a[1], a[2]))
1176 vs[1] = obj.matrix_world @ Vector((a[0], b[1], a[2]))
1177 vs[2] = obj.matrix_world @ Vector((b[0], b[1], a[2]))
1178 vs[3] = obj.matrix_world @ Vector((b[0], a[1], a[2]))
1179 vs[4] = obj.matrix_world @ Vector((a[0], a[1], b[2]))
1180 vs[5] = obj.matrix_world @ Vector((a[0], b[1], b[2]))
1181 vs[6] = obj.matrix_world @ Vector((b[0], b[1], b[2]))
1182 vs[7] = obj.matrix_world @ Vector((b[0], a[1], b[2]))
1183
1184 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1185 (0,4),(1,5),(2,6),(3,7)]
1186
1187 for l in indices:
1188 v0 = vs[l[0]]
1189 v1 = vs[l[1]]
1190 verts += [(v0[0],v0[1],v0[2])]
1191 verts += [(v1[0],v1[1],v1[2])]
1192 colours += [(0,1,0,1),(0,1,0,1)]
1193
1194 elif obj.cv_data.classtype == 'k_classtype_block':
1195 a = obj.data.cv_data.v0
1196 b = obj.data.cv_data.v1
1197
1198 vs = [None]*8
1199 vs[0] = obj.matrix_world @ Vector((a[0], a[1], a[2]))
1200 vs[1] = obj.matrix_world @ Vector((a[0], b[1], a[2]))
1201 vs[2] = obj.matrix_world @ Vector((b[0], b[1], a[2]))
1202 vs[3] = obj.matrix_world @ Vector((b[0], a[1], a[2]))
1203 vs[4] = obj.matrix_world @ Vector((a[0], a[1], b[2]))
1204 vs[5] = obj.matrix_world @ Vector((a[0], b[1], b[2]))
1205 vs[6] = obj.matrix_world @ Vector((b[0], b[1], b[2]))
1206 vs[7] = obj.matrix_world @ Vector((b[0], a[1], b[2]))
1207
1208 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
1209 (0,4),(1,5),(2,6),(3,7)]
1210
1211 for l in indices:
1212 v0 = vs[l[0]]
1213 v1 = vs[l[1]]
1214 verts += [(v0[0],v0[1],v0[2])]
1215 verts += [(v1[0],v1[1],v1[2])]
1216 colours += [(1,1,0,1),(1,1,0,1)]
1217
1218 elif obj.cv_data.classtype == 'k_classtype_capsule':
1219 h = obj.data.cv_data.v0[0]
1220 r = obj.data.cv_data.v0[1]
1221
1222 vs = [None]*10
1223 vs[0] = obj.matrix_world @ Vector((0.0,0.0, h*0.5 ))
1224 vs[1] = obj.matrix_world @ Vector((0.0,0.0,-h*0.5 ))
1225 vs[2] = obj.matrix_world @ Vector(( r,0.0, h*0.5-r))
1226 vs[3] = obj.matrix_world @ Vector(( -r,0.0, h*0.5-r))
1227 vs[4] = obj.matrix_world @ Vector(( r,0.0,-h*0.5+r))
1228 vs[5] = obj.matrix_world @ Vector(( -r,0.0,-h*0.5+r))
1229 vs[6] = obj.matrix_world @ Vector((0.0, r , h*0.5-r))
1230 vs[7] = obj.matrix_world @ Vector((0.0,-r , h*0.5-r))
1231 vs[8] = obj.matrix_world @ Vector((0.0, r ,-h*0.5+r))
1232 vs[9] = obj.matrix_world @ Vector((0.0,-r ,-h*0.5+r))
1233
1234 indices = [(0,1),(2,3),(4,5),(6,7),(8,9)]
1235
1236 for l in indices:
1237 v0 = vs[l[0]]
1238 v1 = vs[l[1]]
1239 verts += [(v0[0],v0[1],v0[2])]
1240 verts += [(v1[0],v1[1],v1[2])]
1241 colours += [(0.5,1,0,1),(0.5,1,0,1)]
1242
1243 elif obj.cv_data.classtype == 'k_classtype_spawn':
1244 vs = [None]*4
1245 vs[0] = obj.matrix_world @ Vector((0,0,0))
1246 vs[1] = obj.matrix_world @ Vector((0,2,0))
1247 vs[2] = obj.matrix_world @ Vector((0.5,1,0))
1248 vs[3] = obj.matrix_world @ Vector((-0.5,1,0))
1249 indices = [(0,1),(1,2),(1,3)]
1250 for l in indices:
1251 v0 = vs[l[0]]
1252 v1 = vs[l[1]]
1253 verts += [(v0[0],v0[1],v0[2])]
1254 verts += [(v1[0],v1[1],v1[2])]
1255 colours += [(0,1,1,1),(0,1,1,1)]
1256
1257 elif obj.cv_data.classtype == 'k_classtype_route':
1258 vs = [None]*2
1259 vs[0] = obj.location
1260 vs[1] = obj.cv_data.target.location
1261 indices = [(0,1)]
1262 for l in indices:
1263 v0 = vs[l[0]]
1264 v1 = vs[l[1]]
1265 verts += [(v0[0],v0[1],v0[2])]
1266 verts += [(v1[0],v1[1],v1[2])]
1267 colours += [(0,1,1,1),(0,1,1,1)]
1268
1269 stack = [None]*64
1270 stack_i = [0]*64
1271 stack[0] = obj.cv_data.target
1272 si = 1
1273 loop_complete = False
1274
1275 while si > 0:
1276 if stack_i[si-1] == 2:
1277 si -= 1
1278 continue
1279
1280 if si == 0: # Loop failed to complete
1281 break
1282
1283 node = stack[si-1]
1284
1285 targets = [None,None]
1286 targets[0] = node.cv_data.target
1287
1288 if node.cv_data.classtype == 'k_classtype_route_node':
1289 targets[1] = node.cv_data.target1
1290
1291 nextnode = targets[stack_i[si-1]]
1292 stack_i[si-1] += 1
1293
1294 if nextnode != None: # branch
1295 if nextnode == stack[0]: # Loop completed
1296 loop_complete = True
1297 break
1298
1299 valid=True
1300 for sj in range(si):
1301 if stack[sj] == nextnode: # invalidated path
1302 valid=False
1303 break
1304
1305 if valid:
1306 stack_i[si] = 0
1307 stack[si] = nextnode
1308 si += 1
1309 continue
1310
1311 if loop_complete:
1312 cc = Vector((obj.cv_data.colour[0],\
1313 obj.cv_data.colour[1],\
1314 obj.cv_data.colour[2],\
1315 1.0))
1316
1317 for sj in range(si):
1318 sk = (sj+1)%si
1319
1320 if stack[sj].cv_data.classtype == 'k_classtype_gate' and \
1321 stack[sk].cv_data.classtype == 'k_classtype_gate':
1322 dist = (stack[sj].location-stack[sk].location).magnitude
1323 drawsbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
1324
1325 else:
1326 drawbpath( stack[sj], stack[sk], cc, cc )
1327
1328 course_count += 1
1329
1330 elif obj.cv_data.classtype == 'k_classtype_car_path':
1331 v0 = obj.matrix_world.to_quaternion() @ Vector((0,1,0))
1332 c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0))
1333 drawbhandle( obj, 1.0, (0.9,0.9,0.9,1.0) )
1334
1335 if obj.cv_data.target != None:
1336 v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((0,1,0))
1337 c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
1338
1339 drawbhandle( obj.cv_data.target, -1.0, (0.5,0.5,0.5,1.0) )
1340 drawbpath( obj, obj.cv_data.target, c0, c1 )
1341
1342 if obj.cv_data.target1 != None:
1343 v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((0,1,0))
1344 c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
1345
1346 drawbhandle( obj.cv_data.target1, -1.0, (0.5,0.5,0.5,1.0) )
1347 drawbpath( obj, obj.cv_data.target1, c0, c1 )
1348
1349 lines = batch_for_shader(\
1350 cv_view_shader, 'LINES', \
1351 { "pos":verts, "color":colours })
1352
1353 lines.draw( cv_view_shader )
1354
1355 def cv_poll_target(scene, obj):
1356 if obj == bpy.context.active_object:
1357 return False
1358 if obj.cv_data.classtype == 'k_classtype_none':
1359 return False
1360 return True
1361
1362 class CV_MESH_SETTINGS(bpy.types.PropertyGroup):
1363 v0: bpy.props.FloatVectorProperty(name="v0",size=3)
1364 v1: bpy.props.FloatVectorProperty(name="v1",size=3)
1365 v2: bpy.props.FloatVectorProperty(name="v2",size=3)
1366 v3: bpy.props.FloatVectorProperty(name="v3",size=3)
1367
1368 class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
1369 uid: bpy.props.IntProperty( name="" )
1370
1371 strp: bpy.props.StringProperty(name="string")
1372
1373 target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
1374 poll=cv_poll_target )
1375 target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
1376 poll=cv_poll_target )
1377
1378 colour: bpy.props.FloatVectorProperty(name="colour",subtype='COLOR',\
1379 min=0.0,max=1.0)
1380
1381 classtype: bpy.props.EnumProperty(
1382 name="Format",
1383 items = [
1384 ('k_classtype_none', "k_classtype_none", "", 0),
1385 ('k_classtype_gate', "k_classtype_gate", "", 1),
1386 ('k_classtype_block', "k_classtype_block", "", 2),
1387 ('k_classtype_spawn', "k_classtype_spawn", "", 3),
1388 ('k_classtype_water', "k_classtype_water", "", 4),
1389 ('k_classtype_car_path', "k_classtype_car_path", "", 5),
1390 ('k_classtype_INSTANCE', "","", 6 ),
1391 ('k_classtype_capsule', "k_classtype_capsule", "", 7 ),
1392 ('k_classtype_route_node', "k_classtype_route_node", "", 8 ),
1393 ('k_classtype_route', "k_classtype_route", "", 9 ),
1394 ('k_classtype_bone',"k_classtype_bone","",10),
1395 ('k_classtype_SKELETON', "","", 11 ),
1396 ('k_classtype_SKIN',"","",12),
1397 ('k_classtype_achievement_box',"k_classtype_achievement_box","",13),
1398 ])
1399
1400 class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
1401 collider: bpy.props.BoolProperty(name="Collider",default=False)
1402 v0: bpy.props.FloatVectorProperty(name="v0",size=3)
1403 v1: bpy.props.FloatVectorProperty(name="v1",size=3)
1404
1405 con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
1406 mins: bpy.props.FloatVectorProperty(name="mins",size=3)
1407 maxs: bpy.props.FloatVectorProperty(name="maxs",size=3)
1408
1409 class CV_BONE_PANEL(bpy.types.Panel):
1410 bl_label="Bone Config"
1411 bl_idname="SCENE_PT_cv_bone"
1412 bl_space_type='PROPERTIES'
1413 bl_region_type='WINDOW'
1414 bl_context='bone'
1415
1416 def draw(_,context):
1417 active_object = context.active_object
1418 if active_object == None: return
1419
1420 bone = active_object.data.bones.active
1421 if bone == None: return
1422
1423 _.layout.prop( bone.cv_data, "collider" )
1424 _.layout.prop( bone.cv_data, "v0" )
1425 _.layout.prop( bone.cv_data, "v1" )
1426
1427 _.layout.label( text="Angle Limits" )
1428 _.layout.prop( bone.cv_data, "con0" )
1429 _.layout.prop( bone.cv_data, "mins" )
1430 _.layout.prop( bone.cv_data, "maxs" )
1431
1432 class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
1433 use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
1434
1435 class CV_OBJ_PANEL(bpy.types.Panel):
1436 bl_label="Entity Config"
1437 bl_idname="SCENE_PT_cv_entity"
1438 bl_space_type='PROPERTIES'
1439 bl_region_type='WINDOW'
1440 bl_context="object"
1441
1442 def draw(_,context):
1443 active_object = bpy.context.active_object
1444 if active_object == None: return
1445 _.layout.prop( active_object.cv_data, "classtype" )
1446
1447 if active_object.cv_data.classtype == 'k_classtype_gate':
1448 _.layout.prop( active_object.cv_data, "target" )
1449
1450 mesh = active_object.data
1451 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
1452 _.layout.prop( mesh.cv_data, "v0" )
1453
1454 elif active_object.cv_data.classtype == 'k_classtype_car_path' or \
1455 active_object.cv_data.classtype == 'k_classtype_route_node':
1456 _.layout.prop( active_object.cv_data, "target" )
1457 _.layout.prop( active_object.cv_data, "target1" )
1458
1459 elif active_object.cv_data.classtype == 'k_classtype_route':
1460 _.layout.prop( active_object.cv_data, "target" )
1461 _.layout.prop( active_object.cv_data, "colour" )
1462
1463 elif active_object.cv_data.classtype == 'k_classtype_block':
1464 mesh = active_object.data
1465
1466 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
1467 _.layout.prop( mesh.cv_data, "v0" )
1468 _.layout.prop( mesh.cv_data, "v1" )
1469 _.layout.prop( mesh.cv_data, "v2" )
1470 _.layout.prop( mesh.cv_data, "v3" )
1471 elif active_object.cv_data.classtype == 'k_classtype_capsule':
1472 mesh = active_object.data
1473 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
1474 _.layout.prop( mesh.cv_data, "v0" )
1475 elif active_object.cv_data.classtype == 'k_classtype_achievement_box':
1476 _.layout.prop( active_object.cv_data, "strp" )
1477
1478 class CV_INTERFACE(bpy.types.Panel):
1479 bl_idname = "VIEW3D_PT_carve"
1480 bl_label = "Carve"
1481 bl_space_type = 'VIEW_3D'
1482 bl_region_type = 'UI'
1483 bl_category = "Carve"
1484
1485 def draw(_, context):
1486 layout = _.layout
1487 layout.prop( context.scene.cv_data, "use_hidden")
1488 layout.operator( "carve.compile_all" )
1489
1490 def test_compile():
1491 view_layer = bpy.context.view_layer
1492 for col in view_layer.layer_collection.children["export"].children:
1493 if not col.hide_viewport or bpy.context.scene.cv_data.use_hidden:
1494 write_model( col.name )
1495
1496 class CV_COMPILE(bpy.types.Operator):
1497 bl_idname="carve.compile_all"
1498 bl_label="Compile All"
1499
1500 def execute(_,context):
1501 test_compile()
1502 #cProfile.runctx("test_compile()",globals(),locals(),sort=1)
1503 #for col in bpy.data.collections["export"].children:
1504 # write_model( col.name )
1505
1506 return {'FINISHED'}
1507
1508 classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\
1509 CV_MESH_SETTINGS, CV_SCENE_SETTINGS, CV_BONE_SETTINGS,\
1510 CV_BONE_PANEL]
1511
1512 def register():
1513 global cv_view_draw_handler
1514
1515 for c in classes:
1516 bpy.utils.register_class(c)
1517
1518 bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS)
1519 bpy.types.Mesh.cv_data = bpy.props.PointerProperty(type=CV_MESH_SETTINGS)
1520 bpy.types.Scene.cv_data = bpy.props.PointerProperty(type=CV_SCENE_SETTINGS)
1521 bpy.types.Bone.cv_data = bpy.props.PointerProperty(type=CV_BONE_SETTINGS)
1522
1523 cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
1524 cv_draw,(),'WINDOW','POST_VIEW')
1525
1526 def unregister():
1527 global cv_view_draw_handler
1528
1529 for c in classes:
1530 bpy.utils.unregister_class(c)
1531
1532 bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')