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