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",
'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
("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):
("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'
# 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]]
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()
#}
#}
+ sr_compile_fonts(collection)
+
def _children( col ):#{
yield col
for c in col.children:#{
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'
#}
#}
+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')
#}
#}
#}
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')
#}
#}
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')
#}
#}
#}
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
+
+ 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
- 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)
+ 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" )
#}
#}
+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):
#{
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 )
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)
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),
('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
)
#}
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]
+ #}
+ #}
+ #}
#}
#}
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 \
--- /dev/null
+#ifndef FONT_H
+#define FONT_H
+
+#include "model.h"
+#include "entity.h"
+#include "camera.h"
+#include "shaders/model_font.h"
+
+typedef struct font3d font3d;
+struct font3d{
+ mdl_context mdl;
+ GLuint texture;
+ glmesh mesh;
+
+ ent_font info;
+ mdl_array_ptr font_variants,
+ glyphs;
+};
+
+VG_STATIC void font3d_load( font3d *font, const char *mdl_path, void *alloc )
+{
+ mdl_open( &font->mdl, mdl_path, alloc );
+ mdl_load_metadata_block( &font->mdl, alloc );
+
+ vg_linear_clear( vg_mem.scratch );
+ mdl_array_ptr fonts;
+ mdl_load_array( &font->mdl, &fonts, "ent_font", vg_mem.scratch );
+ font->info = *((ent_font *)mdl_arritm(&fonts,0));
+
+ mdl_load_array( &font->mdl, &font->font_variants, "ent_font_variant", alloc);
+ mdl_load_array( &font->mdl, &font->glyphs, "ent_glyph", alloc );
+
+ vg_linear_clear( vg_mem.scratch );
+ mdl_load_mesh_block( &font->mdl, vg_mem.scratch );
+ mdl_load_pack_block( &font->mdl, vg_mem.scratch );
+ mdl_close( &font->mdl );
+
+ vg_acquire_thread_sync();
+ {
+ /* upload mesh */
+ mesh_upload( &font->mesh,
+ font->mdl.verts.data, font->mdl.verts.count,
+ font->mdl.indices.data, font->mdl.indices.count );
+
+ /* upload first texture */
+ font->texture = vg_tex2d_new();
+ mdl_texture *tex0 = mdl_arritm( &font->mdl.textures, 0 );
+
+ vg_tex2d_set_error();
+ vg_tex2d_qoi( mdl_arritm( &font->mdl.pack, tex0->file.pack_offset ),
+ tex0->file.pack_size,
+ mdl_pstr( &font->mdl, tex0->file.pstr_path ));
+ vg_tex2d_nearest();
+ vg_tex2d_repeat();
+ }
+ vg_release_thread_sync();
+}
+
+VG_STATIC void font3d_init(void)
+{
+ shader_model_font_register();
+}
+
+VG_STATIC u32 font3d_find_variant( font3d *font, const char *name )
+{
+ for( u32 i=0; i<mdl_arrcount( &font->font_variants ); i ++ ){
+ ent_font_variant *variant = mdl_arritm( &font->font_variants, i );
+
+ if( !strcmp( mdl_pstr( &font->mdl, variant->name ), name ) ){
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+VG_STATIC void font3d_bind( font3d *font, camera *cam )
+{
+ shader_model_font_use();
+ shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
+ shader_model_font_uTexMain( 1 );
+ glActiveTexture( GL_TEXTURE1 );
+ glBindTexture( GL_TEXTURE_2D, font->texture );
+
+ shader_model_font_uPv( cam->mtx.pv );
+ mesh_bind( &font->mesh );
+}
+
+VG_STATIC
+void font3d_simple_draw( font3d *font, u32 variant_id, const char *text,
+ camera *cam, m4x3f transform )
+{
+ v3f offset;
+ v3_zero( offset );
+
+ m4x4f prev_mtx;
+
+ m4x3_expand( transform, prev_mtx );
+ m4x4_mul( cam->mtx_prev.pv, prev_mtx, prev_mtx );
+
+ shader_model_font_uPvmPrev( prev_mtx );
+ shader_model_font_uMdl( transform );
+
+ for( int i=0;; i++ ){
+ u32 c = text[i];
+ if(!c) break;
+
+ if( c < font->info.glyph_utf32_base ) continue;
+ if( c >= font->info.glyph_utf32_base+font->info.glyph_count) continue;
+
+ u32 index = c - font->info.glyph_utf32_base;
+ index += font->info.glyph_start;
+ index += font->info.glyph_count * variant_id;
+ ent_glyph *glyph = mdl_arritm( &font->glyphs, index );
+
+ if( glyph->indice_count ){
+ shader_model_font_uOffset( offset );
+ mesh_drawn( glyph->indice_start, glyph->indice_count );
+ }
+ offset[0] += glyph->size[0];
+ }
+}
+
+VG_STATIC
+float font3d_string_width( font3d *font, u32 variant_id, const char *text )
+{
+ float width = 0.0f;
+ for( int i=0;; i++ ){
+ u32 c = text[i];
+ if(!c) break;
+
+ if( c < font->info.glyph_utf32_base ) continue;
+ if( c >= font->info.glyph_utf32_base+font->info.glyph_count) continue;
+
+ u32 index = c - font->info.glyph_utf32_base;
+ index += font->info.glyph_start;
+ index += font->info.glyph_count * variant_id;
+ ent_glyph *glyph = mdl_arritm( &font->glyphs, index );
+
+ width += glyph->size[0];
+ }
+
+ return width;
+}
+
+#endif /* FONT_H */
--- /dev/null
+#ifndef SHADER_model_font_H
+#define SHADER_model_font_H
+static void shader_model_font_link(void);
+static void shader_model_font_register(void);
+static struct vg_shader _shader_model_font = {
+ .name = "model_font",
+ .link = shader_model_font_link,
+ .vs =
+{
+.orig_file = "shaders/model_font.vs",
+.static_src =
+"layout (location=0) in vec3 a_co;\n"
+"layout (location=1) in vec3 a_norm;\n"
+"layout (location=2) in vec2 a_uv;\n"
+"layout (location=3) in vec4 a_colour;\n"
+"layout (location=4) in vec4 a_weights;\n"
+"layout (location=5) in ivec4 a_groups;\n"
+"\n"
+"#line 1 1 \n"
+"const float k_motion_lerp_amount = 0.01;\n"
+"\n"
+"#line 2 0 \n"
+"\n"
+"out vec3 aMotionVec0;\n"
+"out vec3 aMotionVec1;\n"
+"\n"
+"void vs_motion_out( vec4 vproj0, vec4 vproj1 )\n"
+"{\n"
+" // This magically solves some artifacting errors!\n"
+" //\n"
+" vproj1 = vproj0*(1.0-k_motion_lerp_amount) + vproj1*k_motion_lerp_amount;\n"
+"\n"
+" aMotionVec0 = vec3( vproj0.xy, vproj0.w );\n"
+" aMotionVec1 = vec3( vproj1.xy, vproj1.w );\n"
+"}\n"
+"\n"
+"#line 9 0 \n"
+"\n"
+"uniform mat4x3 uMdl;\n"
+"uniform mat4 uPv;\n"
+"uniform mat4 uPvmPrev;\n"
+"uniform vec3 uOffset;\n"
+"\n"
+"out vec4 aColour;\n"
+"out vec2 aUv;\n"
+"out vec3 aNorm;\n"
+"out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
+"\n"
+"void main()\n"
+"{\n"
+" vec3 co = a_co+uOffset;\n"
+" vec3 world_pos0 = uMdl * vec4( co, 1.0 );\n"
+" vec4 vproj0 = uPv * vec4( world_pos0, 1.0 );\n"
+" vec4 vproj1 = uPvmPrev * vec4( co, 1.0 );\n"
+"\n"
+" vs_motion_out( vproj0, vproj1 );\n"
+"\n"
+" gl_Position = vproj0;\n"
+" aWorldCo = world_pos0;\n"
+" aColour = a_colour;\n"
+" aUv = a_uv;\n"
+" aNorm = mat3(uMdl) * a_norm;\n"
+" aCo = a_co;\n"
+"}\n"
+""},
+ .fs =
+{
+.orig_file = "shaders/model_font.fs",
+.static_src =
+"out vec4 FragColor;\n"
+"\n"
+"uniform sampler2D uTexMain;\n"
+"uniform vec4 uColour;\n"
+"\n"
+"in vec4 aColour;\n"
+"in vec2 aUv;\n"
+"in vec3 aNorm;\n"
+"in vec3 aCo;\n"
+"\n"
+"#line 1 1 \n"
+"const float k_motion_lerp_amount = 0.01;\n"
+"\n"
+"#line 2 0 \n"
+"\n"
+"layout (location = 1) out vec2 oMotionVec;\n"
+"\n"
+"in vec3 aMotionVec0;\n"
+"in vec3 aMotionVec1;\n"
+"\n"
+"void compute_motion_vectors()\n"
+"{\n"
+" // Write motion vectors\n"
+" vec2 vmotion0 = aMotionVec0.xy / aMotionVec0.z;\n"
+" vec2 vmotion1 = aMotionVec1.xy / aMotionVec1.z;\n"
+"\n"
+" oMotionVec = (vmotion1-vmotion0) * (1.0/k_motion_lerp_amount);\n"
+"}\n"
+"\n"
+"#line 12 0 \n"
+"\n"
+"void main()\n"
+"{\n"
+" compute_motion_vectors();\n"
+" vec4 diffuse = texture( uTexMain, aUv );\n"
+" FragColor = vec4( diffuse.rgb, 1.0 ) * uColour;\n"
+"}\n"
+""},
+};
+
+static GLuint _uniform_model_font_uMdl;
+static GLuint _uniform_model_font_uPv;
+static GLuint _uniform_model_font_uPvmPrev;
+static GLuint _uniform_model_font_uOffset;
+static GLuint _uniform_model_font_uTexMain;
+static GLuint _uniform_model_font_uColour;
+static void shader_model_font_uMdl(m4x3f m){
+ glUniformMatrix4x3fv(_uniform_model_font_uMdl,1,GL_FALSE,(float*)m);
+}
+static void shader_model_font_uPv(m4x4f m){
+ glUniformMatrix4fv(_uniform_model_font_uPv,1,GL_FALSE,(float*)m);
+}
+static void shader_model_font_uPvmPrev(m4x4f m){
+ glUniformMatrix4fv(_uniform_model_font_uPvmPrev,1,GL_FALSE,(float*)m);
+}
+static void shader_model_font_uOffset(v3f v){
+ glUniform3fv(_uniform_model_font_uOffset,1,v);
+}
+static void shader_model_font_uTexMain(int i){
+ glUniform1i(_uniform_model_font_uTexMain,i);
+}
+static void shader_model_font_uColour(v4f v){
+ glUniform4fv(_uniform_model_font_uColour,1,v);
+}
+static void shader_model_font_register(void){
+ vg_shader_register( &_shader_model_font );
+}
+static void shader_model_font_use(void){ glUseProgram(_shader_model_font.id); }
+static void shader_model_font_link(void){
+ _uniform_model_font_uMdl = glGetUniformLocation( _shader_model_font.id, "uMdl" );
+ _uniform_model_font_uPv = glGetUniformLocation( _shader_model_font.id, "uPv" );
+ _uniform_model_font_uPvmPrev = glGetUniformLocation( _shader_model_font.id, "uPvmPrev" );
+ _uniform_model_font_uOffset = glGetUniformLocation( _shader_model_font.id, "uOffset" );
+ _uniform_model_font_uTexMain = glGetUniformLocation( _shader_model_font.id, "uTexMain" );
+ _uniform_model_font_uColour = glGetUniformLocation( _shader_model_font.id, "uColour" );
+}
+#endif /* SHADER_model_font_H */
#include <time.h>
#include "world.h"
#include "world_gate.h"
+#include "font.h"
#if 0
#include "shaders/vblend.h"
{
for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i++ ){
ent_route *route = mdl_arritm( &world->ent_route, i );
- route->active_checkpoint = 0xffffffff;
+ route->active_checkpoint = 0xffff;
}
for( u32 i=0; i<mdl_arrcount( &world->ent_gate ); i++ ){
vg_info( "------- time lap %s -------\n",
mdl_pstr(&world->meta,route->pstr_name) );
- double total_time = 0.0;
+ double start_time = 0.0;
u32 last_version=0;
- int validated = 1;
+
+ u32 valid_count=0;
for( u32 i=0; i<route->checkpoints_count; i++ ){
u32 cpid = route->checkpoints_start+(i+route->active_checkpoint);
rg = mdl_arritm( &world->ent_gate, rg->target );
if( i == 0 )
- total_time = rg->timing_time;
+ start_time = rg->timing_time;
else{
- if( last_version+1 != rg->timing_version )
- validated = 0;
+ if( last_version+1 == rg->timing_version )
+ valid_count ++;
+ else
+ valid_count = 0;
}
last_version = rg->timing_version;
vg_info( "%u %f\n", rg->timing_version, rg->timing_time );
}
+ if( world_global.current_run_version == last_version+1 )
+ valid_count ++;
+ else
+ valid_count = 0;
+
vg_info( "%u %f\n", world_global.current_run_version, world_global.time );
- if( validated && (world_global.current_run_version == last_version+1)){
- total_time = world_global.time - total_time;
- world_routes_local_set_record( world, route, total_time );
+ if( valid_count==route->checkpoints_count ){
+ double lap_time = world_global.time - start_time;
+ world_routes_local_set_record( world, route, lap_time );
}
+
+ route->valid_checkpoints = valid_count+1;
+ route->timing_base = start_time;
+
+ vg_info( "valid: %u\n", valid_count );
vg_info( "----------------------------\n" );
}
ent_route *route = mdl_arritm( &world->ent_route, i );
u32 active_prev = route->active_checkpoint;
- route->active_checkpoint = 0xffffffff;
+ route->active_checkpoint = 0xffff;
for( u32 j=0; j<4; j++ ){
if( dest->routes[j] == i ){
0xff5442f5 };
u32 cc = 0xffcccccc;
- if( route->active_checkpoint != 0xffffffff ){
+ if( route->active_checkpoint != 0xffff ){
cc = colours[i%vg_list_size(colours)];
}
v3_add( (v3f){0.0f,0.1f,0.0f}, start_gate->co[0], p[0] );
p[0][3] = start_gate->ref_count;
- p[0][3] -= (float)start_gate->ref_total * 0.5f;
+ p[0][3] -= (float)start_gate->route_count * 0.5f;
start_gate->ref_count ++;
if( !c0->path_count )
if( i == route->checkpoints_count-1)
p[2][3] -= 1.0f;
- p[2][3] -= (float)collector->ref_total * 0.5f;
+ p[2][3] -= (float)collector->route_count * 0.5f;
//collector->ref_count ++;
}
for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
ent_gate *gate = mdl_arritm( &world->ent_gate, i );
gate->ref_count = 0;
- gate->ref_total = 0;
+ gate->route_count = 0;
}
for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
- start_gate->ref_total ++;
+ start_gate->route_count ++;
if( !c0->path_count )
continue;
for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
ent_route *route = mdl_arritm( &world->ent_route, i );
- int target = route->active_checkpoint == 0xffffffff? 0: 1;
+ int target = route->active_checkpoint == 0xffff? 0: 1;
route->factive = vg_lerpf( route->factive, target, 0.6f*vg.time_delta );
}
}
mdl_draw_submesh( &route->sm );
}
+ /* timers
+ * ---------------------------------------------------- */
+ if( layer_depth == 0 ){
+ font3d_bind( &test_font, cam );
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, i );
+
+ if( route->active_checkpoint != 0xffff ){
+ v4f colour;
+ float brightness = 0.3f + world->ub_lighting.g_day_phase;
+ v3_muls( route->colour, brightness, colour );
+ colour[3] = 1.0f-route->factive;
+
+ shader_model_font_uColour( colour );
+
+ u32 next = route->active_checkpoint+1+layer_depth;
+ next = next % route->checkpoints_count;
+ next += route->checkpoints_start;
+
+ ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next );
+ ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
+
+
+ u32 j=0;
+ for( ; j<4; j++ ){
+ if( gate->routes[j] == i ){
+ break;
+ }
+ }
+ float h0 = 0.8f,
+ h1 = 1.2f,
+ depth = 0.4f,
+ size = 0.4f;
+
+ char text[256];
+
+ if( route->valid_checkpoints >= route->checkpoints_count ){
+ double lap_time = world_global.time - route->timing_base;
+ snprintf( text, 255, "%.1f", lap_time );
+ }
+ else{
+ snprintf( text, 255, "%hu/%hu", route->valid_checkpoints,
+ route->checkpoints_count );
+ }
+
+ float align_r = font3d_string_width( &test_font, 0, text ) * size;
+
+ v3f positions[] = {
+ { -0.92f, h0, depth },
+ { 0.92f - align_r, h0, depth },
+ { -0.92f, h1, depth },
+ { 0.92f - align_r, h1, depth },
+ };
+
+ ent_gate *dest = mdl_arritm( &world->ent_gate, gate->target );
+
+ if( dest->route_count == 1 ){
+ positions[0][0] = -align_r*0.5f;
+ positions[0][1] = h1;
+ }
+
+ m4x3f model;
+ m3x3_copy( gate->to_world, model );
+ float ratio = v3_length(model[0]) / v3_length(model[1]);
+
+ m3x3_scale( model, (v3f){ size, size*ratio, 0.1f } );
+ m4x3_mulv( gate->to_world, positions[j], model[3] );
+
+ font3d_simple_draw( &test_font, 0, text, cam, model );
+ }
+ }
+ }
+
+ /* gate markers
+ * ---------------------------------------------------- */
+
shader_model_gate_use();
shader_model_gate_uPv( cam->mtx.pv );
shader_model_gate_uCam( cam->pos );
for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
ent_route *route = mdl_arritm( &world->ent_route, i );
- if( route->active_checkpoint != 0xffffffff ){
+ if( route->active_checkpoint != 0xffff ){
v4f colour;
float brightness = 0.3f + world->ub_lighting.g_day_phase;
v3_muls( route->colour, brightness, colour );