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