1 # Copyright (C) 2022 Harry Godden (hgn)
5 "author": "Harry Godden (hgn)",
12 "category":"Import/Export",
15 print( "Convexer reload" )
17 #from mathutils import *
18 import bpy
, gpu
, math
, os
, time
, mathutils
20 from gpu_extras
.batch
import batch_for_shader
21 from bpy
.app
.handlers
import persistent
24 vmt_param_dynamic_class
= None
26 # libcxr interface (TODO: We can probably automate this)
27 # ======================================================
30 def __init__(_
,name
,argtypes
,restype
):
37 _
.call
= getattr(so
,_
.name
)
38 _
.call
.argtypes
= _
.argtypes
41 _
.call
.restype
= _
.restype
44 libc_dlclose
= cdll
.LoadLibrary(None).dlclose
45 libc_dlclose
.argtypes
= [c_void_p
]
47 # Callback ctypes wrapper...
49 c_libcxr_log_callback
= None
50 c_libcxr_line_callback
= None
52 # Structure definitions
53 class cxr_edge(Structure
):
54 _fields_
= [("i0",c_int32
),
56 ("freestyle",c_int32
)]
58 class cxr_static_loop(Structure
):
59 _fields_
= [("index",c_int32
),
60 ("edge_index",c_int32
),
63 class cxr_polygon(Structure
):
64 _fields_
= [("loop_start",c_int32
),
65 ("loop_total",c_int32
),
66 ("normal",c_double
* 3),
67 ("center",c_double
* 3),
68 ("material_id",c_int32
)]
70 class cxr_material(Structure
):
71 _fields_
= [("res",c_int32
* 2),
74 class cxr_static_mesh(Structure
):
75 _fields_
= [("vertices",POINTER(c_double
* 3)),
76 ("edges",POINTER(cxr_edge
)),
77 ("loops",POINTER(cxr_static_loop
)),
78 ("polys",POINTER(cxr_polygon
)),
79 ("materials",POINTER(cxr_material
)),
81 ("poly_count",c_int32
),
82 ("vertex_count",c_int32
),
83 ("edge_count",c_int32
),
84 ("loop_count",c_int32
),
85 ("material_count",c_int32
)]
87 class cxr_tri_mesh(Structure
):
88 _fields_
= [("vertices",POINTER(c_double
*3)),
89 ("colours",POINTER(c_double
*4)),
90 ("indices",POINTER(c_int32
)),
91 ("indices_count",c_int32
),
92 ("vertex_count",c_int32
)]
94 class cxr_vmf_context(Structure
):
95 _fields_
= [("mapversion",c_int32
),
97 ("detailvbsp",c_char_p
),
98 ("detailmaterial",c_char_p
),
100 ("offset",c_double
*3),
101 ("lightmap_scale",c_int32
),
102 ("brush_count",c_int32
),
103 ("entity_count",c_int32
),
104 ("face_count",c_int32
)]
107 libcxr_decompose
= extern( "cxr_decompose", \
108 [POINTER(cxr_static_mesh
)], c_void_p
)
110 libcxr_free_world
= extern( "cxr_free_world", [c_void_p
], None )
111 libcxr_write_test_data
= extern( "cxr_write_test_data", \
112 [POINTER(cxr_static_mesh
)], None )
113 libcxr_world_preview
= extern( "cxr_world_preview", [c_void_p
], \
114 POINTER(cxr_tri_mesh
))
115 libcxr_free_tri_mesh
= extern( "cxr_free_tri_mesh", [c_void_p
], None )
118 libcxr_begin_vmf
= extern( "cxr_begin_vmf", \
119 [POINTER(cxr_vmf_context
), c_void_p
], None )
121 libcxr_vmf_begin_entities
= extern( "cxr_vmf_begin_entities", \
122 [POINTER(cxr_vmf_context
), c_void_p
], None )
124 libcxr_push_world_vmf
= extern("cxr_push_world_vmf", \
125 [c_void_p
,POINTER(cxr_vmf_context
),c_void_p
], None )
127 libcxr_end_vmf
= extern( "cxr_end_vmf", \
128 [POINTER(cxr_vmf_context
),c_void_p
], None )
131 libcxr_vdf_open
= extern( "cxr_vdf_open", [c_char_p
], c_void_p
)
132 libcxr_vdf_close
= extern( "cxr_vdf_close", [c_void_p
], None )
133 libcxr_vdf_put
= extern( "cxr_vdf_put", [c_void_p
,c_char_p
], None )
134 libcxr_vdf_node
= extern( "cxr_vdf_node", [c_void_p
,c_char_p
], None )
135 libcxr_vdf_edon
= extern( "cxr_vdf_edon", [c_void_p
], None )
136 libcxr_vdf_kv
= extern( "cxr_vdf_kv", [c_void_p
,c_char_p
,c_char_p
], None )
139 libcxr_lightpatch_bsp
= extern( "cxr_lightpatch_bsp", [c_char_p
], None )
141 libcxr_funcs
= [ libcxr_decompose
, libcxr_free_world
, libcxr_begin_vmf
, \
142 libcxr_vmf_begin_entities
, libcxr_push_world_vmf
, \
143 libcxr_end_vmf
, libcxr_vdf_open
, libcxr_vdf_close
, \
144 libcxr_vdf_put
, libcxr_vdf_node
, libcxr_vdf_edon
,
145 libcxr_vdf_kv
, libcxr_lightpatch_bsp
, libcxr_write_test_data
,\
146 libcxr_world_preview
, libcxr_free_tri_mesh
]
152 libnbvtf_convert
= extern( "nbvtf_convert", \
153 [c_char_p
,c_int32
,c_int32
,c_int32
,c_int32
,c_int32
,c_uint32
,c_char_p
],
156 libnbvtf_funcs
= [ libnbvtf_convert
]
161 NBVTF_IMAGE_FORMAT_RGBA8888
= 0
162 NBVTF_IMAGE_FORMAT_RGB888
= 2
163 NBVTF_IMAGE_FORMAT_DXT1
= 13
164 NBVTF_IMAGE_FORMAT_DXT5
= 15
165 NBVTF_TEXTUREFLAGS_CLAMPS
= 0x00000004
166 NBVTF_TEXTUREFLAGS_CLAMPT
= 0x00000008
167 NBVTF_TEXTUREFLAGS_NORMAL
= 0x00000080
168 NBVTF_TEXTUREFLAGS_NOMIP
= 0x00000100
169 NBVTF_TEXTUREFLAGS_NOLOD
= 0x00000200
171 # Wrapper for vdf functions to allow: with o = vdf_structure ...
172 class vdf_structure():
173 def __init__(_
,path
):
176 _
.fp
= libcxr_vdf_open
.call( _
.path
.encode('utf-8') )
178 print( F
"Could not open file {_.path}" )
181 def __exit__(_
,type,value
,traceback
):
183 libcxr_vdf_close
.call(_
.fp
)
186 libcxr_vdf_put
.call(_
.fp
, s
.encode('utf-8') )
188 libcxr_vdf_node
.call(_
.fp
, name
.encode('utf-8') )
190 libcxr_vdf_edon
.call(_
.fp
)
192 libcxr_vdf_kv
.call(_
.fp
, k
.encode('utf-8'), v
.encode('utf-8'))
194 class cxr_object_context():
195 def __init__(_
,scale
,offset_z
):
199 debug_gpu_lines
= None
200 debug_gpu_mesh
= None
201 debug_gpu_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
202 debug_draw_handler
= None
205 def libcxr_log_callback(logStr
):
206 print( F
"{logStr.decode('utf-8')}",end
='' )
208 debug_lines_positions
= None
209 debug_lines_colours
= None
211 def libcxr_reset_debug_lines():
212 global debug_lines_positions
213 global debug_lines_colours
215 debug_lines_positions
= []
216 debug_lines_colours
= []
218 def libcxr_batch_debug_lines():
219 global debug_lines_positions
220 global debug_lines_colours
221 global debug_gpu_lines
222 global debug_gpu_shader
224 debug_gpu_lines
= batch_for_shader(\
225 debug_gpu_shader
, 'LINES',\
226 { "pos": debug_lines_positions
, "color": debug_lines_colours
})
229 def cxr_on_load(dummy
):
230 libcxr_reset_debug_lines()
231 libcxr_batch_debug_lines()
234 def cxr_dgraph_update(scene
,dgraph
):
236 print( F
"Hallo {time.time()}" )
238 def libcxr_line_callback(p0
,p1
,colour
):
239 global debug_lines_positions
240 global debug_lines_colours
241 debug_lines_positions
+= [(p0
[0],p0
[1],p0
[2])]
242 debug_lines_positions
+= [(p1
[0],p1
[1],p1
[2])]
243 debug_lines_colours
+= [(colour
[0],colour
[1],colour
[2],colour
[3])]
244 debug_lines_colours
+= [(colour
[0],colour
[1],colour
[2],colour
[3])]
247 global debug_gpu_lines
248 global debug_gpu_shader
249 global debug_gpu_mesh
251 debug_gpu_shader
.bind()
253 gpu
.state
.depth_test_set('LESS_EQUAL')
254 gpu
.state
.depth_mask_set(False)
255 gpu
.state
.line_width_set(1.5)
256 gpu
.state
.face_culling_set('BACK')
258 gpu
.state
.blend_set('ALPHA')
259 if debug_gpu_lines
!= None:
260 debug_gpu_lines
.draw(debug_gpu_shader
)
262 gpu
.state
.blend_set('ADDITIVE')
263 if debug_gpu_mesh
!= None:
264 debug_gpu_mesh
.draw(debug_gpu_shader
)
266 class CXR_RELOAD(bpy
.types
.Operator
):
267 bl_idname
="convexer.reload"
268 bl_label
="Reload convexer"
270 def execute(_
,context
):
271 global libcxr
, libnbvtf
, libcxr_funcs
, libnbvtf_funcs
274 libnbvtf
= cdll
.LoadLibrary( os
.path
.dirname(__file__
)+'/libnbvtf.so')
277 _handle
= libcxr
._handle
279 for i
in range(10): libc_dlclose( _handle
)
283 libcxr
= cdll
.LoadLibrary( os
.path
.dirname(__file__
)+'/libcxr.so')
285 build_time
= c_char_p
.in_dll(libcxr
,'cxr_build_time')
286 print( F
"libcxr build time: {build_time.value}" )
288 for fd
in libnbvtf_funcs
:
289 fd
.loadfrom( libnbvtf
)
291 for fd
in libcxr_funcs
:
292 fd
.loadfrom( libcxr
)
295 global c_libcxr_log_callback
, c_libcxr_line_callback
297 LOG_FUNCTION_TYPE
= CFUNCTYPE(None,c_char_p
)
298 c_libcxr_log_callback
= LOG_FUNCTION_TYPE(libcxr_log_callback
)
299 libcxr
.cxr_set_log_function(cast(c_libcxr_log_callback
,c_void_p
))
301 LINE_FUNCTION_TYPE
= CFUNCTYPE(None,\
302 POINTER(c_double
),POINTER(c_double
),POINTER(c_double
))
303 c_libcxr_line_callback
= LINE_FUNCTION_TYPE(libcxr_line_callback
)
304 libcxr
.cxr_set_line_function(cast(c_libcxr_line_callback
,c_void_p
))
312 bpy
.ops
.convexer
.reload()
320 dig
.append( int( v
% 5 ) )
323 ret
+= [ 'a','e','i','o','u' ][d
]
326 def asset_uid(asset
):
327 if isinstance(asset
,str):
329 name
= to_aeiou(asset
.cxr_data
.asset_id
)
330 if bpy
.context
.scene
.cxr_data
.include_names
:
331 name
+= asset
.name
.replace('.','_')
334 # -> <project_name>/<asset_name>
335 def asset_name(asset
):
336 return F
"{bpy.context.scene.cxr_data.project_name}/{asset_uid(asset)}"
338 # -> <subdir>/<project_name>/<asset_name>
339 def asset_path(subdir
, asset
):
340 return F
"{subdir}/{asset_name(asset_uid(asset))}"
342 # -> <csgo>/<subdir>/<project_name>/<asset_name>
343 def asset_full_path(sdir
,asset
):
344 return F
"{bpy.context.scene.cxr_data.subdir}/"+\
345 F
"{asset_path(sdir,asset_uid(asset))}"
347 # view_layer.update() doesnt seem to work,
348 # tag_redraw() seems to have broken
349 # therefore, change a property
351 ob
= bpy
.context
.scene
.objects
[0]
352 ob
.hide_render
= ob
.hide_render
354 # the 'real' way to refresh the scene
355 #for area in bpy.context.window.screen.areas:
356 # if area.type == 'view_3d':
359 # The default shader is the first entry
362 "LightMappedGeneric":
364 "name": "Light Mapped",
369 "name": "Vertex Lit",
384 def material_tex_image(v
):
386 "ShaderNodeTexImage":
392 cxr_graph_mapping
= {
393 "ShaderNodeBsdfPrincipled":
399 "Color1": material_tex_image("basetexture"),
400 "Color2": material_tex_image("decaltexture")
402 "ShaderNodeTexImage":
404 "image":"$basetexture"
407 [("VertexLitGeneric","$color2"),\
408 ("UnlitGeneric","$color2"),\
409 ("LightMappedGeneric","$color")]
413 "ShaderNodeNormalMap":
415 "Color": material_tex_image("bumpmap")
421 cxr_shader_params
= {
425 "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"),
429 "name": "Base Texture",
435 "name": "Decal Texture",
441 "name": "Blend Mode",
444 ('0',"AlphaOver","Default",'',0),
445 ('1',"Multiply","",'',1),
446 ('2',"Modulate","",'',2),
447 ('3',"Additive","",'',3)
455 "name": "Normal Map",
457 "flags": NBVTF_TEXTUREFLAGS_NORMAL
,
478 "shaders": ("VertexLitGeneric", "LightMappedGeneric"),
498 "$phongfresnelranges":
500 "name": "Fresnel Ranges",
502 "default":(1.0,1.0,1.0)
516 "default": (1.0,1.0,1.0)
520 "name": "Light Scale",
524 "$envmaplightscaleminmax":
535 "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"),
539 "name": "Translucent",
545 "name": "Alpha Test",
549 "$alphatestreference":
565 def ent_get_origin(obj
,context
):
566 return obj
.location
* context
.scale
568 def ent_get_angles(obj
,context
):
569 euler
= [ a
*57.295779513 for a
in obj
.rotation_euler
]
576 def ent_baseclass(classes
, other
):
579 base
.update(x
.copy())
582 ent_origin
= { "origin": ent_get_origin
}
583 ent_angles
= { "angles": ent_get_angles
}
584 ent_transform
= ent_baseclass( [ent_origin
], ent_angles
)
586 def ent_lights(obj
,context
):
587 kvs
= ent_baseclass([ent_origin
],\
589 "_distance": (0.0 if obj
.data
.cxr_data
.realtime
else -1.0),
590 "_light": [int(pow(obj
.data
.color
[i
],1.0/2.2)*255.0) for i
in range(3)] +\
591 [int(obj
.data
.energy
* bpy
.context
.scene
.cxr_data
.light_scale
)],
592 "_lightHDR": '-1 -1 -1 1',
596 if obj
.data
.type == 'SPOT':
597 kvs
['_cone'] = obj
.data
.spot_size
*(57.295779513/2.0)
598 kvs
['_inner_cone'] = (1.0-obj
.data
.spot_blend
)*kvs
['_cone']
600 # Blenders spotlights are -z forward
601 # Source is +x, however, it seems to use a completely different system.
602 # Since we dont care about roll for spotlights, we just take the
603 # pitch and yaw via trig
605 _
,mtx_rot
,_
= obj
.matrix_world
.decompose()
606 fwd
= mtx_rot
@ mathutils
.Vector((0,0,-1))
608 kvs
['pitch'] = math
.asin(fwd
[2]) * 57.295779513
609 kvs
['angles'] = [ 0.0, math
.atan2(fwd
[1],fwd
[0]) * 57.295779513, 0.0 ]
610 kvs
['_quadratic_attn'] = 0.0 # Source spotlights + quadratic falloff look
613 # Blender's default has a much more 'nice'
615 kvs
['_linear_attn'] = 1.0
617 elif obj
.data
.type == 'POINT':
618 kvs
['_quadratic_attn'] = 1.0
619 kvs
['_linear_attn'] = 0.0
621 elif obj
.data
.type == 'SUN':
626 def ent_cubemap(obj
,context
):
627 return ent_baseclass([ent_origin
],\
628 {"cubemapsize": obj
.data
.cxr_data
.size
})
631 "info_player_counterterrorist":
635 "keyvalues": ent_baseclass([ent_transform
],\
637 "priority": {"type": "int", "default": 0 },
638 "enabled": {"type": "int", "default": 1 },
641 "info_player_terrorist":
645 "keyvalues": ent_baseclass([ent_transform
],\
647 "priority": {"type": "int", "default": 0 },
648 "enabled": {"type": "int", "default": 1 },
651 "light": { "keyvalues": ent_lights
},
652 "light_spot": { "keyvalues": ent_lights
},
654 "env_cubemap": { "keyvalues": ent_cubemap
},
662 "TeamNum": {"type": "int", "default": 0 }
667 def cxr_intrinsic_classname(obj
):
668 if obj
.type == 'LIGHT':
670 'SPOT': "light_spot",
672 'SUN': "light_directional" }[ obj
.data
.type ]
674 elif obj
.type == 'LIGHT_PROBE':
676 elif obj
.type == 'EMPTY':
682 def cxr_custom_class(obj
):
683 if obj
.type == 'MESH': custom_class
= obj
.cxr_data
.brushclass
684 else: custom_class
= obj
.cxr_data
.classname
688 def cxr_classname(obj
):
689 intr
= cxr_intrinsic_classname(obj
)
690 if intr
!= None: return intr
692 custom_class
= cxr_custom_class(obj
)
693 if custom_class
!= 'NONE':
699 # intinsic: (k, False, value)
700 # property: (k, True, value or default)
704 def cxr_entity_keyvalues(obj
,context
,classname
):
705 if classname
not in cxr_entities
: return None
709 entdef
= cxr_entities
[classname
]
710 kvs
= entdef
['keyvalues']
712 if callable(kvs
): kvs
= kvs(obj
, context
)
719 if isinstance(kv
,dict):
721 value
= obj
[ F
"cxrkv_{k}" ]
724 value
= kv(obj
,context
)
726 if isinstance(value
,mathutils
.Vector
):
727 value
= [_
for _
in value
]
729 result
+= [(k
, isprop
, value
)]
733 def material_info(mat
):
735 info
['res'] = (512,512)
736 info
['name'] = 'tools/toolsnodraw'
738 if mat
== None or mat
.use_nodes
== False:
742 if mat
.cxr_data
.shader
== 'Builtin':
743 info
['name'] = mat
.name
746 if not hasattr(material_info
,'references'):
747 material_info
.references
= set()
750 material_info
.references
.add(mat
)
751 info
['name'] = asset_name(mat
)
753 # Using the cxr_graph_mapping as a reference, go through the shader
754 # graph and gather all $props from it.
756 def _graph_read( node_def
, node
=None, depth
=0 ):
760 def _variant_apply( val
):
763 if isinstance( val
, str ):
766 for shader_variant
in val
:
767 if shader_variant
[0] == mat
.cxr_data
.shader
:
768 return shader_variant
[1]
772 _graph_read
.extracted
= []
774 for node_idname
in node_def
:
775 for n
in mat
.node_tree
.nodes
:
776 if n
.bl_idname
== node_idname
:
777 node_def
= node_def
[node_idname
]
781 for link
in node_def
:
782 if isinstance( node_def
[link
], dict ):
783 inputt
= node
.inputs
[link
]
784 inputt_def
= node_def
[link
]
788 # look for definitions for the connected node type
789 con
= inputt
.links
[0].from_node
791 for node_idname
in inputt_def
:
792 if con
.bl_idname
== node_idname
:
793 con_def
= inputt_def
[ node_idname
]
794 _graph_read( con_def
, con
, depth
+1 )
796 # No definition found! :(
797 # TODO: Make a warning for this?
800 if "default" in inputt_def
:
801 prop
= _variant_apply( inputt_def
['default'] )
802 info
[prop
] = inputt
.default_value
804 prop
= _variant_apply( node_def
[link
] )
805 info
[prop
] = getattr(node
,link
)
807 _graph_read(cxr_graph_mapping
)
809 if "$basetexture" in info
:
810 export_res
= info
['$basetexture'].cxr_data
.export_res
811 info
['res'] = (export_res
[0], export_res
[1])
815 def mesh_cxr_format(obj
):
816 dgraph
= bpy
.context
.evaluated_depsgraph_get()
817 data
= obj
.evaluated_get(dgraph
).data
819 _
,mtx_rot
,_
= obj
.matrix_world
.decompose()
821 mesh
= cxr_static_mesh()
823 vertex_data
= ((c_double
*3)*len(data
.vertices
))()
824 for i
, vert
in enumerate(data
.vertices
):
825 v
= obj
.matrix_world
@ vert
.co
826 vertex_data
[i
][0] = c_double(v
[0])
827 vertex_data
[i
][1] = c_double(v
[1])
828 vertex_data
[i
][2] = c_double(v
[2])
830 loop_data
= (cxr_static_loop
*len(data
.loops
))()
831 polygon_data
= (cxr_polygon
*len(data
.polygons
))()
833 for i
, poly
in enumerate(data
.polygons
):
834 loop_start
= poly
.loop_start
835 loop_end
= poly
.loop_start
+ poly
.loop_total
836 for loop_index
in range(loop_start
, loop_end
):
837 loop
= data
.loops
[loop_index
]
838 loop_data
[loop_index
].index
= loop
.vertex_index
839 loop_data
[loop_index
].edge_index
= loop
.edge_index
842 uv
= data
.uv_layers
.active
.data
[loop_index
].uv
843 loop_data
[loop_index
].uv
[0] = c_double(uv
[0])
844 loop_data
[loop_index
].uv
[1] = c_double(uv
[1])
846 loop_data
[loop_index
].uv
[0] = c_double(0.0)
847 loop_data
[loop_index
].uv
[1] = c_double(0.0)
848 center
= obj
.matrix_world
@ poly
.center
849 normal
= mtx_rot
@ poly
.normal
851 polygon_data
[i
].loop_start
= poly
.loop_start
852 polygon_data
[i
].loop_total
= poly
.loop_total
853 polygon_data
[i
].normal
[0] = normal
[0]
854 polygon_data
[i
].normal
[1] = normal
[1]
855 polygon_data
[i
].normal
[2] = normal
[2]
856 polygon_data
[i
].center
[0] = center
[0]
857 polygon_data
[i
].center
[1] = center
[1]
858 polygon_data
[i
].center
[2] = center
[2]
859 polygon_data
[i
].material_id
= poly
.material_index
861 edge_data
= (cxr_edge
*len(data
.edges
))()
863 for i
, edge
in enumerate(data
.edges
):
864 edge_data
[i
].i0
= edge
.vertices
[0]
865 edge_data
[i
].i1
= edge
.vertices
[1]
866 edge_data
[i
].freestyle
= edge
.use_freestyle_mark
868 material_data
= (cxr_material
*len(obj
.material_slots
))()
870 for i
, ms
in enumerate(obj
.material_slots
):
871 inf
= material_info(ms
.material
)
872 material_data
[i
].res
[0] = inf
['res'][0]
873 material_data
[i
].res
[1] = inf
['res'][1]
874 material_data
[i
].vmt_path
= inf
['name'].encode('utf-8')
876 mesh
.edges
= cast(edge_data
, POINTER(cxr_edge
))
877 mesh
.vertices
= cast(vertex_data
, POINTER(c_double
*3))
878 mesh
.loops
= cast(loop_data
,POINTER(cxr_static_loop
))
879 mesh
.polys
= cast(polygon_data
, POINTER(cxr_polygon
))
880 mesh
.materials
= cast(material_data
, POINTER(cxr_material
))
882 mesh
.poly_count
= len(data
.polygons
)
883 mesh
.vertex_count
= len(data
.vertices
)
884 mesh
.edge_count
= len(data
.edges
)
885 mesh
.loop_count
= len(data
.loops
)
886 mesh
.material_count
= len(obj
.material_slots
)
890 class CXR_WRITE_VMF(bpy
.types
.Operator
):
891 bl_idname
="convexer.write_vmf"
894 def execute(_
,context
):
896 libcxr_reset_debug_lines()
898 # Setup output and state
899 filepath
= bpy
.data
.filepath
900 directory
= os
.path
.dirname(filepath
)
901 settings
= context
.scene
.cxr_data
903 asset_dir
= F
"{directory}/bin"
904 material_dir
= F
"{settings.subdir}/materials/{settings.project_name}"
905 model_dir
= F
"{settings.subdir}/models/{settings.project_name}"
907 os
.makedirs( asset_dir
, exist_ok
=True )
908 os
.makedirs( material_dir
, exist_ok
=True )
909 os
.makedirs( model_dir
, exist_ok
=True )
912 material_info
.references
= set()
914 output_vmf
= F
"{directory}/{settings.project_name}.vmf"
916 with
vdf_structure(output_vmf
) as m
:
917 print( F
"Write: {output_vmf}" )
919 vmfinfo
= cxr_vmf_context()
920 vmfinfo
.mapversion
= 4
921 vmfinfo
.skyname
= b
"sky_csgo_night02b"
922 vmfinfo
.detailvbsp
= b
"detail.vbsp"
923 vmfinfo
.detailmaterial
= b
"detail/detailsprites"
924 vmfinfo
.lightmap_scale
= 12
925 vmfinfo
.brush_count
= 0
926 vmfinfo
.entity_count
= 0
927 vmfinfo
.face_count
= 0
929 libcxr_begin_vmf
.call( pointer(vmfinfo
), m
.fp
)
931 # Make sure all of our asset types have a unique ID
932 def _uid_prepare(objtype
):
938 if vs
.asset_id
in used_ids
:
941 id_max
= max(id_max
,vs
.asset_id
)
942 used_ids
+=[vs
.asset_id
]
943 for vs
in to_generate
:
947 _uid_prepare(bpy
.data
.materials
)
948 _uid_prepare(bpy
.data
.images
)
949 _uid_prepare(bpy
.data
.collections
)
951 # Export Brushes and displacement
952 def _collect(collection
,transform
):
953 if collection
.name
.startswith('.'):
956 if collection
.hide_render
:
959 if collection
.name
.startswith('mdl_'):
960 _collect
.heros
+= [(collection
,transform
)]
963 for obj
in collection
.objects
:
964 if obj
.hide_get(): continue
966 classname
= cxr_classname( obj
)
968 if classname
!= None:
969 _collect
.entities
+= [( obj
,transform
,classname
)]
970 elif obj
.type == 'MESH':
971 _collect
.geo
+= [(obj
,transform
)]
973 for c
in collection
.children
:
974 _collect( c
, transform
)
976 _collect
.a_models
= set()
977 _collect
.entities
= []
981 transform_main
= cxr_object_context( \
982 context
.scene
.cxr_data
.scale_factor
, 0.0 )
984 transform_sky
= cxr_object_context( \
985 context
.scene
.cxr_data
.skybox_scale_factor
, \
986 context
.scene
.cxr_data
.skybox_offset
)
988 if 'main' in bpy
.data
.collections
:
989 _collect( bpy
.data
.collections
['main'], transform_main
)
991 if 'skybox' in bpy
.data
.collections
:
992 _collect( bpy
.data
.collections
['skybox'], transform_sky
)
994 def _buildsolid( obj
, ctx
):
997 baked
= mesh_cxr_format( brush
[0] )
998 world
= libcxr_decompose
.call( baked
)
1003 vmfinfo
.scale
= brush
[1].scale
1004 vmfinfo
.offset
[0] = 0.0
1005 vmfinfo
.offset
[1] = 0.0
1006 vmfinfo
.offset
[2] = brush
[1].offset_z
1008 libcxr_push_world_vmf
.call( world
, pointer(vmfinfo
), m
.fp
)
1009 libcxr_free_world
.call( world
)
1014 for brush
in _collect
.geo
:
1015 if not _buildsolid( brush
[0], brush
[1] ):
1017 return {'CANCELLED'}
1022 for entity
in _collect
.entities
:
1027 m
.kv( 'classname', cls
)
1029 kvs
= cxr_entity_keyvalues( obj
, ctx
, cls
)
1032 if isinstance(kv
[2], list):
1033 m
.kv( kv
[0], ' '.join([str(_
) for _
in kv
[2]]) )
1034 else: m
.kv( kv
[0], str(kv
[2]) )
1036 if not _buildsolid( obj
, ctx
):
1037 return {'CANCELLED'}
1041 print( "[CONVEXER] Compile materials / textures" )
1043 for mat
in material_info
.references
:
1044 compile_material(mat
)
1046 print( "[CONVEXER] Compiling models" )
1048 libcxr_batch_debug_lines()
1053 class CXR_DEV_OPERATOR(bpy
.types
.Operator
):
1054 bl_idname
="convexer.dev_test"
1055 bl_label
="Export development data"
1057 def execute(_
,context
):
1060 # Prepare input data
1061 mesh_src
= mesh_cxr_format(context
.active_object
)
1063 libcxr_reset_debug_lines()
1064 libcxr_write_test_data
.call( pointer(mesh_src
) )
1065 libcxr_batch_debug_lines()
1070 class CXR_PREVIEW_OPERATOR(bpy
.types
.Operator
):
1071 bl_idname
="convexer.preview"
1074 def execute(_
,context
):
1077 libcxr_reset_debug_lines()
1079 mesh_src
= mesh_cxr_format(context
.active_object
)
1080 world
= libcxr_decompose
.call( mesh_src
)
1082 global debug_gpu_shader
, debug_gpu_mesh
1085 debug_gpu_mesh
= None
1086 libcxr_batch_debug_lines()
1087 return {'CANCELLED'}
1089 ptrpreview
= libcxr_world_preview
.call( world
)
1090 preview
= ptrpreview
[0]
1092 vertices
= preview
.vertices
[:preview
.vertex_count
]
1093 vertices
= [(_
[0],_
[1],_
[2]) for _
in vertices
]
1095 colours
= preview
.colours
[:preview
.vertex_count
]
1096 colours
= [(_
[0],_
[1],_
[2],_
[3]) for _
in colours
]
1098 indices
= preview
.indices
[:preview
.indices_count
]
1099 indices
= [ (indices
[i
*3+0],indices
[i
*3+1],indices
[i
*3+2]) \
1100 for i
in range(int(preview
.indices_count
/3)) ]
1102 debug_gpu_mesh
= batch_for_shader(
1103 debug_gpu_shader
, 'TRIS',
1104 { "pos": vertices
, "color": colours
},
1108 libcxr_free_tri_mesh
.call( ptrpreview
)
1109 libcxr_free_world
.call( world
)
1111 libcxr_batch_debug_lines()
1115 class CXR_INTERFACE(bpy
.types
.Panel
):
1117 bl_idname
="SCENE_PT_convexer"
1118 bl_space_type
='PROPERTIES'
1119 bl_region_type
='WINDOW'
1122 def draw(_
,context
):
1123 _
.layout
.operator("convexer.reload")
1124 _
.layout
.operator("convexer.dev_test")
1125 _
.layout
.operator("convexer.preview")
1126 _
.layout
.operator("convexer.write_vmf")
1128 settings
= context
.scene
.cxr_data
1130 _
.layout
.prop(settings
, "debug")
1131 _
.layout
.prop(settings
, "scale_factor")
1132 _
.layout
.prop(settings
, "lightmap_scale")
1133 _
.layout
.prop(settings
, "light_scale" )
1135 box
= _
.layout
.box()
1137 box
.prop(settings
, "project_name")
1138 box
.prop(settings
, "subdir")
1140 box
= _
.layout
.box()
1141 box
.operator("convexer.detect_compilers")
1142 box
.prop(settings
, "exe_studiomdl")
1143 box
.prop(settings
, "exe_vbsp")
1144 box
.prop(settings
, "exe_vvis")
1145 box
.prop(settings
, "exe_vrad")
1147 # COmpile image using NBVTF and hash it
1148 def compile_image(img
,flags
):
1152 name
= asset_name(img
)
1153 src_path
= bpy
.path
.abspath(img
.filepath
)
1155 dims
= img
.cxr_data
.export_res
1157 'RGBA': NBVTF_IMAGE_FORMAT_RGBA8888
,
1158 'DXT1': NBVTF_IMAGE_FORMAT_DXT1
,
1159 'DXT5': NBVTF_IMAGE_FORMAT_DXT5
,
1160 'RGB': NBVTF_IMAGE_FORMAT_RGB888
1161 }[ img
.cxr_data
.fmt
]
1163 mipmap
= img
.cxr_data
.mipmap
1164 lod
= img
.cxr_data
.lod
1165 clamp
= img
.cxr_data
.clamp
1167 userflag_hash
= F
"{mipmap}.{lod}.{clamp}"
1168 file_hash
= F
"{name}.{os.path.getmtime(src_path)}"
1169 comphash
= F
"{file_hash}.{dims[0]}.{dims[1]}.{fmt}.{userflag_hash}"
1171 if img
.cxr_data
.last_hash
!= comphash
:
1172 print( F
"Texture update: {img.filepath}" )
1174 src
= src_path
.encode('utf-8')
1175 dst
= (asset_full_path('materials',img
)+'.vtf').encode('utf-8')
1179 # texture setting flags
1180 if not lod
: flags_full |
= NBVTF_TEXTUREFLAGS_NOLOD
1182 flags_full |
= NBVTF_TEXTUREFLAGS_CLAMPS
1183 flags_full |
= NBVTF_TEXTUREFLAGS_CLAMPT
1185 if libnbvtf_convert
.call(src
,dims
[0],dims
[1],mipmap
,fmt
,0,flags_full
,dst
):
1186 img
.cxr_data
.last_hash
= comphash
1190 def compile_material(mat
):
1191 print( F
"Compile {asset_full_path('materials',mat)}.vmt" )
1193 info
= material_info(mat
)
1194 properties
= mat
.cxr_data
1198 def _mlayer( layer
):
1199 nonlocal properties
, props
1202 if isinstance(layer
[decl
],dict): # $property definition
1204 ptype
= pdef
['type']
1210 if 'shaders' in pdef
and properties
.shader
not in pdef
['shaders']:
1213 # Group expansion (does it have subdefinitions?)
1215 if isinstance(pdef
[ch
],dict):
1224 if ptype
== 'intrinsic':
1228 prop
= getattr(properties
,decl
)
1229 default
= pdef
['default']
1231 if not isinstance(prop
,str) and \
1232 not isinstance(prop
,bpy
.types
.Image
) and \
1233 hasattr(prop
,'__getitem__'):
1234 prop
= tuple([p
for p
in prop
])
1238 props
+= [(decl
,pdef
,prop
)]
1243 if expandview
: _mlayer(pdef
)
1245 _mlayer( cxr_shader_params
)
1247 with
vdf_structure( F
"{asset_full_path('materials',mat)}.vmt" ) as vmt
:
1248 vmt
.node( properties
.shader
)
1249 vmt
.put( "// Convexer export\n" )
1259 if 'exponent' in pdef
: return str(pow( v
, pdef
['exponent'] ))
1262 if isinstance(prop
,bpy
.types
.Image
):
1265 flags
= pdef
['flags']
1266 vmt
.kv( decl
,compile_image(prop
,flags
))
1268 elif isinstance(prop
,bool):
1269 vmt
.kv( decl
, '1' if prop
else '0' )
1270 elif isinstance(prop
,str):
1271 vmt
.kv( decl
, prop
)
1272 elif isinstance(prop
,float) or isinstance(prop
,int):
1273 vmt
.kv( decl
, _numeric(prop
) )
1274 elif isinstance(prop
,tuple):
1275 vmt
.kv( decl
, F
"[{' '.join([_numeric(_) for _ in prop])}]" )
1277 vmt
.put( F
"// (cxr) unkown shader value type'{type(prop)}'" )
1281 class CXR_MATERIAL_PANEL(bpy
.types
.Panel
):
1282 bl_label
="VMT Properties"
1283 bl_idname
="SCENE_PT_convexer_vmt"
1284 bl_space_type
='PROPERTIES'
1285 bl_region_type
='WINDOW'
1286 bl_context
="material"
1288 def draw(_
,context
):
1289 active_object
= bpy
.context
.active_object
1290 if active_object
== None: return
1292 active_material
= active_object
.active_material
1293 if active_material
== None: return
1295 properties
= active_material
.cxr_data
1296 info
= material_info( active_material
)
1298 _
.layout
.label(text
=F
"{info['name']} @{info['res'][0]}x{info['res'][1]}")
1299 _
.layout
.prop( properties
, "shader" )
1302 _
.layout
.label(text
=F
"{xk}:={info[xk]}")
1304 def _mtex( name
, img
, uiParent
):
1307 box
= uiParent
.box()
1308 box
.label( text
=F
'{name} "{img.filepath}"' )
1310 if ((x
& (x
- 1)) == 0):
1313 closest_diff
= 10000000
1315 dist
= abs((1 << i
)-x
)
1316 if dist
< closest_diff
:
1321 return 1 << (closest
+1)
1323 return 1 << (closest
-1)
1328 row
.prop( img
.cxr_data
, "export_res" )
1329 row
.prop( img
.cxr_data
, "fmt" )
1332 row
.prop( img
.cxr_data
, "mipmap" )
1333 row
.prop( img
.cxr_data
, "lod" )
1334 row
.prop( img
.cxr_data
, "clamp" )
1336 img
.cxr_data
.export_res
[0] = _p2( img
.cxr_data
.export_res
[0] )
1337 img
.cxr_data
.export_res
[1] = _p2( img
.cxr_data
.export_res
[1] )
1339 def _mview( layer
, uiParent
):
1343 if isinstance(layer
[decl
],dict): # $property definition
1345 ptype
= pdef
['type']
1351 if ('shaders' in pdef
) and \
1352 (properties
.shader
not in pdef
['shaders']):
1355 if ptype
== 'intrinsic':
1356 if decl
not in info
:
1361 if isinstance(pdef
[ch
],dict):
1362 if ptype
== 'ui' or ptype
== 'intrinsic':
1364 elif getattr(properties
,decl
) == pdef
['default']:
1367 thisnode
= uiParent
.box()
1371 thisnode
.label( text
=decl
)
1372 elif ptype
== 'intrinsic':
1373 if isinstance(info
[decl
], bpy
.types
.Image
):
1374 _mtex( decl
, info
[decl
], thisnode
)
1376 # hidden intrinsic value.
1377 # Means its a float array or something not an image
1378 thisnode
.label(text
=F
"-- hidden intrinsic '{decl}' --")
1380 thisnode
.prop(properties
,decl
)
1381 if expandview
: _mview(pdef
,thisnode
)
1383 _mview( cxr_shader_params
, _
.layout
)
1385 def cxr_entity_changeclass(_
,context
):
1386 active_object
= context
.active_object
1388 # Create ID properties
1390 classname
= active_object
.cxr_data
.classname
1392 if classname
in cxr_entities
:
1393 entdef
= cxr_entities
[classname
]
1395 kvs
= entdef
['keyvalues']
1396 if callable(kvs
): kvs
= kvs(active_object
)
1402 if callable(kv
) or not isinstance(kv
,dict): continue
1404 if key
not in active_object
:
1405 active_object
[key
] = kv
['default']
1406 id_prop
= active_object
.id_properties_ui(key
)
1407 id_prop
.update(default
=kv
['default'])
1409 class CXR_ENTITY_PANEL(bpy
.types
.Panel
):
1410 bl_label
="Entity Config"
1411 bl_idname
="SCENE_PT_convexer_entity"
1412 bl_space_type
='PROPERTIES'
1413 bl_region_type
='WINDOW'
1416 def draw(_
,context
):
1417 active_object
= bpy
.context
.active_object
1419 if active_object
== None: return
1421 default_context
= cxr_object_context( \
1422 bpy
.context
.scene
.cxr_data
.scale_factor
, 0.0 )
1424 ecn
= cxr_intrinsic_classname( active_object
)
1425 classname
= cxr_custom_class( active_object
)
1428 if active_object
.type == 'MESH':
1429 _
.layout
.prop( active_object
.cxr_data
, 'brushclass' )
1430 else: _
.layout
.prop( active_object
.cxr_data
, 'classname' )
1432 if classname
== 'NONE':
1435 _
.layout
.label(text
=F
"<implementation defined ({ecn})>")
1436 _
.layout
.enabled
=False
1439 kvs
= cxr_entity_keyvalues( active_object
, default_context
, classname
)
1443 _
.layout
.prop( active_object
, F
'["cxrkv_{kv[0]}"]', text
=kv
[0])
1445 row
= _
.layout
.row()
1447 row
.label( text
=F
'{kv[0]}: {repr(kv[2])}' )
1449 _
.layout
.label( text
=F
"ERROR: NO CLASS DEFINITION" )
1451 class CXR_LIGHT_PANEL(bpy
.types
.Panel
):
1452 bl_label
= "Source Settings"
1453 bl_idname
= "LIGHT_PT_cxr"
1454 bl_space_type
= 'PROPERTIES'
1455 bl_region_type
= 'WINDOW'
1458 def draw(self
, context
):
1459 layout
= self
.layout
1460 scene
= context
.scene
1462 active_object
= bpy
.context
.active_object
1463 if active_object
== None: return
1465 if active_object
.type == 'LIGHT' or \
1466 active_object
.type == 'LIGHT_PROBE':
1468 properties
= active_object
.data
.cxr_data
1470 if active_object
.type == 'LIGHT':
1471 layout
.prop( properties
, "realtime" )
1472 elif active_object
.type == 'LIGHT_PROBE':
1473 layout
.prop( properties
, "size" )
1475 class CXR_IMAGE_SETTINGS(bpy
.types
.PropertyGroup
):
1476 export_res
: bpy
.props
.IntVectorProperty(
1478 description
="Texture Export Resolution",
1484 fmt
: bpy
.props
.EnumProperty(
1487 ('DXT1', "DXT1", "BC1 compressed", '', 0),
1488 ('DXT5', "DXT5", "BC3 compressed", '', 1),
1489 ('RGB', "RGB", "Uncompressed", '', 2),
1490 ('RGBA', "RGBA", "Uncompressed (with alpha)", '', 3)
1492 description
="Image format",
1495 last_hash
: bpy
.props
.StringProperty( name
="" )
1496 asset_id
: bpy
.props
.IntProperty(name
="intl_assetid",default
=0)
1498 mipmap
: bpy
.props
.BoolProperty(name
="MIP",default
=True)
1499 lod
: bpy
.props
.BoolProperty(name
="LOD",default
=True)
1500 clamp
: bpy
.props
.BoolProperty(name
="CLAMP",default
=False)
1502 class CXR_LIGHT_SETTINGS(bpy
.types
.PropertyGroup
):
1503 realtime
: bpy
.props
.BoolProperty(name
="Realtime Light", default
=True)
1505 class CXR_CUBEMAP_SETTINGS(bpy
.types
.PropertyGroup
):
1506 size
: bpy
.props
.EnumProperty(
1509 ('1',"1x1",'','',0),
1510 ('2',"2x2",'','',1),
1511 ('3',"4x4",'','',2),
1512 ('4',"8x8",'','',3),
1513 ('5',"16x16",'','',4),
1514 ('6',"32x32",'','',5),
1515 ('7',"64x64",'','',6),
1516 ('8',"128x128",'','',7),
1517 ('9',"256x256",'','',8)
1519 description
="Texture resolution",
1522 class CXR_ENTITY_SETTINGS(bpy
.types
.PropertyGroup
):
1523 entity
: bpy
.props
.BoolProperty(name
="")
1525 enum_pointents
= [('NONE',"None","")]
1526 enum_brushents
= [('NONE',"None","")]
1528 for classname
in cxr_entities
:
1529 entdef
= cxr_entities
[classname
]
1530 if 'allow' in entdef
:
1531 itm
= [(classname
, classname
, "")]
1532 if 'EMPTY' in entdef
['allow']: enum_pointents
+= itm
1533 else: enum_brushents
+= itm
1535 classname
: bpy
.props
.EnumProperty(items
=enum_pointents
, name
="Class", \
1536 update
=cxr_entity_changeclass
, default
='NONE' )
1538 brushclass
: bpy
.props
.EnumProperty(items
=enum_brushents
, name
="Class", \
1539 update
=cxr_entity_changeclass
, default
='NONE' )
1541 class CXR_MODEL_SETTINGS(bpy
.types
.PropertyGroup
):
1542 last_hash
: bpy
.props
.StringProperty( name
="" )
1543 asset_id
: bpy
.props
.IntProperty(name
="vmf_settings",default
=0)
1545 class CXR_SCENE_SETTINGS(bpy
.types
.PropertyGroup
):
1546 project_name
: bpy
.props
.StringProperty( name
="Project Name" )
1547 subdir
: bpy
.props
.StringProperty( name
="Subdirectory" )
1549 exe_studiomdl
: bpy
.props
.StringProperty( name
="studiomdl" )
1550 exe_vbsp
: bpy
.props
.StringProperty( name
="vbsp" )
1551 opt_vbsp
: bpy
.props
.StringProperty( name
="args" )
1552 exe_vvis
: bpy
.props
.StringProperty( name
="vvis" )
1553 opt_vvis
: bpy
.props
.StringProperty( name
="args" )
1554 exe_vrad
: bpy
.props
.StringProperty( name
="vrad" )
1555 opt_vrad
: bpy
.props
.StringProperty( name
="args" )
1557 debug
: bpy
.props
.BoolProperty(name
="Debug",default
=False)
1558 scale_factor
: bpy
.props
.FloatProperty( name
="VMF Scale factor", \
1559 default
=32.0,min=1.0)
1560 skybox_scale_factor
: bpy
.props
.FloatProperty( name
="Sky Scale factor", \
1561 default
=1.0,min=0.01)
1563 skybox_offset
: bpy
.props
.FloatProperty(name
="Sky offset",default
=-4096.0)
1564 light_scale
: bpy
.props
.FloatProperty(name
="Light Scale",default
=1.0/5.0)
1565 include_names
: bpy
.props
.BoolProperty(name
="Append original file names",\
1567 lightmap_scale
: bpy
.props
.IntProperty(name
="Global Lightmap Scale",\
1570 class CXR_DETECT_COMPILERS(bpy
.types
.Operator
):
1571 bl_idname
="convexer.detect_compilers"
1572 bl_label
="Find compilers"
1574 def execute(self
,context
):
1575 scene
= context
.scene
1576 settings
= scene
.cxr_data
1577 subdir
= settings
.subdir
1579 for exename
in ['studiomdl','vbsp','vvis','vrad']:
1580 searchpath
= os
.path
.normpath(F
'{subdir}/../bin/{exename}.exe')
1581 if os
.path
.exists(searchpath
):
1582 settings
[F
'exe_{exename}'] = searchpath
1586 classes
= [ CXR_RELOAD
, CXR_DEV_OPERATOR
, CXR_INTERFACE
, \
1587 CXR_WRITE_VMF
, CXR_MATERIAL_PANEL
, CXR_IMAGE_SETTINGS
,\
1588 CXR_MODEL_SETTINGS
, CXR_ENTITY_SETTINGS
, CXR_CUBEMAP_SETTINGS
,\
1589 CXR_LIGHT_SETTINGS
, CXR_SCENE_SETTINGS
, CXR_DETECT_COMPILERS
,\
1590 CXR_ENTITY_PANEL
, CXR_LIGHT_PANEL
, CXR_PREVIEW_OPERATOR
]
1593 global debug_draw_handler
, vmt_param_dynamic_class
1596 bpy
.utils
.register_class(c
)
1598 # Build dynamic VMT properties class defined by cxr_shader_params
1599 annotations_dict
= {}
1601 def _dvmt_propogate(layer
):
1602 nonlocal annotations_dict
1605 if isinstance(layer
[decl
],dict): # $property definition
1609 if pdef
['type'] == 'bool':
1610 prop
= bpy
.props
.BoolProperty(\
1611 name
= pdef
['name'],\
1612 default
= pdef
['default'])
1614 elif pdef
['type'] == 'float':
1615 prop
= bpy
.props
.FloatProperty(\
1616 name
= pdef
['name'],\
1617 default
= pdef
['default'])
1619 elif pdef
['type'] == 'vector':
1620 if 'subtype' in pdef
:
1621 prop
= bpy
.props
.FloatVectorProperty(\
1622 name
= pdef
['name'],\
1623 subtype
= pdef
['subtype'],\
1624 default
= pdef
['default'],\
1625 size
= len(pdef
['default']))
1627 prop
= bpy
.props
.FloatVectorProperty(\
1628 name
= pdef
['name'],\
1629 default
= pdef
['default'],\
1630 size
= len(pdef
['default']))
1632 elif pdef
['type'] == 'string':
1633 prop
= bpy
.props
.StringProperty(\
1634 name
= pdef
['name'],\
1635 default
= pdef
['default'])
1637 elif pdef
['type'] == 'enum':
1638 prop
= bpy
.props
.EnumProperty(\
1639 name
= pdef
['name'],\
1640 items
= pdef
['items'],\
1641 default
= pdef
['default'])
1644 annotations_dict
[decl
] = prop
1646 # Recurse into sub-definitions
1647 _dvmt_propogate(pdef
)
1649 annotations_dict
["shader"] = bpy
.props
.EnumProperty(\
1652 cxr_shaders
[_
]["name"],\
1653 '') for _
in cxr_shaders
],\
1654 default
= next(iter(cxr_shaders
)))
1656 annotations_dict
["asset_id"] = bpy
.props
.IntProperty(name
="intl_assetid",\
1659 _dvmt_propogate( cxr_shader_params
)
1660 vmt_param_dynamic_class
= type(
1662 (bpy
.types
.PropertyGroup
,),{
1663 "__annotations__": annotations_dict
1667 bpy
.utils
.register_class( vmt_param_dynamic_class
)
1670 bpy
.types
.Material
.cxr_data
= \
1671 bpy
.props
.PointerProperty(type=vmt_param_dynamic_class
)
1672 bpy
.types
.Image
.cxr_data
= \
1673 bpy
.props
.PointerProperty(type=CXR_IMAGE_SETTINGS
)
1674 bpy
.types
.Object
.cxr_data
= \
1675 bpy
.props
.PointerProperty(type=CXR_ENTITY_SETTINGS
)
1676 bpy
.types
.Collection
.cxr_data
= \
1677 bpy
.props
.PointerProperty(type=CXR_MODEL_SETTINGS
)
1678 bpy
.types
.Light
.cxr_data
= \
1679 bpy
.props
.PointerProperty(type=CXR_LIGHT_SETTINGS
)
1680 bpy
.types
.LightProbe
.cxr_data
= \
1681 bpy
.props
.PointerProperty(type=CXR_CUBEMAP_SETTINGS
)
1682 bpy
.types
.Scene
.cxr_data
= \
1683 bpy
.props
.PointerProperty(type=CXR_SCENE_SETTINGS
)
1685 # CXR Scene settings
1688 debug_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
1689 cxr_draw
,(),'WINDOW','POST_VIEW')
1691 bpy
.app
.handlers
.load_post
.append(cxr_on_load
)
1692 bpy
.app
.handlers
.depsgraph_update_post
.append(cxr_dgraph_update
)
1695 global debug_draw_handler
, vmt_param_dynamic_class
1697 bpy
.utils
.unregister_class( vmt_param_dynamic_class
)
1699 bpy
.utils
.unregister_class(c
)
1701 bpy
.app
.handlers
.depsgraph_update_post
.remove(cxr_dgraph_update
)
1702 bpy
.app
.handlers
.load_post
.remove(cxr_on_load
)
1704 bpy
.types
.SpaceView3D
.draw_handler_remove(debug_draw_handler
,'WINDOW')