X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=c8639711a06a352c4a852945ea929c22e693aecd;hb=6b9993651343af73bd48e2213910bbaadb41edaf;hp=cbd2a1989087302c7844d535e44cd73b5aaf1a3a;hpb=72c40f1bc7a732f6a628dbf8a4135ac0bf3efa4e;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index cbd2a19..c863971 100644 --- a/blender_export.py +++ b/blender_export.py @@ -3,6 +3,7 @@ import cProfile from ctypes import * from mathutils import * from gpu_extras.batch import batch_for_shader +from bpy_extras import mesh_utils bl_info = { "name":"Skaterift .mdl exporter", @@ -24,7 +25,8 @@ sr_entity_alias = { 'ent_water': 5, 'ent_volume': 6, 'ent_audio': 7, - 'ent_marker': 8 + 'ent_marker': 8, + 'ent_glyph': 9 } class mdl_vert(Structure): # 48 bytes. Quite large. Could compress @@ -171,7 +173,8 @@ class ent_gate(Structure): ("transport",(c_float*3)*4), ("_anonymous_union",version_refcount_union), ("timing_time",c_double), - ("routes",c_uint16*4)] + ("routes",c_uint16*4), + ("route_count",c_uint8)] #} class ent_route_node(Structure): @@ -289,6 +292,29 @@ class ent_marker(Structure): ("name",c_uint32)] #} +class ent_glyph(Structure): +#{ + _fields_ = [("size",c_float*2), + ("indice_start",c_uint32), + ("indice_count",c_uint32)] +#} + +class ent_font_variant(Structure): +#{ + _fields_ = [("name",c_uint32), + ("material_id",c_uint32)] +#} + +class ent_font(Structure): +#{ + _fields_ = [("alias",c_uint32), + ("variant_start",c_uint32), + ("variant_count",c_uint32), + ("glyph_start",c_uint32), + ("glyph_count",c_uint32), + ("glyph_utf32_base",c_uint32)] +#} + def obj_ent_type( obj ): #{ if obj.type == 'ARMATURE': return 'mdl_armature' @@ -688,8 +714,7 @@ def sr_compile_mesh( obj ): # Write the vertex / indice data # for tri_index, tri in enumerate(data.loop_triangles):#{ - if tri.material_index != material_id: - continue + if tri.material_index != material_id: continue for j in range(3):#{ vert = data.vertices[tri.vertices[j]] @@ -852,6 +877,136 @@ def sr_compile_mesh( obj ): sr_compile.mesh_data.extend(bytearray(node)) #} +def sr_compile_fonts( collection ): +#{ + print( F"[SR] Compiling fonts" ) + + glyph_count = 0 + variant_count = 0 + + for obj in collection.all_objects:#{ + if obj_ent_type(obj) != 'ent_font': continue + + data = obj.SR_data.ent_font[0] + + font=ent_font() + font.alias = sr_compile_string( data.alias ) + font.variant_start = variant_count + font.variant_count = 0 + font.glyph_start = glyph_count + + glyph_base = data.glyphs[0].utf32 + glyph_range = data.glyphs[-1].utf32 - glyph_base + + font.glyph_utf32_base = glyph_base + font.glyph_count = glyph_range + + for i in range(len(data.variants)):#{ + data_var = data.variants[i] + if not data_var.mesh: continue + + mesh = data_var.mesh.data + + variant = ent_font_variant() + variant.name = sr_compile_string( data_var.tipo ) + + # fonts (variants) only support one material each + mat = None + if len(mesh.materials) != 0: + mat = mesh.materials[0] + variant.material_id = sr_compile_material( mat ) + + font.variant_count += 1 + + islands = mesh_utils.mesh_linked_triangles(mesh) + centroids = [Vector((0,0)) for _ in range(len(islands))] + + for j in range(len(islands)):#{ + for tri in islands[j]:#{ + centroids[j].x += tri.center[0] + centroids[j].y += tri.center[2] + #} + + centroids[j] /= len(islands[j]) + #} + + for j in range(glyph_range):#{ + data_glyph = data.glyphs[j] + glyph = ent_glyph() + glyph.indice_start = len(sr_compile.indice_data)//sizeof(c_uint32) + glyph.indice_count = 0 + glyph.size[0] = data_glyph.bounds[2] + glyph.size[1] = data_glyph.bounds[3] + + vertex_reference = {} + + for k in range(len(islands)):#{ + if centroids[k].x < data_glyph.bounds[0] or \ + centroids[k].x > data_glyph.bounds[0]+data_glyph.bounds[2] or\ + centroids[k].y < data_glyph.bounds[1] or \ + centroids[k].y > data_glyph.bounds[1]+data_glyph.bounds[3]: + #{ + continue + #} + + for l in range(len(islands[k])):#{ + tri = islands[k][l] + for m in range(3):#{ + vert = mesh.vertices[tri.vertices[m]] + li = tri.loops[m] + vi = mesh.loops[li].vertex_index + + # Gather vertex information + # + co = [vert.co[_] for _ in range(3)] + co[0] -= data_glyph.bounds[0] + co[2] -= data_glyph.bounds[1] + norm = mesh.loops[li].normal + uv = (0,0) + if mesh.uv_layers: uv = mesh.uv_layers.active.data[li].uv + + TOLERENCE = float(10**4) + key = (int(co[0]*TOLERENCE+0.5), + int(co[1]*TOLERENCE+0.5), + int(co[2]*TOLERENCE+0.5), + int(norm[0]*TOLERENCE+0.5), + int(norm[1]*TOLERENCE+0.5), + int(norm[2]*TOLERENCE+0.5), + int(uv[0]*TOLERENCE+0.5), + int(uv[1]*TOLERENCE+0.5)) + + if key in vertex_reference: + index = vertex_reference[key] + else:#{ + vindex = len(sr_compile.vertex_data)//sizeof(mdl_vert) + index = bytearray(c_uint32(vindex)) + vertex_reference[key] = index + v = mdl_vert() + v.co[0] = co[0] + v.co[1] = co[2] + v.co[2] = -co[1] + v.norm[0] = norm[0] + v.norm[1] = norm[2] + v.norm[2] = -norm[1] + v.uv[0] = uv[0] + v.uv[1] = uv[1] + + sr_compile.vertex_data.extend(bytearray(v)) + #} + + glyph.indice_count += 1 + sr_compile.indice_data.extend( index ) + #} + #} + #} + sr_ent_push( glyph ) + #} + sr_ent_push( variant ) + #} + sr_ent_push( font ) + #} +#} + def sr_compile_armature( obj ): #{ node = mdl_armature() @@ -1135,13 +1290,21 @@ def sr_compile( collection ): #} elif ent_type == 'ent_gate': #{ gate = ent_gate() - gate.type = 0 obj_data = obj.SR_data.ent_gate[0] mesh_data = obj.data.SR_data.ent_gate[0] - if obj_data.target:#{ - gate.target = sr_compile.entity_ids[obj_data.target.name] - gate.type = 1 + + if obj_data.tipo == 'default':#{ + if obj_data.target:#{ + gate.target = sr_compile.entity_ids[obj_data.target.name] + gate.type = 1 + #} + #} + elif obj_data.tipo == 'nonlocal':#{ + gate.target = sr_compile_string(obj_data.key) + gate.type = 2 #} + else: gate.type = 0 + gate.dimensions[0] = mesh_data.dimensions[0] gate.dimensions[1] = mesh_data.dimensions[1] gate.dimensions[2] = mesh_data.dimensions[2] @@ -1252,6 +1415,8 @@ def sr_compile( collection ): #} #} + sr_compile_fonts(collection) + def _children( col ):#{ yield col for c in col.children:#{ @@ -1609,6 +1774,10 @@ class SR_INTERFACE(bpy.types.Panel): active_object = context.active_object if not active_object: return + _.layout.operator( 'skaterift.copy_entity_data', \ + text=F'Copy entity data to {len(context.selected_objects)-1} '+\ + F'other objects' ) + box = _.layout.box() row = box.row() row.alignment = 'CENTER' @@ -1765,6 +1934,20 @@ class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup): target: bpy.props.PointerProperty( \ type=bpy.types.Object, name="destination", \ poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate'])) + + key: bpy.props.StringProperty() + tipo: bpy.props.EnumProperty(items=(('default', 'Default', ""), + ('nonlocal', 'Non-Local', ""),)) + + @staticmethod + def sr_inspector( layout, data ): + #{ + box = layout.box() + box.prop( data[0], 'tipo', text="subtype" ) + + if data[0].tipo == 'default': box.prop( data[0], 'target' ) + elif data[0].tipo == 'nonlocal': box.prop( data[0], 'key' ) + #} #} class SR_MESH_ENT_GATE(bpy.types.PropertyGroup): @@ -1789,15 +1972,86 @@ class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList): #} #} +def internal_listdel_execute(self,context,ent_name,collection_name): +#{ + active_object = context.active_object + data = getattr(active_object.SR_data,ent_name)[0] + lista = getattr(data,collection_name) + index = getattr(data,F'{collection_name}_index') + + lista.remove(index) + + setattr(data,F'{collection_name}_index', min(max(0,index-1), len(lista)-1)) + return{'FINISHED'} +#} + +def internal_listadd_execute(self,context,ent_name,collection_name): +#{ + active_object = context.active_object + getattr(getattr(active_object.SR_data,ent_name)[0],collection_name).add() + return{'FINISHED'} +#} + +def copy_propgroup( de, to ): +#{ + for a in de.__annotations__:#{ + if isinstance(getattr(de,a), bpy.types.bpy_prop_collection):#{ + ca = getattr(de,a) + cb = getattr(to,a) + + while len(cb) != len(ca):#{ + if len(cb) < len(ca): cb.add() + else: cb.remove(0) + #} + for i in range(len(ca)):#{ + copy_propgroup(ca[i],cb[i]) + #} + #} + else:#{ + setattr(to,a,getattr(de,a)) + #} + #} +#} + +class SR_OT_COPY_ENTITY_DATA(bpy.types.Operator): +#{ + bl_idname = "skaterift.copy_entity_data" + bl_label = "Copy entity data" + + def execute(self, context):#{ + data = context.active_object.SR_data + new_type = data.ent_type + print( F"Copy entity data from: {context.active_object.name}" ) + + for obj in context.selected_objects:#{ + if obj != context.active_object:#{ + print( F" To: {obj.name}" ) + + obj.SR_data.ent_type = new_type + + if active_object.type == 'MESH':#{ + col = getattr( obj.data.SR_data, new_type, None ) + if col != None and len(col)==0: col.add() + mdata = context.active_object.data.SR_data + copy_propgroup( getattr(mdata,new_type)[0], col[0] ) + #} + + col = getattr( obj.SR_data, new_type, None ) + if col != None and len(col)==0: col.add() + copy_propgroup( getattr(data,new_type)[0], col[0] ) + #} + #} + return{'FINISHED'} + #} +#} + class SR_OT_ROUTE_LIST_NEW_ITEM(bpy.types.Operator): #{ bl_idname = "skaterift.new_entry" bl_label = "Add gate" def execute(self, context):#{ - active_object = context.active_object - active_object.SR_data.ent_route[0].gates.add() - return{'FINISHED'} + return internal_listadd_execute(self,context,'ent_route','gates') #} #} @@ -1809,20 +2063,14 @@ class SR_OT_ROUTE_LIST_DEL_ITEM(bpy.types.Operator): @classmethod def poll(cls, context):#{ active_object = context.active_object - if obj_ent_type(active_object) == 'ent_gate':#{ + if obj_ent_type(active_object) == 'ent_route':#{ return active_object.SR_data.ent_route[0].gates #} else: return False #} def execute(self, context):#{ - active_object = context.active_object - lista = active_object.SR_data.ent_route[0].gates - index = active_object.SR_data.ent_route[0].gates_index - lista.remove(index) - active_object.SR_data.ent_route[0].gates_index = \ - min(max(0, index-1), len(lista) - 1) - return{'FINISHED'} + return internal_listdel_execute(self,context,'ent_route','gates') #} #} @@ -1832,9 +2080,7 @@ class SR_OT_AUDIO_LIST_NEW_ITEM(bpy.types.Operator): bl_label = "Add file" def execute(self, context):#{ - active_object = context.active_object - active_object.SR_data.ent_audio[0].files.add() - return{'FINISHED'} + return internal_listadd_execute(self,context,'ent_audio','files') #} #} @@ -1852,17 +2098,116 @@ class SR_OT_AUDIO_LIST_DEL_ITEM(bpy.types.Operator): else: return False #} + def execute(self, context):#{ + return internal_listdel_execute(self,context,'ent_audio','files') + return{'FINISHED'} + #} +#} + +class SR_OT_GLYPH_LIST_NEW_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.gl_new_entry" + bl_label = "Add glyph" + def execute(self, context):#{ active_object = context.active_object - lista = active_object.SR_data.ent_audio[0].files - index = active_object.SR_data.ent_audio[0].file_index - lista.remove(index) - active_object.SR_data.ent_audio[0].file_index = \ - min(max(0, index-1), len(lista) - 1) + + font = active_object.SR_data.ent_font[0] + font.glyphs.add() + + if len(font.glyphs) > 1:#{ + prev = font.glyphs[-2] + cur = font.glyphs[-1] + + cur.bounds = prev.bounds + cur.utf32 = prev.utf32+1 + #} + return{'FINISHED'} #} #} +class SR_OT_GLYPH_LIST_DEL_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.gl_del_entry" + bl_label = "Remove Glyph" + + @classmethod + def poll(cls, context):#{ + active_object = context.active_object + if obj_ent_type(active_object) == 'ent_font':#{ + return active_object.SR_data.ent_font[0].glyphs + #} + else: return False + #} + + def execute(self, context):#{ + return internal_listdel_execute(self,context,'ent_font','glyphs') + #} +#} + +class SR_OT_GLYPH_LIST_MOVE_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.gl_move_item" + bl_label = "aa" + direction: bpy.props.EnumProperty(items=(('UP', 'Up', ""), + ('DOWN', 'Down', ""),)) + + @classmethod + def poll(cls, context):#{ + active_object = context.active_object + if obj_ent_type(active_object) == 'ent_font':#{ + return active_object.SR_data.ent_font[0].glyphs + #} + else: return False + #} + + def execute(_, context):#{ + active_object = context.active_object + data = active_object.SR_data.ent_font[0] + + index = data.glyphs_index + neighbor = index + (-1 if _.direction == 'UP' else 1) + data.glyphs.move( neighbor, index ) + + list_length = len(data.glyphs) - 1 + new_index = index + (-1 if _.direction == 'UP' else 1) + + data.glyphs_index = max(0, min(new_index, list_length)) + + return{'FINISHED'} + #} +#} + +class SR_OT_FONT_VARIANT_LIST_NEW_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.fv_new_entry" + bl_label = "Add variant" + + def execute(self, context):#{ + return internal_listadd_execute(self,context,'ent_font','variants') + #} +#} + +class SR_OT_FONT_VARIANT_LIST_DEL_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.fv_del_entry" + bl_label = "Remove variant" + + @classmethod + def poll(cls, context):#{ + active_object = context.active_object + if obj_ent_type(active_object) == 'ent_font':#{ + return active_object.SR_data.ent_font[0].variants + #} + else: return False + #} + + def execute(self, context):#{ + return internal_listdel_execute(self,context,'ent_font','variants') + #} +#} + class SR_OBJECT_ENT_AUDIO_FILE_ENTRY(bpy.types.PropertyGroup): #{ path: bpy.props.StringProperty( name="Path" ) @@ -1883,6 +2228,37 @@ class SR_UL_AUDIO_LIST(bpy.types.UIList): #} #} +class SR_UL_FONT_VARIANT_LIST(bpy.types.UIList): +#{ + bl_idname = 'SR_UL_FONT_VARIANT_LIST' + + def draw_item(_,context,layout,data,item,icon,active_data,active_propname): + #{ + layout.prop( item, 'mesh', emboss=False ) + layout.prop( item, 'tipo' ) + #} +#} + +class SR_UL_FONT_GLYPH_LIST(bpy.types.UIList): +#{ + bl_idname = 'SR_UL_FONT_GLYPH_LIST' + + def draw_item(_,context,layout,data,item,icon,active_data,active_propname): + #{ + s0 = layout.split(factor=0.3) + c = s0.column() + s1 = c.split(factor=0.3) + c = s1.column() + row = c.row() + lbl = chr(item.utf32) if item.utf32 >= 32 and item.utf32 <= 126 else 'ERR' + row.label(text=lbl) + c = s1.column() + c.prop( item, 'utf32', text='', emboss=True ) + c = s0.column() + row = c.row() + row.prop( item, 'bounds', text='', emboss=False ) + #} +#} class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup): #{ @@ -1941,7 +2317,7 @@ class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup): class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup): #{ files: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO_FILE_ENTRY) - file_index: bpy.props.IntProperty() + files_index: bpy.props.IntProperty() flag_3d: bpy.props.BoolProperty( name="3D audio",default=True ) flag_loop: bpy.props.BoolProperty( name="Loop",default=False ) @@ -2020,6 +2396,59 @@ class SR_OBJECT_ENT_MARKER(bpy.types.PropertyGroup): alias: bpy.props.StringProperty() #} +class SR_OBJECT_ENT_GLYPH(bpy.types.PropertyGroup): +#{ + mini: bpy.props.FloatVectorProperty(size=2) + maxi: bpy.props.FloatVectorProperty(size=2) + utf32: bpy.props.IntProperty() +#} + +class SR_OBJECT_ENT_GLYPH_ENTRY(bpy.types.PropertyGroup): +#{ + bounds: bpy.props.FloatVectorProperty(size=4,subtype='NONE') + utf32: bpy.props.IntProperty() +#} + +class SR_OBJECT_ENT_FONT_VARIANT(bpy.types.PropertyGroup): +#{ + mesh: bpy.props.PointerProperty(type=bpy.types.Object) + tipo: bpy.props.StringProperty() +#} + +class SR_OBJECT_ENT_FONT(bpy.types.PropertyGroup): +#{ + variants: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT_VARIANT) + glyphs: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH_ENTRY) + alias: bpy.props.StringProperty() + + glyphs_index: bpy.props.IntProperty() + variants_index: bpy.props.IntProperty() + + @staticmethod + def sr_inspector( layout, data ): + #{ + layout.prop( data[0], 'alias' ) + + layout.label( text='Variants' ) + layout.template_list('SR_UL_FONT_VARIANT_LIST', 'Variants', \ + data[0], 'variants', data[0], 'variants_index',\ + rows=5 ) + row = layout.row() + row.operator( 'skaterift.fv_new_entry', text='Add' ) + row.operator( 'skaterift.fv_del_entry', text='Remove' ) + + layout.label( text='ASCII Glyphs' ) + layout.template_list('SR_UL_FONT_GLYPH_LIST', 'Glyphs', \ + data[0], 'glyphs', data[0], 'glyphs_index', rows=5) + + row = layout.row() + row.operator( 'skaterift.gl_new_entry', text='Add' ) + row.operator( 'skaterift.gl_del_entry', text='Remove' ) + row.operator( 'skaterift.gl_move_item', text='^' ).direction='UP' + row.operator( 'skaterift.gl_move_item', text='v' ).direction='DOWN' + #} +#} + class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): #{ ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE) @@ -2028,7 +2457,8 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): ent_volume: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_VOLUME) ent_audio: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO) ent_marker: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MARKER) - + ent_glyph: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH) + ent_font: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT) ent_type: bpy.props.EnumProperty( name="Type", items=[('none', 'None', '', 0), @@ -2039,7 +2469,9 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): ('ent_water', 'Water Surface', '', 5), ('ent_volume', 'Volume', '', 6 ), ('ent_audio', 'Audio Files', '', 7), - ('ent_marker', 'Marker', '', 8)], + ('ent_marker', 'Marker', '', 8), + ('ent_font', 'Font', '', 9), + ('ent_font_variant','Font variant','',10)], update=sr_on_type_change ) #} @@ -2962,6 +3394,29 @@ def cv_draw(): if obj.SR_data.ent_audio[0].flag_3d: cv_draw_sphere( obj.location, obj.scale[0], (1,1,0) ) #} + elif ent_type == 'ent_font':#{ + data = obj.SR_data.ent_font[0] + + for i in range(len(data.variants)):#{ + sub = data.variants[i].mesh + if not sub: continue + + for ch in data.glyphs:#{ + mini = (ch.bounds[0],ch.bounds[1]) + maxi = (ch.bounds[2]+mini[0],ch.bounds[3]+mini[1]) + p0 = sub.matrix_world @ Vector((mini[0],0.0,mini[1])) + p1 = sub.matrix_world @ Vector((maxi[0],0.0,mini[1])) + p2 = sub.matrix_world @ Vector((maxi[0],0.0,maxi[1])) + p3 = sub.matrix_world @ Vector((mini[0],0.0,maxi[1])) + + if i == data.variants_index: cc = (0.5,0.5,0.5) + else: cc = (0,0,0) + + cv_view_verts += [p0,p1,p1,p2,p2,p3,p3,p0] + cv_view_colours += [cc,cc,cc,cc,cc,cc,cc,cc] + #} + #} + #} #} #} @@ -2983,11 +3438,19 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\ SR_OBJECT_ENT_GATE, SR_MESH_ENT_GATE, SR_OBJECT_ENT_SPAWN, \ SR_OBJECT_ENT_ROUTE_ENTRY, SR_UL_ROUTE_NODE_LIST, \ SR_OBJECT_ENT_ROUTE, SR_OT_ROUTE_LIST_NEW_ITEM,\ + SR_OT_GLYPH_LIST_NEW_ITEM, SR_OT_GLYPH_LIST_DEL_ITEM,\ + SR_OT_GLYPH_LIST_MOVE_ITEM,\ SR_OT_AUDIO_LIST_NEW_ITEM,SR_OT_AUDIO_LIST_DEL_ITEM,\ - SR_OBJECT_ENT_VOLUME, + SR_OT_FONT_VARIANT_LIST_NEW_ITEM,SR_OT_FONT_VARIANT_LIST_DEL_ITEM,\ + SR_OT_COPY_ENTITY_DATA, \ + SR_OBJECT_ENT_VOLUME, \ SR_UL_AUDIO_LIST, SR_OBJECT_ENT_AUDIO_FILE_ENTRY,\ SR_OT_ROUTE_LIST_DEL_ITEM,\ - SR_OBJECT_ENT_AUDIO,SR_OBJECT_ENT_MARKER,\ + SR_OBJECT_ENT_AUDIO,SR_OBJECT_ENT_MARKER,SR_OBJECT_ENT_GLYPH,\ + SR_OBJECT_ENT_FONT_VARIANT, + SR_OBJECT_ENT_GLYPH_ENTRY,\ + SR_UL_FONT_VARIANT_LIST,SR_UL_FONT_GLYPH_LIST,\ + SR_OBJECT_ENT_FONT,\ \ SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \