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