routes
[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 ("colour",c_float*4),
24 ("uv",c_float*2)]
25
26 class mdl_submesh(Structure):
27 _pack_ = 1
28 _fields_ = [("indice_start",c_uint32),
29 ("indice_count",c_uint32),
30 ("vertex_start",c_uint32),
31 ("vertex_count",c_uint32),
32 ("bbx",(c_float*3)*2),
33 ("material_id",c_uint32)] # index into the material array
34
35 class mdl_material(Structure):
36 _pack_ = 1
37 _fields_ = [("pstr_name",c_uint32)]
38
39 class mdl_node(Structure):
40 _pack_ = 1
41 _fields_ = [("co",c_float*3),
42 ( "q",c_float*4),
43 ( "s",c_float*3),
44 ("submesh_start",c_uint32),
45 ("submesh_count",c_uint32),
46 ("classtype",c_uint32),
47 ("offset",c_uint32),
48 ("pstr_name",c_uint32)]
49
50 class mdl_header(Structure):
51 _pack_ = 1
52 _fields_ = [("identifier",c_uint32),
53 ("version",c_uint32),
54 ("file_length",c_uint32),
55 ("vertex_count",c_uint32),
56 ("vertex_offset",c_uint32),
57
58 ("indice_count",c_uint32),
59 ("indice_offset",c_uint32),
60
61 ("submesh_count",c_uint32),
62 ("submesh_offset",c_uint32),
63
64 ("material_count",c_uint32),
65 ("material_offset",c_uint32),
66
67 ("node_count",c_uint32),
68 ("node_offset",c_uint32),
69
70 ("strings_offset",c_uint32),
71 ("entdata_offset",c_uint32)
72 ]
73
74 # Entity types
75 # ==========================================
76
77 class classtype_gate(Structure):
78 _pack_ = 1
79 _fields_ = [("target",c_uint32),
80 ("dims",c_float*3)]
81
82 class classtype_block(Structure):
83 _pack_ = 1
84 _fields_ = [("bbx",(c_float*3)*2)]
85
86 class classtype_spawn(Structure):
87 _pack_ = 1
88 _fields_ = [("temp",c_uint32)]
89
90 class classtype_water(Structure):
91 _pack_ = 1
92 _fields_ = [("temp",c_uint32)]
93
94 class classtype_car_path(Structure):
95 _pack_ = 1
96 _fields_ = [("target",c_uint32),
97 ("target1",c_uint32)]
98
99 class classtype_instance(Structure):
100 _pack_ = 1
101 _fields_ = [("pstr_file",c_uint32)]
102
103 class classtype_capsule(Structure):
104 _pack_ = 1
105 _fields_ = [("height",c_float),
106 ("radius",c_float)]
107
108 class classtype_route_node(Structure):
109 _pack_ = 1
110 _fields_ = [("target",c_uint32),
111 ("target1",c_uint32)]
112
113 class classtype_route(Structure):
114 _pack_ = 1
115 _fields_ = [("pstr_name",c_uint32),
116 ("id_start",c_uint32),
117 ("colour",c_float*3)]
118
119 # Exporter
120 # ==============================================================================
121
122 def write_model(name):
123 print( F"Create mode {name}" )
124
125 header = mdl_header()
126 header.identifier = 0xABCD0000
127 header.version = 0
128 header.vertex_count = 0
129 header.indice_count = 0
130 header.submesh_count = 0
131 header.node_count = 0
132 header.material_count = 0
133 header.file_length = 0
134
135 mesh_cache = {}
136 string_cache = {}
137 material_cache = {}
138
139 strings_buffer = b''
140
141 material_buffer = []
142 submesh_buffer = []
143 vertex_buffer = []
144 indice_buffer = []
145 node_buffer = []
146 entdata_buffer = []
147 entdata_length = 0
148
149 def emplace_string( s ):
150 nonlocal string_cache, strings_buffer
151
152 if s in string_cache:
153 return string_cache[s]
154
155 string_cache[s] = len( strings_buffer )
156 strings_buffer += (s+'\0').encode('utf-8')
157 return string_cache[s]
158
159 def emplace_material( mat ):
160 nonlocal material_cache, material_buffer
161
162 if mat.name in material_cache:
163 return material_cache[mat.name]
164
165 material_cache[mat.name] = header.material_count
166 dest = mdl_material()
167 dest.pstr_name = emplace_string( mat.name )
168 material_buffer += [dest]
169
170 header.material_count += 1
171 return material_cache[mat.name]
172
173 # Create root or empty node and materials
174 #
175 none_material = c_uint32(69)
176 none_material.name = ""
177 emplace_material( none_material )
178
179 root = mdl_node()
180 root.co[0] = 0
181 root.co[1] = 0
182 root.co[2] = 0
183 root.q[0] = 0
184 root.q[1] = 0
185 root.q[2] = 0
186 root.q[3] = 1
187 root.s[0] = 1
188 root.s[1] = 1
189 root.s[2] = 1
190 root.pstr_name = emplace_string('')
191 root.submesh_start = 0
192 root.submesh_count = 0
193 root.offset = 0
194 root.classtype = 0
195 node_buffer += [root]
196
197 # Do exporting
198 #
199 print( " assigning ids" )
200 collection = bpy.data.collections[name]
201
202 header.node_count = 1
203 for obj in collection.all_objects:
204 obj.cv_data.uid = header.node_count
205 header.node_count += 1
206
207 print( " compiling data" )
208 for obj in collection.all_objects:
209 print( F" [{obj.cv_data.uid}/{header.node_count-1}] {obj.name}" )
210
211 node = mdl_node()
212 node.co[0] = obj.location[0]
213 node.co[1] = obj.location[2]
214 node.co[2] = -obj.location[1]
215
216 # Convert rotation quat to our space type
217 quat = obj.matrix_world.to_quaternion()
218 node.q[0] = quat[1]
219 node.q[1] = quat[3]
220 node.q[2] = -quat[2]
221 node.q[3] = quat[0]
222
223 node.s[0] = obj.scale[0]
224 node.s[1] = obj.scale[2]
225 node.s[2] = obj.scale[1]
226 node.pstr_name = emplace_string( obj.name )
227
228 # Process entity data
229 #
230 node.offset = entdata_length
231 classtype = obj.cv_data.classtype
232
233 if classtype == 'k_classtype_gate':
234 node.classtype = 1
235 entdata_length += sizeof( classtype_gate )
236
237 gate = classtype_gate()
238 gate.target = 0
239 if obj.cv_data.target != None:
240 gate.target = obj.cv_data.target.cv_data.uid
241
242 if obj.type == 'MESH':
243 gate.dims[0] = obj.data.cv_data.v0[0]
244 gate.dims[1] = obj.data.cv_data.v0[1]
245 gate.dims[2] = obj.data.cv_data.v0[2]
246 else:
247 gate.dims[0] = obj.cv_data.v0[0]
248 gate.dims[1] = obj.cv_data.v0[1]
249 gate.dims[2] = obj.cv_data.v0[2]
250
251 entdata_buffer += [gate]
252
253 elif classtype == 'k_classtype_block':
254 node.classtype = 2
255 entdata_length += sizeof( classtype_block )
256
257 source = obj.data.cv_data
258
259 block = classtype_block()
260 block.bbx[0][0] = source.v0[0]
261 block.bbx[0][1] = source.v0[2]
262 block.bbx[0][2] = -source.v1[1]
263
264 block.bbx[1][0] = source.v1[0]
265 block.bbx[1][1] = source.v1[2]
266 block.bbx[1][2] = -source.v0[1]
267 entdata_buffer += [block]
268
269 elif classtype == 'k_classtype_spawn':
270 node.classtype = 3
271
272 elif classtype == 'k_classtype_water':
273 node.classtype = 4
274 elif classtype == 'k_classtype_car_path':
275 node.classtype = 5
276 entdata_length += sizeof( classtype_car_path )
277
278 pn = classtype_car_path()
279 pn.target = 0
280 pn.target1 = 0
281
282 if obj.cv_data.target != None:
283 pn.target = obj.cv_data.target.cv_data.uid
284 if obj.cv_data.target1 != None:
285 pn.target1 = obj.cv_data.target1.cv_data.uid
286
287 entdata_buffer += [pn]
288 elif obj.is_instancer:
289 target = obj.instance_collection
290
291 node.classtype = 6
292 entdata_length += sizeof( classtype_instance )
293
294 inst = classtype_instance()
295 inst.pstr_file = emplace_string( F"models/{target.name}.mdl" )
296 entdata_buffer += [inst]
297 elif classtype == 'k_classtype_capsule':
298 node.classtype = 7
299 elif classtype == 'k_classtype_route_node':
300 node.classtype = 8
301 entdata_length += sizeof( classtype_route_node )
302
303 rn = classtype_route_node()
304 if obj.cv_data.target != None:
305 rn.target = obj.cv_data.target.cv_data.uid
306 if obj.cv_data.target1 != None:
307 rn.target1 = obj.cv_data.target1.cv_data.uid
308
309 entdata_buffer += [rn]
310 elif classtype == 'k_classtype_route':
311 node.classtype = 9
312 entdata_length += sizeof( classtype_route )
313 r = classtype_route()
314 r.pstr_name = emplace_string("not-implemented")
315 r.colour[0] = obj.cv_data.colour[0]
316 r.colour[1] = obj.cv_data.colour[1]
317 r.colour[2] = obj.cv_data.colour[2]
318
319 if obj.cv_data.target != None:
320 r.id_start = obj.cv_data.target.cv_data.uid
321
322 entdata_buffer += [r]
323
324 # classtype == 'k_classtype_none':
325 else:
326 node.classtype = 0
327 node.offset = 0
328
329 # Process meshes
330 #
331 node.submesh_start = header.submesh_count
332 node.submesh_count = 0
333
334 if obj.type == 'MESH':
335 default_mat = c_uint32(69)
336 default_mat.name = ""
337
338 # Dont use the cache if we have modifiers that affect the normals
339 #
340 use_cache = True
341 for mod in obj.modifiers:
342 if mod.type == 'DATA_TRANSFER':
343 use_cache = False
344
345 if use_cache and obj.data.name in mesh_cache:
346 ref = mesh_cache[obj.data.name]
347 node.submesh_start = ref.submesh_start
348 node.submesh_count = ref.submesh_count
349 node_buffer += [node]
350 continue
351
352 dgraph = bpy.context.evaluated_depsgraph_get()
353 data = obj.evaluated_get(dgraph).data
354 data.calc_loop_triangles()
355 data.calc_normals_split()
356
357 mat_list = data.materials if len(data.materials) > 0 else [default_mat]
358 for material_id, mat in enumerate(mat_list):
359 mref = {}
360
361 sm = mdl_submesh()
362 sm.indice_start = header.indice_count
363 sm.vertex_start = header.vertex_count
364 sm.vertex_count = 0
365 sm.indice_count = 0
366 sm.material_id = emplace_material( mat )
367
368 for i in range(3):
369 sm.bbx[0][i] = 999999
370 sm.bbx[1][i] = -999999
371
372 boffa = {}
373
374 # Write the vertex / indice data
375 #
376 for tri_index, tri in enumerate(data.loop_triangles):
377 if tri.material_index != material_id:
378 continue
379
380 for j in range(3):
381 vert = data.vertices[tri.vertices[j]]
382 li = tri.loops[j]
383
384 co = vert.co
385 norm = data.loops[li].normal
386 uv = (0,0)
387 colour = (1,1,1,1)
388 if data.uv_layers:
389 uv = data.uv_layers.active.data[li].uv
390 if data.vertex_colors:
391 colour = data.vertex_colors.active.data[li].color
392
393 TOLERENCE = 4
394 m = float(10**TOLERENCE)
395
396 key = (int(co[0]*m+0.5),\
397 int(co[1]*m+0.5),\
398 int(co[2]*m+0.5),\
399 int(norm[0]*m+0.5),\
400 int(norm[1]*m+0.5),\
401 int(norm[2]*m+0.5),\
402 int(uv[0]*m+0.5),\
403 int(uv[1]*m+0.5),\
404 int(colour[0]*m+0.5),\
405 int(colour[1]*m+0.5),\
406 int(colour[2]*m+0.5),\
407 int(colour[3]*m+0.5))
408
409 if key in boffa:
410 indice_buffer += [boffa[key]]
411 else:
412 index = c_uint32(sm.vertex_count)
413 sm.vertex_count += 1
414
415 boffa[key] = index
416 indice_buffer += [index]
417
418 v = mdl_vert()
419 v.co[0] = co[0]
420 v.co[1] = co[2]
421 v.co[2] = -co[1]
422 v.norm[0] = norm[0]
423 v.norm[1] = norm[2]
424 v.norm[2] = -norm[1]
425 v.uv[0] = uv[0]
426 v.uv[1] = uv[1]
427 v.colour[0] = colour[0]
428 v.colour[1] = colour[1]
429 v.colour[2] = colour[2]
430 v.colour[3] = colour[3]
431 vertex_buffer += [v]
432
433 for i in range(3):
434 sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
435 sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
436
437 sm.indice_count += 1
438
439 if sm.vertex_count == 0:
440 for j in range(2):
441 for i in range(3):
442 sm.bbx[j][i] = 0
443
444 submesh_buffer += [sm]
445 node.submesh_count += 1
446 header.submesh_count += 1
447 header.vertex_count += sm.vertex_count
448 header.indice_count += sm.indice_count
449
450 mesh_cache[obj.data.name] = node
451 node_buffer += [node]
452
453 # Write data arrays
454 #
455 print( "Writing data" )
456 fpos = sizeof(header)
457
458 header.node_offset = fpos
459 fpos += sizeof(mdl_node)*header.node_count
460
461 header.submesh_offset = fpos
462 fpos += sizeof(mdl_submesh)*header.submesh_count
463
464 header.material_offset = fpos
465 fpos += sizeof(mdl_material)*header.material_count
466
467 header.entdata_offset = fpos
468 fpos += entdata_length
469
470 header.vertex_offset = fpos
471 fpos += sizeof(mdl_vert)*header.vertex_count
472
473 header.indice_offset = fpos
474 fpos += sizeof(c_uint32)*header.indice_count
475
476 header.strings_offset = fpos
477 fpos += len(strings_buffer)
478
479 header.file_length = fpos
480
481 fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb")
482 fp.write( bytearray( header ) )
483
484 for node in node_buffer:
485 fp.write( bytearray(node) )
486 for sm in submesh_buffer:
487 fp.write( bytearray(sm) )
488 for mat in material_buffer:
489 fp.write( bytearray(mat) )
490 for ed in entdata_buffer:
491 fp.write( bytearray(ed) )
492 for v in vertex_buffer:
493 fp.write( bytearray(v) )
494 for i in indice_buffer:
495 fp.write( bytearray(i) )
496 fp.write( strings_buffer )
497 fp.close()
498
499 print( F"Completed {name}.mdl" )
500
501 # Clicky clicky GUI
502 # ------------------------------------------------------------------------------
503
504 cv_view_draw_handler = None
505 cv_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
506
507 def cv_draw():
508 global cv_view_shader
509 cv_view_shader.bind()
510 gpu.state.depth_mask_set(False)
511 gpu.state.line_width_set(2.0)
512 gpu.state.face_culling_set('BACK')
513 gpu.state.depth_test_set('LESS')
514 gpu.state.blend_set('NONE')
515
516 verts = []
517 colours = []
518
519 #def drawbezier(p0,h0,p1,h1,c0,c1):
520 # nonlocal verts, colours
521
522 # verts += [p0]
523 # verts += [h0]
524 # colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
525 # verts += [p1]
526 # verts += [h1]
527 # colours += [(1.0,1.0,1,1),(1,1,1,1)]
528 #
529 # last = p0
530 # for i in range(10):
531 # t = (i+1)/10
532 # a0 = 1-t
533
534 # tt = t*t
535 # ttt = tt*t
536 # p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
537 # verts += [(last[0],last[1],last[2])]
538 # verts += [(p[0],p[1],p[2])]
539 # colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
540 # last = p
541
542 course_count = 0
543
544 def drawbhandle(obj, direction, colour):
545 nonlocal verts, colours
546 p0 = obj.location
547 h0 = obj.matrix_world @ Vector((0,direction,0))
548 verts += [p0]
549 verts += [h0]
550 colours += [colour,colour]
551
552 def drawbezier(p0,h0,p1,h1,c0,c1):
553 nonlocal verts, colours
554
555 last = p0
556 for i in range(10):
557 t = (i+1)/10
558 a0 = 1-t
559
560 tt = t*t
561 ttt = tt*t
562 p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
563 verts += [(last[0],last[1],last[2])]
564 verts += [(p[0],p[1],p[2])]
565 colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
566 last = p
567
568 def drawsbpath(o0,o1,c0,c1,s0,s1):
569 nonlocal course_count
570
571 offs = ((course_count % 2)*2-1) * course_count * 0.02
572
573 p0 = o0.matrix_world @ Vector((offs, 0,0))
574 h0 = o0.matrix_world @ Vector((offs, s0,0))
575 p1 = o1.matrix_world @ Vector((offs, 0,0))
576 h1 = o1.matrix_world @ Vector((offs,-s1,0))
577 drawbezier(p0,h0,p1,h1,c0,c1)
578
579 def drawbpath(o0,o1,c0,c1):
580 drawsbpath(o0,o1,c0,c1,1.0,1.0)
581
582 def drawbline(p0,p1,c0,c1):
583 nonlocal verts, colours
584 verts += [p0,p1]
585 colours += [c0,c1]
586
587 for obj in bpy.context.collection.objects:
588
589 if obj.cv_data.classtype == 'k_classtype_gate':
590 if obj.type == 'MESH':
591 dims = obj.data.cv_data.v0
592 else:
593 dims = obj.cv_data.v0
594
595 vs = [None]*9
596 c = Vector((0,0,dims[2]))
597
598 vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
599 vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
600 vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
601 vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
602 vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
603 vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
604 vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
605 vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
606 vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
607
608 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
609
610 for l in indices:
611 v0 = vs[l[0]]
612 v1 = vs[l[1]]
613 verts += [(v0[0],v0[1],v0[2])]
614 verts += [(v1[0],v1[1],v1[2])]
615 colours += [(1,1,0,1),(1,1,0,1)]
616
617 sw = (0.4,0.4,0.4,0.2)
618 if obj.cv_data.target != None:
619 drawbline( obj.location, obj.cv_data.target.location, sw,sw )
620
621 elif obj.cv_data.classtype == 'k_classtype_route_node':
622 sw = Vector((0.4,0.4,0.4,0.2))
623 sw2 = Vector((1.5,0.2,0.2,0.0))
624 if obj.cv_data.target != None:
625 drawbpath( obj, obj.cv_data.target, sw, sw )
626 if obj.cv_data.target1 != None:
627 drawbpath( obj, obj.cv_data.target1, sw, sw )
628
629 drawbhandle( obj, 1.0, (0.8,0.8,0.8,1.0) )
630 drawbhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
631
632 p1 = obj.location+ \
633 obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
634 drawbline( obj.location, p1, sw,sw2 )
635
636
637 elif obj.cv_data.classtype == 'k_classtype_block':
638 a = obj.data.cv_data.v0
639 b = obj.data.cv_data.v1
640
641 vs = [None]*8
642 vs[0] = obj.matrix_world @ Vector((a[0], a[1], a[2]))
643 vs[1] = obj.matrix_world @ Vector((a[0], b[1], a[2]))
644 vs[2] = obj.matrix_world @ Vector((b[0], b[1], a[2]))
645 vs[3] = obj.matrix_world @ Vector((b[0], a[1], a[2]))
646 vs[4] = obj.matrix_world @ Vector((a[0], a[1], b[2]))
647 vs[5] = obj.matrix_world @ Vector((a[0], b[1], b[2]))
648 vs[6] = obj.matrix_world @ Vector((b[0], b[1], b[2]))
649 vs[7] = obj.matrix_world @ Vector((b[0], a[1], b[2]))
650
651 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
652 (0,4),(1,5),(2,6),(3,7)]
653
654 for l in indices:
655 v0 = vs[l[0]]
656 v1 = vs[l[1]]
657 verts += [(v0[0],v0[1],v0[2])]
658 verts += [(v1[0],v1[1],v1[2])]
659 colours += [(1,1,0,1),(1,1,0,1)]
660
661 elif obj.cv_data.classtype == 'k_classtype_capsule':
662 h = obj.data.cv_data.v0[0]
663 r = obj.data.cv_data.v0[1]
664
665 vs = [None]*10
666 vs[0] = obj.matrix_world @ Vector((0.0,0.0, h*0.5 ))
667 vs[1] = obj.matrix_world @ Vector((0.0,0.0,-h*0.5 ))
668 vs[2] = obj.matrix_world @ Vector(( r,0.0, h*0.5-r))
669 vs[3] = obj.matrix_world @ Vector(( -r,0.0, h*0.5-r))
670 vs[4] = obj.matrix_world @ Vector(( r,0.0,-h*0.5+r))
671 vs[5] = obj.matrix_world @ Vector(( -r,0.0,-h*0.5+r))
672 vs[6] = obj.matrix_world @ Vector((0.0, r , h*0.5-r))
673 vs[7] = obj.matrix_world @ Vector((0.0,-r , h*0.5-r))
674 vs[8] = obj.matrix_world @ Vector((0.0, r ,-h*0.5+r))
675 vs[9] = obj.matrix_world @ Vector((0.0,-r ,-h*0.5+r))
676
677 indices = [(0,1),(2,3),(4,5),(6,7),(8,9)]
678
679 for l in indices:
680 v0 = vs[l[0]]
681 v1 = vs[l[1]]
682 verts += [(v0[0],v0[1],v0[2])]
683 verts += [(v1[0],v1[1],v1[2])]
684 colours += [(0.5,1,0,1),(0.5,1,0,1)]
685
686 elif obj.cv_data.classtype == 'k_classtype_spawn':
687 vs = [None]*4
688 vs[0] = obj.matrix_world @ Vector((0,0,0))
689 vs[1] = obj.matrix_world @ Vector((0,2,0))
690 vs[2] = obj.matrix_world @ Vector((0.5,1,0))
691 vs[3] = obj.matrix_world @ Vector((-0.5,1,0))
692 indices = [(0,1),(1,2),(1,3)]
693 for l in indices:
694 v0 = vs[l[0]]
695 v1 = vs[l[1]]
696 verts += [(v0[0],v0[1],v0[2])]
697 verts += [(v1[0],v1[1],v1[2])]
698 colours += [(0,1,1,1),(0,1,1,1)]
699
700 elif obj.cv_data.classtype == 'k_classtype_route':
701 vs = [None]*2
702 vs[0] = obj.location
703 vs[1] = obj.cv_data.target.location
704 indices = [(0,1)]
705 for l in indices:
706 v0 = vs[l[0]]
707 v1 = vs[l[1]]
708 verts += [(v0[0],v0[1],v0[2])]
709 verts += [(v1[0],v1[1],v1[2])]
710 colours += [(0,1,1,1),(0,1,1,1)]
711
712 stack = [None]*64
713 stack_i = [0]*64
714 stack[0] = obj.cv_data.target
715 si = 1
716 loop_complete = False
717
718 while si > 0:
719 if stack_i[si-1] == 2:
720 si -= 1
721 continue
722
723 if si == 0: # Loop failed to complete
724 break
725
726 node = stack[si-1]
727
728 targets = [None,None]
729 targets[0] = node.cv_data.target
730
731 if node.cv_data.classtype == 'k_classtype_route_node':
732 targets[1] = node.cv_data.target1
733
734 nextnode = targets[stack_i[si-1]]
735 stack_i[si-1] += 1
736
737 if nextnode != None: # branch
738 if nextnode == stack[0]: # Loop completed
739 loop_complete = True
740 break
741
742 valid=True
743 for sj in range(si):
744 if stack[sj] == nextnode: # invalidated path
745 valid=False
746 break
747
748 if valid:
749 stack_i[si] = 0
750 stack[si] = nextnode
751 si += 1
752 continue
753
754 if loop_complete:
755 course_colours = [Vector((0,0.8,0.2,1.0)), \
756 Vector((0,0.3,0.9,1.0)), \
757 Vector((0.4,0.0,0.8,1.0)),\
758 Vector((0.5,0.8,0.0,1.0)),\
759 Vector((0.0,0.7,0.6,1.0)),\
760 Vector((0.2,0.9,0.5,1.0)) ]
761
762 cc = course_colours[ course_count % len(course_colours) ]
763
764 for sj in range(si):
765 sk = (sj+1)%si
766
767 if stack[sj].cv_data.classtype == 'k_classtype_gate' and \
768 stack[sk].cv_data.classtype == 'k_classtype_gate':
769 dist = (stack[sj].location-stack[sk].location).magnitude
770 drawsbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
771
772 else:
773 drawbpath( stack[sj], stack[sk], cc, cc )
774
775 course_count += 1
776
777 elif obj.cv_data.classtype == 'k_classtype_car_path':
778 v0 = obj.matrix_world.to_quaternion() @ Vector((0,1,0))
779 c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0))
780 drawbhandle( obj, 1.0, (0.9,0.9,0.9,1.0) )
781
782 if obj.cv_data.target != None:
783 v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((0,1,0))
784 c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
785
786 drawbhandle( obj.cv_data.target, -1.0, (0.5,0.5,0.5,1.0) )
787 drawbpath( obj, obj.cv_data.target, c0, c1 )
788
789 if obj.cv_data.target1 != None:
790 v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((0,1,0))
791 c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
792
793 drawbhandle( obj.cv_data.target1, -1.0, (0.5,0.5,0.5,1.0) )
794 drawbpath( obj, obj.cv_data.target1, c0, c1 )
795
796 lines = batch_for_shader(\
797 cv_view_shader, 'LINES', \
798 { "pos":verts, "color":colours })
799
800 lines.draw( cv_view_shader )
801
802 def cv_poll_target(scene, obj):
803 if obj == bpy.context.active_object:
804 return False
805 if obj.cv_data.classtype == 'k_classtype_none':
806 return False
807 return True
808
809 class CV_MESH_SETTINGS(bpy.types.PropertyGroup):
810 v0: bpy.props.FloatVectorProperty(name="v0",size=3)
811 v1: bpy.props.FloatVectorProperty(name="v1",size=3)
812 v2: bpy.props.FloatVectorProperty(name="v2",size=3)
813 v3: bpy.props.FloatVectorProperty(name="v3",size=3)
814
815 class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
816 uid: bpy.props.IntProperty( name="" )
817
818 target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
819 poll=cv_poll_target )
820 target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
821 poll=cv_poll_target )
822
823 colour: bpy.props.FloatVectorProperty(name="colour",subtype='COLOR',\
824 min=0.0,max=1.0)
825
826 classtype: bpy.props.EnumProperty(
827 name="Format",
828 items = [
829 ('k_classtype_none', "k_classtype_none", "", 0),
830 ('k_classtype_gate', "k_classtype_gate", "", 1),
831 ('k_classtype_block', "k_classtype_block", "", 2),
832 ('k_classtype_spawn', "k_classtype_spawn", "", 3),
833 ('k_classtype_water', "k_classtype_water", "", 4),
834 ('k_classtype_car_path', "k_classtype_car_path", "", 5),
835 ('k_classtype_capsule', "k_classtype_capsule", "", 7 ),
836 ('k_classtype_route_node', "k_classtype_route_node", "", 8 ),
837 ('k_classtype_route', "k_classtype_route", "", 9 )
838 ])
839
840 class CV_OBJ_PANEL(bpy.types.Panel):
841 bl_label="Entity Config"
842 bl_idname="SCENE_PT_cv_entity"
843 bl_space_type='PROPERTIES'
844 bl_region_type='WINDOW'
845 bl_context="object"
846
847 def draw(_,context):
848 active_object = bpy.context.active_object
849 if active_object == None: return
850 _.layout.prop( active_object.cv_data, "classtype" )
851
852 if active_object.cv_data.classtype == 'k_classtype_gate':
853 _.layout.prop( active_object.cv_data, "target" )
854
855 mesh = active_object.data
856 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
857 _.layout.prop( mesh.cv_data, "v0" )
858
859 elif active_object.cv_data.classtype == 'k_classtype_car_path' or \
860 active_object.cv_data.classtype == 'k_classtype_route_node':
861 _.layout.prop( active_object.cv_data, "target" )
862 _.layout.prop( active_object.cv_data, "target1" )
863
864 elif active_object.cv_data.classtype == 'k_classtype_route':
865 _.layout.prop( active_object.cv_data, "target" )
866 _.layout.prop( active_object.cv_data, "colour" )
867
868 elif active_object.cv_data.classtype == 'k_classtype_block':
869 mesh = active_object.data
870
871 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
872 _.layout.prop( mesh.cv_data, "v0" )
873 _.layout.prop( mesh.cv_data, "v1" )
874 _.layout.prop( mesh.cv_data, "v2" )
875 _.layout.prop( mesh.cv_data, "v3" )
876 elif active_object.cv_data.classtype == 'k_classtype_capsule':
877 mesh = active_object.data
878 _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
879 _.layout.prop( mesh.cv_data, "v0" )
880
881 class CV_INTERFACE(bpy.types.Panel):
882 bl_idname = "VIEW3D_PT_carve"
883 bl_label = "Carve"
884 bl_space_type = 'VIEW_3D'
885 bl_region_type = 'UI'
886 bl_category = "Carve"
887
888 def draw(_, context):
889 layout = _.layout
890 layout.operator( "carve.compile_all" )
891
892 def test_compile():
893 for col in bpy.data.collections["export"].children:
894 write_model( col.name )
895
896 class CV_COMPILE(bpy.types.Operator):
897 bl_idname="carve.compile_all"
898 bl_label="Compile All"
899
900 def execute(_,context):
901 test_compile()
902 #cProfile.runctx("test_compile()",globals(),locals(),sort=1)
903 #for col in bpy.data.collections["export"].children:
904 # write_model( col.name )
905
906 return {'FINISHED'}
907
908 classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\
909 CV_MESH_SETTINGS]
910
911 def register():
912 global cv_view_draw_handler
913
914 for c in classes:
915 bpy.utils.register_class(c)
916
917 bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS)
918 bpy.types.Mesh.cv_data = bpy.props.PointerProperty(type=CV_MESH_SETTINGS)
919
920 cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
921 cv_draw,(),'WINDOW','POST_VIEW')
922
923 def unregister():
924 global cv_view_draw_handler
925
926 for c in classes:
927 bpy.utils.unregister_class(c)
928
929 bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')