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