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