some gate improvements
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
1 import bpy, math, gpu, os
2 import cProfile
3 from ctypes import *
4 from mathutils import *
5 from gpu_extras.batch import batch_for_shader
6 from bpy_extras import mesh_utils
7
8 bl_info = {
9 "name":"Skaterift .mdl exporter",
10 "author": "Harry Godden (hgn)",
11 "version": (0,2),
12 "blender":(3,1,0),
13 "location":"Export",
14 "description":"",
15 "warning":"",
16 "wiki_url":"",
17 "category":"Import/Export",
18 }
19
20 sr_entity_list = [
21 ('none', 'None', '', 0 ),
22 ('ent_gate', 'Gate', '', 1 ),
23 ('ent_spawn', 'Spawn Point', '', 2 ),
24 ('ent_route_node', 'Routing Path', '', 3 ),
25 ('ent_route', 'Skate Course', '', 4 ),
26 ('ent_water', 'Water Surface', '', 5 ),
27 ('ent_volume', 'Volume/Trigger', '', 6 ),
28 ('ent_audio', 'Audio', '', 7 ),
29 ('ent_marker', 'Marker', '', 8 ),
30 ('ent_font', 'Font', '', 9 ),
31 ('ent_font_variant', 'Font:Variant', '', 10 ),
32 ('ent_traffic', 'Traffic Model', '', 11 ),
33 ('ent_skateshop', 'Skate Shop', '', 12 ),
34 ('ent_camera', 'Camera', '', 13 ),
35 ('ent_swspreview', 'Workshop Preview', '', 14 ),
36 ('ent_menuitem', 'Menu Item', '', 15 ),
37 ('ent_worldinfo', 'World Info', '', 16 ),
38 ('ent_ccmd', 'CCmd', '', 17 ),
39 ('ent_challenge', 'Challenge', '', 18 )
40 ]
41
42 MDL_VERSION_NR = 102
43
44 def get_entity_enum_id( alias ):
45 #{
46 for et in sr_entity_list:#{
47 if et[0] == alias:#{
48 return et[3]
49 #}
50 #}
51
52 return 0
53 #}
54
55 class mdl_vert(Structure): # 48 bytes. Quite large. Could compress
56 #{ # the normals and uvs to i16s. Not an
57 _pack_ = 1 # real issue, yet.
58 _fields_ = [("co",c_float*3),
59 ("norm",c_float*3),
60 ("uv",c_float*2),
61 ("colour",c_uint8*4),
62 ("weights",c_uint16*4),
63 ("groups",c_uint8*4)]
64 #}
65
66 class mdl_transform(Structure):
67 #{
68 _fields_ = [("co",c_float*3),
69 ( "s",c_float*3),
70 ( "q",c_float*4)]
71 #}
72
73 class mdl_submesh(Structure):
74 #{
75 _fields_ = [("indice_start",c_uint32),
76 ("indice_count",c_uint32),
77 ("vertex_start",c_uint32),
78 ("vertex_count",c_uint32),
79 ("bbx",(c_float*3)*2),
80 ("material_id",c_uint16), # index into the material array
81 ("flags",c_uint16)]
82 #}
83
84 class mdl_material(Structure):
85 #{
86 _fields_ = [("pstr_name",c_uint32),
87 ("shader",c_uint32),
88 ("flags",c_uint32),
89 ("surface_prop",c_uint32),
90 ("colour",c_float*4),
91 ("colour1",c_float*4),
92 ("tex_diffuse",c_uint32),
93 ("tex_none0",c_uint32),
94 ("tex_none1",c_uint32)]
95 #}
96
97 class mdl_bone(Structure):
98 #{
99 _fields_ = [("co",c_float*3),("end",c_float*3),
100 ("parent",c_uint32),
101 ("collider",c_uint32),
102 ("ik_target",c_uint32),
103 ("ik_pole",c_uint32),
104 ("flags",c_uint32),
105 ("pstr_name",c_uint32),
106 ("hitbox",(c_float*3)*2),
107 ("conevx",c_float*3),("conevy",c_float*3),("coneva",c_float*3),
108 ("conet",c_float)]
109 #}
110
111 class mdl_armature(Structure):
112 #{
113 _fields_ = [("transform",mdl_transform),
114 ("bone_start",c_uint32),
115 ("bone_count",c_uint32),
116 ("anim_start",c_uint32),
117 ("anim_count",c_uint32)]
118 #}
119
120 class mdl_animation(Structure):
121 #{
122 _fields_ = [("pstr_name",c_uint32),
123 ("length",c_uint32),
124 ("rate",c_float),
125 ("keyframe_start",c_uint32)]
126 #}
127
128 class mdl_mesh(Structure):
129 #{
130 _fields_ = [("transform",mdl_transform),
131 ("submesh_start",c_uint32),
132 ("submesh_count",c_uint32),
133 ("pstr_name",c_uint32),
134 ("entity_id",c_uint32),
135 ("armature_id",c_uint32)]
136 #}
137
138 class mdl_file(Structure):
139 #{
140 _fields_ = [("path",c_uint32),
141 ("pack_offset",c_uint32),
142 ("pack_size",c_uint32)]
143 #}
144
145 class mdl_texture(Structure):
146 #{
147 _fields_ = [("file",mdl_file),
148 ("glname",c_uint32)]
149 #}
150
151 class mdl_array(Structure):
152 #{
153 _fields_ = [("file_offset",c_uint32),
154 ("item_count",c_uint32),
155 ("item_size",c_uint32),
156 ("name",c_byte*16)]
157 #}
158
159 class mdl_header(Structure):
160 #{
161 _fields_ = [("version",c_uint32),
162 ("arrays",mdl_array)]
163 #}
164
165 class ent_spawn(Structure):
166 #{
167 _fields_ = [("transform",mdl_transform),
168 ("pstr_name",c_uint32)]
169 #}
170
171 class ent_light(Structure):
172 #{
173 _fields_ = [("transform",mdl_transform),
174 ("daytime",c_uint32),
175 ("type",c_uint32),
176 ("colour",c_float*4),
177 ("angle",c_float),
178 ("range",c_float),
179 ("inverse_world",(c_float*3)*4), # Runtime
180 ("angle_sin_cos",(c_float*2))] # Runtime
181 #}
182
183 class version_refcount_union(Union):
184 #{
185 _fields_ = [("timing_version",c_uint32),
186 ("ref_count",c_uint8)]
187 #}
188
189 class ent_gate(Structure):
190 #{
191 _fields_ = [("flags",c_uint32),
192 ("target", c_uint32),
193 ("key",c_uint32),
194 ("dimensions", c_float*3),
195 ("co", (c_float*3)*2),
196 ("q", (c_float*4)*2),
197 ("to_world",(c_float*3)*4),
198 ("transport",(c_float*3)*4),
199 ("_anonymous_union",version_refcount_union),
200 ("timing_time",c_double),
201 ("routes",c_uint16*4),
202 ("route_count",c_uint8),
203 ("submesh_start",c_uint32), # v102+
204 ("submesh_count",c_uint32), # v102+ (can be 0)
205 ]
206 #}
207
208 class ent_route_node(Structure):
209 #{
210 _fields_ = [("co",c_float*3),
211 ("ref_count",c_uint8),
212 ("ref_total",c_uint8)]
213 #}
214
215 class ent_path_index(Structure):
216 #{
217 _fields_ = [("index",c_uint16)]
218 #}
219
220 class vg_audio_clip(Structure):
221 #{
222 _fields_ = [("path",c_uint64),
223 ("flags",c_uint32),
224 ("size",c_uint32),
225 ("data",c_uint64)]
226 #}
227
228 class union_file_audio_clip(Union):
229 #{
230 _fields_ = [("file",mdl_file),
231 ("reserved",vg_audio_clip)]
232 #}
233
234 class ent_audio_clip(Structure):
235 #{
236 _fields_ = [("_anon",union_file_audio_clip),
237 ("probability",c_float)]
238 #}
239
240 class ent_checkpoint(Structure):
241 #{
242 _fields_ = [("gate_index",c_uint16),
243 ("path_start",c_uint16),
244 ("path_count",c_uint16)]
245 #}
246
247 class ent_route(Structure):
248 #{
249 _fields_ = [("transform",mdl_transform),
250 ("pstr_name",c_uint32),
251 ("checkpoints_start",c_uint16),
252 ("checkpoints_count",c_uint16),
253 ("colour",c_float*4),
254 ("active",c_uint32), #runtime
255 ("factive",c_float),
256 ("board_transform",(c_float*3)*4),
257 ("sm",mdl_submesh),
258 ("latest_pass",c_double)]
259 #}
260
261 class ent_water(Structure):
262 #{
263 _fields_ = [("transform",mdl_transform),
264 ("max_dist",c_float),
265 ("reserved0",c_uint32),
266 ("reserved1",c_uint32)]
267 #}
268
269 class volume_trigger(Structure):
270 #{
271 _fields_ = [("event",c_uint32),
272 ("blank",c_uint32)]
273 #}
274
275 class volume_particles(Structure):
276 #{
277 _fields_ = [("blank",c_uint32),
278 ("blank2",c_uint32)]
279 #}
280
281 class volume_union(Union):
282 #{
283 _fields_ = [("trigger",volume_trigger),
284 ("particles",volume_particles)]
285 #}
286
287 class ent_volume(Structure):
288 #{
289 _fields_ = [("transform",mdl_transform),
290 ("to_world",(c_float*3)*4),
291 ("to_local",(c_float*3)*4),
292 ("type",c_uint32),
293 ("target",c_uint32),
294 ("_anon",volume_union)]
295 #}
296
297 class ent_audio(Structure):
298 #{
299 _fields_ = [("transform",mdl_transform),
300 ("flags",c_uint32),
301 ("clip_start",c_uint32),
302 ("clip_count",c_uint32),
303 ("volume",c_float),
304 ("crossfade",c_float),
305 ("channel_behaviour",c_uint32),
306 ("group",c_uint32),
307 ("probability_curve",c_uint32),
308 ("max_channels",c_uint32)]
309 #}
310
311 class ent_marker(Structure):
312 #{
313 _fields_ = [("transform",mdl_transform),
314 ("name",c_uint32)]
315 #}
316
317 class ent_glyph(Structure):
318 #{
319 _fields_ = [("size",c_float*2),
320 ("indice_start",c_uint32),
321 ("indice_count",c_uint32)]
322 #}
323
324 class ent_font_variant(Structure):
325 #{
326 _fields_ = [("name",c_uint32),
327 ("material_id",c_uint32)]
328 #}
329
330 class ent_font(Structure):
331 #{
332 _fields_ = [("alias",c_uint32),
333 ("variant_start",c_uint32),
334 ("variant_count",c_uint32),
335 ("glyph_start",c_uint32),
336 ("glyph_count",c_uint32),
337 ("glyph_utf32_base",c_uint32)]
338 #}
339
340 class ent_traffic(Structure):
341 #{
342 _fields_ = [("transform",mdl_transform),
343 ("submesh_start",c_uint32),
344 ("submesh_count",c_uint32),
345 ("start_node",c_uint32),
346 ("node_count",c_uint32),
347 ("speed",c_float),
348 ("t",c_float),
349 ("index",c_uint32)]
350 #}
351
352 # Skateshop
353 # ---------------------------------------------------------------
354 class ent_skateshop_characters(Structure):
355 #{
356 _fields_ = [("id_display",c_uint32),
357 ("id_info",c_uint32)]
358 #}
359 class ent_skateshop_boards(Structure):
360 #{
361 _fields_ = [("id_display",c_uint32),
362 ("id_info",c_uint32),
363 ("id_rack",c_uint32)]
364 #}
365 class ent_skateshop_worlds(Structure):
366 #{
367 _fields_ = [("id_display",c_uint32),
368 ("id_info",c_uint32)]
369 #}
370 class ent_skateshop_anon_union(Union):
371 #{
372 _fields_ = [("boards",ent_skateshop_boards),
373 ("character",ent_skateshop_characters),
374 ("worlds",ent_skateshop_worlds)]
375 #}
376 class ent_skateshop(Structure):
377 #{
378 _fields_ = [("transform",mdl_transform), ("type",c_uint32),
379 ("id_camera",c_uint32),
380 ("_anonymous_union",ent_skateshop_anon_union)]
381 #}
382
383 class ent_swspreview(Structure):
384 #{
385 _fields_ = [("id_camera",c_uint32),
386 ("id_display",c_uint32),
387 ("id_display1",c_uint32)]
388 #}
389
390 # Menu
391 # -----------------------------------------------------------------
392 class ent_menuitem_slider(Structure):
393 #{
394 _fields_ = [("id_min",c_uint32),
395 ("id_max",c_uint32),
396 ("id_handle",c_uint32),
397 ("pstr_data",c_uint32)]
398 #}
399 class ent_menuitem_button(Structure):
400 #{
401 _fields_ = [("pstr",c_uint32)]
402 #}
403 class ent_menuitem_checkmark(Structure):
404 #{
405 _fields_ = [("id_check",c_uint32),
406 ("pstr_data",c_uint32),
407 ("offset",c_float*3)]
408 #}
409 class ent_menuitem_page(Structure):
410 #{
411 _fields_ = [("pstr_name",c_uint32),
412 ("id_entrypoint",c_uint32),
413 ("id_viewpoint",c_uint32)]
414 #}
415 class ent_menuitem_anon_union(Union):
416 #{
417 _fields_ = [("slider",ent_menuitem_slider),
418 ("button",ent_menuitem_button),
419 ("checkmark",ent_menuitem_checkmark),
420 ("page",ent_menuitem_page)]
421 #}
422 class ent_menuitem(Structure):
423 #{
424 _fields_ = [("type",c_uint32), ("groups",c_uint32),
425 ("id_links",c_uint32*4),
426 ("factive",c_float), ("fvisible",c_float),
427 #-- TODO: Refactor this into a simple mesh structure
428 ("transform",mdl_transform),
429 ("submesh_start",c_uint32),("submesh_count",c_uint32),
430 ("_u64",c_uint64),
431 #-- end
432 ("_anonymous_union", ent_menuitem_anon_union)]
433 #}
434
435 class ent_camera(Structure):
436 #{
437 _fields_ = [("transform",mdl_transform),
438 ("fov",c_float)]
439 #}
440
441 class ent_worldinfo(Structure):
442 #{
443 _fields_ = [("pstr_name",c_uint32),
444 ("pstr_author",c_uint32), # unused
445 ("pstr_desc",c_uint32), # unused
446 ("timezone",c_float)]
447 #}
448
449 class ent_ccmd(Structure):
450 #{
451 _fields_ = [("pstr_command",c_uint32)]
452 #}
453
454 class ent_challenge(Structure):#{
455 _fields_ = [("transform",mdl_transform),
456 ("submesh_start",c_uint32), ("submesh_count",c_uint32),
457 ("id_next",c_uint32),
458 ("filter",c_uint32),
459 ("time_limit",c_float)]
460
461 sr_functions = { 0: 'trigger',
462 1: 'start_challenge' }
463 #}
464
465 def obj_ent_type( obj ):
466 #{
467 if obj.type == 'ARMATURE': return 'mdl_armature'
468 elif obj.type == 'LIGHT': return 'ent_light'
469 elif obj.type == 'CAMERA': return 'ent_camera'
470 else: return obj.SR_data.ent_type
471 #}
472
473 def sr_filter_ent_type( obj, ent_types ):
474 #{
475 if obj == bpy.context.active_object: return False
476
477 for c0 in obj.users_collection:#{
478 for c1 in bpy.context.active_object.users_collection:#{
479 if c0 == c1:#{
480 return obj_ent_type( obj ) in ent_types
481 #}
482 #}
483 #}
484
485 return False
486 #}
487
488 def v4_dot( a, b ):#{
489 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]
490 #}
491
492 def q_identity( q ):#{
493 q[0] = 0.0
494 q[1] = 0.0
495 q[2] = 0.0
496 q[3] = 1.0
497 #}
498
499 def q_normalize( q ):#{
500 l2 = v4_dot(q,q)
501 if( l2 < 0.00001 ):#{
502 q_identity( q )
503 #}
504 else:#{
505 s = 1.0/math.sqrt(l2)
506 q[0] *= s
507 q[1] *= s
508 q[2] *= s
509 q[3] *= s
510 #}
511 #}
512
513 def compile_obj_transform( obj, transform ):
514 #{
515 co = obj.matrix_world @ Vector((0,0,0))
516
517 # This was changed from matrix_local on 09.05.23
518 q = obj.matrix_world.to_quaternion()
519 s = obj.scale
520 q_normalize( q )
521
522 # Setup transform
523 #
524 transform.co[0] = co[0]
525 transform.co[1] = co[2]
526 transform.co[2] = -co[1]
527 transform.q[0] = q[1]
528 transform.q[1] = q[3]
529 transform.q[2] = -q[2]
530 transform.q[3] = q[0]
531 transform.s[0] = s[0]
532 transform.s[1] = s[2]
533 transform.s[2] = s[1]
534 #}
535
536 def int_align_to( v, align ):
537 #{
538 while(v%align)!=0: v += 1
539 return v
540 #}
541
542 def bytearray_align_to( buffer, align, w=b'\xaa' ):
543 #{
544 while (len(buffer) % align) != 0: buffer.extend(w)
545 return buffer
546 #}
547
548 def bytearray_print_hex( s, w=16 ):
549 #{
550 for r in range((len(s)+(w-1))//w):#{
551 i0=(r+0)*w
552 i1=min((r+1)*w,len(s))
553 print( F'{r*w:06x}| \x1B[31m', end='')
554 print( F"{' '.join('{:02x}'.format(x) for x in s[i0:i1]):<48}",end='' )
555 print( "\x1B[0m", end='')
556 print( ''.join(chr(x) if (x>=33 and x<=126) else '.' for x in s[i0:i1] ) )
557 #}
558 #}
559
560 def sr_compile_string( s ):
561 #{
562 if s in sr_compile.string_cache: return sr_compile.string_cache[s]
563
564 index = len( sr_compile.string_data )
565 sr_compile.string_cache[s] = index
566 sr_compile.string_data.extend( c_uint32(hash_djb2(s)) )
567 sr_compile.string_data.extend( s.encode('utf-8') )
568 sr_compile.string_data.extend( b'\0' )
569
570 bytearray_align_to( sr_compile.string_data, 4 )
571 return index
572 #}
573
574 def material_tex_image(v):
575 #{
576 return {
577 "Image Texture":
578 {
579 "image": F"{v}"
580 }
581 }
582 #}
583
584 cxr_graph_mapping = \
585 {
586 # Default shader setup
587 "Principled BSDF":
588 {
589 "Base Color":
590 {
591 "Image Texture":
592 {
593 "image": "tex_diffuse"
594 },
595 "Mix":
596 {
597 "A": material_tex_image("tex_diffuse"),
598 "B": material_tex_image("tex_decal")
599 },
600 },
601 "Normal":
602 {
603 "Normal Map":
604 {
605 "Color": material_tex_image("tex_normal")
606 }
607 }
608 },
609 "Emission":
610 {
611 "Color": material_tex_image("tex_diffuse")
612 }
613 }
614
615 # https://harrygodden.com/git/?p=convexer.git;a=blob;f=__init__.py;#l1164
616 #
617 def material_info(mat):
618 #{
619 info = {}
620
621 # Using the cxr_graph_mapping as a reference, go through the shader
622 # graph and gather all $props from it.
623 #
624 def _graph_read( node_def, node=None, depth=0 ):#{
625 nonlocal mat
626 nonlocal info
627
628 # Find rootnodes
629 #
630 if node == None:#{
631 _graph_read.extracted = []
632
633 for node_idname in node_def:#{
634 for n in mat.node_tree.nodes:#{
635 if n.name == node_idname:#{
636 node_def = node_def[node_idname]
637 node = n
638 break
639 #}
640 #}
641 #}
642 #}
643
644 for link in node_def:#{
645 link_def = node_def[link]
646
647 if isinstance( link_def, dict ):#{
648 node_link = None
649 for x in node.inputs:#{
650 if isinstance( x, bpy.types.NodeSocketColor ):#{
651 if link == x.name:#{
652 node_link = x
653 break
654 #}
655 #}
656 #}
657
658 if node_link and node_link.is_linked:#{
659 # look for definitions for the connected node type
660 #
661 from_node = node_link.links[0].from_node
662
663 node_name = from_node.name.split('.')[0]
664 if node_name in link_def:#{
665 from_node_def = link_def[ node_name ]
666
667 _graph_read( from_node_def, from_node, depth+1 )
668 #}
669 #}
670 else:#{
671 if "default" in link_def:#{
672 prop = link_def['default']
673 info[prop] = node_link.default_value
674 #}
675 #}
676 #}
677 else:#{
678 prop = link_def
679 info[prop] = getattr( node, link )
680 #}
681 #}
682 #}
683
684 _graph_read( cxr_graph_mapping )
685 return info
686 #}
687
688 def vg_str_bin( s ):
689 #{
690 decoded = bytearray()
691 for i in range(len(s)//2):#{
692 c = (ord(s[i*2+0])-0x41)
693 c |= (ord(s[i*2+1])-0x41)<<4
694 decoded.extend(bytearray(c_uint8(c))) #??
695 #}
696 return decoded
697 #}
698
699 def sr_pack_file( file, path, data ):
700 #{
701 file.path = sr_compile_string( path )
702 file.pack_offset = len( sr_compile.pack_data )
703 file.pack_size = len( data )
704
705 sr_compile.pack_data.extend( data )
706 bytearray_align_to( sr_compile.pack_data, 16 )
707 #}
708
709 def sr_compile_texture( img ):
710 #{
711 if img == None:
712 return 0
713
714 name = os.path.splitext( img.name )[0]
715
716 if name in sr_compile.texture_cache:
717 return sr_compile.texture_cache[name]
718
719 texture_index = (len(sr_compile.texture_data)//sizeof(mdl_texture)) +1
720
721 tex = mdl_texture()
722 tex.glname = 0
723
724 if sr_compile.pack_textures:#{
725 filedata = qoi_encode( img )
726 sr_pack_file( tex.file, name, filedata )
727 #}
728
729 sr_compile.texture_cache[name] = texture_index
730 sr_compile.texture_data.extend( bytearray(tex) )
731 return texture_index
732 #}
733
734 def sr_compile_material( mat ):
735 #{
736 if mat == None:
737 return 0
738 if mat.name in sr_compile.material_cache:
739 return sr_compile.material_cache[mat.name]
740
741 index = (len(sr_compile.material_data)//sizeof(mdl_material))+1
742 sr_compile.material_cache[mat.name] = index
743
744 m = mdl_material()
745 m.pstr_name = sr_compile_string( mat.name )
746
747 flags = 0x00
748 if mat.SR_data.collision:#{
749 flags |= 0x2 # collision flag
750 if (mat.SR_data.shader != 'invisible') and \
751 (mat.SR_data.shader != 'boundary'):#{
752 if mat.SR_data.skate_surface: flags |= 0x1
753 if mat.SR_data.grow_grass: flags |= 0x4
754 if mat.SR_data.grind_surface: flags |= 0x8
755 if mat.SR_data.preview_visibile: flags |= 0x40
756 #}
757 if mat.SR_data.shader == 'invisible': flags |= 0x10
758 if mat.SR_data.shader == 'boundary': flags |= (0x10|0x20)
759 #}
760
761 m.flags = flags
762
763 m.surface_prop = int(mat.SR_data.surface_prop)
764
765 if mat.SR_data.shader == 'standard': m.shader = 0
766 if mat.SR_data.shader == 'standard_cutout': m.shader = 1
767 if mat.SR_data.shader == 'terrain_blend':#{
768 m.shader = 2
769
770 m.colour[0] = pow( mat.SR_data.sand_colour[0], 1.0/2.2 )
771 m.colour[1] = pow( mat.SR_data.sand_colour[1], 1.0/2.2 )
772 m.colour[2] = pow( mat.SR_data.sand_colour[2], 1.0/2.2 )
773 m.colour[3] = 1.0
774
775 m.colour1[0] = mat.SR_data.blend_offset[0]
776 m.colour1[1] = mat.SR_data.blend_offset[1]
777 #}
778
779 if mat.SR_data.shader == 'vertex_blend':#{
780 m.shader = 3
781
782 m.colour1[0] = mat.SR_data.blend_offset[0]
783 m.colour1[1] = mat.SR_data.blend_offset[1]
784 #}
785
786 if mat.SR_data.shader == 'water':#{
787 m.shader = 4
788
789 m.colour[0] = pow( mat.SR_data.shore_colour[0], 1.0/2.2 )
790 m.colour[1] = pow( mat.SR_data.shore_colour[1], 1.0/2.2 )
791 m.colour[2] = pow( mat.SR_data.shore_colour[2], 1.0/2.2 )
792 m.colour[3] = 1.0
793 m.colour1[0] = pow( mat.SR_data.ocean_colour[0], 1.0/2.2 )
794 m.colour1[1] = pow( mat.SR_data.ocean_colour[1], 1.0/2.2 )
795 m.colour1[2] = pow( mat.SR_data.ocean_colour[2], 1.0/2.2 )
796 m.colour1[3] = 1.0
797 #}
798
799 if mat.SR_data.shader == 'invisible':#{
800 m.shader = 5
801 #}
802
803 if mat.SR_data.shader == 'boundary':#{
804 m.shader = 6
805 #}
806
807 if mat.SR_data.shader == 'fxglow':#{
808 m.shader = 7
809 #}
810
811 inf = material_info( mat )
812
813 if mat.SR_data.shader == 'standard' or \
814 mat.SR_data.shader == 'standard_cutout' or \
815 mat.SR_data.shader == 'terrain_blend' or \
816 mat.SR_data.shader == 'vertex_blend' or \
817 mat.SR_data.shader == 'fxglow': #{
818 if 'tex_diffuse' in inf:
819 m.tex_diffuse = sr_compile_texture(inf['tex_diffuse'])
820 #}
821
822 sr_compile.material_data.extend( bytearray(m) )
823 return index
824 #}
825
826 def sr_armature_bones( armature ):
827 #{
828 def _recurse_bone( b ):
829 #{
830 yield b
831 for c in b.children: yield from _recurse_bone( c )
832 #}
833
834 for b in armature.data.bones:
835 if not b.parent:
836 yield from _recurse_bone( b )
837 #}
838
839 def sr_entity_id( obj ):#{
840 if not obj: return 0
841
842 tipo = get_entity_enum_id( obj_ent_type(obj) )
843 index = sr_compile.entity_ids[ obj.name ]
844
845 return (tipo&0xffff)<<16 | (index&0xffff)
846 #}
847
848 # Returns submesh_start,count and armature_id
849 def sr_compile_mesh_internal( obj ):
850 #{
851 can_use_cache = True
852 armature = None
853
854 submesh_start = 0
855 submesh_count = 0
856 armature_id = 0
857
858 for mod in obj.modifiers:#{
859 if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
860 mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
861 mod.type == 'ARRAY':
862 #{
863 can_use_cache = False
864 #}
865
866 if mod.type == 'ARMATURE': #{
867 armature = mod.object
868 rig_weight_groups = \
869 ['0 [ROOT]']+[_.name for _ in sr_armature_bones(mod.object)]
870 armature_id = sr_compile.entity_ids[armature.name]
871
872 POSE_OR_REST_CACHE = armature.data.pose_position
873 armature.data.pose_position = 'REST'
874 #}
875 #}
876
877 # Check the cache first
878 #
879 if can_use_cache and (obj.data.name in sr_compile.mesh_cache):#{
880 ref = sr_compile.mesh_cache[obj.data.name]
881 submesh_start = ref[0]
882 submesh_count = ref[1]
883 return (submesh_start,submesh_count,armature_id)
884 #}
885
886 # Compile a whole new mesh
887 #
888 submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
889 submesh_count = 0
890
891 dgraph = bpy.context.evaluated_depsgraph_get()
892 data = obj.evaluated_get(dgraph).data
893 data.calc_loop_triangles()
894 data.calc_normals_split()
895
896 # Mesh is split into submeshes based on their material
897 #
898 mat_list = data.materials if len(data.materials) > 0 else [None]
899 for material_id, mat in enumerate(mat_list): #{
900 mref = {}
901
902 sm = mdl_submesh()
903 sm.indice_start = len(sr_compile.indice_data)//sizeof(c_uint32)
904 sm.vertex_start = len(sr_compile.vertex_data)//sizeof(mdl_vert)
905 sm.vertex_count = 0
906 sm.indice_count = 0
907 sm.material_id = sr_compile_material( mat )
908
909 INF=99999999.99999999
910 for i in range(3):#{
911 sm.bbx[0][i] = INF
912 sm.bbx[1][i] = -INF
913 #}
914
915 # Keep a reference to very very very similar vertices
916 # i have no idea how to speed it up.
917 #
918 vertex_reference = {}
919
920 # Write the vertex / indice data
921 #
922 for tri_index, tri in enumerate(data.loop_triangles):#{
923 if tri.material_index != material_id: continue
924
925 for j in range(3):#{
926 vert = data.vertices[tri.vertices[j]]
927 li = tri.loops[j]
928 vi = data.loops[li].vertex_index
929
930 # Gather vertex information
931 #
932 co = vert.co
933 norm = data.loops[li].normal
934 uv = (0,0)
935 colour = (255,255,255,255)
936 groups = [0,0,0,0]
937 weights = [0,0,0,0]
938
939 # Uvs
940 #
941 if data.uv_layers:
942 uv = data.uv_layers.active.data[li].uv
943
944 # Vertex Colours
945 #
946 if data.vertex_colors:#{
947 colour = data.vertex_colors.active.data[li].color
948 colour = (int(colour[0]*255.0),\
949 int(colour[1]*255.0),\
950 int(colour[2]*255.0),\
951 int(colour[3]*255.0))
952 #}
953
954 # Weight groups: truncates to the 3 with the most influence. The
955 # fourth bone ID is never used by the shader so it
956 # is always 0
957 #
958 if armature:#{
959 src_groups = [_ for _ in data.vertices[vi].groups \
960 if obj.vertex_groups[_.group].name in \
961 rig_weight_groups ]
962
963 weight_groups = sorted( src_groups, key = \
964 lambda a: a.weight, reverse=True )
965 tot = 0.0
966 for ml in range(3):#{
967 if len(weight_groups) > ml:#{
968 g = weight_groups[ml]
969 name = obj.vertex_groups[g.group].name
970 weight = g.weight
971 weights[ml] = weight
972 groups[ml] = rig_weight_groups.index(name)
973 tot += weight
974 #}
975 #}
976
977 if len(weight_groups) > 0:#{
978 inv_norm = (1.0/tot) * 65535.0
979 for ml in range(3):#{
980 weights[ml] = int( weights[ml] * inv_norm )
981 weights[ml] = min( weights[ml], 65535 )
982 weights[ml] = max( weights[ml], 0 )
983 #}
984 #}
985 #}
986 else:#{
987 li1 = tri.loops[(j+1)%3]
988 vi1 = data.loops[li1].vertex_index
989 e0 = data.edges[ data.loops[li].edge_index ]
990
991 if e0.use_freestyle_mark and \
992 ((e0.vertices[0] == vi and e0.vertices[1] == vi1) or \
993 (e0.vertices[0] == vi1 and e0.vertices[1] == vi)):
994 #{
995 weights[0] = 1
996 #}
997 #}
998
999 TOLERENCE = float(10**4)
1000 key = (int(co[0]*TOLERENCE+0.5),
1001 int(co[1]*TOLERENCE+0.5),
1002 int(co[2]*TOLERENCE+0.5),
1003 int(norm[0]*TOLERENCE+0.5),
1004 int(norm[1]*TOLERENCE+0.5),
1005 int(norm[2]*TOLERENCE+0.5),
1006 int(uv[0]*TOLERENCE+0.5),
1007 int(uv[1]*TOLERENCE+0.5),
1008 colour[0], # these guys are already quantized
1009 colour[1], # .
1010 colour[2], # .
1011 colour[3], # .
1012 weights[0], # v
1013 weights[1],
1014 weights[2],
1015 weights[3],
1016 groups[0],
1017 groups[1],
1018 groups[2],
1019 groups[3])
1020
1021 if key in vertex_reference:
1022 index = vertex_reference[key]
1023 else:#{
1024 index = bytearray(c_uint32(sm.vertex_count))
1025 sm.vertex_count+=1
1026
1027 vertex_reference[key] = index
1028 v = mdl_vert()
1029 v.co[0] = co[0]
1030 v.co[1] = co[2]
1031 v.co[2] = -co[1]
1032 v.norm[0] = norm[0]
1033 v.norm[1] = norm[2]
1034 v.norm[2] = -norm[1]
1035 v.uv[0] = uv[0]
1036 v.uv[1] = uv[1]
1037 v.colour[0] = colour[0]
1038 v.colour[1] = colour[1]
1039 v.colour[2] = colour[2]
1040 v.colour[3] = colour[3]
1041 v.weights[0] = weights[0]
1042 v.weights[1] = weights[1]
1043 v.weights[2] = weights[2]
1044 v.weights[3] = weights[3]
1045 v.groups[0] = groups[0]
1046 v.groups[1] = groups[1]
1047 v.groups[2] = groups[2]
1048 v.groups[3] = groups[3]
1049
1050 for i in range(3):#{
1051 sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
1052 sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
1053 #}
1054
1055 sr_compile.vertex_data.extend(bytearray(v))
1056 #}
1057
1058 sm.indice_count += 1
1059 sr_compile.indice_data.extend( index )
1060 #}
1061 #}
1062
1063 # Make sure bounding box isn't -inf -> inf if no vertices
1064 #
1065 if sm.vertex_count == 0:
1066 for j in range(2):
1067 for i in range(3):
1068 sm.bbx[j][i] = 0
1069
1070 # Add submesh to encoder
1071 #
1072 sr_compile.submesh_data.extend( bytearray(sm) )
1073 submesh_count += 1
1074 #}
1075
1076 if armature:#{
1077 armature.data.pose_position = POSE_OR_REST_CACHE
1078 #}
1079
1080 # Save a reference to this mesh since we want to reuse the submesh indices
1081 # later.
1082 sr_compile.mesh_cache[obj.data.name]=(submesh_start,submesh_count)
1083 return (submesh_start,submesh_count,armature_id)
1084 #}
1085
1086 def sr_compile_mesh( obj ):
1087 #{
1088 node=mdl_mesh()
1089 compile_obj_transform(obj, node.transform)
1090 node.pstr_name = sr_compile_string(obj.name)
1091 ent_type = obj_ent_type( obj )
1092
1093 node.entity_id = 0
1094
1095 if ent_type != 'none':#{
1096 ent_id_lwr = sr_compile.entity_ids[obj.name]
1097 ent_id_upr = get_entity_enum_id( obj_ent_type(obj) )
1098 node.entity_id = (ent_id_upr << 16) | ent_id_lwr
1099 #}
1100
1101 node.submesh_start, node.submesh_count, node.armature_id = \
1102 sr_compile_mesh_internal( obj )
1103
1104 sr_compile.mesh_data.extend(bytearray(node))
1105 #}
1106
1107 def sr_compile_fonts( collection ):
1108 #{
1109 print( F"[SR] Compiling fonts" )
1110
1111 glyph_count = 0
1112 variant_count = 0
1113
1114 for obj in collection.all_objects:#{
1115 if obj_ent_type(obj) != 'ent_font': continue
1116
1117 data = obj.SR_data.ent_font[0]
1118
1119 font=ent_font()
1120 font.alias = sr_compile_string( data.alias )
1121 font.variant_start = variant_count
1122 font.variant_count = 0
1123 font.glyph_start = glyph_count
1124
1125 glyph_base = data.glyphs[0].utf32
1126 glyph_range = data.glyphs[-1].utf32+1 - glyph_base
1127
1128 font.glyph_utf32_base = glyph_base
1129 font.glyph_count = glyph_range
1130
1131 for i in range(len(data.variants)):#{
1132 data_var = data.variants[i]
1133 if not data_var.mesh: continue
1134
1135 mesh = data_var.mesh.data
1136
1137 variant = ent_font_variant()
1138 variant.name = sr_compile_string( data_var.tipo )
1139
1140 # fonts (variants) only support one material each
1141 mat = None
1142 if len(mesh.materials) != 0:
1143 mat = mesh.materials[0]
1144 variant.material_id = sr_compile_material( mat )
1145
1146 font.variant_count += 1
1147
1148 islands = mesh_utils.mesh_linked_triangles(mesh)
1149 centroids = [Vector((0,0)) for _ in range(len(islands))]
1150
1151 for j in range(len(islands)):#{
1152 for tri in islands[j]:#{
1153 centroids[j].x += tri.center[0]
1154 centroids[j].y += tri.center[2]
1155 #}
1156
1157 centroids[j] /= len(islands[j])
1158 #}
1159
1160 for j in range(glyph_range):#{
1161 data_glyph = data.glyphs[j]
1162 glyph = ent_glyph()
1163 glyph.indice_start = len(sr_compile.indice_data)//sizeof(c_uint32)
1164 glyph.indice_count = 0
1165 glyph.size[0] = data_glyph.bounds[2]
1166 glyph.size[1] = data_glyph.bounds[3]
1167
1168 vertex_reference = {}
1169
1170 for k in range(len(islands)):#{
1171 if centroids[k].x < data_glyph.bounds[0] or \
1172 centroids[k].x > data_glyph.bounds[0]+data_glyph.bounds[2] or\
1173 centroids[k].y < data_glyph.bounds[1] or \
1174 centroids[k].y > data_glyph.bounds[1]+data_glyph.bounds[3]:
1175 #{
1176 continue
1177 #}
1178
1179 for l in range(len(islands[k])):#{
1180 tri = islands[k][l]
1181 for m in range(3):#{
1182 vert = mesh.vertices[tri.vertices[m]]
1183 li = tri.loops[m]
1184 vi = mesh.loops[li].vertex_index
1185
1186 # Gather vertex information
1187 #
1188 co = [vert.co[_] for _ in range(3)]
1189 co[0] -= data_glyph.bounds[0]
1190 co[2] -= data_glyph.bounds[1]
1191 norm = mesh.loops[li].normal
1192 uv = (0,0)
1193 if mesh.uv_layers: uv = mesh.uv_layers.active.data[li].uv
1194
1195 TOLERENCE = float(10**4)
1196 key = (int(co[0]*TOLERENCE+0.5),
1197 int(co[1]*TOLERENCE+0.5),
1198 int(co[2]*TOLERENCE+0.5),
1199 int(norm[0]*TOLERENCE+0.5),
1200 int(norm[1]*TOLERENCE+0.5),
1201 int(norm[2]*TOLERENCE+0.5),
1202 int(uv[0]*TOLERENCE+0.5),
1203 int(uv[1]*TOLERENCE+0.5))
1204
1205 if key in vertex_reference:
1206 index = vertex_reference[key]
1207 else:#{
1208 vindex = len(sr_compile.vertex_data)//sizeof(mdl_vert)
1209 index = bytearray(c_uint32(vindex))
1210 vertex_reference[key] = index
1211 v = mdl_vert()
1212 v.co[0] = co[0]
1213 v.co[1] = co[2]
1214 v.co[2] = -co[1]
1215 v.norm[0] = norm[0]
1216 v.norm[1] = norm[2]
1217 v.norm[2] = -norm[1]
1218 v.uv[0] = uv[0]
1219 v.uv[1] = uv[1]
1220
1221 sr_compile.vertex_data.extend(bytearray(v))
1222 #}
1223
1224 glyph.indice_count += 1
1225 sr_compile.indice_data.extend( index )
1226 #}
1227 #}
1228 #}
1229 sr_ent_push( glyph )
1230 #}
1231 sr_ent_push( variant )
1232 #}
1233 sr_ent_push( font )
1234 #}
1235 #}
1236
1237 def sr_compile_menus( collection ):
1238 #{
1239 print( "[SR1] Compiling menus" )
1240 groups = []
1241
1242 for obj in collection.all_objects:#{
1243 if obj_ent_type(obj) != 'ent_menuitem': continue
1244 obj_data = obj.SR_data.ent_menuitem[0]
1245
1246 bitmask = 0x00000000
1247
1248 for col in obj.users_collection:#{
1249 name = col.name
1250 if name not in groups: groups.append( name )
1251 bitmask |= (0x1 << groups.index(name))
1252 #}
1253
1254 item = ent_menuitem()
1255 item.type = int( obj_data.tipo )
1256 item.groups = bitmask
1257
1258 compile_obj_transform( obj, item.transform )
1259 if obj.type == 'MESH':#{
1260 item.submesh_start, item.submesh_count, _ = \
1261 sr_compile_mesh_internal( obj )
1262 #}
1263
1264 if item.type == 1 or item.type == 2:#{
1265 item_button = item._anonymous_union.button
1266 item_button.pstr = sr_compile_string( obj_data.string )
1267 #}
1268 elif item.type == 3:#{
1269 item_checkmark = item._anonymous_union.checkmark
1270 item_checkmark.pstr_data = sr_compile_string( obj_data.string )
1271 item_checkmark.id_check = sr_entity_id( obj_data.checkmark )
1272 delta = obj_data.checkmark.location - obj.location
1273 item_checkmark.offset[0] = delta[0]
1274 item_checkmark.offset[1] = delta[2]
1275 item_checkmark.offset[2] = -delta[1]
1276 #}
1277 elif item.type == 4:#{
1278 item_slider = item._anonymous_union.slider
1279 item_slider.id_min = sr_entity_id( obj_data.slider_minloc )
1280 item_slider.id_max = sr_entity_id( obj_data.slider_maxloc )
1281 item_slider.id_handle = sr_entity_id( obj_data.slider_handle )
1282 item_slider.pstr_data = sr_compile_string( obj_data.string )
1283 #}
1284 elif item.type == 5:#{
1285 item_page = item._anonymous_union.page
1286 item_page.pstr_name = sr_compile_string( obj_data.string )
1287 item_page.id_entrypoint = sr_entity_id( obj_data.newloc )
1288 item_page.id_viewpoint = sr_entity_id( obj_data.camera )
1289 #}
1290
1291 if obj_data.link0:
1292 item.id_links[0] = sr_entity_id( obj_data.link0 )
1293 if obj_data.link1:
1294 item.id_links[1] = sr_entity_id( obj_data.link1 )
1295 if item.type != 4:#{
1296 if obj_data.link2:
1297 item.id_links[2] = sr_entity_id( obj_data.link2 )
1298 if obj_data.link3:
1299 item.id_links[3] = sr_entity_id( obj_data.link3 )
1300 #}
1301
1302 sr_ent_push( item )
1303 #}
1304 #}
1305
1306 def sr_compile_armature( obj ):
1307 #{
1308 node = mdl_armature()
1309 node.bone_start = len(sr_compile.bone_data)//sizeof(mdl_bone)
1310 node.bone_count = 0
1311 node.anim_start = len(sr_compile.anim_data)//sizeof(mdl_animation)
1312 node.anim_count = 0
1313
1314 bones = [_ for _ in sr_armature_bones(obj)]
1315 bones_names = [None]+[_.name for _ in bones]
1316
1317 for b in bones:#{
1318 bone = mdl_bone()
1319 if b.use_deform: bone.flags = 0x1
1320 if b.parent: bone.parent = bones_names.index(b.parent.name)
1321
1322 bone.collider = int(b.SR_data.collider)
1323
1324 if bone.collider>0:#{
1325 bone.hitbox[0][0] = b.SR_data.collider_min[0]
1326 bone.hitbox[0][1] = b.SR_data.collider_min[2]
1327 bone.hitbox[0][2] = -b.SR_data.collider_max[1]
1328 bone.hitbox[1][0] = b.SR_data.collider_max[0]
1329 bone.hitbox[1][1] = b.SR_data.collider_max[2]
1330 bone.hitbox[1][2] = -b.SR_data.collider_min[1]
1331 #}
1332
1333 if b.SR_data.cone_constraint:#{
1334 bone.flags |= 0x4
1335 bone.conevx[0] = b.SR_data.conevx[0]
1336 bone.conevx[1] = b.SR_data.conevx[2]
1337 bone.conevx[2] = -b.SR_data.conevx[1]
1338 bone.conevy[0] = b.SR_data.conevy[0]
1339 bone.conevy[1] = b.SR_data.conevy[2]
1340 bone.conevy[2] = -b.SR_data.conevy[1]
1341 bone.coneva[0] = b.SR_data.coneva[0]
1342 bone.coneva[1] = b.SR_data.coneva[2]
1343 bone.coneva[2] = -b.SR_data.coneva[1]
1344 bone.conet = b.SR_data.conet
1345 #}
1346
1347 bone.co[0] = b.head_local[0]
1348 bone.co[1] = b.head_local[2]
1349 bone.co[2] = -b.head_local[1]
1350 bone.end[0] = b.tail_local[0] - bone.co[0]
1351 bone.end[1] = b.tail_local[2] - bone.co[1]
1352 bone.end[2] = -b.tail_local[1] - bone.co[2]
1353 bone.pstr_name = sr_compile_string( b.name )
1354
1355 for c in obj.pose.bones[b.name].constraints:#{
1356 if c.type == 'IK':#{
1357 bone.flags |= 0x2
1358 bone.ik_target = bones_names.index(c.subtarget)
1359 bone.ik_pole = bones_names.index(c.pole_subtarget)
1360 #}
1361 #}
1362
1363 node.bone_count += 1
1364 sr_compile.bone_data.extend(bytearray(bone))
1365 #}
1366
1367 # Compile anims
1368 #
1369 if obj.animation_data and sr_compile.pack_animations: #{
1370 # So we can restore later
1371 #
1372 previous_frame = bpy.context.scene.frame_current
1373 previous_action = obj.animation_data.action
1374 POSE_OR_REST_CACHE = obj.data.pose_position
1375 obj.data.pose_position = 'POSE'
1376
1377 for NLALayer in obj.animation_data.nla_tracks:#{
1378 for NLAStrip in NLALayer.strips:#{
1379 # set active
1380 #
1381 for a in bpy.data.actions:#{
1382 if a.name == NLAStrip.name:#{
1383 obj.animation_data.action = a
1384 break
1385 #}
1386 #}
1387
1388 # Clip to NLA settings
1389 #
1390 anim_start = int(NLAStrip.action_frame_start)
1391 anim_end = int(NLAStrip.action_frame_end)
1392
1393 # Export strips
1394 #
1395 anim = mdl_animation()
1396 anim.pstr_name = sr_compile_string( NLAStrip.action.name )
1397 anim.rate = 30.0
1398 anim.keyframe_start = len(sr_compile.keyframe_data)//\
1399 sizeof(mdl_transform)
1400 anim.length = anim_end-anim_start
1401
1402 i = 0
1403 # Export the keyframes
1404 for frame in range(anim_start,anim_end):#{
1405 bpy.context.scene.frame_set(frame)
1406
1407 for rb in bones:#{
1408 pb = obj.pose.bones[rb.name]
1409
1410 # relative bone matrix
1411 if rb.parent is not None:#{
1412 offset_mtx = rb.parent.matrix_local
1413 offset_mtx = offset_mtx.inverted_safe() @ \
1414 rb.matrix_local
1415
1416 inv_parent = pb.parent.matrix @ offset_mtx
1417 inv_parent.invert_safe()
1418 fpm = inv_parent @ pb.matrix
1419 #}
1420 else:#{
1421 bone_mtx = rb.matrix.to_4x4()
1422 local_inv = rb.matrix_local.inverted_safe()
1423 fpm = bone_mtx @ local_inv @ pb.matrix
1424 #}
1425
1426 loc, rot, sca = fpm.decompose()
1427
1428 # rotation
1429 lc_m = pb.matrix_channel.to_3x3()
1430 if pb.parent is not None:#{
1431 smtx = pb.parent.matrix_channel.to_3x3()
1432 lc_m = smtx.inverted() @ lc_m
1433 #}
1434 rq = lc_m.to_quaternion()
1435 q_normalize( rq )
1436
1437 kf = mdl_transform()
1438 kf.co[0] = loc[0]
1439 kf.co[1] = loc[2]
1440 kf.co[2] = -loc[1]
1441 kf.q[0] = rq[1]
1442 kf.q[1] = rq[3]
1443 kf.q[2] = -rq[2]
1444 kf.q[3] = rq[0]
1445 kf.s[0] = sca[0]
1446 kf.s[1] = sca[1]
1447 kf.s[2] = sca[2]
1448 sr_compile.keyframe_data.extend(bytearray(kf))
1449
1450 i+=1
1451 #}
1452 #}
1453
1454 # Add to animation buffer
1455 #
1456 sr_compile.anim_data.extend(bytearray(anim))
1457 node.anim_count += 1
1458
1459 # Report progress
1460 #
1461 print( F"[SR] | anim( {NLAStrip.action.name} )" )
1462 #}
1463 #}
1464
1465 # Restore context to how it was before
1466 #
1467 bpy.context.scene.frame_set( previous_frame )
1468 obj.animation_data.action = previous_action
1469 obj.data.pose_position = POSE_OR_REST_CACHE
1470 #}
1471
1472 sr_compile.armature_data.extend(bytearray(node))
1473 #}
1474
1475 def sr_ent_push( struct ):
1476 #{
1477 clase = type(struct).__name__
1478
1479 if clase not in sr_compile.entity_data:#{
1480 sr_compile.entity_data[ clase ] = bytearray()
1481 sr_compile.entity_info[ clase ] = { 'size': sizeof(struct) }
1482 #}
1483
1484 index = len(sr_compile.entity_data[ clase ])//sizeof(struct)
1485 sr_compile.entity_data[ clase ].extend( bytearray(struct) )
1486 return index
1487 #}
1488
1489 def sr_array_title( arr, name, count, size, offset ):
1490 #{
1491 for i in range(len(name)):#{
1492 arr.name[i] = ord(name[i])
1493 #}
1494 arr.file_offset = offset
1495 arr.item_count = count
1496 arr.item_size = size
1497 #}
1498
1499 def hash_djb2(s):
1500 #{
1501 picadillo = 5381
1502 for x in s:#{
1503 picadillo = (((picadillo << 5) + picadillo) + ord(x)) & 0xFFFFFFFF
1504 #}
1505 return picadillo
1506 #}
1507
1508 def sr_compile( collection ):
1509 #{
1510 print( F"[SR] compiler begin ({collection.name}.mdl)" )
1511
1512 #settings
1513 sr_compile.pack_textures = collection.SR_data.pack_textures
1514 sr_compile.pack_animations = collection.SR_data.animations
1515
1516 # caches
1517 sr_compile.string_cache = {}
1518 sr_compile.mesh_cache = {}
1519 sr_compile.material_cache = {}
1520 sr_compile.texture_cache = {}
1521
1522 # compiled data
1523 sr_compile.mesh_data = bytearray()
1524 sr_compile.submesh_data = bytearray()
1525 sr_compile.vertex_data = bytearray()
1526 sr_compile.indice_data = bytearray()
1527 sr_compile.bone_data = bytearray()
1528 sr_compile.material_data = bytearray()
1529 sr_compile.armature_data = bytearray()
1530 sr_compile.anim_data = bytearray()
1531 sr_compile.keyframe_data = bytearray()
1532 sr_compile.texture_data = bytearray()
1533
1534 # just bytes not structures
1535 sr_compile.string_data = bytearray()
1536 sr_compile.pack_data = bytearray()
1537
1538 # variable
1539 sr_compile.entity_data = {}
1540 sr_compile.entity_info = {}
1541
1542 print( F"[SR] assign entity ID's" )
1543 sr_compile.entities = {}
1544 sr_compile.entity_ids = {}
1545
1546 # begin
1547 # -------------------------------------------------------
1548
1549 sr_compile_string( "null" )
1550
1551 mesh_count = 0
1552 for obj in collection.all_objects: #{
1553 if obj.type == 'MESH':#{
1554 mesh_count += 1
1555 #}
1556
1557 ent_type = obj_ent_type( obj )
1558 if ent_type == 'none': continue
1559
1560 if ent_type not in sr_compile.entities: sr_compile.entities[ent_type] = []
1561 sr_compile.entity_ids[obj.name] = len( sr_compile.entities[ent_type] )
1562 sr_compile.entities[ent_type] += [obj]
1563 #}
1564
1565 print( F"[SR] Compiling geometry" )
1566 i=0
1567 for obj in collection.all_objects:#{
1568 if obj.type == 'MESH':#{
1569 i+=1
1570
1571 ent_type = obj_ent_type( obj )
1572
1573 # entity ignore mesh list
1574 #
1575 if ent_type == 'ent_traffic': continue
1576 if ent_type == 'ent_font': continue
1577 if ent_type == 'ent_font_variant': continue
1578 if ent_type == 'ent_menuitem': continue
1579 if ent_type == 'ent_challenge': continue
1580
1581 #TODO: This is messy.
1582 if ent_type == 'ent_gate':#{
1583 obj_data = obj.SR_data.ent_gate[0]
1584 if obj_data.custom: continue
1585 #}
1586 #--------------------------
1587
1588 print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
1589 sr_compile_mesh( obj )
1590 #}
1591 #}
1592
1593 audio_clip_count = 0
1594
1595 for ent_type, arr in sr_compile.entities.items():#{
1596 print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
1597
1598 for i in range(len(arr)):#{
1599 obj = arr[i]
1600
1601 print( F"[SR] {i+1: 3}/{len(arr)} {obj.name:<40} ",end='\r' )
1602
1603 if ent_type == 'mdl_armature': sr_compile_armature(obj)
1604 elif ent_type == 'ent_light': #{
1605 light = ent_light()
1606 compile_obj_transform( obj, light.transform )
1607 light.daytime = obj.data.SR_data.daytime
1608 if obj.data.type == 'POINT':#{
1609 light.type = 0
1610 #}
1611 elif obj.data.type == 'SPOT':#{
1612 light.type = 1
1613 light.angle = obj.data.spot_size*0.5
1614 #}
1615 light.range = obj.data.cutoff_distance
1616 light.colour[0] = obj.data.color[0]
1617 light.colour[1] = obj.data.color[1]
1618 light.colour[2] = obj.data.color[2]
1619 light.colour[3] = obj.data.energy
1620 sr_ent_push( light )
1621 #}
1622 elif ent_type == 'ent_camera': #{
1623 cam = ent_camera()
1624 compile_obj_transform( obj, cam.transform )
1625 cam.fov = obj.data.angle * 45.0
1626 sr_ent_push(cam)
1627 #}
1628 elif ent_type == 'ent_gate': #{
1629 gate = ent_gate()
1630 obj_data = obj.SR_data.ent_gate[0]
1631 mesh_data = obj.data.SR_data.ent_gate[0]
1632
1633 flags = 0x0000
1634
1635 if obj_data.tipo == 'default':#{
1636 if obj_data.target:#{
1637 gate.target = sr_compile.entity_ids[obj_data.target.name]
1638 flags |= 0x0001
1639 #}
1640 #}
1641 elif obj_data.tipo == 'nonlocal':#{
1642 gate.target = 0
1643 gate.key = sr_compile_string(obj_data.key)
1644 flags |= 0x0002
1645 #}
1646
1647 if obj_data.flip: flags |= 0x0004
1648 if obj_data.custom:#{
1649 flags |= 0x0008
1650 gate.submesh_start, gate.submesh_count, _ = \
1651 sr_compile_mesh_internal( obj )
1652 #}
1653 gate.flags = flags
1654
1655 gate.dimensions[0] = mesh_data.dimensions[0]
1656 gate.dimensions[1] = mesh_data.dimensions[1]
1657 gate.dimensions[2] = mesh_data.dimensions[2]
1658
1659 q = [obj.matrix_local.to_quaternion(), (0,0,0,1)]
1660 co = [obj.matrix_world @ Vector((0,0,0)), (0,0,0)]
1661
1662 if obj_data.target:#{
1663 q[1] = obj_data.target.matrix_local.to_quaternion()
1664 co[1]= obj_data.target.matrix_world @ Vector((0,0,0))
1665 #}
1666
1667 # Setup transform
1668 #
1669 for x in range(2):#{
1670 gate.co[x][0] = co[x][0]
1671 gate.co[x][1] = co[x][2]
1672 gate.co[x][2] = -co[x][1]
1673 gate.q[x][0] = q[x][1]
1674 gate.q[x][1] = q[x][3]
1675 gate.q[x][2] = -q[x][2]
1676 gate.q[x][3] = q[x][0]
1677 #}
1678
1679 sr_ent_push( gate )
1680 #}
1681 elif ent_type == 'ent_spawn': #{
1682 spawn = ent_spawn()
1683 compile_obj_transform( obj, spawn.transform )
1684 obj_data = obj.SR_data.ent_spawn[0]
1685 spawn.pstr_name = sr_compile_string( obj_data.alias )
1686 sr_ent_push( spawn )
1687 #}
1688 elif ent_type == 'ent_water':#{
1689 water = ent_water()
1690 compile_obj_transform( obj, water.transform )
1691 water.max_dist = 0.0
1692 sr_ent_push( water )
1693 #}
1694 elif ent_type == 'ent_audio':#{
1695 obj_data = obj.SR_data.ent_audio[0]
1696 audio = ent_audio()
1697 compile_obj_transform( obj, audio.transform )
1698 audio.clip_start = audio_clip_count
1699 audio.clip_count = len(obj_data.files)
1700 audio_clip_count += audio.clip_count
1701 audio.max_channels = obj_data.max_channels
1702 audio.volume = obj_data.volume
1703
1704 # TODO flags:
1705 # - allow/disable doppler
1706 # - channel group tags with random colours
1707 # - transition properties
1708
1709 if obj_data.flag_loop: audio.flags |= 0x1
1710 if obj_data.flag_nodoppler: audio.flags |= 0x2
1711 if obj_data.flag_3d: audio.flags |= 0x4
1712 if obj_data.flag_auto: audio.flags |= 0x8
1713 if obj_data.formato == '0': audio.flags |= 0x000
1714 elif obj_data.formato == '1': audio.flags |= 0x400
1715 elif obj_data.formato == '2': audio.flags |= 0x1000
1716
1717 audio.channel_behaviour = int(obj_data.channel_behaviour)
1718 if audio.channel_behaviour >= 1:#{
1719 audio.group = obj_data.group
1720 #}
1721 if audio.channel_behaviour == 2:#{
1722 audio.crossfade = obj_data.transition_duration
1723 #}
1724 audio.probability_curve = int(obj_data.probability_curve)
1725
1726 for ci in range(audio.clip_count):#{
1727 entry = obj_data.files[ci]
1728 clip = ent_audio_clip()
1729 clip.probability = entry.probability
1730 if obj_data.formato == '2':#{
1731 sr_pack_file( clip._anon.file, '', vg_str_bin(entry.path) )
1732 #}
1733 else:#{
1734 clip._anon.file.path = sr_compile_string( entry.path )
1735 clip._anon.file.pack_offset = 0
1736 clip._anon.file.pack_size = 0
1737 #}
1738 sr_ent_push( clip )
1739 #}
1740 sr_ent_push( audio )
1741 #}
1742 elif ent_type == 'ent_volume':#{
1743 obj_data = obj.SR_data.ent_volume[0]
1744 volume = ent_volume()
1745 volume.type = int(obj_data.subtype)
1746 compile_obj_transform( obj, volume.transform )
1747
1748 if obj_data.target:#{
1749 volume.target = sr_entity_id( obj_data.target )
1750 volume._anon.trigger.event = obj_data.event
1751 #}
1752
1753 sr_ent_push(volume)
1754 #}
1755 elif ent_type == 'ent_marker':#{
1756 marker = ent_marker()
1757 marker.name = sr_compile_string( obj.SR_data.ent_marker[0].alias )
1758 compile_obj_transform( obj, marker.transform )
1759 sr_ent_push(marker)
1760 #}
1761 elif ent_type == 'ent_skateshop':#{
1762 skateshop = ent_skateshop()
1763 obj_data = obj.SR_data.ent_skateshop[0]
1764 skateshop.type = int(obj_data.tipo)
1765 if skateshop.type == 0:#{
1766 boardshop = skateshop._anonymous_union.boards
1767 boardshop.id_display = sr_entity_id( obj_data.mark_display )
1768 boardshop.id_info = sr_entity_id( obj_data.mark_info )
1769 boardshop.id_rack = sr_entity_id( obj_data.mark_rack )
1770 #}
1771 elif skateshop.type == 1:#{
1772 charshop = skateshop._anonymous_union.character
1773 charshop.id_display = sr_entity_id( obj_data.mark_display )
1774 charshop.id_info = sr_entity_id( obj_data.mark_info )
1775 #}
1776 elif skateshop.type == 2:#{
1777 worldshop = skateshop._anonymous_union.worlds
1778 worldshop.id_display = sr_entity_id( obj_data.mark_display )
1779 worldshop.id_info = sr_entity_id( obj_data.mark_info )
1780 #}
1781 skateshop.id_camera = sr_entity_id( obj_data.cam )
1782 compile_obj_transform( obj, skateshop.transform )
1783 sr_ent_push(skateshop)
1784 #}
1785 elif ent_type == 'ent_swspreview':#{
1786 workshop_preview = ent_swspreview()
1787 obj_data = obj.SR_data.ent_swspreview[0]
1788 workshop_preview.id_display = sr_entity_id( obj_data.mark_display )
1789 workshop_preview.id_display1 = sr_entity_id( obj_data.mark_display1)
1790 workshop_preview.id_camera = sr_entity_id( obj_data.cam )
1791 sr_ent_push( workshop_preview )
1792 #}
1793 elif ent_type == 'ent_worldinfo':#{
1794 worldinfo = ent_worldinfo()
1795 obj_data = obj.SR_data.ent_worldinfo[0]
1796 worldinfo.pstr_name = sr_compile_string( obj_data.name )
1797 worldinfo.pstr_author = sr_compile_string( obj_data.author )
1798 worldinfo.pstr_desc = sr_compile_string( obj_data.desc )
1799 worldinfo.timezone = obj_data.timezone
1800 sr_ent_push( worldinfo )
1801 #}
1802 elif ent_type == 'ent_ccmd':#{
1803 ccmd = ent_ccmd()
1804 obj_data = obj.SR_data.ent_ccmd[0]
1805 ccmd.pstr_command = sr_compile_string( obj_data.command )
1806 sr_ent_push( ccmd )
1807 #}
1808 elif ent_type == 'ent_challenge':#{
1809 challenge = ent_challenge()
1810 obj_data = obj.SR_data.ent_challenge[0]
1811 challenge.id_next = sr_entity_id( obj_data.proxima )
1812 challenge.filter = 0
1813 challenge.time_limit = obj_data.time_limit
1814
1815 compile_obj_transform( obj, challenge.transform )
1816 challenge.submesh_start, challenge.submesh_count, _ = \
1817 sr_compile_mesh_internal( obj )
1818
1819 sr_ent_push( challenge )
1820 #}
1821 #}
1822 #}
1823
1824 sr_compile_menus( collection )
1825 sr_compile_fonts( collection )
1826
1827 def _children( col ):#{
1828 yield col
1829 for c in col.children:#{
1830 yield from _children(c)
1831 #}
1832 #}
1833
1834 checkpoint_count = 0
1835 pathindice_count = 0
1836 routenode_count = 0
1837
1838 for col in _children(collection):#{
1839 print( F"Adding routes for subcollection: {col.name}" )
1840 route_gates = []
1841 route_curves = []
1842 routes = []
1843 traffics = []
1844
1845 for obj in col.objects:#{
1846 if obj.type == 'ARMATURE': pass
1847 else:#{
1848 ent_type = obj_ent_type( obj )
1849
1850 if ent_type == 'ent_gate':
1851 route_gates += [obj]
1852 elif ent_type == 'ent_route_node':#{
1853 if obj.type == 'CURVE':#{
1854 route_curves += [obj]
1855 #}
1856 #}
1857 elif ent_type == 'ent_route':
1858 routes += [obj]
1859 elif ent_type == 'ent_traffic':
1860 traffics += [obj]
1861 #}
1862 #}
1863
1864 dij = create_node_graph( route_curves, route_gates )
1865
1866 for obj in routes:#{
1867 obj_data = obj.SR_data.ent_route[0]
1868 route = ent_route()
1869 route.pstr_name = sr_compile_string( obj_data.alias )
1870 route.checkpoints_start = checkpoint_count
1871 route.checkpoints_count = 0
1872
1873 for ci in range(3):
1874 route.colour[ci] = obj_data.colour[ci]
1875 route.colour[3] = 1.0
1876
1877 compile_obj_transform( obj, route.transform )
1878 checkpoints = obj_data.gates
1879
1880 for i in range(len(checkpoints)):#{
1881 gi = checkpoints[i].target
1882 gj = checkpoints[(i+1)%len(checkpoints)].target
1883 gate = gi
1884
1885 if gi:#{
1886 dest = gi.SR_data.ent_gate[0].target
1887 gi = dest
1888 #}
1889
1890 if gi==gj: continue # error?
1891 if not gi or not gj: continue
1892
1893 checkpoint = ent_checkpoint()
1894 checkpoint.gate_index = sr_compile.entity_ids[gate.name]
1895 checkpoint.path_start = pathindice_count
1896 checkpoint.path_count = 0
1897
1898 path = solve_graph( dij, gi.name, gj.name )
1899
1900 if path:#{
1901 for pi in range(len(path)):#{
1902 pathindice = ent_path_index()
1903 pathindice.index = routenode_count + path[pi]
1904 sr_ent_push( pathindice )
1905
1906 checkpoint.path_count += 1
1907 pathindice_count += 1
1908 #}
1909 #}
1910
1911 sr_ent_push( checkpoint )
1912 route.checkpoints_count += 1
1913 checkpoint_count += 1
1914 #}
1915
1916 sr_ent_push( route )
1917 #}
1918
1919 for obj in traffics:#{
1920 traffic = ent_traffic()
1921 compile_obj_transform( obj, traffic.transform )
1922 traffic.submesh_start, traffic.submesh_count, _ = \
1923 sr_compile_mesh_internal( obj )
1924
1925 # find best subsection
1926
1927 graph_keys = list(dij.graph)
1928 min_dist = 100.0
1929 best_point = 0
1930
1931 for j in range(len(dij.points)):#{
1932 point = dij.points[j]
1933 dist = (point-obj.location).magnitude
1934
1935 if dist < min_dist:#{
1936 min_dist = dist
1937 best_point = j
1938 #}
1939 #}
1940
1941 # scan to each edge
1942 best_begin = best_point
1943 best_end = best_point
1944
1945 while True:#{
1946 map0 = dij.subsections[best_begin]
1947 if map0[1] == -1: break
1948 best_begin = map0[1]
1949 #}
1950 while True:#{
1951 map1 = dij.subsections[best_end]
1952 if map1[2] == -1: break
1953 best_end = map1[2]
1954 #}
1955
1956 traffic.start_node = routenode_count + best_begin
1957 traffic.node_count = best_end - best_begin
1958 traffic.index = best_point - best_begin
1959 traffic.speed = obj.SR_data.ent_traffic[0].speed
1960 traffic.t = 0.0
1961
1962 sr_ent_push(traffic)
1963 #}
1964
1965 for point in dij.points:#{
1966 rn = ent_route_node()
1967 rn.co[0] = point[0]
1968 rn.co[1] = point[2]
1969 rn.co[2] = -point[1]
1970 sr_ent_push( rn )
1971 #}
1972
1973 routenode_count += len(dij.points)
1974 #}
1975
1976 print( F"[SR] Writing file" )
1977
1978 file_array_instructions = {}
1979 file_offset = 0
1980
1981 def _write_array( name, item_size, data ):#{
1982 nonlocal file_array_instructions, file_offset
1983
1984 count = len(data)//item_size
1985 file_array_instructions[name] = {'count':count, 'size':item_size,\
1986 'data':data, 'offset': file_offset}
1987 file_offset += len(data)
1988 file_offset = int_align_to( file_offset, 8 )
1989 #}
1990
1991 _write_array( 'strings', 1, sr_compile.string_data )
1992 _write_array( 'mdl_mesh', sizeof(mdl_mesh), sr_compile.mesh_data )
1993 _write_array( 'mdl_submesh', sizeof(mdl_submesh), sr_compile.submesh_data )
1994 _write_array( 'mdl_material', sizeof(mdl_material), sr_compile.material_data)
1995 _write_array( 'mdl_texture', sizeof(mdl_texture), sr_compile.texture_data)
1996 _write_array( 'mdl_armature', sizeof(mdl_armature), sr_compile.armature_data)
1997 _write_array( 'mdl_bone', sizeof(mdl_bone), sr_compile.bone_data )
1998
1999 for name, buffer in sr_compile.entity_data.items():#{
2000 _write_array( name, sr_compile.entity_info[name]['size'], buffer )
2001 #}
2002
2003 _write_array( 'mdl_animation', sizeof(mdl_animation), sr_compile.anim_data)
2004 _write_array( 'mdl_keyframe', sizeof(mdl_transform),sr_compile.keyframe_data)
2005 _write_array( 'mdl_vert', sizeof(mdl_vert), sr_compile.vertex_data )
2006 _write_array( 'mdl_indice', sizeof(c_uint32), sr_compile.indice_data )
2007 _write_array( 'pack', 1, sr_compile.pack_data )
2008
2009 header_size = int_align_to( sizeof(mdl_header), 8 )
2010 index_size = int_align_to( sizeof(mdl_array)*len(file_array_instructions),8 )
2011
2012 folder = bpy.path.abspath(bpy.context.scene.SR_data.export_dir)
2013 path = F"{folder}{collection.name}.mdl"
2014 print( path )
2015
2016 os.makedirs(os.path.dirname(path),exist_ok=True)
2017 fp = open( path, "wb" )
2018 header = mdl_header()
2019 header.version = MDL_VERSION_NR
2020 sr_array_title( header.arrays, \
2021 'index', len(file_array_instructions), \
2022 sizeof(mdl_array), header_size )
2023
2024 fp.write( bytearray_align_to( bytearray(header), 8 ) )
2025
2026 print( F'[SR] {"name":>16}| count | offset' )
2027 index = bytearray()
2028 for name,info in file_array_instructions.items():#{
2029 arr = mdl_array()
2030 offset = info['offset'] + header_size + index_size
2031 sr_array_title( arr, name, info['count'], info['size'], offset )
2032 index.extend( bytearray(arr) )
2033
2034 print( F'[SR] {name:>16}| {info["count"]: 8} '+\
2035 F' 0x{info["offset"]:02x}' )
2036 #}
2037 fp.write( bytearray_align_to( index, 8 ) )
2038 #bytearray_print_hex( index )
2039
2040 for name,info in file_array_instructions.items():#{
2041 fp.write( bytearray_align_to( info['data'], 8 ) )
2042 #}
2043
2044 fp.close()
2045
2046 print( '[SR] done' )
2047 #}
2048
2049 class SR_SCENE_SETTINGS(bpy.types.PropertyGroup):
2050 #{
2051 use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
2052 export_dir: bpy.props.StringProperty( name="Export Dir", subtype='DIR_PATH' )
2053 gizmos: bpy.props.BoolProperty( name="Draw Gizmos", default=True )
2054
2055 panel: bpy.props.EnumProperty(
2056 name='Panel',
2057 description='',
2058 items=[
2059 ('EXPORT', 'Export', '', 'MOD_BUILD',0),
2060 ('ENTITY', 'Entity', '', 'MONKEY',1),
2061 ('SETTINGS', 'Settings', 'Settings', 'PREFERENCES',2),
2062 ],
2063 )
2064 #}
2065
2066 class SR_COLLECTION_SETTINGS(bpy.types.PropertyGroup):
2067 #{
2068 pack_textures: bpy.props.BoolProperty( name="Pack Textures", default=False )
2069 animations: bpy.props.BoolProperty( name="Export animation", default=True)
2070 #}
2071
2072 def sr_get_mirror_bone( bones ):
2073 #{
2074 side = bones.active.name[-1:]
2075 other_name = bones.active.name[:-1]
2076 if side == 'L': other_name += 'R'
2077 elif side == 'R': other_name += 'L'
2078 else: return None
2079
2080 for b in bones:#{
2081 if b.name == other_name:
2082 return b
2083 #}
2084
2085 return None
2086 #}
2087
2088 class SR_MIRROR_BONE_X(bpy.types.Operator):
2089 #{
2090 bl_idname="skaterift.mirror_bone"
2091 bl_label="Mirror bone attributes - SkateRift"
2092
2093 def execute(_,context):
2094 #{
2095 active_object = context.active_object
2096 bones = active_object.data.bones
2097 a = bones.active
2098 b = sr_get_mirror_bone( bones )
2099
2100 if not b: return {'FINISHED'}
2101
2102 b.SR_data.collider = a.SR_data.collider
2103
2104 def _v3copyflipy( a, b ):#{
2105 b[0] = a[0]
2106 b[1] = -a[1]
2107 b[2] = a[2]
2108 #}
2109
2110 _v3copyflipy( a.SR_data.collider_min, b.SR_data.collider_min )
2111 _v3copyflipy( a.SR_data.collider_max, b.SR_data.collider_max )
2112 b.SR_data.collider_min[1] = -a.SR_data.collider_max[1]
2113 b.SR_data.collider_max[1] = -a.SR_data.collider_min[1]
2114
2115 b.SR_data.cone_constraint = a.SR_data.cone_constraint
2116
2117 _v3copyflipy( a.SR_data.conevx, b.SR_data.conevy )
2118 _v3copyflipy( a.SR_data.conevy, b.SR_data.conevx )
2119 _v3copyflipy( a.SR_data.coneva, b.SR_data.coneva )
2120
2121 b.SR_data.conet = a.SR_data.conet
2122
2123 # redraw
2124 ob = bpy.context.scene.objects[0]
2125 ob.hide_render = ob.hide_render
2126 return {'FINISHED'}
2127 #}
2128 #}
2129
2130 class SR_COMPILE(bpy.types.Operator):
2131 #{
2132 bl_idname="skaterift.compile_all"
2133 bl_label="Compile All"
2134
2135 def execute(_,context):
2136 #{
2137 view_layer = bpy.context.view_layer
2138 for col in view_layer.layer_collection.children["export"].children:
2139 if not col.hide_viewport or bpy.context.scene.SR_data.use_hidden:
2140 sr_compile( bpy.data.collections[col.name] )
2141
2142 return {'FINISHED'}
2143 #}
2144 #}
2145
2146 class SR_COMPILE_THIS(bpy.types.Operator):
2147 #{
2148 bl_idname="skaterift.compile_this"
2149 bl_label="Compile This collection"
2150
2151 def execute(_,context):
2152 #{
2153 col = bpy.context.collection
2154 sr_compile( col )
2155
2156 return {'FINISHED'}
2157 #}
2158 #}
2159
2160 class SR_INTERFACE(bpy.types.Panel):
2161 #{
2162 bl_idname = "VIEW3D_PT_skate_rift"
2163 bl_label = "Skate Rift"
2164 bl_space_type = 'VIEW_3D'
2165 bl_region_type = 'UI'
2166 bl_category = "Skate Rift"
2167
2168 def draw(_, context):
2169 #{
2170 # Compiler section
2171
2172 row = _.layout.row()
2173 row.scale_y = 1.75
2174 row.prop( context.scene.SR_data, 'panel', expand=True )
2175
2176 if context.scene.SR_data.panel == 'SETTINGS': #{
2177 _.layout.prop( context.scene.SR_data, 'gizmos' )
2178 #}
2179 elif context.scene.SR_data.panel == 'EXPORT': #{
2180 _.layout.prop( context.scene.SR_data, "export_dir" )
2181 col = bpy.context.collection
2182
2183 found_in_export = False
2184 export_count = 0
2185 view_layer = bpy.context.view_layer
2186 for c1 in view_layer.layer_collection.children["export"].children: #{
2187 if not c1.hide_viewport or bpy.context.scene.SR_data.use_hidden:
2188 export_count += 1
2189
2190 if c1.name == col.name: #{
2191 found_in_export = True
2192 #}
2193 #}
2194
2195 box = _.layout.box()
2196 row = box.row()
2197 row.alignment = 'CENTER'
2198 row.scale_y = 1.5
2199
2200 if found_in_export: #{
2201 row.label( text=col.name + ".mdl" )
2202 box.prop( col.SR_data, "pack_textures" )
2203 box.prop( col.SR_data, "animations" )
2204 box.operator( "skaterift.compile_this" )
2205 #}
2206 else: #{
2207 row.enabled=False
2208 row.label( text=col.name )
2209
2210 row = box.row()
2211 row.enabled=False
2212 row.alignment = 'CENTER'
2213 row.scale_y = 1.5
2214 row.label( text="This collection is not in the export group" )
2215 #}
2216
2217 box = _.layout.box()
2218 row = box.row()
2219
2220 split = row.split( factor=0.3, align=True )
2221 split.prop( context.scene.SR_data, "use_hidden", text="hidden" )
2222
2223 row1 = split.row()
2224 if export_count == 0:
2225 row1.enabled=False
2226 row1.operator( "skaterift.compile_all", \
2227 text=F"Compile all ({export_count} collections)" )
2228 #}
2229 elif context.scene.SR_data.panel == 'ENTITY': #{
2230 active_object = context.active_object
2231 if not active_object: return
2232
2233 _.layout.operator( 'skaterift.copy_entity_data', \
2234 text=F'Copy entity data to {len(context.selected_objects)-1} '+\
2235 F'other objects' )
2236
2237 box = _.layout.box()
2238 row = box.row()
2239 row.alignment = 'CENTER'
2240 row.label( text=active_object.name )
2241 row.scale_y = 1.5
2242
2243 def _draw_prop_collection( data ): #{
2244 nonlocal box
2245 row = box.row()
2246 row.alignment = 'CENTER'
2247 row.enabled = False
2248 row.scale_y = 1.5
2249 row.label( text=F'{data[0]}' )
2250
2251 if hasattr(type(data[0]),'sr_inspector'):#{
2252 type(data[0]).sr_inspector( box, data )
2253 #}
2254 else:#{
2255 for a in data[0].__annotations__:
2256 box.prop( data[0], a )
2257 #}
2258 #}
2259
2260 if active_object.type == 'ARMATURE': #{
2261 if active_object.mode == 'POSE': #{
2262 bones = active_object.data.bones
2263 mb = sr_get_mirror_bone( bones )
2264 if mb:#{
2265 box.operator( "skaterift.mirror_bone", \
2266 text=F'Mirror attributes to {mb.name}' )
2267 #}
2268
2269 _draw_prop_collection( [bones.active.SR_data ] )
2270 #}
2271 else: #{
2272 row = box.row()
2273 row.alignment='CENTER'
2274 row.scale_y=2.0
2275 row.enabled=False
2276 row.label( text="Enter pose mode to modify bone properties" )
2277 #}
2278 #}
2279 elif active_object.type == 'LIGHT': #{
2280 _draw_prop_collection( [active_object.data.SR_data] )
2281 #}
2282 elif active_object.type in ['EMPTY','CURVE','MESH']:#{
2283 box.prop( active_object.SR_data, "ent_type" )
2284 ent_type = active_object.SR_data.ent_type
2285
2286 col = getattr( active_object.SR_data, ent_type, None )
2287 if col != None and len(col)!=0: _draw_prop_collection( col )
2288
2289 if active_object.type == 'MESH':#{
2290 col = getattr( active_object.data.SR_data, ent_type, None )
2291 if col != None and len(col)!=0: _draw_prop_collection( col )
2292 #}
2293 #}
2294 #}
2295 #}
2296 #}
2297
2298 class SR_MATERIAL_PANEL(bpy.types.Panel):
2299 #{
2300 bl_label="Skate Rift material"
2301 bl_idname="MATERIAL_PT_sr_material"
2302 bl_space_type='PROPERTIES'
2303 bl_region_type='WINDOW'
2304 bl_context="material"
2305
2306 def draw(_,context):
2307 #{
2308 active_object = bpy.context.active_object
2309 if active_object == None: return
2310 active_mat = active_object.active_material
2311 if active_mat == None: return
2312
2313 info = material_info( active_mat )
2314
2315 if 'tex_diffuse' in info:#{
2316 _.layout.label( icon='INFO', \
2317 text=F"{info['tex_diffuse'].name} will be compiled" )
2318 #}
2319
2320 _.layout.prop( active_mat.SR_data, "shader" )
2321 _.layout.prop( active_mat.SR_data, "surface_prop" )
2322 _.layout.prop( active_mat.SR_data, "collision" )
2323
2324 if active_mat.SR_data.collision:#{
2325 box = _.layout.box()
2326 row = box.row()
2327
2328 if (active_mat.SR_data.shader != 'invisible') and \
2329 (active_mat.SR_data.shader != 'boundary'):#{
2330 row.prop( active_mat.SR_data, "skate_surface" )
2331 row.prop( active_mat.SR_data, "grind_surface" )
2332 row.prop( active_mat.SR_data, "grow_grass" )
2333 row.prop( active_mat.SR_data, "preview_visibile" )
2334 #}
2335 #}
2336
2337 if active_mat.SR_data.shader == "terrain_blend":#{
2338 box = _.layout.box()
2339 box.prop( active_mat.SR_data, "blend_offset" )
2340 box.prop( active_mat.SR_data, "sand_colour" )
2341 #}
2342 elif active_mat.SR_data.shader == "vertex_blend":#{
2343 box = _.layout.box()
2344 box.label( icon='INFO', text="Uses vertex colours, the R channel" )
2345 box.prop( active_mat.SR_data, "blend_offset" )
2346 #}
2347 elif active_mat.SR_data.shader == "water":#{
2348 box = _.layout.box()
2349 box.label( icon='INFO', text="Depth scale of 16 meters" )
2350 box.prop( active_mat.SR_data, "shore_colour" )
2351 box.prop( active_mat.SR_data, "ocean_colour" )
2352 #}
2353 #}
2354 #}
2355
2356 def sr_get_type_enum( scene, context ):
2357 #{
2358 items = [('none','None',"")]
2359 mesh_entities=['ent_gate','ent_water']
2360 point_entities=['ent_spawn','ent_route_node','ent_route']
2361
2362 for e in point_entities: items += [(e,e,'')]
2363
2364 if context.scene.SR_data.panel == 'ENTITY': #{
2365 if context.active_object.type == 'MESH': #{
2366 for e in mesh_entities: items += [(e,e,'')]
2367 #}
2368 #}
2369 else: #{
2370 for e in mesh_entities: items += [(e,e,'')]
2371 #}
2372
2373 return items
2374 #}
2375
2376 def sr_on_type_change( _, context ):
2377 #{
2378 obj = context.active_object
2379 ent_type = obj.SR_data.ent_type
2380 if ent_type == 'none': return
2381 if obj.type == 'MESH':#{
2382 col = getattr( obj.data.SR_data, ent_type, None )
2383 if col != None and len(col)==0: col.add()
2384 #}
2385
2386 col = getattr( obj.SR_data, ent_type, None )
2387 if col != None and len(col)==0: col.add()
2388 #}
2389
2390 class SR_OBJECT_ENT_SPAWN(bpy.types.PropertyGroup):
2391 #{
2392 alias: bpy.props.StringProperty( name='alias' )
2393 #}
2394
2395 class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup):
2396 #{
2397 target: bpy.props.PointerProperty( \
2398 type=bpy.types.Object, name="destination", \
2399 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate']))
2400
2401 key: bpy.props.StringProperty()
2402 tipo: bpy.props.EnumProperty(items=(('default', 'Default', ""),
2403 ('nonlocal', 'Non-Local', "")))
2404
2405 flip: bpy.props.BoolProperty( name="Flip exit", default=False )
2406 custom: bpy.props.BoolProperty( name="Mesh is surface", default=False )
2407
2408 @staticmethod
2409 def sr_inspector( layout, data ):
2410 #{
2411 box = layout.box()
2412 box.prop( data[0], 'tipo', text="subtype" )
2413
2414 if data[0].tipo == 'default': box.prop( data[0], 'target' )
2415 elif data[0].tipo == 'nonlocal': box.prop( data[0], 'key' )
2416
2417 flags = box.box()
2418 flags.prop( data[0], 'flip' )
2419 flags.prop( data[0], 'custom' )
2420 #}
2421 #}
2422
2423 class SR_MESH_ENT_GATE(bpy.types.PropertyGroup):
2424 #{
2425 dimensions: bpy.props.FloatVectorProperty(name="dimensions",size=3)
2426 #}
2427
2428 class SR_OBJECT_ENT_ROUTE_ENTRY(bpy.types.PropertyGroup):
2429 #{
2430 target: bpy.props.PointerProperty( \
2431 type=bpy.types.Object, name='target', \
2432 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate']))
2433 #}
2434
2435 class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList):
2436 #{
2437 bl_idname = 'SR_UL_ROUTE_NODE_LIST'
2438
2439 def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
2440 #{
2441 layout.prop( item, 'target', text='', emboss=False )
2442 #}
2443 #}
2444
2445 def internal_listdel_execute(self,context,ent_name,collection_name):
2446 #{
2447 active_object = context.active_object
2448 data = getattr(active_object.SR_data,ent_name)[0]
2449 lista = getattr(data,collection_name)
2450 index = getattr(data,F'{collection_name}_index')
2451
2452 lista.remove(index)
2453
2454 setattr(data,F'{collection_name}_index', min(max(0,index-1), len(lista)-1))
2455 return{'FINISHED'}
2456 #}
2457
2458 def internal_listadd_execute(self,context,ent_name,collection_name):
2459 #{
2460 active_object = context.active_object
2461 getattr(getattr(active_object.SR_data,ent_name)[0],collection_name).add()
2462 return{'FINISHED'}
2463 #}
2464
2465 def copy_propgroup( de, to ):
2466 #{
2467 for a in de.__annotations__:#{
2468 if isinstance(getattr(de,a), bpy.types.bpy_prop_collection):#{
2469 ca = getattr(de,a)
2470 cb = getattr(to,a)
2471
2472 while len(cb) != len(ca):#{
2473 if len(cb) < len(ca): cb.add()
2474 else: cb.remove(0)
2475 #}
2476 for i in range(len(ca)):#{
2477 copy_propgroup(ca[i],cb[i])
2478 #}
2479 #}
2480 else:#{
2481 setattr(to,a,getattr(de,a))
2482 #}
2483 #}
2484 #}
2485
2486 class SR_OT_COPY_ENTITY_DATA(bpy.types.Operator):
2487 #{
2488 bl_idname = "skaterift.copy_entity_data"
2489 bl_label = "Copy entity data"
2490
2491 def execute(self, context):#{
2492 data = context.active_object.SR_data
2493 new_type = data.ent_type
2494 print( F"Copy entity data from: {context.active_object.name}" )
2495
2496 for obj in context.selected_objects:#{
2497 if obj != context.active_object:#{
2498 print( F" To: {obj.name}" )
2499
2500 obj.SR_data.ent_type = new_type
2501
2502 if active_object.type == 'MESH':#{
2503 col = getattr( obj.data.SR_data, new_type, None )
2504 if col != None and len(col)==0: col.add()
2505 mdata = context.active_object.data.SR_data
2506 copy_propgroup( getattr(mdata,new_type)[0], col[0] )
2507 #}
2508
2509 col = getattr( obj.SR_data, new_type, None )
2510 if col != None and len(col)==0: col.add()
2511 copy_propgroup( getattr(data,new_type)[0], col[0] )
2512 #}
2513 #}
2514 return{'FINISHED'}
2515 #}
2516 #}
2517
2518 class SR_OT_ROUTE_LIST_NEW_ITEM(bpy.types.Operator):
2519 #{
2520 bl_idname = "skaterift.new_entry"
2521 bl_label = "Add gate"
2522
2523 def execute(self, context):#{
2524 return internal_listadd_execute(self,context,'ent_route','gates')
2525 #}
2526 #}
2527
2528 class SR_OT_ROUTE_LIST_DEL_ITEM(bpy.types.Operator):
2529 #{
2530 bl_idname = "skaterift.del_entry"
2531 bl_label = "Remove gate"
2532
2533 @classmethod
2534 def poll(cls, context):#{
2535 active_object = context.active_object
2536 if obj_ent_type(active_object) == 'ent_route':#{
2537 return active_object.SR_data.ent_route[0].gates
2538 #}
2539 else: return False
2540 #}
2541
2542 def execute(self, context):#{
2543 return internal_listdel_execute(self,context,'ent_route','gates')
2544 #}
2545 #}
2546
2547 class SR_OT_AUDIO_LIST_NEW_ITEM(bpy.types.Operator):
2548 #{
2549 bl_idname = "skaterift.al_new_entry"
2550 bl_label = "Add file"
2551
2552 def execute(self, context):#{
2553 return internal_listadd_execute(self,context,'ent_audio','files')
2554 #}
2555 #}
2556
2557 class SR_OT_AUDIO_LIST_DEL_ITEM(bpy.types.Operator):
2558 #{
2559 bl_idname = "skaterift.al_del_entry"
2560 bl_label = "Remove file"
2561
2562 @classmethod
2563 def poll(cls, context):#{
2564 active_object = context.active_object
2565 if obj_ent_type(active_object) == 'ent_audio':#{
2566 return active_object.SR_data.ent_audio[0].files
2567 #}
2568 else: return False
2569 #}
2570
2571 def execute(self, context):#{
2572 return internal_listdel_execute(self,context,'ent_audio','files')
2573 return{'FINISHED'}
2574 #}
2575 #}
2576
2577 class SR_OT_GLYPH_LIST_NEW_ITEM(bpy.types.Operator):
2578 #{
2579 bl_idname = "skaterift.gl_new_entry"
2580 bl_label = "Add glyph"
2581
2582 def execute(self, context):#{
2583 active_object = context.active_object
2584
2585 font = active_object.SR_data.ent_font[0]
2586 font.glyphs.add()
2587
2588 if len(font.glyphs) > 1:#{
2589 prev = font.glyphs[-2]
2590 cur = font.glyphs[-1]
2591
2592 cur.bounds = prev.bounds
2593 cur.utf32 = prev.utf32+1
2594 #}
2595
2596 return{'FINISHED'}
2597 #}
2598 #}
2599
2600 class SR_OT_GLYPH_LIST_DEL_ITEM(bpy.types.Operator):
2601 #{
2602 bl_idname = "skaterift.gl_del_entry"
2603 bl_label = "Remove Glyph"
2604
2605 @classmethod
2606 def poll(cls, context):#{
2607 active_object = context.active_object
2608 if obj_ent_type(active_object) == 'ent_font':#{
2609 return active_object.SR_data.ent_font[0].glyphs
2610 #}
2611 else: return False
2612 #}
2613
2614 def execute(self, context):#{
2615 return internal_listdel_execute(self,context,'ent_font','glyphs')
2616 #}
2617 #}
2618
2619 class SR_OT_GLYPH_LIST_MOVE_ITEM(bpy.types.Operator):
2620 #{
2621 bl_idname = "skaterift.gl_move_item"
2622 bl_label = "aa"
2623 direction: bpy.props.EnumProperty(items=(('UP', 'Up', ""),
2624 ('DOWN', 'Down', ""),))
2625
2626 @classmethod
2627 def poll(cls, context):#{
2628 active_object = context.active_object
2629 if obj_ent_type(active_object) == 'ent_font':#{
2630 return active_object.SR_data.ent_font[0].glyphs
2631 #}
2632 else: return False
2633 #}
2634
2635 def execute(_, context):#{
2636 active_object = context.active_object
2637 data = active_object.SR_data.ent_font[0]
2638
2639 index = data.glyphs_index
2640 neighbor = index + (-1 if _.direction == 'UP' else 1)
2641 data.glyphs.move( neighbor, index )
2642
2643 list_length = len(data.glyphs) - 1
2644 new_index = index + (-1 if _.direction == 'UP' else 1)
2645
2646 data.glyphs_index = max(0, min(new_index, list_length))
2647
2648 return{'FINISHED'}
2649 #}
2650 #}
2651
2652 class SR_OT_FONT_VARIANT_LIST_NEW_ITEM(bpy.types.Operator):
2653 #{
2654 bl_idname = "skaterift.fv_new_entry"
2655 bl_label = "Add variant"
2656
2657 def execute(self, context):#{
2658 return internal_listadd_execute(self,context,'ent_font','variants')
2659 #}
2660 #}
2661
2662 class SR_OT_FONT_VARIANT_LIST_DEL_ITEM(bpy.types.Operator):
2663 #{
2664 bl_idname = "skaterift.fv_del_entry"
2665 bl_label = "Remove variant"
2666
2667 @classmethod
2668 def poll(cls, context):#{
2669 active_object = context.active_object
2670 if obj_ent_type(active_object) == 'ent_font':#{
2671 return active_object.SR_data.ent_font[0].variants
2672 #}
2673 else: return False
2674 #}
2675
2676 def execute(self, context):#{
2677 return internal_listdel_execute(self,context,'ent_font','variants')
2678 #}
2679 #}
2680
2681 class SR_OBJECT_ENT_AUDIO_FILE_ENTRY(bpy.types.PropertyGroup):
2682 #{
2683 path: bpy.props.StringProperty( name="Path" )
2684 probability: bpy.props.FloatProperty( name="Probability",default=100.0 )
2685 #}
2686
2687 class SR_UL_AUDIO_LIST(bpy.types.UIList):
2688 #{
2689 bl_idname = 'SR_UL_AUDIO_LIST'
2690
2691 def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
2692 #{
2693 split = layout.split(factor=0.7)
2694 c = split.column()
2695 c.prop( item, 'path', text='', emboss=False )
2696 c = split.column()
2697 c.prop( item, 'probability', text='%', emboss=True )
2698 #}
2699 #}
2700
2701 class SR_UL_FONT_VARIANT_LIST(bpy.types.UIList):
2702 #{
2703 bl_idname = 'SR_UL_FONT_VARIANT_LIST'
2704
2705 def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
2706 #{
2707 layout.prop( item, 'mesh', emboss=False )
2708 layout.prop( item, 'tipo' )
2709 #}
2710 #}
2711
2712 class SR_UL_FONT_GLYPH_LIST(bpy.types.UIList):
2713 #{
2714 bl_idname = 'SR_UL_FONT_GLYPH_LIST'
2715
2716 def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
2717 #{
2718 s0 = layout.split(factor=0.3)
2719 c = s0.column()
2720 s1 = c.split(factor=0.3)
2721 c = s1.column()
2722 row = c.row()
2723 lbl = chr(item.utf32) if item.utf32 >= 32 and item.utf32 <= 126 else \
2724 f'x{item.utf32:x}'
2725 row.label(text=lbl)
2726 c = s1.column()
2727 c.prop( item, 'utf32', text='', emboss=True )
2728 c = s0.column()
2729 row = c.row()
2730 row.prop( item, 'bounds', text='', emboss=False )
2731 #}
2732 #}
2733
2734 class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
2735 #{
2736 gates: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE_ENTRY)
2737 gates_index: bpy.props.IntProperty()
2738
2739 colour: bpy.props.FloatVectorProperty( \
2740 name="Colour",\
2741 subtype='COLOR',\
2742 min=0.0,max=1.0,\
2743 default=Vector((0.79,0.63,0.48)),\
2744 description="Route colour"\
2745 )
2746
2747 alias: bpy.props.StringProperty(\
2748 name="Alias",\
2749 default="Untitled Course")
2750
2751 @staticmethod
2752 def sr_inspector( layout, data ):
2753 #{
2754 layout.prop( data[0], 'alias' )
2755 layout.prop( data[0], 'colour' )
2756
2757 layout.label( text='Checkpoints' )
2758 layout.template_list('SR_UL_ROUTE_NODE_LIST', 'Checkpoints', \
2759 data[0], 'gates', data[0], 'gates_index', rows=5)
2760
2761 row = layout.row()
2762 row.operator( 'skaterift.new_entry', text='Add' )
2763 row.operator( 'skaterift.del_entry', text='Remove' )
2764 #}
2765 #}
2766
2767 class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{
2768 subtype: bpy.props.EnumProperty(
2769 name="Subtype",
2770 items=[('0','Trigger',''),
2771 ('1','Particles (0.1s)','')]
2772 )
2773
2774 target: bpy.props.PointerProperty( \
2775 type=bpy.types.Object, name="Target", \
2776 poll=lambda self,obj: sr_filter_ent_type(obj,\
2777 ['ent_audio','ent_skateshop','ent_ccmd',\
2778 'ent_challenge']))
2779
2780 event: bpy.props.IntProperty( name="Event/Method" )
2781
2782 @staticmethod
2783 def sr_inspector( layout, data ):#{
2784 layout.prop( data[0], 'subtype' )
2785 layout.prop( data[0], 'target' )
2786
2787 row = layout.row()
2788 row.prop( data[0], 'event' )
2789
2790 if data[0].target:#{
2791 tipo = data[0].target.SR_data.ent_type
2792 cls = globals()[ tipo ]
2793
2794 table = getattr( cls, 'sr_functions', None )
2795 if table:#{
2796 if data[0].event in table:#{
2797 row.label( text=table[data[0].event] )
2798 #}
2799 else:#{
2800 row.label( text="undefined function" )
2801 #}
2802 #}
2803 #}
2804 #}
2805 #}
2806
2807 class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup):
2808 #{
2809 files: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO_FILE_ENTRY)
2810 files_index: bpy.props.IntProperty()
2811
2812 flag_3d: bpy.props.BoolProperty( name="3D audio",default=True )
2813 flag_loop: bpy.props.BoolProperty( name="Loop",default=False )
2814 flag_auto: bpy.props.BoolProperty( name="Play at start",default=False )
2815 flag_nodoppler: bpy.props.BoolProperty( name="No Doppler",default=False )
2816
2817 group: bpy.props.IntProperty( name="Group ID", default=0 )
2818 formato: bpy.props.EnumProperty(
2819 name="Format",
2820 items=[('0','Uncompressed Mono',''),
2821 ('1','Compressed Vorbis',''),
2822 ('2','[vg] Bird Synthesis','')]
2823 )
2824 probability_curve: bpy.props.EnumProperty(
2825 name="Probability Curve",
2826 items=[('0','Constant',''),
2827 ('1','Wildlife Daytime',''),
2828 ('2','Wildlife Nighttime','')])
2829 channel_behaviour: bpy.props.EnumProperty(
2830 name="Channel Behaviour",
2831 items=[('0','Unlimited',''),
2832 ('1','Discard if group full', ''),
2833 ('2','Crossfade if group full','')])
2834
2835 transition_duration: bpy.props.FloatProperty(name="Transition Time",\
2836 default=0.2)
2837
2838 max_channels: bpy.props.IntProperty( name="Max Channels", default=1 )
2839 volume: bpy.props.FloatProperty( name="Volume",default=1.0 )
2840
2841 @staticmethod
2842 def sr_inspector( layout, data ):
2843 #{
2844 layout.prop( data[0], 'formato' )
2845 layout.prop( data[0], 'volume' )
2846
2847 box = layout.box()
2848 box.label( text='Channels' )
2849 split = box.split(factor=0.3)
2850 c = split.column()
2851 c.prop( data[0], 'max_channels' )
2852 c = split.column()
2853 c.prop( data[0], 'channel_behaviour', text='Behaviour' )
2854 if data[0].channel_behaviour >= '1':
2855 box.prop( data[0], 'group' )
2856 if data[0].channel_behaviour == '2':
2857 box.prop( data[0], 'transition_duration' )
2858
2859 box = layout.box()
2860 box.label( text='Flags' )
2861 box.prop( data[0], 'flag_3d' )
2862 if data[0].flag_3d: box.prop( data[0], 'flag_nodoppler' )
2863
2864 box.prop( data[0], 'flag_loop' )
2865 box.prop( data[0], 'flag_auto' )
2866
2867 layout.prop( data[0], 'probability_curve' )
2868
2869 split = layout.split(factor=0.7)
2870 c = split.column()
2871 c.label( text='Filepath' )
2872 c = split.column()
2873 c.label( text='Chance' )
2874 layout.template_list('SR_UL_AUDIO_LIST', 'Files', \
2875 data[0], 'files', data[0], 'files_index', rows=5)
2876
2877 row = layout.row()
2878 row.operator( 'skaterift.al_new_entry', text='Add' )
2879 row.operator( 'skaterift.al_del_entry', text='Remove' )
2880 #}
2881 #}
2882
2883 class SR_OBJECT_ENT_MARKER(bpy.types.PropertyGroup):
2884 #{
2885 alias: bpy.props.StringProperty()
2886 #}
2887
2888 class SR_OBJECT_ENT_GLYPH(bpy.types.PropertyGroup):
2889 #{
2890 mini: bpy.props.FloatVectorProperty(size=2)
2891 maxi: bpy.props.FloatVectorProperty(size=2)
2892 utf32: bpy.props.IntProperty()
2893 #}
2894
2895 class SR_OBJECT_ENT_GLYPH_ENTRY(bpy.types.PropertyGroup):
2896 #{
2897 bounds: bpy.props.FloatVectorProperty(size=4,subtype='NONE')
2898 utf32: bpy.props.IntProperty()
2899 #}
2900
2901 class SR_OBJECT_ENT_FONT_VARIANT(bpy.types.PropertyGroup):
2902 #{
2903 mesh: bpy.props.PointerProperty(type=bpy.types.Object)
2904 tipo: bpy.props.StringProperty()
2905 #}
2906
2907 class SR_OBJECT_ENT_FONT(bpy.types.PropertyGroup):
2908 #{
2909 variants: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT_VARIANT)
2910 glyphs: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH_ENTRY)
2911 alias: bpy.props.StringProperty()
2912
2913 glyphs_index: bpy.props.IntProperty()
2914 variants_index: bpy.props.IntProperty()
2915
2916 @staticmethod
2917 def sr_inspector( layout, data ):
2918 #{
2919 layout.prop( data[0], 'alias' )
2920
2921 layout.label( text='Variants' )
2922 layout.template_list('SR_UL_FONT_VARIANT_LIST', 'Variants', \
2923 data[0], 'variants', data[0], 'variants_index',\
2924 rows=5 )
2925 row = layout.row()
2926 row.operator( 'skaterift.fv_new_entry', text='Add' )
2927 row.operator( 'skaterift.fv_del_entry', text='Remove' )
2928
2929 layout.label( text='ASCII Glyphs' )
2930 layout.template_list('SR_UL_FONT_GLYPH_LIST', 'Glyphs', \
2931 data[0], 'glyphs', data[0], 'glyphs_index', rows=5)
2932
2933 row = layout.row()
2934 row.operator( 'skaterift.gl_new_entry', text='Add' )
2935 row.operator( 'skaterift.gl_del_entry', text='Remove' )
2936 row.operator( 'skaterift.gl_move_item', text='^' ).direction='UP'
2937 row.operator( 'skaterift.gl_move_item', text='v' ).direction='DOWN'
2938 #}
2939 #}
2940
2941 class SR_OBJECT_ENT_TRAFFIC(bpy.types.PropertyGroup):
2942 #{
2943 speed: bpy.props.FloatProperty(default=1.0)
2944 #}
2945
2946 class SR_OBJECT_ENT_SKATESHOP(bpy.types.PropertyGroup):
2947 #{
2948 tipo: bpy.props.EnumProperty( name='Type',
2949 items=[('0','boards',''),
2950 ('1','character',''),
2951 ('2','world','')] )
2952 mark_rack: bpy.props.PointerProperty( \
2953 type=bpy.types.Object, name="Board Rack", \
2954 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
2955 mark_display: bpy.props.PointerProperty( \
2956 type=bpy.types.Object, name="Selected Board Display", \
2957 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
2958 mark_info: bpy.props.PointerProperty( \
2959 type=bpy.types.Object, name="Selected Board Info", \
2960 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
2961 cam: bpy.props.PointerProperty( \
2962 type=bpy.types.Object, name="Viewpoint", \
2963 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
2964 #}
2965
2966 class SR_OBJECT_ENT_WORKSHOP_PREVIEW(bpy.types.PropertyGroup):
2967 #{
2968 mark_display: bpy.props.PointerProperty( \
2969 type=bpy.types.Object, name="Board Display", \
2970 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
2971 mark_display1: bpy.props.PointerProperty( \
2972 type=bpy.types.Object, name="Board Display (other side)", \
2973 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
2974 cam: bpy.props.PointerProperty( \
2975 type=bpy.types.Object, name="Viewpoint", \
2976 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
2977 #}
2978
2979 class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
2980 #{
2981 link0: bpy.props.PointerProperty( \
2982 type=bpy.types.Object, name="Link 0", \
2983 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
2984 link1: bpy.props.PointerProperty( \
2985 type=bpy.types.Object, name="Link 1", \
2986 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
2987 link2: bpy.props.PointerProperty( \
2988 type=bpy.types.Object, name="Link 2", \
2989 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
2990 link3: bpy.props.PointerProperty( \
2991 type=bpy.types.Object, name="Link 3", \
2992 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
2993
2994 newloc: bpy.props.PointerProperty( \
2995 type=bpy.types.Object, name="New location", \
2996 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
2997 camera: bpy.props.PointerProperty( \
2998 type=bpy.types.Object, name="Camera", \
2999 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
3000
3001 slider_minloc: bpy.props.PointerProperty( \
3002 type=bpy.types.Object, name="Slider min", \
3003 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
3004 slider_maxloc: bpy.props.PointerProperty( \
3005 type=bpy.types.Object, name="Slider max", \
3006 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
3007 slider_handle: bpy.props.PointerProperty( \
3008 type=bpy.types.Object, name="Slider handle", \
3009 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
3010
3011 checkmark: bpy.props.PointerProperty( \
3012 type=bpy.types.Object, name="Checked", \
3013 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
3014
3015 string: bpy.props.StringProperty( name="String" )
3016 tipo: bpy.props.EnumProperty( name='Type',
3017 items=[('0','visual',''),
3018 ('1','event button',''),
3019 ('2','page button',''),
3020 ('3','toggle', ''),
3021 ('4','slider',''),
3022 ('5','page','')])
3023
3024 @staticmethod
3025 def sr_inspector( layout, data ):
3026 #{
3027 data = data[0]
3028 box = layout.box()
3029 box.prop( data, 'tipo' )
3030
3031 if data.tipo == '0':#{
3032 return
3033 #}
3034 elif data.tipo == '1':#{
3035 box.prop( data, 'string', text='Event' )
3036 #}
3037 elif data.tipo == '2':#{
3038 box.prop( data, 'string', text='Page' )
3039 box.prop( data, 'newloc' )
3040 #}
3041 elif data.tipo == '3':#{
3042 box.prop( data, 'string', text='Data (i32)' )
3043 box.prop( data, 'checkmark' )
3044 #}
3045 elif data.tipo == '4':#{
3046 box.prop( data, 'string', text='Data (f32)' )
3047 box.prop( data, 'slider_minloc' )
3048 box.prop( data, 'slider_maxloc' )
3049 box.prop( data, 'slider_handle' )
3050 box = box.box()
3051 box.label( text="Links" )
3052 box.prop( data, 'link0', text='v0' )
3053 box.prop( data, 'link1', text='v1' )
3054 return
3055 #}
3056 elif data.tipo == '5':#{
3057 box.prop( data, 'string', text='Page Name' )
3058 box.prop( data, 'newloc', text='Entry Point' )
3059 box.prop( data, 'camera', text='Viewpoint' )
3060 return
3061 #}
3062
3063 box = box.box()
3064 box.label( text="Links" )
3065 box.prop( data, 'link0' )
3066 box.prop( data, 'link1' )
3067 box.prop( data, 'link2' )
3068 box.prop( data, 'link3' )
3069 #}
3070 #}
3071
3072 class SR_OBJECT_ENT_WORLD_INFO(bpy.types.PropertyGroup):
3073 #{
3074 name: bpy.props.StringProperty(name="Name")
3075 desc: bpy.props.StringProperty(name="Description")
3076 author: bpy.props.StringProperty(name="Author")
3077 timezone: bpy.props.FloatProperty(name="Timezone(hrs) (UTC0 +hrs)")
3078 #}
3079
3080 class SR_OBJECT_ENT_CCMD(bpy.types.PropertyGroup):
3081 #{
3082 command: bpy.props.StringProperty(name="Command Line")
3083 #}
3084
3085 class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
3086 proxima: bpy.props.PointerProperty( \
3087 type=bpy.types.Object, name="Next", \
3088 poll=lambda self,obj: sr_filter_ent_type(obj,['ent_challenge']))
3089 target: bpy.props.PointerProperty( \
3090 type=bpy.types.Object, name="Target", \
3091 poll=lambda self,obj: sr_filter_ent_type(obj,\
3092 ['ent_audio','ent_ccmd']))
3093 event: bpy.props.IntProperty( name="Event/Method" )
3094 time_limit: bpy.props.FloatProperty( name="Time Limit", default=1.0 )
3095 #}
3096
3097 class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
3098 #{
3099 ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE)
3100 ent_spawn: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SPAWN)
3101 ent_route: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE)
3102 ent_volume: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_VOLUME)
3103 ent_audio: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO)
3104 ent_marker: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MARKER)
3105 ent_glyph: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH)
3106 ent_font: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT)
3107 ent_traffic: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_TRAFFIC)
3108 ent_skateshop: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SKATESHOP)
3109 ent_swspreview: \
3110 bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORKSHOP_PREVIEW)
3111 ent_menuitem: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MENU_ITEM)
3112 ent_worldinfo: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORLD_INFO)
3113 ent_ccmd: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CCMD)
3114 ent_challenge: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CHALLENGE)
3115
3116 ent_type: bpy.props.EnumProperty(
3117 name="Type",
3118 items=sr_entity_list,
3119 update=sr_on_type_change
3120 )
3121 #}
3122
3123 class SR_MESH_PROPERTIES(bpy.types.PropertyGroup):
3124 #{
3125 ent_gate: bpy.props.CollectionProperty(type=SR_MESH_ENT_GATE)
3126 #}
3127
3128 class SR_LIGHT_PROPERTIES(bpy.types.PropertyGroup):
3129 #{
3130 daytime: bpy.props.BoolProperty( name='Daytime' )
3131 #}
3132
3133 class SR_BONE_PROPERTIES(bpy.types.PropertyGroup):
3134 #{
3135 collider: bpy.props.EnumProperty( name='Collider Type',
3136 items=[('0','none',''),
3137 ('1','box',''),
3138 ('2','capsule','')])
3139
3140 collider_min: bpy.props.FloatVectorProperty( name='Collider Min', size=3 )
3141 collider_max: bpy.props.FloatVectorProperty( name='Collider Max', size=3 )
3142
3143 cone_constraint: bpy.props.BoolProperty( name='Cone constraint' )
3144
3145 conevx: bpy.props.FloatVectorProperty( name='vx' )
3146 conevy: bpy.props.FloatVectorProperty( name='vy' )
3147 coneva: bpy.props.FloatVectorProperty( name='va' )
3148 conet: bpy.props.FloatProperty( name='t' )
3149
3150 @staticmethod
3151 def sr_inspector( layout, data ):
3152 #{
3153 data = data[0]
3154 box = layout.box()
3155 box.prop( data, 'collider' )
3156
3157 if int(data.collider)>0:#{
3158 row = box.row()
3159 row.prop( data, 'collider_min' )
3160 row = box.row()
3161 row.prop( data, 'collider_max' )
3162 #}
3163
3164 box = layout.box()
3165 box.prop( data, 'cone_constraint' )
3166 if data.cone_constraint:#{
3167 row = box.row()
3168 row.prop( data, 'conevx' )
3169 row = box.row()
3170 row.prop( data, 'conevy' )
3171 row = box.row()
3172 row.prop( data, 'coneva' )
3173 box.prop( data, 'conet' )
3174 #}
3175 #}
3176 #}
3177
3178 class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
3179 #{
3180 shader: bpy.props.EnumProperty(
3181 name="Format",
3182 items = [
3183 ('standard',"standard",''),
3184 ('standard_cutout', "standard_cutout", ''),
3185 ('terrain_blend', "terrain_blend", ''),
3186 ('vertex_blend', "vertex_blend", ''),
3187 ('water',"water",''),
3188 ('invisible','Invisible',''),
3189 ('boundary','Boundary',''),
3190 ('fxglow','FX Glow',''),
3191 ])
3192
3193 surface_prop: bpy.props.EnumProperty(
3194 name="Surface Property",
3195 items = [
3196 ('0','concrete',''),
3197 ('1','wood',''),
3198 ('2','grass',''),
3199 ('3','tiles',''),
3200 ('4','metal','')
3201 ])
3202
3203 collision: bpy.props.BoolProperty( \
3204 name="Collisions Enabled",\
3205 default=True,\
3206 description = "Can the player collide with this material?"\
3207 )
3208 skate_surface: bpy.props.BoolProperty( \
3209 name="Skate Target", \
3210 default=True,\
3211 description = "Should the game try to target this surface?" \
3212 )
3213 grind_surface: bpy.props.BoolProperty( \
3214 name="Grindable", \
3215 default=True,\
3216 description = "Can you grind on this surface?" \
3217 )
3218 grow_grass: bpy.props.BoolProperty( \
3219 name="Grow Grass", \
3220 default=False,\
3221 description = "Spawn grass sprites on this surface?" \
3222 )
3223 preview_visibile: bpy.props.BoolProperty( \
3224 name="Preview visibile", \
3225 default=True,\
3226 description = "Show this material in preview models?" \
3227 )
3228 blend_offset: bpy.props.FloatVectorProperty( \
3229 name="Blend Offset", \
3230 size=2, \
3231 default=Vector((0.5,0.0)),\
3232 description="When surface is more than 45 degrees, add this vector " +\
3233 "to the UVs" \
3234 )
3235 sand_colour: bpy.props.FloatVectorProperty( \
3236 name="Sand Colour",\
3237 subtype='COLOR',\
3238 min=0.0,max=1.0,\
3239 default=Vector((0.79,0.63,0.48)),\
3240 description="Blend to this colour near the 0 coordinate on UP axis"\
3241 )
3242 shore_colour: bpy.props.FloatVectorProperty( \
3243 name="Shore Colour",\
3244 subtype='COLOR',\
3245 min=0.0,max=1.0,\
3246 default=Vector((0.03,0.32,0.61)),\
3247 description="Water colour at the shoreline"\
3248 )
3249 ocean_colour: bpy.props.FloatVectorProperty( \
3250 name="Ocean Colour",\
3251 subtype='COLOR',\
3252 min=0.0,max=1.0,\
3253 default=Vector((0.0,0.006,0.03)),\
3254 description="Water colour in the deep bits"\
3255 )
3256 #}
3257
3258 # ---------------------------------------------------------------------------- #
3259 # #
3260 # GUI section #
3261 # #
3262 # ---------------------------------------------------------------------------- #
3263
3264 cv_view_draw_handler = None
3265 cv_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
3266 cv_view_verts = []
3267 cv_view_colours = []
3268 cv_view_course_i = 0
3269
3270 # Draw axis alligned sphere at position with radius
3271 #
3272 def cv_draw_sphere( pos, radius, colour ):
3273 #{
3274 global cv_view_verts, cv_view_colours
3275
3276 ly = pos + Vector((0,0,radius))
3277 lx = pos + Vector((0,radius,0))
3278 lz = pos + Vector((0,0,radius))
3279
3280 pi = 3.14159265358979323846264
3281
3282 for i in range(16):#{
3283 t = ((i+1.0) * 1.0/16.0) * pi * 2.0
3284 s = math.sin(t)
3285 c = math.cos(t)
3286
3287 py = pos + Vector((s*radius,0.0,c*radius))
3288 px = pos + Vector((s*radius,c*radius,0.0))
3289 pz = pos + Vector((0.0,s*radius,c*radius))
3290
3291 cv_view_verts += [ px, lx ]
3292 cv_view_verts += [ py, ly ]
3293 cv_view_verts += [ pz, lz ]
3294
3295 cv_view_colours += [ colour, colour, colour, colour, colour, colour ]
3296
3297 ly = py
3298 lx = px
3299 lz = pz
3300 #}
3301 cv_draw_lines()
3302 #}
3303
3304 # Draw axis alligned sphere at position with radius
3305 #
3306 def cv_draw_halfsphere( pos, tx, ty, tz, radius, colour ):
3307 #{
3308 global cv_view_verts, cv_view_colours
3309
3310 ly = pos + tz*radius
3311 lx = pos + ty*radius
3312 lz = pos + tz*radius
3313
3314 pi = 3.14159265358979323846264
3315
3316 for i in range(16):#{
3317 t = ((i+1.0) * 1.0/16.0) * pi
3318 s = math.sin(t)
3319 c = math.cos(t)
3320
3321 s1 = math.sin(t*2.0)
3322 c1 = math.cos(t*2.0)
3323
3324 py = pos + s*tx*radius + c *tz*radius
3325 px = pos + s*tx*radius + c *ty*radius
3326 pz = pos + s1*ty*radius + c1*tz*radius
3327
3328 cv_view_verts += [ px, lx ]
3329 cv_view_verts += [ py, ly ]
3330 cv_view_verts += [ pz, lz ]
3331
3332 cv_view_colours += [ colour, colour, colour, colour, colour, colour ]
3333
3334 ly = py
3335 lx = px
3336 lz = pz
3337 #}
3338 cv_draw_lines()
3339 #}
3340
3341 # Draw transformed -1 -> 1 cube
3342 #
3343 def cv_draw_ucube( transform, colour, s=Vector((1,1,1)), o=Vector((0,0,0)) ):
3344 #{
3345 global cv_view_verts, cv_view_colours
3346
3347 a = o + -1.0 * s
3348 b = o + 1.0 * s
3349
3350 vs = [None]*8
3351 vs[0] = transform @ Vector((a[0], a[1], a[2]))
3352 vs[1] = transform @ Vector((a[0], b[1], a[2]))
3353 vs[2] = transform @ Vector((b[0], b[1], a[2]))
3354 vs[3] = transform @ Vector((b[0], a[1], a[2]))
3355 vs[4] = transform @ Vector((a[0], a[1], b[2]))
3356 vs[5] = transform @ Vector((a[0], b[1], b[2]))
3357 vs[6] = transform @ Vector((b[0], b[1], b[2]))
3358 vs[7] = transform @ Vector((b[0], a[1], b[2]))
3359
3360 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
3361 (0,4),(1,5),(2,6),(3,7)]
3362
3363 for l in indices:#{
3364 v0 = vs[l[0]]
3365 v1 = vs[l[1]]
3366 cv_view_verts += [(v0[0],v0[1],v0[2])]
3367 cv_view_verts += [(v1[0],v1[1],v1[2])]
3368 cv_view_colours += [colour, colour]
3369 #}
3370 cv_draw_lines()
3371 #}
3372
3373 # Draw line with colour
3374 #
3375 def cv_draw_line( p0, p1, colour ):
3376 #{
3377 global cv_view_verts, cv_view_colours
3378
3379 cv_view_verts += [p0,p1]
3380 cv_view_colours += [colour, colour]
3381 cv_draw_lines()
3382 #}
3383
3384 # Draw line with colour(s)
3385 #
3386 def cv_draw_line2( p0, p1, c0, c1 ):
3387 #{
3388 global cv_view_verts, cv_view_colours
3389
3390 cv_view_verts += [p0,p1]
3391 cv_view_colours += [c0,c1]
3392 cv_draw_lines()
3393 #}
3394
3395 #
3396 #
3397 def cv_tangent_basis( n, tx, ty ):
3398 #{
3399 if abs( n[0] ) >= 0.57735027:#{
3400 tx[0] = n[1]
3401 tx[1] = -n[0]
3402 tx[2] = 0.0
3403 #}
3404 else:#{
3405 tx[0] = 0.0
3406 tx[1] = n[2]
3407 tx[2] = -n[1]
3408 #}
3409
3410 tx.normalize()
3411 _ty = n.cross( tx )
3412
3413 ty[0] = _ty[0]
3414 ty[1] = _ty[1]
3415 ty[2] = _ty[2]
3416 #}
3417
3418 # Draw coloured arrow
3419 #
3420 def cv_draw_arrow( p0, p1, c0, size=0.15 ):
3421 #{
3422 global cv_view_verts, cv_view_colours
3423
3424 n = p1-p0
3425 midpt = p0 + n*0.5
3426 n.normalize()
3427
3428 tx = Vector((1,0,0))
3429 ty = Vector((1,0,0))
3430 cv_tangent_basis( n, tx, ty )
3431
3432 cv_view_verts += [p0,p1, midpt+(tx-n)*size,midpt, midpt+(-tx-n)*size,midpt ]
3433 cv_view_colours += [c0,c0,c0,c0,c0,c0]
3434 #cv_draw_lines()
3435 #}
3436
3437 def cv_draw_line_dotted( p0, p1, c0, dots=10 ):
3438 #{
3439 global cv_view_verts, cv_view_colours
3440
3441 for i in range(dots):#{
3442 t0 = i/dots
3443 t1 = (i+0.25)/dots
3444
3445 p2 = p0*(1.0-t0)+p1*t0
3446 p3 = p0*(1.0-t1)+p1*t1
3447
3448 cv_view_verts += [p2,p3]
3449 cv_view_colours += [c0,c0]
3450 #}
3451 #cv_draw_lines()
3452 #}
3453
3454 # Drawhandles of a bezier control point
3455 #
3456 def cv_draw_bhandle( obj, direction, colour ):
3457 #{
3458 global cv_view_verts, cv_view_colours
3459
3460 p0 = obj.location
3461 h0 = obj.matrix_world @ Vector((0,direction,0))
3462
3463 cv_view_verts += [p0]
3464 cv_view_verts += [h0]
3465 cv_view_colours += [colour,colour]
3466 cv_draw_lines()
3467 #}
3468
3469 # Draw a bezier curve (at fixed resolution 10)
3470 #
3471 def cv_draw_bezier( p0,h0,p1,h1,c0,c1 ):
3472 #{
3473 global cv_view_verts, cv_view_colours
3474
3475 last = p0
3476 for i in range(10):#{
3477 t = (i+1)/10
3478 a0 = 1-t
3479
3480 tt = t*t
3481 ttt = tt*t
3482 p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
3483
3484 cv_view_verts += [(last[0],last[1],last[2])]
3485 cv_view_verts += [(p[0],p[1],p[2])]
3486 cv_view_colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
3487
3488 last = p
3489 #}
3490 cv_draw_lines()
3491 #}
3492
3493 # I think this one extends the handles of the bezier otwards......
3494 #
3495 def cv_draw_sbpath( o0,o1,c0,c1,s0,s1 ):
3496 #{
3497 global cv_view_course_i
3498
3499 offs = ((cv_view_course_i % 2)*2-1) * cv_view_course_i * 0.02
3500
3501 p0 = o0.matrix_world @ Vector((offs, 0,0))
3502 h0 = o0.matrix_world @ Vector((offs, s0,0))
3503 p1 = o1.matrix_world @ Vector((offs, 0,0))
3504 h1 = o1.matrix_world @ Vector((offs,-s1,0))
3505
3506 cv_draw_bezier( p0,h0,p1,h1,c0,c1 )
3507 cv_draw_lines()
3508 #}
3509
3510 # Flush the lines buffers. This is called often because god help you if you want
3511 # to do fixed, fast buffers in this catastrophic programming language.
3512 #
3513 def cv_draw_lines():
3514 #{
3515 global cv_view_shader, cv_view_verts, cv_view_colours
3516
3517 if len(cv_view_verts) < 2:
3518 return
3519
3520 lines = batch_for_shader(\
3521 cv_view_shader, 'LINES', \
3522 { "pos":cv_view_verts, "color":cv_view_colours })
3523
3524 if bpy.context.scene.SR_data.gizmos:
3525 lines.draw( cv_view_shader )
3526
3527 cv_view_verts = []
3528 cv_view_colours = []
3529 #}
3530
3531 # I dont remember what this does exactly
3532 #
3533 def cv_draw_bpath( o0,o1,c0,c1 ):
3534 #{
3535 cv_draw_sbpath( o0,o1,c0,c1,1.0,1.0 )
3536 #}
3537
3538 # Semi circle to show the limit. and some lines
3539 #
3540 def draw_limit( obj, center, major, minor, amin, amax, colour ):
3541 #{
3542 global cv_view_verts, cv_view_colours
3543 f = 0.05
3544 ay = major*f
3545 ax = minor*f
3546
3547 for x in range(16):#{
3548 t0 = x/16
3549 t1 = (x+1)/16
3550 a0 = amin*(1.0-t0)+amax*t0
3551 a1 = amin*(1.0-t1)+amax*t1
3552
3553 p0 = center + major*f*math.cos(a0) + minor*f*math.sin(a0)
3554 p1 = center + major*f*math.cos(a1) + minor*f*math.sin(a1)
3555
3556 p0=obj.matrix_world @ p0
3557 p1=obj.matrix_world @ p1
3558 cv_view_verts += [p0,p1]
3559 cv_view_colours += [colour,colour]
3560
3561 if x == 0:#{
3562 cv_view_verts += [p0,center]
3563 cv_view_colours += [colour,colour]
3564 #}
3565 if x == 15:#{
3566 cv_view_verts += [p1,center]
3567 cv_view_colours += [colour,colour]
3568 #}
3569 #}
3570
3571 cv_view_verts += [center+major*1.2*f,center+major*f*0.8]
3572 cv_view_colours += [colour,colour]
3573
3574 cv_draw_lines()
3575 #}
3576
3577 # Cone and twist limit
3578 #
3579 def draw_cone_twist( center, vx, vy, va ):
3580 #{
3581 global cv_view_verts, cv_view_colours
3582 axis = vy.cross( vx )
3583 axis.normalize()
3584
3585 size = 0.12
3586
3587 cv_view_verts += [center, center+va*size]
3588 cv_view_colours += [ (1,1,1), (1,1,1) ]
3589
3590 for x in range(32):#{
3591 t0 = (x/32) * math.tau
3592 t1 = ((x+1)/32) * math.tau
3593
3594 c0 = math.cos(t0)
3595 s0 = math.sin(t0)
3596 c1 = math.cos(t1)
3597 s1 = math.sin(t1)
3598
3599 p0 = center + (axis + vx*c0 + vy*s0).normalized() * size
3600 p1 = center + (axis + vx*c1 + vy*s1).normalized() * size
3601
3602 col0 = ( abs(c0), abs(s0), 0.0 )
3603 col1 = ( abs(c1), abs(s1), 0.0 )
3604
3605 cv_view_verts += [center, p0, p0, p1]
3606 cv_view_colours += [ (0,0,0), col0, col0, col1 ]
3607 #}
3608
3609 cv_draw_lines()
3610 #}
3611
3612 # Draws constraints and stuff for the skeleton. This isnt documented and wont be
3613 #
3614 def draw_skeleton_helpers( obj ):
3615 #{
3616 global cv_view_verts, cv_view_colours
3617
3618 if obj.data.pose_position != 'REST':#{
3619 return
3620 #}
3621
3622 for bone in obj.data.bones:#{
3623 c = bone.head_local
3624 a = Vector((bone.SR_data.collider_min[0],
3625 bone.SR_data.collider_min[1],
3626 bone.SR_data.collider_min[2]))
3627 b = Vector((bone.SR_data.collider_max[0],
3628 bone.SR_data.collider_max[1],
3629 bone.SR_data.collider_max[2]))
3630
3631 if bone.SR_data.collider == '1':#{
3632 vs = [None]*8
3633 vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
3634 vs[1]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+a[2]))
3635 vs[2]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+a[2]))
3636 vs[3]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+a[2]))
3637 vs[4]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+b[2]))
3638 vs[5]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+b[2]))
3639 vs[6]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+b[2]))
3640 vs[7]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+b[2]))
3641
3642 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
3643 (0,4),(1,5),(2,6),(3,7)]
3644
3645 for l in indices:#{
3646 v0 = vs[l[0]]
3647 v1 = vs[l[1]]
3648
3649 cv_view_verts += [(v0[0],v0[1],v0[2])]
3650 cv_view_verts += [(v1[0],v1[1],v1[2])]
3651 cv_view_colours += [(0.5,0.5,0.5),(0.5,0.5,0.5)]
3652 #}
3653 #}
3654 elif bone.SR_data.collider == '2':#{
3655 v0 = b-a
3656 major_axis = 0
3657 largest = -1.0
3658
3659 for i in range(3):#{
3660 if abs(v0[i]) > largest:#{
3661 largest = abs(v0[i])
3662 major_axis = i
3663 #}
3664 #}
3665
3666 v1 = Vector((0,0,0))
3667 v1[major_axis] = 1.0
3668
3669 tx = Vector((0,0,0))
3670 ty = Vector((0,0,0))
3671
3672 cv_tangent_basis( v1, tx, ty )
3673 r = (abs(tx.dot( v0 )) + abs(ty.dot( v0 ))) * 0.25
3674 l = v0[ major_axis ] - r*2
3675
3676 p0 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l*-0.5 )
3677 p1 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l* 0.5 )
3678
3679 colour = [0.2,0.2,0.2]
3680 colour[major_axis] = 0.5
3681
3682 cv_draw_halfsphere( p0, -v1, ty, tx, r, colour )
3683 cv_draw_halfsphere( p1, v1, ty, tx, r, colour )
3684 cv_draw_line( p0+tx* r, p1+tx* r, colour )
3685 cv_draw_line( p0+tx*-r, p1+tx*-r, colour )
3686 cv_draw_line( p0+ty* r, p1+ty* r, colour )
3687 cv_draw_line( p0+ty*-r, p1+ty*-r, colour )
3688 #}
3689 else:#{
3690 continue
3691 #}
3692
3693 center = obj.matrix_world @ c
3694 if bone.SR_data.cone_constraint:#{
3695 vx = Vector([bone.SR_data.conevx[_] for _ in range(3)])
3696 vy = Vector([bone.SR_data.conevy[_] for _ in range(3)])
3697 va = Vector([bone.SR_data.coneva[_] for _ in range(3)])
3698 draw_cone_twist( center, vx, vy, va )
3699 #}
3700 #}
3701 #}
3702
3703 def cv_ent_gate( obj ):
3704 #{
3705 global cv_view_verts, cv_view_colours
3706
3707 if obj.type != 'MESH': return
3708
3709 mesh_data = obj.data.SR_data.ent_gate[0]
3710 data = obj.SR_data.ent_gate[0]
3711 dims = mesh_data.dimensions
3712
3713 vs = [None]*9
3714 c = Vector((0,0,dims[2]))
3715
3716 vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
3717 vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
3718 vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
3719 vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
3720 vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
3721 vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
3722 vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
3723 vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
3724 vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
3725
3726 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
3727
3728 r3d = bpy.context.area.spaces.active.region_3d
3729
3730 p0 = r3d.view_matrix.inverted().translation
3731 v0 = (obj.matrix_world@Vector((0,0,0))) - p0
3732 v1 = obj.matrix_world.to_3x3() @ Vector((0,1,0))
3733
3734 if v0.dot(v1) > 0.0: cc = (0,1,0)
3735 else: cc = (1,0,0)
3736
3737 for l in indices:#{
3738 v0 = vs[l[0]]
3739 v1 = vs[l[1]]
3740 cv_view_verts += [(v0[0],v0[1],v0[2])]
3741 cv_view_verts += [(v1[0],v1[1],v1[2])]
3742 cv_view_colours += [cc,cc]
3743 #}
3744
3745 sw = (0.4,0.4,0.4)
3746 if data.target != None:
3747 cv_draw_arrow( obj.location, data.target.location, sw )
3748 #}
3749
3750 def cv_ent_volume( obj ):
3751 #{
3752 global cv_view_verts, cv_view_colours
3753
3754 data = obj.SR_data.ent_volume[0]
3755
3756 if data.subtype == '0':#{
3757 cv_draw_ucube( obj.matrix_world, (0,1,0) )
3758
3759 if data.target:#{
3760 cv_draw_arrow( obj.location, data.target.location, (0,1,0) )
3761 #}
3762 #}
3763 elif data.subtype == '1':#{
3764 cv_draw_ucube( obj.matrix_world, (1,1,0) )
3765
3766 if data.target:#{
3767 cv_draw_arrow( obj.location, data.target.location, (1,1,0) )
3768 #}
3769 #}
3770 #}
3771
3772 def dijkstra( graph, start_node, target_node ):
3773 #{
3774 unvisited = [_ for _ in graph]
3775 shortest_path = {}
3776 previous_nodes = {}
3777
3778 for n in unvisited:
3779 shortest_path[n] = 9999999.999999
3780 shortest_path[start_node] = 0
3781
3782 while unvisited:#{
3783 current_min_node = None
3784 for n in unvisited:#{
3785 if current_min_node == None:
3786 current_min_node = n
3787 elif shortest_path[n] < shortest_path[current_min_node]:
3788 current_min_node = n
3789 #}
3790
3791 for branch in graph[current_min_node]:#{
3792 tentative_value = shortest_path[current_min_node]
3793 tentative_value += graph[current_min_node][branch]
3794 if tentative_value < shortest_path[branch]:#{
3795 shortest_path[branch] = tentative_value
3796 previous_nodes[branch] = current_min_node
3797 #}
3798 #}
3799
3800 unvisited.remove(current_min_node)
3801 #}
3802
3803 path = []
3804 node = target_node
3805 while node != start_node:#{
3806 path.append(node)
3807
3808 if node not in previous_nodes: return None
3809 node = previous_nodes[node]
3810 #}
3811
3812 # Add the start node manually
3813 path.append(start_node)
3814 return path
3815 #}
3816
3817 class dij_graph():
3818 #{
3819 def __init__(_,points,graph,subsections):#{
3820 _.points = points
3821 _.graph = graph
3822 _.subsections = subsections
3823 #}
3824 #}
3825
3826 def create_node_graph( curves, gates ):
3827 #{
3828 # add endpoints of curves
3829 graph = {}
3830 route_points = []
3831 subsections = []
3832 point_count = 0
3833 spline_count = 0
3834
3835 for c in range(len(curves)):#{
3836 for s in range(len(curves[c].data.splines)):#{
3837 spline = curves[c].data.splines[s]
3838 l = len(spline.points)
3839 if l < 2: continue
3840
3841 dist = round(spline.calc_length(),2)
3842
3843 ia = point_count
3844 ib = point_count+l-1
3845
3846 graph[ia] = { ib: dist }
3847 graph[ib] = { ia: dist }
3848
3849 for i in range(len(spline.points)):#{
3850 wco = curves[c].matrix_world @ spline.points[i].co
3851 route_points.append(Vector((wco[0],wco[1],wco[2]+0.5)))
3852
3853 previous = ia+i-1
3854 proxima = ia+i+1
3855
3856 if i == 0: previous = -1
3857 if i == len(spline.points)-1: proxima = -1
3858
3859 subsections.append((spline_count,previous,proxima))
3860 point_count += 1
3861 #}
3862
3863 spline_count += 1
3864 #}
3865 #}
3866
3867 # link endpoints
3868 graph_keys = list(graph)
3869 for i in range(len(graph_keys)-1):#{
3870 for j in range(i+1, len(graph_keys)):#{
3871 if i%2==0 and i+1==j: continue
3872
3873 ni = graph_keys[i]
3874 nj = graph_keys[j]
3875 pi = route_points[ni]
3876 pj = route_points[nj]
3877
3878 dist = round((pj-pi).magnitude,2)
3879
3880 if dist < 10.0:#{
3881 graph[ni][nj] = dist
3882 graph[nj][ni] = dist
3883 #}
3884 #}
3885 #}
3886
3887 # add and link gates( by name )
3888 for gate in gates:#{
3889 v1 = gate.matrix_world.to_3x3() @ Vector((0,1,0))
3890 if gate.SR_data.ent_gate[0].target:
3891 v1 = v1 * -1.0
3892
3893 graph[ gate.name ] = {}
3894
3895 for i in range(len(graph_keys)):#{
3896 ni = graph_keys[i]
3897 pi = route_points[ni]
3898
3899 v0 = pi-gate.location
3900 if v0.dot(v1) < 0.0: continue
3901
3902 dist = round(v0.magnitude,2)
3903
3904 if dist < 10.0:#{
3905 graph[ gate.name ][ ni ] = dist
3906 graph[ ni ][ gate.name ] = dist
3907 #}
3908 #}
3909 #}
3910
3911 return dij_graph(route_points,graph,subsections)
3912 #}
3913
3914 def solve_graph( dij, start, end ):
3915 #{
3916 path = dijkstra( dij.graph, end, start )
3917 full = []
3918
3919 if path:#{
3920 for sj in range(1,len(path)-2):#{
3921 i0 = path[sj]
3922 i1 = path[sj+1]
3923 map0 = dij.subsections[i0]
3924 map1 = dij.subsections[i1]
3925
3926 if map0[0] == map1[0]:#{
3927 if map0[1] == -1: direction = 2
3928 else: direction = 1
3929 sent = 0
3930
3931 while True:#{
3932 map0 = dij.subsections[i0]
3933 i1 = map0[direction]
3934 if i1 == -1: break
3935
3936 full.append( i0 )
3937 sent += 1
3938 i0 = i1
3939 if sent > 50: break
3940 #}
3941 #}
3942 else:#{
3943 full.append( i0 )
3944 #}
3945 #}
3946
3947 full.append( path[-2] )
3948 #}
3949 return full
3950 #}
3951
3952 def cv_draw_route( route, dij ):
3953 #{
3954 pole = Vector((0.2,0.2,10))
3955 hat = Vector((1,8,0.2))
3956 cc = (route.SR_data.ent_route[0].colour[0],
3957 route.SR_data.ent_route[0].colour[1],
3958 route.SR_data.ent_route[0].colour[2])
3959
3960 cv_draw_ucube(route.matrix_world,cc,Vector((0.5,-7.5,6)),\
3961 Vector((0,-6.5,5.5)))
3962 cv_draw_ucube(route.matrix_world,cc,pole, Vector(( 0.5, 0.5,0)) )
3963 cv_draw_ucube(route.matrix_world,cc,pole, Vector(( 0.5,-13.5,0)) )
3964 cv_draw_ucube(route.matrix_world,cc,hat, Vector((-0.5,-6.5, 12)) )
3965 cv_draw_ucube(route.matrix_world,cc,hat, Vector((-0.5,-6.5,-1)) )
3966
3967 checkpoints = route.SR_data.ent_route[0].gates
3968
3969 for i in range(len(checkpoints)):#{
3970 gi = checkpoints[i].target
3971 gj = checkpoints[(i+1)%len(checkpoints)].target
3972
3973 if gi:#{
3974 dest = gi.SR_data.ent_gate[0].target
3975 if dest:
3976 cv_draw_line_dotted( gi.location, dest.location, cc )
3977 gi = dest
3978 #}
3979
3980 if gi==gj: continue # error?
3981 if not gi or not gj: continue
3982
3983 path = solve_graph( dij, gi.name, gj.name )
3984
3985 if path:#{
3986 cv_draw_arrow(gi.location,dij.points[path[0]],cc,1.5)
3987 cv_draw_arrow(dij.points[path[len(path)-1]],gj.location,cc,1.5)
3988 for j in range(len(path)-1):#{
3989 i0 = path[j]
3990 i1 = path[j+1]
3991 o0 = dij.points[ i0 ]
3992 o1 = dij.points[ i1 ]
3993 cv_draw_arrow(o0,o1,cc,1.5)
3994 #}
3995 #}
3996 else:#{
3997 cv_draw_line_dotted( gi.location, gj.location, cc )
3998 #}
3999 #}
4000 #}
4001
4002 def cv_draw():
4003 #{
4004 global cv_view_shader
4005 global cv_view_verts
4006 global cv_view_colours
4007 global cv_view_course_i
4008
4009 cv_view_course_i = 0
4010 cv_view_verts = []
4011 cv_view_colours = []
4012
4013 cv_view_shader.bind()
4014 gpu.state.depth_mask_set(False)
4015 gpu.state.line_width_set(2.0)
4016 gpu.state.face_culling_set('BACK')
4017 gpu.state.depth_test_set('LESS')
4018 gpu.state.blend_set('NONE')
4019
4020 route_gates = []
4021 route_curves = []
4022 routes = []
4023
4024 for obj in bpy.context.collection.objects:#{
4025 if obj.type == 'ARMATURE':#{
4026 if obj.data.pose_position == 'REST':
4027 draw_skeleton_helpers( obj )
4028 #}
4029 else:#{
4030 ent_type = obj_ent_type( obj )
4031
4032 if ent_type == 'ent_gate':#{
4033 cv_ent_gate( obj )
4034 route_gates += [obj]
4035 #}
4036 elif ent_type == 'ent_route_node':#{
4037 if obj.type == 'CURVE':#{
4038 route_curves += [obj]
4039 #}
4040 #}
4041 elif ent_type == 'ent_route':
4042 routes += [obj]
4043 elif ent_type == 'ent_volume':#{
4044 cv_ent_volume( obj )
4045 #}
4046 elif ent_type == 'ent_challenge':#{
4047 data = obj.SR_data.ent_challenge[0]
4048 if data.proxima:#{
4049 cv_draw_arrow( obj.location, data.proxima.location, (0,0.2,1.0) )
4050 #}
4051 #}
4052 elif ent_type == 'ent_audio':#{
4053 if obj.SR_data.ent_audio[0].flag_3d:
4054 cv_draw_sphere( obj.location, obj.scale[0], (1,1,0) )
4055 #}
4056 elif ent_type == 'ent_font':#{
4057 data = obj.SR_data.ent_font[0]
4058
4059 for i in range(len(data.variants)):#{
4060 sub = data.variants[i].mesh
4061 if not sub: continue
4062
4063 for ch in data.glyphs:#{
4064 mini = (ch.bounds[0],ch.bounds[1])
4065 maxi = (ch.bounds[2]+mini[0],ch.bounds[3]+mini[1])
4066 p0 = sub.matrix_world @ Vector((mini[0],0.0,mini[1]))
4067 p1 = sub.matrix_world @ Vector((maxi[0],0.0,mini[1]))
4068 p2 = sub.matrix_world @ Vector((maxi[0],0.0,maxi[1]))
4069 p3 = sub.matrix_world @ Vector((mini[0],0.0,maxi[1]))
4070
4071 if i == data.variants_index: cc = (0.5,0.5,0.5)
4072 else: cc = (0,0,0)
4073
4074 cv_view_verts += [p0,p1,p1,p2,p2,p3,p3,p0]
4075 cv_view_colours += [cc,cc,cc,cc,cc,cc,cc,cc]
4076 #}
4077 #}
4078 #}
4079 elif ent_type == 'ent_skateshop':#{
4080 data = obj.SR_data.ent_skateshop[0]
4081 display = data.mark_display
4082 info = data.mark_info
4083
4084 if data.tipo == '0':#{
4085 cc = (0.0,0.9,0.6)
4086 cc1 = (0.4,0.9,0.2)
4087 cc2 = (0.9,0.6,0.1)
4088
4089 rack = data.mark_rack
4090
4091 rack_cu = Vector((3.15,2.0,0.1))*0.5
4092 rack_co = Vector((0.0,0.0,0.0))
4093 display_cu = Vector((0.3,1.2,0.1))*0.5
4094 display_co = Vector((0.0,0.0,0.1))*0.5
4095 info_cu = Vector((1.2,0.01,0.3))*0.5
4096 info_co = Vector((0.0,0.0,0.0))*0.5
4097 #}
4098 elif data.tipo == '1':#{
4099 rack = None
4100 cc1 = (1.0,0.0,0.0)
4101 cc2 = (1.0,0.5,0.0)
4102 display_cu = Vector((0.4,0.4,2.0))*0.5
4103 display_co = Vector((0.0,0.0,1.0))*0.5
4104 info_cu = Vector((1.2,0.01,0.3))*0.5
4105 info_co = Vector((0.0,0.0,0.0))*0.5
4106 #}
4107 elif data.tipo == '2':#{
4108 rack = None
4109 cc1 = (1.0,0.0,0.0)
4110 cc2 = (1.0,0.5,0.0)
4111 display_cu = Vector((1.0,1.0,0.5))*0.5
4112 display_co = Vector((0.0,0.0,0.5))*0.5
4113 info_cu = Vector((1.2,0.01,0.3))*0.5
4114 info_co = Vector((0.0,0.0,0.0))*0.5
4115 #}
4116
4117 if rack:
4118 cv_draw_ucube( rack.matrix_world, cc, rack_cu, rack_co )
4119 if display:
4120 cv_draw_ucube( display.matrix_world, cc1, display_cu, display_co)
4121 if info:
4122 cv_draw_ucube( info.matrix_world, cc2, info_cu, info_co )
4123 #}
4124 elif ent_type == 'ent_swspreview':#{
4125 cc1 = (0.4,0.9,0.2)
4126 data = obj.SR_data.ent_swspreview[0]
4127 display = data.mark_display
4128 display1 = data.mark_display1
4129 display_cu = Vector((0.3,1.2,0.1))*0.5
4130 display_co = Vector((0.0,0.0,0.1))*0.5
4131 if display:
4132 cv_draw_ucube( display.matrix_world, cc1, display_cu, display_co)
4133 if display1:
4134 cv_draw_ucube(display1.matrix_world, cc1, display_cu, display_co)
4135 #}
4136 elif ent_type == 'ent_menuitem':#{
4137 for i,col in enumerate(obj.users_collection):#{
4138 colour32 = hash_djb2( col.name )
4139 r = pow(((colour32 ) & 0xff) / 255.0, 2.2 )
4140 g = pow(((colour32>>8 ) & 0xff) / 255.0, 2.2 )
4141 b = pow(((colour32>>16) & 0xff) / 255.0, 2.2 )
4142 cc = (r,g,b)
4143 vs = [None for _ in range(8)]
4144 scale = i*0.02
4145 for j in range(8):#{
4146 v0 = Vector([(obj.bound_box[j][z]+\
4147 ((-1.0 if obj.bound_box[j][z]<0.0 else 1.0)*scale)) \
4148 for z in range(3)])
4149 vs[j] = obj.matrix_world @ v0
4150 #}
4151 indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
4152 (0,4),(1,5),(2,6),(3,7)]
4153 for l in indices:#{
4154 v0 = vs[l[0]]
4155 v1 = vs[l[1]]
4156 cv_view_verts += [(v0[0],v0[1],v0[2])]
4157 cv_view_verts += [(v1[0],v1[1],v1[2])]
4158 cv_view_colours += [cc,cc]
4159 #}
4160 #}
4161 cv_draw_lines()
4162 cc = (1.0,1.0,1.0)
4163 data = obj.SR_data.ent_menuitem[0]
4164 if data.tipo == '4':#{
4165 if data.slider_minloc and data.slider_maxloc:#{
4166 v0 = data.slider_minloc.location
4167 v1 = data.slider_maxloc.location
4168 cv_draw_line( v0, v1, cc )
4169 #}
4170 #}
4171
4172 colour32 = hash_djb2(obj.name)
4173 r = ((colour32 ) & 0xff) / 255.0
4174 g = ((colour32>>8 ) & 0xff) / 255.0
4175 b = ((colour32>>16) & 0xff) / 255.0
4176 cc = (r,g,b)
4177 origin = obj.location + (Vector((r,g,b))*2.0-Vector((1.0,1.0,1.0)))\
4178 * 0.04
4179
4180 size = 0.01
4181
4182 if data.tipo != '0':#{
4183 if data.tipo == '4':#{
4184 if data.link0:#{
4185 cv_draw_arrow( origin, data.link0.location, cc, size )
4186 #}
4187 if data.link1:#{
4188 cv_draw_arrow( origin, data.link1.location, cc, size )
4189 #}
4190 #}
4191 else:#{
4192 if data.link0:#{
4193 cv_draw_arrow( origin, data.link0.location, cc, size )
4194 #}
4195 if data.link1:#{
4196 cv_draw_arrow( origin, data.link1.location, cc, size )
4197 #}
4198 if data.link2:#{
4199 cv_draw_arrow( origin, data.link2.location, cc, size )
4200 #}
4201 if data.link3:#{
4202 cv_draw_arrow( origin, data.link3.location, cc, size )
4203 #}
4204 #}
4205 #}
4206 #}
4207 #}
4208 #}
4209
4210 dij = create_node_graph( route_curves, route_gates )
4211
4212 #cv_draw_route_map( route_nodes )
4213 for route in routes:#{
4214 cv_draw_route( route, dij )
4215 #}
4216
4217 cv_draw_lines()
4218 return
4219 #}
4220
4221 classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
4222 SR_COLLECTION_SETTINGS, SR_SCENE_SETTINGS, \
4223 SR_COMPILE, SR_COMPILE_THIS, SR_MIRROR_BONE_X,\
4224 \
4225 SR_OBJECT_ENT_GATE, SR_MESH_ENT_GATE, SR_OBJECT_ENT_SPAWN, \
4226 SR_OBJECT_ENT_ROUTE_ENTRY, SR_UL_ROUTE_NODE_LIST, \
4227 SR_OBJECT_ENT_ROUTE, SR_OT_ROUTE_LIST_NEW_ITEM,\
4228 SR_OT_GLYPH_LIST_NEW_ITEM, SR_OT_GLYPH_LIST_DEL_ITEM,\
4229 SR_OT_GLYPH_LIST_MOVE_ITEM,\
4230 SR_OT_AUDIO_LIST_NEW_ITEM,SR_OT_AUDIO_LIST_DEL_ITEM,\
4231 SR_OT_FONT_VARIANT_LIST_NEW_ITEM,SR_OT_FONT_VARIANT_LIST_DEL_ITEM,\
4232 SR_OT_COPY_ENTITY_DATA, \
4233 SR_OBJECT_ENT_VOLUME, \
4234 SR_UL_AUDIO_LIST, SR_OBJECT_ENT_AUDIO_FILE_ENTRY,\
4235 SR_OT_ROUTE_LIST_DEL_ITEM,\
4236 SR_OBJECT_ENT_AUDIO,SR_OBJECT_ENT_MARKER,SR_OBJECT_ENT_GLYPH,\
4237 SR_OBJECT_ENT_FONT_VARIANT,
4238 SR_OBJECT_ENT_GLYPH_ENTRY,\
4239 SR_UL_FONT_VARIANT_LIST,SR_UL_FONT_GLYPH_LIST,\
4240 SR_OBJECT_ENT_FONT,SR_OBJECT_ENT_TRAFFIC,SR_OBJECT_ENT_SKATESHOP,\
4241 SR_OBJECT_ENT_WORKSHOP_PREVIEW,SR_OBJECT_ENT_MENU_ITEM,\
4242 SR_OBJECT_ENT_WORLD_INFO,SR_OBJECT_ENT_CCMD,\
4243 SR_OBJECT_ENT_CHALLENGE,\
4244 \
4245 SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES,
4246 SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \
4247 ]
4248
4249 def register():
4250 #{
4251 for c in classes:
4252 bpy.utils.register_class(c)
4253
4254 bpy.types.Scene.SR_data = \
4255 bpy.props.PointerProperty(type=SR_SCENE_SETTINGS)
4256 bpy.types.Collection.SR_data = \
4257 bpy.props.PointerProperty(type=SR_COLLECTION_SETTINGS)
4258
4259 bpy.types.Object.SR_data = \
4260 bpy.props.PointerProperty(type=SR_OBJECT_PROPERTIES)
4261 bpy.types.Light.SR_data = \
4262 bpy.props.PointerProperty(type=SR_LIGHT_PROPERTIES)
4263 bpy.types.Bone.SR_data = \
4264 bpy.props.PointerProperty(type=SR_BONE_PROPERTIES)
4265 bpy.types.Mesh.SR_data = \
4266 bpy.props.PointerProperty(type=SR_MESH_PROPERTIES)
4267 bpy.types.Material.SR_data = \
4268 bpy.props.PointerProperty(type=SR_MATERIAL_PROPERTIES)
4269
4270 global cv_view_draw_handler
4271 cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
4272 cv_draw,(),'WINDOW','POST_VIEW')
4273 #}
4274
4275 def unregister():
4276 #{
4277 for c in classes:
4278 bpy.utils.unregister_class(c)
4279
4280 global cv_view_draw_handler
4281 bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')
4282 #}
4283
4284 # ---------------------------------------------------------------------------- #
4285 # #
4286 # QOI encoder #
4287 # #
4288 # ---------------------------------------------------------------------------- #
4289 # #
4290 # Transliteration of: #
4291 # https://github.com/phoboslab/qoi/blob/master/qoi.h #
4292 # #
4293 # Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org #
4294 # SPDX-License-Identifier: MIT #
4295 # QOI - The "Quite OK Image" format for fast, lossless image compression #
4296 # #
4297 # ---------------------------------------------------------------------------- #
4298
4299 class qoi_rgba_t(Structure):
4300 #{
4301 _pack_ = 1
4302 _fields_ = [("r",c_uint8),
4303 ("g",c_uint8),
4304 ("b",c_uint8),
4305 ("a",c_uint8)]
4306 #}
4307
4308 QOI_OP_INDEX = 0x00 # 00xxxxxx
4309 QOI_OP_DIFF = 0x40 # 01xxxxxx
4310 QOI_OP_LUMA = 0x80 # 10xxxxxx
4311 QOI_OP_RUN = 0xc0 # 11xxxxxx
4312 QOI_OP_RGB = 0xfe # 11111110
4313 QOI_OP_RGBA = 0xff # 11111111
4314
4315 QOI_MASK_2 = 0xc0 # 11000000
4316
4317 def qoi_colour_hash( c ):
4318 #{
4319 return c.r*3 + c.g*5 + c.b*7 + c.a*11
4320 #}
4321
4322 def qoi_eq( a, b ):
4323 #{
4324 return (a.r==b.r) and (a.g==b.g) and (a.b==b.b) and (a.a==b.a)
4325 #}
4326
4327 def qoi_32bit( v ):
4328 #{
4329 return bytearray([ (0xff000000 & v) >> 24, \
4330 (0x00ff0000 & v) >> 16, \
4331 (0x0000ff00 & v) >> 8, \
4332 (0x000000ff & v) ])
4333 #}
4334
4335 def qoi_encode( img ):
4336 #{
4337 data = bytearray()
4338
4339 print(F"{' ':<30}",end='\r')
4340 print(F"[QOI] Encoding {img.name}.qoi[{img.size[0]},{img.size[1]}]",end='\r')
4341
4342 index = [ qoi_rgba_t() for _ in range(64) ]
4343
4344 # Header
4345 #
4346 data.extend( bytearray(c_uint32(0x66696f71)) )
4347 data.extend( qoi_32bit( img.size[0] ) )
4348 data.extend( qoi_32bit( img.size[1] ) )
4349 data.extend( bytearray(c_uint8(4)) )
4350 data.extend( bytearray(c_uint8(0)) )
4351
4352 run = 0
4353 px_prev = qoi_rgba_t()
4354 px_prev.r = c_uint8(0)
4355 px_prev.g = c_uint8(0)
4356 px_prev.b = c_uint8(0)
4357 px_prev.a = c_uint8(255)
4358
4359 px = qoi_rgba_t()
4360 px.r = c_uint8(0)
4361 px.g = c_uint8(0)
4362 px.b = c_uint8(0)
4363 px.a = c_uint8(255)
4364
4365 px_len = img.size[0] * img.size[1]
4366 paxels = [ int(min(max(_,0),1)*255) for _ in img.pixels ]
4367
4368 for px_pos in range( px_len ): #{
4369 idx = px_pos * img.channels
4370 nc = img.channels-1
4371
4372 px.r = paxels[idx+min(0,nc)]
4373 px.g = paxels[idx+min(1,nc)]
4374 px.b = paxels[idx+min(2,nc)]
4375 px.a = paxels[idx+min(3,nc)]
4376
4377 if qoi_eq( px, px_prev ): #{
4378 run += 1
4379
4380 if (run == 62) or (px_pos == px_len-1): #{
4381 data.extend( bytearray( c_uint8(QOI_OP_RUN | (run-1))) )
4382 run = 0
4383 #}
4384 #}
4385 else: #{
4386 if run > 0: #{
4387 data.extend( bytearray( c_uint8(QOI_OP_RUN | (run-1))) )
4388 run = 0
4389 #}
4390
4391 index_pos = qoi_colour_hash(px) % 64
4392
4393 if qoi_eq( index[index_pos], px ): #{
4394 data.extend( bytearray( c_uint8(QOI_OP_INDEX | index_pos)) )
4395 #}
4396 else: #{
4397 index[ index_pos ].r = px.r
4398 index[ index_pos ].g = px.g
4399 index[ index_pos ].b = px.b
4400 index[ index_pos ].a = px.a
4401
4402 if px.a == px_prev.a: #{
4403 vr = int(px.r) - int(px_prev.r)
4404 vg = int(px.g) - int(px_prev.g)
4405 vb = int(px.b) - int(px_prev.b)
4406
4407 vg_r = vr - vg
4408 vg_b = vb - vg
4409
4410 if (vr > -3) and (vr < 2) and\
4411 (vg > -3) and (vg < 2) and\
4412 (vb > -3) and (vb < 2):
4413 #{
4414 op = QOI_OP_DIFF | (vr+2) << 4 | (vg+2) << 2 | (vb+2)
4415 data.extend( bytearray( c_uint8(op) ))
4416 #}
4417 elif (vg_r > -9) and (vg_r < 8) and\
4418 (vg > -33) and (vg < 32 ) and\
4419 (vg_b > -9) and (vg_b < 8):
4420 #{
4421 op = QOI_OP_LUMA | (vg+32)
4422 delta = (vg_r+8) << 4 | (vg_b + 8)
4423 data.extend( bytearray( c_uint8(op) ) )
4424 data.extend( bytearray( c_uint8(delta) ))
4425 #}
4426 else: #{
4427 data.extend( bytearray( c_uint8(QOI_OP_RGB) ) )
4428 data.extend( bytearray( c_uint8(px.r) ))
4429 data.extend( bytearray( c_uint8(px.g) ))
4430 data.extend( bytearray( c_uint8(px.b) ))
4431 #}
4432 #}
4433 else: #{
4434 data.extend( bytearray( c_uint8(QOI_OP_RGBA) ) )
4435 data.extend( bytearray( c_uint8(px.r) ))
4436 data.extend( bytearray( c_uint8(px.g) ))
4437 data.extend( bytearray( c_uint8(px.b) ))
4438 data.extend( bytearray( c_uint8(px.a) ))
4439 #}
4440 #}
4441 #}
4442
4443 px_prev.r = px.r
4444 px_prev.g = px.g
4445 px_prev.b = px.b
4446 px_prev.a = px.a
4447 #}
4448
4449 # Padding
4450 for i in range(7):
4451 data.extend( bytearray( c_uint8(0) ))
4452 data.extend( bytearray( c_uint8(1) ))
4453 bytearray_align_to( data, 16, b'\x00' )
4454
4455 return data
4456 #}