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