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