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