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