Blender 2.8 refactor

This commit is contained in:
Jack Andersen 2019-05-07 17:47:34 -10:00
parent 8b1b674a7d
commit 5c59acddf2
48 changed files with 1966 additions and 3460 deletions

View File

@ -1,11 +1,11 @@
# Node Grid Arranger Class # Node Grid Arranger Class
NODE_PADDING = 80 NODE_PADDING = 80
FRAME_NAMES = ['Dynamics','Textures','Combiners','Output'] FRAME_NAMES = ['Textures','Output']
FRAME_WIDTHS = [250, 250, 800, 180] FRAME_WIDTHS = [400, 180]
TOTAL_WIDTH = 0.0 TOTAL_WIDTH = 0.0
for width in FRAME_WIDTHS: for width in FRAME_WIDTHS:
TOTAL_WIDTH += width + NODE_PADDING TOTAL_WIDTH += width + NODE_PADDING
FRAME_COLORS = [(0.6,0.46,0.6),(0.6,0.48,0.44),(0.33,0.48,0.6),(0.53,0.6,0.47)] FRAME_COLORS = [(0.6,0.48,0.44),(0.53,0.6,0.47)]
class Nodegrid: class Nodegrid:
def __init__(self, nodetree, cycles=False): def __init__(self, nodetree, cycles=False):

View File

@ -134,11 +134,11 @@ def load_func(self, context):
def register(): def register():
bpy.utils.register_class(FILE_OT_hecl_patching_save) bpy.utils.register_class(FILE_OT_hecl_patching_save)
bpy.utils.register_class(FILE_OT_hecl_patching_load) bpy.utils.register_class(FILE_OT_hecl_patching_load)
bpy.types.INFO_MT_file_external_data.append(load_func) bpy.types.TOPBAR_MT_file_external_data.append(load_func)
bpy.types.INFO_MT_file_external_data.append(save_func) bpy.types.TOPBAR_MT_file_external_data.append(save_func)
def unregister(): def unregister():
bpy.utils.unregister_class(FILE_OT_hecl_patching_save) bpy.utils.unregister_class(FILE_OT_hecl_patching_save)
bpy.utils.unregister_class(FILE_OT_hecl_patching_load) bpy.utils.unregister_class(FILE_OT_hecl_patching_load)
bpy.types.INFO_MT_file_external_data.remove(load_func) bpy.types.TOPBAR_MT_file_external_data.remove(load_func)
bpy.types.INFO_MT_file_external_data.remove(save_func) bpy.types.TOPBAR_MT_file_external_data.remove(save_func)

View File

@ -2,7 +2,7 @@ bl_info = {
"name": "HECL", "name": "HECL",
"author": "Jack Andersen <jackoalan@gmail.com>", "author": "Jack Andersen <jackoalan@gmail.com>",
"version": (1, 0), "version": (1, 0),
"blender": (2, 78), "blender": (2, 80, 0),
"tracker_url": "https://github.com/AxioDL/hecl/issues/new", "tracker_url": "https://github.com/AxioDL/hecl/issues/new",
"location": "Properties > Scene > HECL", "location": "Properties > Scene > HECL",
"description": "Enables blender to gather meshes, materials, and textures for hecl", "description": "Enables blender to gather meshes, materials, and textures for hecl",
@ -11,11 +11,10 @@ bl_info = {
# Package import # Package import
from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching
Nodegrid = Nodegrid.Nodegrid Nodegrid = Nodegrid.Nodegrid
import bpy, os, sys, struct parent_armature = sact.SACTSubtype.parent_armature
from bpy.app.handlers import persistent import bpy, os, sys, struct, math
from mathutils import Vector from mathutils import Vector
# Appendable list allowing external addons to register additional resource types # Appendable list allowing external addons to register additional resource types
hecl_typeS = [ hecl_typeS = [
('NONE', "None", "Active scene not using HECL", None), ('NONE', "None", "Active scene not using HECL", None),
@ -46,11 +45,33 @@ class hecl_scene_panel(bpy.types.Panel):
type_row = layout.row(align=True) type_row = layout.row(align=True)
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type') type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
if context.scene.hecl_type == 'MESH' or context.scene.hecl_type == 'AREA' or context.scene.hecl_type == 'ACTOR':
sm_row = layout.row(align=True)
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'ORIGINAL')
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'PBR')
for exp_type in hecl_typeS: for exp_type in hecl_typeS:
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]): if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
exp_type[3](self.layout, context) exp_type[3](self.layout, context)
break break
# Light Panel
class hecl_light_panel(bpy.types.Panel):
bl_idname = "DATA_PT_hecl_light"
bl_label = "HECL"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return context.light
def draw(self, context):
layout = self.layout
layout.prop(context.light, 'hecl_falloff_constant')
layout.prop(context.light, 'hecl_falloff_linear')
layout.prop(context.light, 'hecl_falloff_quadratic')
# Blender export-type registration # Blender export-type registration
def register_export_type_enum(): def register_export_type_enum():
@ -107,6 +128,14 @@ def mesh_aabb(writepipebuf):
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2])) writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2])) writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
def shader_model_update(self, context):
value = 0.0
if self.hecl_shader_model == 'PBR':
value = 1.0
for shad in ('RetroShader', 'RetroDynamicShader', 'RetroDynamicAlphaShader', 'RetroDynamicCharacterShader'):
if shad in bpy.data.node_groups and 'NewShaderModel' in bpy.data.node_groups[shad].nodes:
bpy.data.node_groups[shad].nodes['NewShaderModel'].outputs[0].default_value = value
# Load scene callback # Load scene callback
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
@persistent @persistent
@ -114,13 +143,13 @@ def scene_loaded(dummy):
# Hide everything from an external library # Hide everything from an external library
for o in bpy.context.scene.objects: for o in bpy.context.scene.objects:
if o.library: if o.library:
o.hide = True o.hide_set(True)
# Show PATH library objects as wireframes # Show PATH library objects as wireframes
if bpy.context.scene.hecl_type == 'PATH': if bpy.context.scene.hecl_type == 'PATH':
if bpy.context.scene.background_set: if bpy.context.scene.background_set:
for o in bpy.context.scene.background_set.objects: for o in bpy.context.scene.background_set.objects:
o.draw_type = 'WIRE' o.display_type = 'WIRE'
if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects: if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects:
path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj] path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj]
path_obj.show_wire = True path_obj.show_wire = True
@ -134,14 +163,11 @@ def scene_loaded(dummy):
mesh_obj = bpy.data.objects[subtype.linked_mesh] mesh_obj = bpy.data.objects[subtype.linked_mesh]
if subtype.linked_armature in bpy.data.objects: if subtype.linked_armature in bpy.data.objects:
arm_obj = bpy.data.objects[subtype.linked_armature] arm_obj = bpy.data.objects[subtype.linked_armature]
mesh_obj.parent = arm_obj parent_armature(mesh_obj, arm_obj)
mesh_obj.parent_type = 'ARMATURE'
for overlay in subtype.overlays: for overlay in subtype.overlays:
if overlay.linked_mesh in bpy.data.objects: if overlay.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[overlay.linked_mesh] mesh_obj = bpy.data.objects[overlay.linked_mesh]
mesh_obj.parent = arm_obj parent_armature(mesh_obj, arm_obj)
mesh_obj.parent_type = 'ARMATURE'
# Show only the active mesh and action # Show only the active mesh and action
if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context): if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context):
@ -149,6 +175,28 @@ def scene_loaded(dummy):
if sact.SACTAction.SACTAction_load.poll(bpy.context): if sact.SACTAction.SACTAction_load.poll(bpy.context):
bpy.ops.scene.sactaction_load() bpy.ops.scene.sactaction_load()
shader_model_update(bpy.context.scene, bpy.context)
def power_of_distance(context, light, dist):
color = light.color
return dist * dist * context.scene.eevee.light_threshold / max(color[0], max(color[1], color[2]))
def power_of_coefficients(context, light):
epsilon = 1.19e-07
if light.hecl_falloff_linear < epsilon and light.hecl_falloff_quadratic < epsilon:
return 0.0
color = light.color
intens = max(color[0], max(color[1], color[2]))
if light.hecl_falloff_quadratic > epsilon:
if intens <= epsilon:
return 0.0
return power_of_distance(context, light, math.sqrt(intens / (0.0588235 * light.hecl_falloff_quadratic)))
if light.hecl_falloff_linear > epsilon:
return power_of_distance(context, light, intens / (0.0588235 * light.hecl_falloff_linear))
return 0.0
def set_light_falloff(self, context):
self.energy = power_of_coefficients(context, self)
# Registration # Registration
def register(): def register():
@ -161,7 +209,33 @@ def register():
mapu.register() mapu.register()
path.register() path.register()
bpy.utils.register_class(hecl_scene_panel) bpy.utils.register_class(hecl_scene_panel)
bpy.utils.register_class(hecl_light_panel)
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True) bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
bpy.types.Light.hecl_falloff_constant = bpy.props.FloatProperty(
name="HECL Falloff Constant",
description="Constant falloff coefficient",
update=set_light_falloff,
default=1.0,
min=0.0)
bpy.types.Light.hecl_falloff_linear = bpy.props.FloatProperty(
name="HECL Falloff Linear",
description="Linear falloff coefficient",
update=set_light_falloff,
default=0.0,
min=0.0)
bpy.types.Light.hecl_falloff_quadratic = bpy.props.FloatProperty(
name="HECL Falloff Quadratic",
description="Quadratic falloff coefficient",
update=set_light_falloff,
default=0.0,
min=0.0)
bpy.types.Scene.hecl_shader_model = bpy.props.EnumProperty(name="HECL Shader Model",
description="Which shader model to use for rendering",
items=[
('ORIGINAL', "Original", "Close approximation of GameCube materials"),
('PBR', "PBR", "Hybrid PBR materials replacing original reflection")],
update=shader_model_update,
default='ORIGINAL')
bpy.app.handlers.load_post.append(scene_loaded) bpy.app.handlers.load_post.append(scene_loaded)
Patching.register() Patching.register()
@ -172,6 +246,7 @@ def unregister():
srea.unregister() srea.unregister()
path.unregister() path.unregister()
bpy.utils.unregister_class(hecl_scene_panel) bpy.utils.unregister_class(hecl_scene_panel)
bpy.utils.unregister_class(hecl_light_panel)
Patching.unregister() Patching.unregister()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -4,7 +4,7 @@ from mathutils import Quaternion
def draw(layout, context): def draw(layout, context):
if bpy.context.active_object: if bpy.context.active_object:
obj = bpy.context.active_object obj = bpy.context.active_object
layout.label("Widget Settings:", icon='OBJECT_DATA') layout.label(text="Widget Settings:", icon='OBJECT_DATA')
layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type') layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type')
#layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent') #layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent')
row = layout.row(align=True) row = layout.row(align=True)
@ -56,9 +56,9 @@ def draw(layout, context):
layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity') layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity')
layout.prop(obj, 'retro_meter_worker_count', text='Worker Count') layout.prop(obj, 'retro_meter_worker_count', text='Worker Count')
elif obj.retro_widget_type == 'RETRO_LITE': elif obj.retro_widget_type == 'RETRO_LITE':
if obj.data and obj.type == 'LAMP': if obj.data and obj.type == 'LIGHT':
layout.prop(obj.data, 'retro_light_index', text='Index') layout.prop(obj.data, 'retro_light_index', text='Index')
layout.label("Angular Falloff:", icon='LAMP_SPOT') layout.label(text="Angular Falloff:", icon='LIGHT')
row = layout.row(align=True) row = layout.row(align=True)
row.prop(obj.data, 'retro_light_angle_constant', text='Constant') row.prop(obj.data, 'retro_light_angle_constant', text='Constant')
row.prop(obj.data, 'retro_light_angle_linear', text='Linear') row.prop(obj.data, 'retro_light_angle_linear', text='Linear')
@ -201,7 +201,7 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
cutoff = 0.0 cutoff = 0.0
if obj.data.type == 'POINT': if obj.data.type == 'POINT':
type_enum = 4 type_enum = 4
elif obj.data.type == 'HEMI': elif obj.data.type == 'SUN':
type_enum = 2 type_enum = 2
elif obj.data.type == 'SPOT': elif obj.data.type == 'SPOT':
type_enum = 0 type_enum = 0
@ -231,10 +231,10 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
path_hash = 0xffffffff path_hash = 0xffffffff
if len(obj.data.materials): if len(obj.data.materials):
material = obj.data.materials[0] material = obj.data.materials[0]
if len(material.texture_slots) and material.texture_slots[0]: if 'Image Texture' in material.node_tree.nodes:
tex_slot = material.texture_slots[0] image_node = material.node_tree.nodes['Image Texture']
if tex_slot.texture.type == 'IMAGE' and tex_slot.texture.image: if image_node.image:
image = tex_slot.texture.image image = image_node.image
path = bpy.path.abspath(image.filepath) path = bpy.path.abspath(image.filepath)
path_hash = path_hasher.hashpath32(path) path_hash = path_hasher.hashpath32(path)
@ -254,7 +254,7 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
else: else:
buffer += struct.pack('>b', False) buffer += struct.pack('>b', False)
angMtx = angle.to_matrix() * obj.matrix_local.to_3x3() angMtx = angle.to_matrix() @ obj.matrix_local.to_3x3()
buffer += struct.pack('>fffffffffffffffIH', buffer += struct.pack('>fffffffffffffffIH',
obj.matrix_local[0][3], obj.matrix_local[0][3],
obj.matrix_local[1][3], obj.matrix_local[1][3],
@ -384,8 +384,8 @@ def register():
bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100) bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100)
bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1) bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1)
bpy.types.Lamp.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0) bpy.types.Light.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0)
bpy.types.Lamp.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0) bpy.types.Light.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0)
bpy.types.Lamp.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0) bpy.types.Light.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0)
bpy.types.Lamp.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0) bpy.types.Light.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0)

View File

@ -1,5 +1,4 @@
import bpy, bmesh, operator, struct import bpy, bmesh, operator, struct
from mathutils import Vector
# Function to quantize normals to 15-bit precision # Function to quantize normals to 15-bit precision
def quant_norm(n): def quant_norm(n):
@ -15,348 +14,87 @@ def quant_luv(n):
uf[i] = int(uf[i] * 32768) / 32768.0 uf[i] = int(uf[i] * 32768) / 32768.0
return uf.freeze() return uf.freeze()
# Class for building unique sets of vertex attributes for VBO generation # Function to output all mesh attribute values
class VertPool: def write_mesh_attrs(writebuf, bm, rna_loops, use_luv, material_slots):
dlay = None
if len(bm.verts.layers.deform):
dlay = bm.verts.layers.deform[0]
# Initialize hash-unique index for each available attribute clays = []
def __init__(self, bm, rna_loops, use_luv, material_slots): for cl in range(len(bm.loops.layers.color)):
self.bm = bm clays.append(bm.loops.layers.color[cl])
self.rna_loops = rna_loops writebuf(struct.pack('I', len(clays)))
self.material_slots = material_slots
self.pos = {}
self.norm = {}
self.skin = {}
self.color = {}
self.uv = {}
self.luv = {}
self.dlay = None
self.clays = []
self.ulays = []
self.luvlay = None
dlay = None luvlay = None
if len(bm.verts.layers.deform): if use_luv:
dlay = bm.verts.layers.deform[0] luvlay = bm.loops.layers.uv[0]
self.dlay = dlay ulays = []
for ul in range(len(bm.loops.layers.uv)):
ulays.append(bm.loops.layers.uv[ul])
writebuf(struct.pack('I', len(ulays)))
clays = [] # Verts
for cl in range(len(bm.loops.layers.color)): writebuf(struct.pack('I', len(bm.verts)))
clays.append(bm.loops.layers.color[cl]) for v in bm.verts:
self.clays = clays writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
if dlay:
luvlay = None sf = tuple(sorted(v[dlay].items()))
if use_luv: writebuf(struct.pack('I', len(sf)))
luvlay = bm.loops.layers.uv[0] total_len = 0.0
self.luvlay = luvlay for ent in sf:
ulays = [] total_len += ent[1]
for ul in range(len(bm.loops.layers.uv)): for ent in sf:
ulays.append(bm.loops.layers.uv[ul]) writebuf(struct.pack('If', ent[0], ent[1] / total_len))
self.ulays = ulays
# Per-vert pool attributes
for v in bm.verts:
pf = v.co.copy().freeze()
if pf not in self.pos:
self.pos[pf] = len(self.pos)
if not rna_loops:
nf = quant_norm(v.normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
if dlay:
sf = tuple(sorted(v[dlay].items()))
if sf not in self.skin:
self.skin[sf] = len(self.skin)
# Per-loop pool attributes
for f in bm.faces:
lightmapped = f.material_index < len(material_slots) and \
material_slots[f.material_index].material['retro_lightmapped']
for l in f.loops:
if rna_loops:
nf = quant_norm(rna_loops[l.index].normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
for cl in range(len(clays)):
cf = l[clays[cl]].copy().freeze()
if cf not in self.color:
self.color[cf] = len(self.color)
start_uvlay = 0
if use_luv and lightmapped:
start_uvlay = 1
uf = quant_luv(l[luvlay].uv)
if uf not in self.luv:
self.luv[uf] = len(self.luv)
for ul in range(start_uvlay, len(ulays)):
uf = l[ulays[ul]].uv.copy().freeze()
if uf not in self.uv:
self.uv[uf] = len(self.uv)
def write_out(self, writebuf, vert_groups):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
writebuf(struct.pack('I', len(self.norm)))
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
writebuf(struct.pack('II', len(self.clays), len(self.color)))
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
luv_count = 0
if self.luvlay is not None:
luv_count = 1
writebuf(struct.pack('II', luv_count, len(self.luv)))
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
writebuf(struct.pack('I', len(vert_groups)))
for vgrp in vert_groups:
writebuf(struct.pack('I', len(vgrp.name)))
writebuf(vgrp.name.encode())
writebuf(struct.pack('I', len(self.skin)))
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
entries = s[0]
writebuf(struct.pack('I', len(entries)))
if len(entries):
total_len = 0.0
for ent in entries:
total_len += ent[1]
for ent in entries:
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
def write_out_map(self, writebuf):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
def get_pos_idx(self, vert):
pf = vert.co.copy().freeze()
return self.pos[pf]
def get_norm_idx(self, loop):
if self.rna_loops:
nf = quant_norm(self.rna_loops[loop.index].normal)
else: else:
nf = quant_norm(loop.vert.normal) writebuf(struct.pack('I', 0))
return self.norm[nf]
def get_skin_idx(self, vert): # Loops
if not self.dlay: loop_count = 0
return 0 for f in bm.faces:
sf = tuple(sorted(vert[self.dlay].items())) for l in f.loops:
return self.skin[sf] loop_count += 1
writebuf(struct.pack('I', loop_count))
for f in bm.faces:
for l in f.loops:
if rna_loops:
nf = quant_norm(rna_loops[l.index].normal)
else:
nf = quant_norm(l.vert.normal)
writebuf(struct.pack('fff', nf[0], nf[1], nf[2]))
for cl in range(len(clays)):
col = l[clays[cl]]
writebuf(struct.pack('fff', col[0], col[1], col[2]))
for cl in range(len(ulays)):
if luvlay and cl == 0 and material_slots[l.face.material_index].material['retro_lightmapped']:
uv = quant_luv(l[luvlay].uv)
else:
uv = l[ulays[cl]].uv
writebuf(struct.pack('ff', uv[0], uv[1]))
writebuf(struct.pack('IIIII', l.vert.index, l.edge.index, l.face.index,
l.link_loop_next.index, l.link_loop_prev.index))
if l.edge.is_contiguous:
writebuf(struct.pack('II', l.link_loop_radial_next.index, l.link_loop_radial_prev.index))
else:
writebuf(struct.pack('II', 0xffffffff, 0xffffffff))
def get_color_idx(self, loop, cidx): # Edges
cf = loop[self.clays[cidx]].copy().freeze() writebuf(struct.pack('I', len(bm.edges)))
return self.color[cf] for e in bm.edges:
for v in e.verts:
def get_uv_idx(self, loop, uidx): writebuf(struct.pack('I', v.index))
if self.luvlay is not None and uidx == 0: writebuf(struct.pack('I', len(e.link_faces)))
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
uf = quant_luv(loop[self.luvlay].uv)
return self.luv[uf]
uf = loop[self.ulays[uidx]].uv.copy().freeze()
return self.uv[uf]
def loops_contiguous(self, la, lb):
if la.vert != lb.vert:
return False
if self.get_norm_idx(la) != self.get_norm_idx(lb):
return False
for cl in range(len(self.clays)):
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
return False
for ul in range(len(self.ulays)):
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
return False
return True
def splitable_edge(self, edge):
if len(edge.link_faces) < 2:
return False
for v in edge.verts:
found = None
for f in edge.link_faces:
for l in f.loops:
if l.vert == v:
if not found:
found = l
break
else:
if not self.loops_contiguous(found, l):
return True
break
return False
def loop_out(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
for cl in range(len(self.clays)):
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
for ul in range(len(self.ulays)):
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
sp = struct.pack('I', self.get_skin_idx(loop.vert))
writebuf(sp)
def null_loop_out(self, writebuf):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', 0xffffffff))
def loop_out_map(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
def vert_out_map(self, writebuf, vert):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(vert)))
def sort_faces_by_skin_group(dlay, faces):
faces_out = []
done_sg = set()
ref_sg = None
while len(faces_out) < len(faces):
for f in faces:
found = False
for v in f.verts:
sg = tuple(sorted(v[dlay].items()))
if sg not in done_sg:
ref_sg = sg
done_sg.add(ref_sg)
found = True
break
if found:
break
for f in faces:
if f in faces_out:
continue
for v in f.verts:
sg = tuple(sorted(v[dlay].items()))
if sg == ref_sg:
faces_out.append(f)
break
return faces_out
def recursive_faces_islands(dlay, list_out, rem_list, skin_slot_set, skin_slot_count, face):
if face not in rem_list:
return []
if dlay:
for v in face.verts:
sg = tuple(sorted(v[dlay].items()))
if sg not in skin_slot_set:
if skin_slot_count > 0 and len(skin_slot_set) == skin_slot_count:
return False
skin_slot_set.add(sg)
list_out.append(face)
rem_list.remove(face)
next_faces = []
for e in face.edges:
if not e.is_contiguous:
continue
for f in e.link_faces: for f in e.link_faces:
if f == face: writebuf(struct.pack('I', f.index))
continue writebuf(struct.pack('b', e.is_contiguous))
next_faces.append(f)
return next_faces
def strip_next_loop(prev_loop, out_count): # Faces
if out_count & 1: writebuf(struct.pack('I', len(bm.faces)))
radial_loop = prev_loop.link_loop_radial_next for f in bm.faces:
loop = radial_loop.link_loop_prev norm = f.normal
return loop, loop writebuf(struct.pack('fff', norm[0], norm[1], norm[2]))
else: centroid = f.calc_center_bounds()
radial_loop = prev_loop.link_loop_radial_prev writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
loop = radial_loop.link_loop_next writebuf(struct.pack('I', f.material_index))
return loop.link_loop_next, loop for l in f.loops:
writebuf(struct.pack('I', l.index))
def write_out_surface(writebuf, output_mode, vert_pool, island_faces, mat_idx):
# Centroid of surface
centroid = Vector()
for f in island_faces:
centroid += f.calc_center_bounds()
centroid /= len(island_faces)
writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
# Material index
writebuf(struct.pack('I', mat_idx))
# AABB of surface
aabb_min = Vector((9999999, 9999999, 9999999))
aabb_max = Vector((-9999999, -9999999, -9999999))
for f in island_faces:
for v in f.verts:
for c in range(3):
if v.co[c] < aabb_min[c]:
aabb_min[c] = v.co[c]
if v.co[c] > aabb_max[c]:
aabb_max[c] = v.co[c]
writebuf(struct.pack('fff', aabb_min[0], aabb_min[1], aabb_min[2]))
writebuf(struct.pack('fff', aabb_max[0], aabb_max[1], aabb_max[2]))
# Average normal of surface
avg_norm = Vector()
for f in island_faces:
avg_norm += f.normal
avg_norm.normalize()
writebuf(struct.pack('fff', avg_norm[0], avg_norm[1], avg_norm[2]))
# Count estimate (as raw triangles)
writebuf(struct.pack('I', len(island_faces) * 3))
# Verts themselves
if output_mode == 'TRIANGLES':
for f in island_faces:
for l in f.loops:
vert_pool.loop_out(writebuf, l)
elif output_mode == 'TRISTRIPS':
prev_loop_emit = None
while len(island_faces):
sel_lists_local = []
for start_face in island_faces:
for l in start_face.loops:
island_local = list(island_faces)
prev_loop = l.link_loop_next
loop = prev_loop.link_loop_next
sel_list = [l, prev_loop, loop]
island_local.remove(start_face)
while True:
if not prev_loop.edge.is_contiguous or prev_loop.edge.tag:
break
loop, prev_loop = strip_next_loop(prev_loop, len(sel_list))
face = loop.face
if face not in island_local:
break
sel_list.append(loop)
island_local.remove(face)
sel_lists_local.append((sel_list, island_local))
max_count = 0
max_sl = None
max_island_faces = None
for sl in sel_lists_local:
if len(sl[0]) > max_count:
max_count = len(sl[0])
max_sl = sl[0]
max_island_faces = sl[1]
island_faces = max_island_faces
if prev_loop_emit:
vert_pool.null_loop_out(writebuf)
for loop in max_sl:
vert_pool.loop_out(writebuf, loop)
prev_loop_emit = loop
writebuf(struct.pack('B', 0))

View File

@ -1,394 +1,162 @@
import bpy, bpy.path, os.path import bpy, bpy.path, os.path, struct
def get_texmap_idx(tex_list, name): def get_texture_path(image):
for i in range(len(tex_list)): return os.path.normpath(bpy.path.abspath(image.filepath))
if tex_list[i] == name:
return i
retval = len(tex_list)
tex_list.append(name)
return retval
def get_texture_path(name): SHADER_TYPES = {
if name not in bpy.data.textures: 'RetroShader': b'RSHD',
raise RuntimeError('unable to find %s texture' % name) 'RetroDynamicShader': b'RDYN',
tex = bpy.data.textures[name] 'RetroDynamicAlphaShader': b'RDAL',
if tex.type != 'IMAGE': 'RetroDynamicCharacterShader': b'RCHR',
raise RuntimeError('%s texture unsupported for %s, please save as IMAGE' % (tex.type, name)) }
img = tex.image
if not img:
raise RuntimeError('image not set in %s' % name)
return os.path.normpath(bpy.path.abspath(img.filepath))
# Trace color node structure PASS_TYPE = {
def recursive_color_trace(mat_obj, mesh_obj, tex_list, node, socket=None): 'Lightmap': b'LMAP',
'Diffuse': b'DIFF',
'Emissive': b'EMIS',
'Specular': b'SPEC',
'ExtendedSpecular': b'ESPC',
'Reflection': b'REFL',
'IndirectTex': b'INDR',
'Alpha': b'ALPH',
}
if node.type == 'OUTPUT': def write_chunks(writebuf, mat_obj, mesh_obj):
if node.inputs['Color'].is_linked:
return recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs['Color'].links[0].from_node, node.inputs['Color'].links[0].from_socket)
else:
return 'vec3(%g,%g,%g)' % (node.inputs['Color'].default_value[0],
node.inputs['Color'].default_value[1],
node.inputs['Color'].default_value[2])
elif node.type == 'MIX_RGB':
if node.inputs[1].is_linked:
a_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
else:
a_input = 'vec3(%g,%g,%g)' % (node.inputs[1].default_value[0],
node.inputs[1].default_value[1],
node.inputs[1].default_value[2])
if node.inputs[2].is_linked:
b_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[2].links[0].from_node, node.inputs[2].links[0].from_socket)
else:
b_input = 'vec3(%g,%g,%g)' % (node.inputs[2].default_value[0],
node.inputs[2].default_value[1],
node.inputs[2].default_value[2])
if node.blend_type == 'MULTIPLY':
if a_input == 'vec3(1,1,1)':
return b_input
elif b_input == 'vec3(1,1,1)':
return a_input
return '(%s * %s)' % (a_input, b_input)
elif node.blend_type == 'ADD':
if a_input == 'vec3(0,0,0)':
return b_input
elif b_input == 'vec3(0,0,0)':
return a_input
return '(%s + %s)' % (a_input, b_input)
else:
raise RuntimeError("HMDL does not support shaders with '{0}' blending modes".format(node.blend_type))
elif node.type == 'TEXTURE':
if not node.texture or not hasattr(node.texture, 'name'):
raise RuntimeError("HMDL texture nodes must specify a texture object")
if not node.inputs['Vector'].is_linked:
raise RuntimeError("HMDL texture nodes must have a 'Geometry' or 'Group' UV modifier node linked")
# Determine matrix generator type
matrix_str = None
soc_from = node.inputs['Vector'].links[0].from_socket
if soc_from.node.type == 'GROUP':
matrix_str = '%s(%%s' % soc_from.node.node_tree.name
if len(soc_from.node.inputs)-1:
matrix_str += ', '
for s in range(len(soc_from.node.inputs)-1):
soc = soc_from.node.inputs[s+1]
if len(soc.links):
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
if soc.type == 'VALUE':
matrix_str += '%g' % soc.default_value
else:
ncomps = len(soc.default_value)
matrix_str += 'vec%d(' % ncomps
for c in range(ncomps-1):
matrix_str += '%g,' % soc.default_value[c]
matrix_str += '%g)' % soc.default_value[ncomps-1]
if s == len(soc_from.node.inputs)-2:
matrix_str += ')'
else:
matrix_str += ', '
else:
matrix_str += ')'
soc_from = soc_from.node.inputs[0].links[0].from_socket
elif soc_from.node.type == 'GEOMETRY':
pass
else:
raise RuntimeError("HMDL texture nodes must have a 'Geometry', 'Group' UV modifier node linked")
if soc_from.node.type != 'GEOMETRY':
raise RuntimeError("Matrix animator nodes must connect to 'Geometry' node")
# Resolve map and matrix index
node_label = soc_from.node.label
if not matrix_str and node_label.startswith('MTX_'):
matrix_str = 'TexMtx(%%s, %d)' % int(node_label[4:])
if soc_from.name == 'UV':
uv_name = soc_from.node.uv_layer
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
if uv_idx == -1:
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
uvsource_str = 'UV(%d)' % uv_idx
elif soc_from.name == 'Normal':
uvsource_str = 'Normal()'
elif soc_from.name == 'View':
uvsource_str = 'View()'
else:
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
call_name = 'Texture'
if node.label.startswith('Diffuse'):
call_name = 'TextureD'
if socket.name == 'Value':
if matrix_str:
uvsource_str = matrix_str % uvsource_str
return '%s(%d, %s).aaa' % (call_name, get_texmap_idx(tex_list, node.texture.name), uvsource_str)
if socket.name == 'Color':
if matrix_str:
uvsource_str = matrix_str % uvsource_str
return '%s(%d, %s)' % (call_name, get_texmap_idx(tex_list, node.texture.name), uvsource_str)
else:
raise RuntimeError("Only the 'Value' or 'Color' output sockets may be used from Texture nodes")
elif node.type == 'GROUP':
group_str = '%s(' % node.node_tree.name
did_first = False
for input in node.inputs:
if input.type == 'RGBA':
if did_first:
group_str += ', '
if input.is_linked:
group_str += recursive_color_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
else:
group_str += 'vec3(%g,%g,%g)' % (input.default_value[0],
input.default_value[1],
input.default_value[2])
did_first = True
group_str += ')'
return group_str
elif node.type == 'RGB':
if node.label.startswith('DYNAMIC_'):
dynamic_index = int(node.label[8:])
return 'ColorReg(%d)' % dynamic_index
return 'vec3(%g,%g,%g)' % (node.outputs['Color'].default_value[0],
node.outputs['Color'].default_value[1],
node.outputs['Color'].default_value[2])
elif node.type == 'MATERIAL':
if mat_obj.use_shadeless:
return 'vec3(1.0)'
else:
return 'Lighting()'
else:
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
# Trace alpha node structure
def recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
if node.type == 'OUTPUT':
if node.inputs['Alpha'].is_linked:
return recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs['Alpha'].links[0].from_node, node.inputs['Alpha'].links[0].from_socket)
else:
return '%g' % node.inputs['Alpha'].default_value
elif node.type == 'MATH':
if node.inputs[0].is_linked:
a_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[0].links[0].from_node, node.inputs[0].links[0].from_socket)
else:
a_input = '%g' % node.inputs[0].default_value
if node.inputs[1].is_linked:
b_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
else:
b_input = '%g' % node.inputs[1].default_value
if node.operation == 'MULTIPLY':
if a_input == '1':
return b_input
elif b_input == '1':
return a_input
return '(%s * %s)' % (a_input, b_input)
elif node.operation == 'ADD':
if a_input == '0':
return b_input
elif b_input == '0':
return a_input
return '(%s + %s)' % (a_input, b_input)
else:
raise RuntimeError("HMDL does not support shaders with '{0}' blending modes".format(node.operation))
elif node.type == 'TEXTURE':
if not node.texture or not hasattr(node.texture, 'name'):
raise RuntimeError("HMDL texture nodes must specify a texture object")
if not node.inputs['Vector'].is_linked:
raise RuntimeError("HMDL texture nodes must have a 'Geometry' or 'Group' UV modifier node linked")
# Determine matrix generator type
matrix_str = None
soc_from = node.inputs['Vector'].links[0].from_socket
if soc_from.node.type == 'GROUP':
matrix_str = '%s(%%s' % soc_from.node.node_tree.name
if len(soc_from.node.inputs)-1:
matrix_str += ', '
for s in range(len(soc_from.node.inputs)-1):
soc = soc_from.node.inputs[s+1]
if len(soc.links):
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
if soc.type == 'VALUE':
matrix_str += '%g' % soc.default_value
else:
ncomps = len(soc.default_value)
matrix_str += 'vec%d(' % ncomps
for c in range(ncomps-1):
matrix_str += '%g,' % soc.default_value[c]
matrix_str += '%g)' % soc.default_value[ncomps-1]
if s == len(soc_from.node.inputs)-2:
matrix_str += ')'
else:
matrix_str += ', '
else:
matrix_str += ')'
soc_from = soc_from.node.inputs[0].links[0].from_socket
elif soc_from.node.type == 'GEOMETRY':
pass
else:
raise RuntimeError("HMDL texture nodes must have a 'Geometry', 'Group' UV modifier node linked")
if soc_from.node.type != 'GEOMETRY':
raise RuntimeError("Matrix animator nodes must connect to 'Geometry' node")
# Resolve map and matrix index
node_label = soc_from.node.label
if not matrix_str and node_label.startswith('MTX_'):
matrix_str = 'TexMtx(%%s, %d)' % int(node_label[4:])
if soc_from.name == 'UV':
uv_name = soc_from.node.uv_layer
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
if uv_idx == -1:
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
uvsource_str = 'UV(%d)' % uv_idx
elif soc_from.name == 'Normal':
uvsource_str = 'Normal()'
elif soc_from.name == 'View':
uvsource_str = 'View()'
else:
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
if socket.name == 'Value':
if matrix_str:
uvsource_str = matrix_str % uvsource_str
return 'Texture(%d, %s).aaa' % (get_texmap_idx(tex_list, node.texture.name), uvsource_str)
else:
raise RuntimeError("Only the 'Value' output sockets may be used from Texture nodes")
elif node.type == 'GROUP':
group_str = '%s(' % node.node_tree.name
did_first = False
for input in node.inputs:
if input.type == 'VALUE':
if did_first:
group_str += ', '
if input.is_linked:
group_str += recursive_alpha_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
else:
group_str += '%g' % input.default_value
did_first = True
group_str += ')'
return group_str
elif node.type == 'VALUE':
if node.label.startswith('DYNAMIC_'):
dynamic_index = int(node.label[8:])
return 'ColorReg(%d).aaa' % dynamic_index
return '%g' % node.outputs['Value'].default_value
elif node.type == 'MATERIAL':
return '1.0'
else:
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
# Trace indirect node structure
def indirect_trace(mat_obj, mesh_obj, tex_list, node):
if node.type == 'OUTPUT':
if node.inputs['Color'].is_linked:
tex_node = node.inputs['Color'].links[0].from_node
if tex_node.type != 'TEXTURE':
raise RuntimeError("HMDL *requires* that an indirect output node is directly connected to TEXTURE")
if not tex_node.texture or not hasattr(tex_node.texture, 'name'):
raise RuntimeError("HMDL texture nodes must specify a texture object")
get_texmap_idx(tex_list, tex_node.texture.name)
def shader(mat_obj, mesh_obj):
if not mat_obj.use_nodes: if not mat_obj.use_nodes:
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name)) raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'OUTPUT': if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'GROUP':
raise RuntimeError("HMDL *requires* that an OUTPUT shader node named 'Output' is present") raise RuntimeError("HMDL *requires* that an group shader node named 'Output' is present")
# Root (output) node # Root (output) node
output_node = mat_obj.node_tree.nodes['Output'] output_node = mat_obj.node_tree.nodes['Output']
if output_node.node_tree.name not in SHADER_TYPES:
raise RuntimeError("HMDL *requires* one of the RetroShader group nodes for the 'Output' node")
writebuf(SHADER_TYPES[output_node.node_tree.name])
# Trace nodes and build result # Count sockets
tex_list = [] chunk_count = 0
color_trace_result = recursive_color_trace(mat_obj, mesh_obj, tex_list, output_node) for inp in output_node.inputs:
alpha_trace_result = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, output_node) if inp.name in PASS_TYPE:
if inp.is_linked:
chunk_count += 1
else:
# Color pass
color_set = False
if inp.type == 'VALUE':
color_set = bool(inp.default_value)
else:
for comp in inp.default_value:
color_set |= bool(comp)
if color_set:
chunk_count += 1
# Trace indirect reflection texture writebuf(struct.pack('I', chunk_count))
if 'IndirectOutput' in mat_obj.node_tree.nodes:
ind_out_node = mat_obj.node_tree.nodes['IndirectOutput']
indirect_trace(mat_obj, mesh_obj, tex_list, ind_out_node)
# Resolve texture paths # Enumerate sockets
tex_paths = [get_texture_path(name) for name in tex_list] for inp in output_node.inputs:
if inp.name in PASS_TYPE:
pass_fourcc = PASS_TYPE[inp.name]
if inp.is_linked:
socket = inp.links[0].from_socket
node = socket.node
if node.type != 'TEX_IMAGE':
raise RuntimeError("HMDL requires all group node inputs connect to Image Texture nodes")
if mat_obj.game_settings.alpha_blend == 'ALPHA' or mat_obj.game_settings.alpha_blend == 'ALPHA_SORT': if not node.image:
return "HECLAlpha(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths raise RuntimeError("HMDL texture nodes must specify an image object")
elif mat_obj.game_settings.alpha_blend == 'ADD':
return "HECLAdditive(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
else:
return "HECLOpaque(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
# DEBUG operator if not node.inputs['Vector'].is_linked:
import bpy raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
class hecl_shader_operator(bpy.types.Operator):
bl_idname = "scene.hecl_shader"
bl_label = "DEBUG HECL shader maker"
bl_description = "Test shader generation utility"
@classmethod # Determine matrix generator type
def poll(cls, context): tex_coord_source = 0xff
return context.object and context.object.type == 'MESH' uv_anim_type = 0xff
uv_anim_args = []
soc_from = node.inputs['Vector'].links[0].from_socket
def execute(self, context): if soc_from.node.type == 'GROUP':
shad, texs = shader(context.object.active_material, context.object) if soc_from.node.node_tree.name.startswith('RetroUVMode'):
uv_anim_type = int(soc_from.node.node_tree.name[11:12])
vs = bpy.data.texts.new('HECL SHADER') if len(soc_from.node.inputs)-1:
vs.write((shad + '\n')) for s in range(len(soc_from.node.inputs)-1):
for tex in texs: soc = soc_from.node.inputs[s+1]
vs.write(tex + '\n') if len(soc.links):
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
if soc.type == 'VALUE':
uv_anim_args.append(soc.default_value)
else:
uv_anim_args.append(soc.default_value[0])
uv_anim_args.append(soc.default_value[1])
soc_from = soc_from.node.inputs[0].links[0].from_socket
return {'FINISHED'} elif soc_from.node.type == 'UVMAP' or soc_from.node.type == 'TEX_COORD':
pass
else:
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
if soc_from.node.type != 'UVMAP' and soc_from.node.type != 'TEX_COORD':
raise RuntimeError("Matrix animator nodes must connect to 'Texture Coordinate' or 'UV Map' node")
# Resolve map and matrix index
node_label = soc_from.node.label
matrix_idx = None
if node_label.startswith('MTX_'):
matrix_idx = int(node_label[4:])
if soc_from.name == 'UV':
if hasattr(soc_from.node, 'uv_map'):
uv_name = soc_from.node.uv_map
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
if uv_idx == -1:
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
tex_coord_source = uv_idx + 2
else:
tex_coord_source = 2
elif soc_from.name == 'Normal':
tex_coord_source = 1
elif soc_from.name == 'Window':
tex_coord_source = 0
else:
raise RuntimeError("Only the 'UV', 'Normal' and 'Window' sockets may be used from 'Texture Coordinate' nodes")
alpha = False
if socket.name == 'Alpha':
alpha = True
writebuf(b'PASS')
writebuf(pass_fourcc)
path = get_texture_path(node.image)
writebuf(struct.pack('I', len(path)))
writebuf(path.encode())
writebuf(struct.pack('B', tex_coord_source))
writebuf(struct.pack('B', uv_anim_type))
writebuf(struct.pack('I', len(uv_anim_args)))
for arg in uv_anim_args:
writebuf(struct.pack('f', arg))
writebuf(struct.pack('B', alpha))
else:
# Color pass
color_set = False
if inp.type == 'VALUE':
color_set = bool(inp.default_value)
else:
for comp in inp.default_value:
color_set |= bool(comp)
if color_set:
writebuf(b'CLR ')
writebuf(pass_fourcc)
if inp.type == 'VALUE':
writebuf(struct.pack('ffff', inp.default_value, inp.default_value,
inp.default_value, inp.default_value))
else:
writebuf(struct.pack('ffff', inp.default_value[0], inp.default_value[1],
inp.default_value[2], inp.default_value[3]))

View File

@ -1,17 +1,13 @@
import struct, bpy, bmesh import struct, bpy, bmesh
from mathutils import Vector
from . import HMDLShader, HMDLMesh from . import HMDLShader, HMDLMesh
def write_out_material(writebuf, mat, mesh_obj): def write_out_material(writebuf, mat, mesh_obj):
hecl_str, texs = HMDLShader.shader(mat, mesh_obj)
writebuf(struct.pack('I', len(mat.name))) writebuf(struct.pack('I', len(mat.name)))
writebuf(mat.name.encode()) writebuf(mat.name.encode())
writebuf(struct.pack('I', len(hecl_str)))
writebuf(hecl_str.encode()) writebuf(struct.pack('I', mat.pass_index))
writebuf(struct.pack('I', len(texs)))
for tex in texs: HMDLShader.write_chunks(writebuf, mat, mesh_obj)
writebuf(struct.pack('I', len(tex)))
writebuf(tex.encode())
prop_count = 0 prop_count = 0
for prop in mat.items(): for prop in mat.items():
@ -24,22 +20,16 @@ def write_out_material(writebuf, mat, mesh_obj):
writebuf(prop[0].encode()) writebuf(prop[0].encode())
writebuf(struct.pack('i', prop[1])) writebuf(struct.pack('i', prop[1]))
transparent = False blend = 0
if mat.game_settings.alpha_blend == 'ALPHA' or mat.game_settings.alpha_blend == 'ALPHA_SORT': if mat.blend_method == 'BLEND':
transparent = True blend = 1
elif mat.game_settings.alpha_blend == 'ADD': elif mat.blend_method == 'ADD':
transparent = True blend = 2
writebuf(struct.pack('b', int(transparent))) writebuf(struct.pack('I', blend))
# If this returns true, the material geometry will be split into contiguous faces
def should_split_into_contiguous_faces(mat):
return False
#return mat.game_settings.alpha_blend != 'OPAQUE' and \
# 'retro_depth_sort' in mat and mat['retro_depth_sort']
# Takes a Blender 'Mesh' object (not the datablock) # Takes a Blender 'Mesh' object (not the datablock)
# and performs a one-shot conversion process to HMDL # and performs a one-shot conversion process to HMDL
def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False): def cook(writebuf, mesh_obj, use_luv=False):
if mesh_obj.type != 'MESH': if mesh_obj.type != 'MESH':
raise RuntimeError("%s is not a mesh" % mesh_obj.name) raise RuntimeError("%s is not a mesh" % mesh_obj.name)
@ -47,13 +37,13 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
copy_name = mesh_obj.name + "_hmdltri" copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new(copy_name) copy_mesh = bpy.data.meshes.new(copy_name)
copy_obj = bpy.data.objects.new(copy_name, copy_mesh) copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER') copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
copy_mesh = copy_obj.data copy_mesh = copy_obj.data
copy_obj.scale = mesh_obj.scale copy_obj.scale = mesh_obj.scale
bpy.context.scene.objects.link(copy_obj) bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = copy_obj bpy.context.view_layer.objects.active = copy_obj
copy_obj.select = True copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris() bpy.ops.mesh.quads_convert_to_tris()
@ -77,26 +67,9 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
pt = copy_obj.bound_box[6] pt = copy_obj.bound_box[6]
writebuf(struct.pack('fff', pt[0], pt[1], pt[2])) writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
# Create master BMesh and VertPool # Create master BMesh
bm_master = bmesh.new() bm_master = bmesh.new()
bm_master.from_mesh(copy_mesh) bm_master.from_mesh(copy_mesh)
vert_pool = HMDLMesh.VertPool(bm_master, rna_loops, use_luv, mesh_obj.material_slots)
# Tag edges where there are distinctive loops
for e in bm_master.edges:
e.tag = vert_pool.splitable_edge(e)
# Sort materials by pass index first
sorted_material_idxs = []
source_mat_set = set(range(len(mesh_obj.data.materials)))
while len(source_mat_set):
min_mat_idx = source_mat_set.pop()
source_mat_set.add(min_mat_idx)
for mat_idx in source_mat_set:
if mesh_obj.data.materials[mat_idx].pass_index < mesh_obj.data.materials[min_mat_idx].pass_index:
min_mat_idx = mat_idx
sorted_material_idxs.append(min_mat_idx)
source_mat_set.discard(min_mat_idx)
# Generate shaders # Generate shaders
if mesh_obj.data.hecl_material_count > 0: if mesh_obj.data.hecl_material_count > 0:
@ -121,84 +94,14 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
for mat in mesh_obj.data.materials: for mat in mesh_obj.data.materials:
write_out_material(writebuf, mat, mesh_obj) write_out_material(writebuf, mat, mesh_obj)
# Output vert pool # Output attribute lists
vert_pool.write_out(writebuf, mesh_obj.vertex_groups) HMDLMesh.write_mesh_attrs(writebuf, bm_master, rna_loops, use_luv, mesh_obj.material_slots)
dlay = None # Vertex groups
if len(bm_master.verts.layers.deform): writebuf(struct.pack('I', len(mesh_obj.vertex_groups)))
dlay = bm_master.verts.layers.deform[0] for vgrp in mesh_obj.vertex_groups:
writebuf(struct.pack('I', len(vgrp.name)))
# Generate material meshes (if opaque) writebuf(vgrp.name.encode())
for mat_idx in sorted_material_idxs:
mat = mesh_obj.data.materials[mat_idx]
if should_split_into_contiguous_faces(mat):
continue
mat_faces_rem = []
for face in bm_master.faces:
if face.material_index == mat_idx:
mat_faces_rem.append(face)
if dlay:
mat_faces_rem = HMDLMesh.sort_faces_by_skin_group(dlay, mat_faces_rem)
while len(mat_faces_rem):
the_list = []
skin_slot_set = set()
faces = list(mat_faces_rem)
for f in faces:
if dlay:
ret_faces = None
for v in f.verts:
sg = tuple(sorted(v[dlay].items()))
if sg not in skin_slot_set:
if max_skin_banks > 0 and len(skin_slot_set) == max_skin_banks:
ret_faces = False
break
skin_slot_set.add(sg)
if ret_faces == False:
break
the_list.append(f)
mat_faces_rem.remove(f)
writebuf(struct.pack('B', 1))
HMDLMesh.write_out_surface(writebuf, output_mode, vert_pool, the_list, mat_idx)
# Generate island meshes (if transparent)
for mat_idx in sorted_material_idxs:
mat = mesh_obj.data.materials[mat_idx]
if not should_split_into_contiguous_faces(mat):
continue
mat_faces_rem = []
for face in bm_master.faces:
if face.material_index == mat_idx:
mat_faces_rem.append(face)
if dlay:
mat_faces_rem = HMDLMesh.sort_faces_by_skin_group(dlay, mat_faces_rem)
while len(mat_faces_rem):
the_list = []
skin_slot_set = set()
faces = [mat_faces_rem[0]]
while len(faces):
next_faces = []
ret_faces = None
for f in faces:
ret_faces = HMDLMesh.recursive_faces_islands(dlay, the_list,
mat_faces_rem,
skin_slot_set,
max_skin_banks, f)
if ret_faces == False:
break
next_faces.extend(ret_faces)
if ret_faces == False:
break
faces = next_faces
writebuf(struct.pack('B', 1))
HMDLMesh.write_out_surface(writebuf, output_mode, vert_pool, the_list, mat_idx)
# No more surfaces
writebuf(struct.pack('B', 0))
# Enumerate custom props # Enumerate custom props
writebuf(struct.pack('I', len(mesh_obj.keys()))) writebuf(struct.pack('I', len(mesh_obj.keys())))
@ -211,7 +114,7 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
# Delete copied mesh from scene # Delete copied mesh from scene
bm_master.free() bm_master.free()
bpy.context.scene.objects.unlink(copy_obj) #bpy.context.scene.objects.unlink(copy_obj)
bpy.data.objects.remove(copy_obj) bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh) bpy.data.meshes.remove(copy_mesh)
@ -231,13 +134,13 @@ def cookcol(writebuf, mesh_obj):
copy_name = mesh_obj.name + "_hmdltri" copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new(copy_name) copy_mesh = bpy.data.meshes.new(copy_name)
copy_obj = bpy.data.objects.new(copy_name, copy_mesh) copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER') copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
copy_mesh = copy_obj.data copy_mesh = copy_obj.data
copy_obj.scale = mesh_obj.scale copy_obj.scale = mesh_obj.scale
bpy.context.scene.objects.link(copy_obj) bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = copy_obj bpy.context.view_layer.objects.active = copy_obj
copy_obj.select = True copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris() bpy.ops.mesh.quads_convert_to_tris()
@ -320,7 +223,7 @@ def cookcol(writebuf, mesh_obj):
# Send verts # Send verts
writebuf(struct.pack('I', len(copy_mesh.vertices))) writebuf(struct.pack('I', len(copy_mesh.vertices)))
for v in copy_mesh.vertices: for v in copy_mesh.vertices:
xfVert = wmtx * v.co xfVert = wmtx @ v.co
writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2])) writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2]))
# Send edges # Send edges
@ -340,7 +243,7 @@ def cookcol(writebuf, mesh_obj):
writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip)) writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip))
# Delete copied mesh from scene # Delete copied mesh from scene
bpy.context.scene.objects.unlink(copy_obj) #bpy.context.scene.objects.unlink(copy_obj)
bpy.data.objects.remove(copy_obj) bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh) bpy.data.meshes.remove(copy_mesh)
@ -348,13 +251,13 @@ def cookcol(writebuf, mesh_obj):
def draw(layout, context): def draw(layout, context):
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects') layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
if not len(context.scene.hecl_mesh_obj): if not len(context.scene.hecl_mesh_obj):
layout.label("Mesh not specified", icon='ERROR') layout.label(text="Mesh not specified", icon='ERROR')
elif context.scene.hecl_mesh_obj not in context.scene.objects: elif context.scene.hecl_mesh_obj not in context.scene.objects:
layout.label("'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR') layout.label(text="'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR')
else: else:
obj = context.scene.objects[context.scene.hecl_mesh_obj] obj = context.scene.objects[context.scene.hecl_mesh_obj]
if obj.type != 'MESH': if obj.type != 'MESH':
layout.label("'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR') layout.label(text="'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR')
layout.prop(obj.data, 'hecl_active_material') layout.prop(obj.data, 'hecl_active_material')
layout.prop(obj.data, 'hecl_material_count') layout.prop(obj.data, 'hecl_material_count')
@ -398,10 +301,8 @@ def register():
description='Blender Empty Object to export during HECL\'s cook process') description='Blender Empty Object to export during HECL\'s cook process')
bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0) bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0)
bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update) bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update)
bpy.utils.register_class(HMDLShader.hecl_shader_operator)
bpy.utils.register_class(hecl_mesh_operator) bpy.utils.register_class(hecl_mesh_operator)
pass pass
def unregister(): def unregister():
bpy.utils.unregister_class(HMDLShader.hecl_shader_operator)
bpy.utils.unregister_class(hecl_mesh_operator) bpy.utils.unregister_class(hecl_mesh_operator)
pass pass

View File

@ -1,8 +1,233 @@
import bpy, struct, bmesh import bpy, struct, bmesh, operator
from . import hmdl
from mathutils import Vector from mathutils import Vector
VertPool = hmdl.HMDLMesh.VertPool
strip_next_loop = hmdl.HMDLMesh.strip_next_loop # Function to quantize normals to 15-bit precision
def quant_norm(n):
nf = n.copy()
for i in range(3):
nf[i] = int(nf[i] * 16384) / 16384.0
return nf.freeze()
# Function to quantize lightmap UVs to 15-bit precision
def quant_luv(n):
uf = n.copy()
for i in range(2):
uf[i] = int(uf[i] * 32768) / 32768.0
return uf.freeze()
# Class for building unique sets of vertex attributes for VBO generation
class VertPool:
# Initialize hash-unique index for each available attribute
def __init__(self, bm, rna_loops, use_luv, material_slots):
self.bm = bm
self.rna_loops = rna_loops
self.material_slots = material_slots
self.pos = {}
self.norm = {}
self.skin = {}
self.color = {}
self.uv = {}
self.luv = {}
self.dlay = None
self.clays = []
self.ulays = []
self.luvlay = None
dlay = None
if len(bm.verts.layers.deform):
dlay = bm.verts.layers.deform[0]
self.dlay = dlay
clays = []
for cl in range(len(bm.loops.layers.color)):
clays.append(bm.loops.layers.color[cl])
self.clays = clays
luvlay = None
if use_luv:
luvlay = bm.loops.layers.uv[0]
self.luvlay = luvlay
ulays = []
for ul in range(len(bm.loops.layers.uv)):
ulays.append(bm.loops.layers.uv[ul])
self.ulays = ulays
# Per-vert pool attributes
for v in bm.verts:
pf = v.co.copy().freeze()
if pf not in self.pos:
self.pos[pf] = len(self.pos)
if not rna_loops:
nf = quant_norm(v.normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
if dlay:
sf = tuple(sorted(v[dlay].items()))
if sf not in self.skin:
self.skin[sf] = len(self.skin)
# Per-loop pool attributes
for f in bm.faces:
lightmapped = f.material_index < len(material_slots) and \
material_slots[f.material_index].material['retro_lightmapped']
for l in f.loops:
if rna_loops:
nf = quant_norm(rna_loops[l.index].normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
for cl in range(len(clays)):
cf = l[clays[cl]].copy().freeze()
if cf not in self.color:
self.color[cf] = len(self.color)
start_uvlay = 0
if use_luv and lightmapped:
start_uvlay = 1
uf = quant_luv(l[luvlay].uv)
if uf not in self.luv:
self.luv[uf] = len(self.luv)
for ul in range(start_uvlay, len(ulays)):
uf = l[ulays[ul]].uv.copy().freeze()
if uf not in self.uv:
self.uv[uf] = len(self.uv)
def write_out(self, writebuf, vert_groups):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
writebuf(struct.pack('I', len(self.norm)))
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
writebuf(struct.pack('II', len(self.clays), len(self.color)))
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
luv_count = 0
if self.luvlay is not None:
luv_count = 1
writebuf(struct.pack('II', luv_count, len(self.luv)))
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
writebuf(struct.pack('I', len(vert_groups)))
for vgrp in vert_groups:
writebuf(struct.pack('I', len(vgrp.name)))
writebuf(vgrp.name.encode())
writebuf(struct.pack('I', len(self.skin)))
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
entries = s[0]
writebuf(struct.pack('I', len(entries)))
if len(entries):
total_len = 0.0
for ent in entries:
total_len += ent[1]
for ent in entries:
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
def write_out_map(self, writebuf):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
def get_pos_idx(self, vert):
pf = vert.co.copy().freeze()
return self.pos[pf]
def get_norm_idx(self, loop):
if self.rna_loops:
nf = quant_norm(self.rna_loops[loop.index].normal)
else:
nf = quant_norm(loop.vert.normal)
return self.norm[nf]
def get_skin_idx(self, vert):
if not self.dlay:
return 0
sf = tuple(sorted(vert[self.dlay].items()))
return self.skin[sf]
def get_color_idx(self, loop, cidx):
cf = loop[self.clays[cidx]].copy().freeze()
return self.color[cf]
def get_uv_idx(self, loop, uidx):
if self.luvlay is not None and uidx == 0:
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
uf = quant_luv(loop[self.luvlay].uv)
return self.luv[uf]
uf = loop[self.ulays[uidx]].uv.copy().freeze()
return self.uv[uf]
def loops_contiguous(self, la, lb):
if la.vert != lb.vert:
return False
if self.get_norm_idx(la) != self.get_norm_idx(lb):
return False
for cl in range(len(self.clays)):
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
return False
for ul in range(len(self.ulays)):
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
return False
return True
def splitable_edge(self, edge):
if len(edge.link_faces) < 2:
return False
for v in edge.verts:
found = None
for f in edge.link_faces:
for l in f.loops:
if l.vert == v:
if not found:
found = l
break
else:
if not self.loops_contiguous(found, l):
return True
break
return False
def loop_out(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
for cl in range(len(self.clays)):
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
for ul in range(len(self.ulays)):
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
sp = struct.pack('I', self.get_skin_idx(loop.vert))
writebuf(sp)
def null_loop_out(self, writebuf):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', 0xffffffff))
def loop_out_map(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
def vert_out_map(self, writebuf, vert):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(vert)))
def strip_next_loop(prev_loop, out_count):
if out_count & 1:
radial_loop = prev_loop.link_loop_radial_next
loop = radial_loop.link_loop_prev
return loop, loop
else:
radial_loop = prev_loop.link_loop_radial_prev
loop = radial_loop.link_loop_next
return loop.link_loop_next, loop
def recursive_faces_islands(list_out, rem_list, face): def recursive_faces_islands(list_out, rem_list, face):
if face not in rem_list: if face not in rem_list:
@ -34,13 +259,13 @@ def cook(writebuf, mesh_obj):
copy_name = mesh_obj.name + "_hmdltri" copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new(copy_name) copy_mesh = bpy.data.meshes.new(copy_name)
copy_obj = bpy.data.objects.new(copy_name, copy_mesh) copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER') copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
copy_mesh = copy_obj.data copy_mesh = copy_obj.data
copy_obj.scale = mesh_obj.scale copy_obj.scale = mesh_obj.scale
bpy.context.scene.objects.link(copy_obj) bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = copy_obj bpy.context.view_layer.objects.active = copy_obj
copy_obj.select = True copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris() bpy.ops.mesh.quads_convert_to_tris()

View File

@ -1,5 +1,6 @@
import bpy, bgl, sys, bmesh, struct import bpy, gpu, sys, bmesh, struct
from mathutils import Vector from mathutils import Vector
from gpu_extras.batch import batch_for_shader
# Convenience class that automatically brings active edit mesh's face into scope for get/set # Convenience class that automatically brings active edit mesh's face into scope for get/set
class HeightRef: class HeightRef:
@ -44,14 +45,6 @@ def set_height(self, val):
if ar.type == 'VIEW_3D': if ar.type == 'VIEW_3D':
ar.tag_redraw() ar.tag_redraw()
# Edit panel
def draw(layout, context):
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='WIRE')
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon='MANIPUL')
if HeightRef().ready:
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
# Simple AABB class # Simple AABB class
class AABB: class AABB:
def __init__(self): def __init__(self):
@ -226,34 +219,39 @@ def cook(writebuf, mesh_obj):
writebuf(struct.pack('I', len(ba))) writebuf(struct.pack('I', len(ba)))
writebuf(ba) writebuf(ba)
try:
line_shader = gpu.shader.from_builtin('3D_FLAT_COLOR')
except:
pass
# Line draw helper # Line draw helper
def draw_line_3d(color, start, end): def draw_line_3d(pos_vbo, color_vbo, color, start, end):
bgl.glColor4f(*color) pos_vbo.append(start)
bgl.glBegin(bgl.GL_LINES) pos_vbo.append(end)
bgl.glVertex3f(*start) color_vbo.append(color)
bgl.glVertex3f(*end) color_vbo.append(color)
# Draw RNA polygon # Draw RNA polygon
def draw_poly(p, obj, obj_mtx, height, top_color, side_color): def draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color):
for ek in p.edge_keys: for ek in p.edge_keys:
co0 = obj_mtx * obj.data.vertices[ek[0]].co co0 = obj_mtx @ obj.data.vertices[ek[0]].co
co1 = obj_mtx * obj.data.vertices[ek[1]].co co1 = obj_mtx @ obj.data.vertices[ek[1]].co
draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)), draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
co1 + Vector((0.0, 0.0, height))) co1 + Vector((0.0, 0.0, height)))
for vk in p.vertices: for vk in p.vertices:
co = obj_mtx * obj.data.vertices[vk].co co = obj_mtx @ obj.data.vertices[vk].co
draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height))) draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
# Draw bmesh face # Draw bmesh face
def draw_face(f, obj_mtx, height, top_color, side_color): def draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color):
for e in f.edges: for e in f.edges:
co0 = obj_mtx * e.verts[0].co co0 = obj_mtx @ e.verts[0].co
co1 = obj_mtx * e.verts[1].co co1 = obj_mtx @ e.verts[1].co
draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)), draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
co1 + Vector((0.0, 0.0, height))) co1 + Vector((0.0, 0.0, height)))
for v in f.verts: for v in f.verts:
co = obj_mtx * v.co co = obj_mtx @ v.co
draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height))) draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
# Viewport hook callback # Viewport hook callback
def draw_callback_3d(self, context): def draw_callback_3d(self, context):
@ -275,6 +273,9 @@ def draw_callback_3d(self, context):
if 'Height' in obj.data.polygon_layers_float: if 'Height' in obj.data.polygon_layers_float:
height_lay = obj.data.polygon_layers_float['Height'] height_lay = obj.data.polygon_layers_float['Height']
pos_vbo = []
color_vbo = []
# Deselected colors # Deselected colors
top_color = (0.0, 0.0, 1.0, 0.7) top_color = (0.0, 0.0, 1.0, 0.7)
side_color = (1.0, 0.0, 0.0, 0.7) side_color = (1.0, 0.0, 0.0, 0.7)
@ -286,33 +287,34 @@ def draw_callback_3d(self, context):
if selected: if selected:
continue continue
height = f[height_lay] height = f[height_lay]
draw_face(f, obj_mtx, height, top_color, side_color) draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
else: else:
for p in obj.data.polygons: for p in obj.data.polygons:
height = 1.0 height = 1.0
if height_lay is not None: if height_lay is not None:
height = height_lay.data[p.index].value height = height_lay.data[p.index].value
draw_poly(p, obj, obj_mtx, height, top_color, side_color) draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color)
bgl.glEnd()
# Selected colors # Selected colors
if bm is not None: if bm is not None:
top_color = (1.0, 0.0, 1.0, 0.7) top_color = (1.0, 0.0, 1.0, 0.7)
side_color = (0.0, 1.0, 0.0, 0.7) side_color = (0.0, 1.0, 0.0, 0.7)
# Avoid z-fighting on selected lines # Avoid z-fighting on selected lines
bgl.glDepthRange(-0.001, 0.999) #bgl.glDepthRange(-0.001, 0.999)
for f in bm.faces: for f in bm.faces:
if height_lay is not None: if height_lay is not None:
selected = f.select selected = f.select
if not selected: if not selected:
continue continue
height = f[height_lay] height = f[height_lay]
draw_face(f, obj_mtx, height, top_color, side_color) draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
bgl.glEnd() #bgl.glEnd()
bgl.glDepthRange(0.0, 1.0) #bgl.glDepthRange(0.0, 1.0)
line_shader.bind()
batch = batch_for_shader(line_shader, 'LINES', {"pos": pos_vbo, "color": color_vbo})
batch.draw(line_shader)
# restore opengl defaults
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
# Toggle height viz button # Toggle height viz button
class PathHeightDrawOperator(bpy.types.Operator): class PathHeightDrawOperator(bpy.types.Operator):
@ -346,17 +348,29 @@ class PathBackgroundWireframeOperator(bpy.types.Operator):
if context.scene.background_set: if context.scene.background_set:
to_wire = False to_wire = False
for o in context.scene.background_set.objects: for o in context.scene.background_set.objects:
if o.draw_type != 'WIRE': if o.display_type != 'WIRE':
to_wire = True to_wire = True
break break
if to_wire: if to_wire:
for o in context.scene.background_set.objects: for o in context.scene.background_set.objects:
o.draw_type = 'WIRE' o.display_type = 'WIRE'
else: else:
for o in context.scene.background_set.objects: for o in context.scene.background_set.objects:
o.draw_type = 'TEXTURED' o.display_type = 'TEXTURED'
return {'FINISHED'} return {'FINISHED'}
# Edit panel
def draw(layout, context):
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='SHADING_WIRE')
if PathHeightDrawOperator._handle_3d:
icon = 'HIDE_OFF'
else:
icon = 'HIDE_ON'
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon=icon)
if HeightRef().ready:
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
# Registration # Registration
def register(): def register():
bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask') bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')

View File

@ -4,7 +4,6 @@ in an interleaved, sparse array for use by the runtime
''' '''
import re import re
import hashlib
import struct import struct
import mathutils import mathutils

View File

@ -15,7 +15,7 @@ def action_type_update(self, context):
# Actor action class # Actor action class
class SACTAction(bpy.types.PropertyGroup): class SACTAction(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Action Name") name: bpy.props.StringProperty(name="Action Name")
# Panel draw # Panel draw
def draw(layout, context): def draw(layout, context):
@ -30,8 +30,8 @@ def draw(layout, context):
row.template_list("UI_UL_list", "SCENE_UL_SACTActions", row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
actor_data, 'actions', actor_data, 'active_action') actor_data, 'actions', actor_data, 'active_action')
col = row.column(align=True) col = row.column(align=True)
col.operator("scene.sactaction_add", icon="ZOOMIN", text="") col.operator("scene.sactaction_add", icon="ADD", text="")
col.operator("scene.sactaction_remove", icon="ZOOMOUT", text="") col.operator("scene.sactaction_remove", icon="REMOVE", text="")
if len(actor_data.actions) and actor_data.active_action >= 0: if len(actor_data.actions) and actor_data.active_action >= 0:
action = actor_data.actions[actor_data.active_action] action = actor_data.actions[actor_data.active_action]
@ -48,7 +48,7 @@ def draw(layout, context):
# Validate # Validate
if linked_action is None: if linked_action is None:
layout.label("Source action not set", icon='ERROR') layout.label(text="Source action not set", icon='ERROR')
else: else:
#layout.prop(linked_action, 'hecl_index', text="Index") #layout.prop(linked_action, 'hecl_index', text="Index")
#layout.prop(linked_action, 'hecl_anim_props', text="Props") #layout.prop(linked_action, 'hecl_anim_props', text="Props")

View File

@ -9,28 +9,26 @@ def active_subtype_update(self, context):
# Actor subtype overlay class # Actor subtype overlay class
class SACTSubtypeOverlay(bpy.types.PropertyGroup): class SACTSubtypeOverlay(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Overlay Name") name: bpy.props.StringProperty(name="Overlay Name")
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_overlay = bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update) show_overlay: bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update)
# Actor attachment class # Actor attachment class
class SACTAttachment(bpy.types.PropertyGroup): class SACTAttachment(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Attachment Name") name: bpy.props.StringProperty(name="Attachment Name")
linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update) linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_attachment = bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update) show_attachment: bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update)
# Actor subtype class # Actor subtype class
class SACTSubtype(bpy.types.PropertyGroup): class SACTSubtype(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Actor Mesh Name") name: bpy.props.StringProperty(name="Actor Mesh Name")
linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update) linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_mesh = bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update) show_mesh: bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update)
overlays =\ overlays: bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List")
bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List") active_overlay: bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
active_overlay =\
bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
# Panel draw # Panel draw
def draw(layout, context): def draw(layout, context):
@ -45,8 +43,8 @@ def draw(layout, context):
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes", row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
actor_data, 'subtypes', actor_data, 'active_subtype') actor_data, 'subtypes', actor_data, 'active_subtype')
col = row.column(align=True) col = row.column(align=True)
col.operator("scene.sactsubtype_add", icon="ZOOMIN", text="") col.operator("scene.sactsubtype_add", icon="ADD", text="")
col.operator("scene.sactsubtype_remove", icon="ZOOMOUT", text="") col.operator("scene.sactsubtype_remove", icon="REMOVE", text="")
if len(actor_data.subtypes) and actor_data.active_subtype >= 0: if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
subtype = actor_data.subtypes[actor_data.active_subtype] subtype = actor_data.subtypes[actor_data.active_subtype]
@ -67,9 +65,9 @@ def draw(layout, context):
# Validate # Validate
if linked_armature is None: if linked_armature is None:
layout.label("Source armature not set", icon='ERROR') layout.label(text="Source armature not set", icon='ERROR')
elif linked_armature is not None and linked_armature.type != 'ARMATURE': elif linked_armature is not None and linked_armature.type != 'ARMATURE':
layout.label("Source armature is not an 'ARMATURE'", icon='ERROR') layout.label(text="Source armature is not an 'ARMATURE'", icon='ERROR')
# Link external mesh search # Link external mesh search
@ -80,13 +78,13 @@ def draw(layout, context):
layout.prop(subtype, 'show_mesh', text="Show Mesh") layout.prop(subtype, 'show_mesh', text="Show Mesh")
# Mesh overlays # Mesh overlays
layout.label("Overlay Meshes:") layout.label(text="Overlay Meshes:")
row = layout.row() row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays", row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays",
subtype, 'overlays', subtype, 'active_overlay') subtype, 'overlays', subtype, 'active_overlay')
col = row.column(align=True) col = row.column(align=True)
col.operator("scene.sactsubtypeoverlay_add", icon="ZOOMIN", text="") col.operator("scene.sactsubtypeoverlay_add", icon="ADD", text="")
col.operator("scene.sactsubtypeoverlay_remove", icon="ZOOMOUT", text="") col.operator("scene.sactsubtypeoverlay_remove", icon="REMOVE", text="")
overlay_mesh = None overlay_mesh = None
if len(subtype.overlays) and subtype.active_overlay >= 0: if len(subtype.overlays) and subtype.active_overlay >= 0:
@ -98,13 +96,13 @@ def draw(layout, context):
layout.prop(overlay, 'show_overlay', text="Show Overlay") layout.prop(overlay, 'show_overlay', text="Show Overlay")
# Mesh attachments # Mesh attachments
layout.label("Attachment Meshes:") layout.label(text="Attachment Meshes:")
row = layout.row() row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments", row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments",
actor_data, 'attachments', actor_data, 'active_attachment') actor_data, 'attachments', actor_data, 'active_attachment')
col = row.column(align=True) col = row.column(align=True)
col.operator("scene.sactattachment_add", icon="ZOOMIN", text="") col.operator("scene.sactattachment_add", icon="ADD", text="")
col.operator("scene.sactattachment_remove", icon="ZOOMOUT", text="") col.operator("scene.sactattachment_remove", icon="REMOVE", text="")
attachment_armature = linked_armature attachment_armature = linked_armature
attachment_mesh = None attachment_mesh = None
@ -121,27 +119,27 @@ def draw(layout, context):
# Validate # Validate
if linked_mesh is None: if linked_mesh is None:
layout.label("Source mesh not set", icon='ERROR') layout.label(text="Source mesh not set", icon='ERROR')
elif linked_mesh.type != 'MESH': elif linked_mesh.type != 'MESH':
layout.label("Source mesh not 'MESH'", icon='ERROR') layout.label(text="Source mesh not 'MESH'", icon='ERROR')
elif linked_armature is not None and linked_mesh not in linked_armature.children: elif linked_armature is not None and linked_mesh not in linked_armature.children:
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR') layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
elif linked_mesh.parent_type != 'ARMATURE': elif linked_mesh.parent_type != 'ARMATURE':
layout.label("Source mesh not 'ARMATURE' parent type", icon='ERROR') layout.label(text="Source mesh not 'ARMATURE' parent type", icon='ERROR')
if overlay_mesh: if overlay_mesh:
if overlay_mesh.type != 'MESH': if overlay_mesh.type != 'MESH':
layout.label("Overlay mesh not 'MESH'", icon='ERROR') layout.label(text="Overlay mesh not 'MESH'", icon='ERROR')
elif overlay_mesh.parent_type != 'ARMATURE': elif overlay_mesh.parent_type != 'ARMATURE':
layout.label("Overlay mesh not 'ARMATURE' parent type", icon='ERROR') layout.label(text="Overlay mesh not 'ARMATURE' parent type", icon='ERROR')
if attachment_mesh: if attachment_mesh:
if attachment_mesh.type != 'MESH': if attachment_mesh.type != 'MESH':
layout.label("Attachment mesh not 'MESH'", icon='ERROR') layout.label(text="Attachment mesh not 'MESH'", icon='ERROR')
elif attachment_armature is not None and attachment_mesh not in attachment_armature.children: elif attachment_armature is not None and attachment_mesh not in attachment_armature.children:
layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR') layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR')
elif attachment_mesh.parent_type != 'ARMATURE': elif attachment_mesh.parent_type != 'ARMATURE':
layout.label("Attachment mesh not 'ARMATURE' parent type", icon='ERROR') layout.label(text="Attachment mesh not 'ARMATURE' parent type", icon='ERROR')
# Subtype 'add' operator # Subtype 'add' operator
@ -195,6 +193,17 @@ class SACTSubtype_remove(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
def parent_armature(mesh_obj, arm_obj):
mesh_obj.parent = None
for mod in mesh_obj.modifiers:
if mod.type == 'ARMATURE':
mod.object = arm_obj
return
mod = mesh_obj.modifiers.new('Parent', 'ARMATURE')
mod.object = arm_obj
#mesh_obj.parent = arm_obj
#mesh_obj.parent_type = 'ARMATURE'
# Subtype 'load' operator # Subtype 'load' operator
class SACTSubtype_load(bpy.types.Operator): class SACTSubtype_load(bpy.types.Operator):
bl_idname = "scene.sactsubtype_load" bl_idname = "scene.sactsubtype_load"
@ -222,49 +231,46 @@ class SACTSubtype_load(bpy.types.Operator):
# Hide armature children # Hide armature children
for object in linked_armature.children: for object in linked_armature.children:
if object.name in context.scene.objects: if object.name in context.scene.objects:
object.hide = True object.hide_set(True)
# Hide all meshes (incl overlays) # Hide all meshes (incl overlays)
for subtype_data in actor_data.subtypes: for subtype_data in actor_data.subtypes:
if subtype_data.linked_mesh in bpy.data.objects: if subtype_data.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[subtype_data.linked_mesh] mesh = bpy.data.objects[subtype_data.linked_mesh]
if mesh.name in context.scene.objects: if mesh.name in context.scene.objects:
mesh.hide = True mesh.hide_set(True)
for overlay in subtype_data.overlays: for overlay in subtype_data.overlays:
if overlay.linked_mesh in bpy.data.objects: if overlay.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[overlay.linked_mesh] mesh = bpy.data.objects[overlay.linked_mesh]
if mesh.name in context.scene.objects: if mesh.name in context.scene.objects:
mesh.hide = True mesh.hide_set(True)
# Hide/Show selected attachment meshes # Hide/Show selected attachment meshes
for attachment in actor_data.attachments: for attachment in actor_data.attachments:
if attachment.linked_mesh in bpy.data.objects: if attachment.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[attachment.linked_mesh] mesh_obj = bpy.data.objects[attachment.linked_mesh]
if mesh_obj.name in context.scene.objects: if mesh_obj.name in context.scene.objects:
mesh_obj.hide = not attachment.show_attachment mesh_obj.hide_set(not attachment.show_attachment)
attachment_armature = linked_armature attachment_armature = linked_armature
if attachment.linked_armature in bpy.data.objects: if attachment.linked_armature in bpy.data.objects:
attachment_armature = bpy.data.objects[attachment.linked_armature] attachment_armature = bpy.data.objects[attachment.linked_armature]
if mesh_obj != attachment_armature: if mesh_obj != attachment_armature:
mesh_obj.parent = attachment_armature parent_armature(mesh_obj, attachment_armature)
mesh_obj.parent_type = 'ARMATURE'
# Show only the chosen subtype (and selected overlays) # Show only the chosen subtype (and selected overlays)
if subtype.linked_mesh in bpy.data.objects: if subtype.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[subtype.linked_mesh] mesh_obj = bpy.data.objects[subtype.linked_mesh]
if subtype.show_mesh: if subtype.show_mesh:
mesh_obj.hide = False mesh_obj.hide_set(False)
if mesh_obj != linked_armature: if mesh_obj != linked_armature:
mesh_obj.parent = linked_armature parent_armature(mesh_obj, linked_armature)
mesh_obj.parent_type = 'ARMATURE'
for overlay in subtype.overlays: for overlay in subtype.overlays:
if overlay.linked_mesh in bpy.data.objects: if overlay.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[overlay.linked_mesh] mesh_obj = bpy.data.objects[overlay.linked_mesh]
if overlay.show_overlay: if overlay.show_overlay:
mesh_obj.hide = False mesh_obj.hide_set(False)
if mesh_obj != linked_armature: if mesh_obj != linked_armature:
mesh_obj.parent = linked_armature parent_armature(mesh_obj, linked_armature)
mesh_obj.parent_type = 'ARMATURE'
return {'FINISHED'} return {'FINISHED'}

View File

@ -1,35 +1,24 @@
from . import SACTSubtype, SACTAction, ANIM from . import SACTSubtype, SACTAction, ANIM
from .. import hmdl
import bpy import bpy
import bpy.path import bpy.path
import re import re
import os.path
import posixpath
import struct import struct
from mathutils import Vector, Quaternion, Euler from mathutils import Vector, Quaternion, Euler
# Actor data class # Actor data class
class SACTData(bpy.types.PropertyGroup): class SACTData(bpy.types.PropertyGroup):
subtypes =\ subtypes: bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List") active_subtype: bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
active_subtype =\ show_subtypes: bpy.props.BoolProperty()
bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
show_subtypes =\
bpy.props.BoolProperty()
attachments = \ attachments: bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List")
bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List") active_attachment: bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
active_attachment = \
bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
actions =\ actions: bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List") active_action: bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
active_action =\ show_actions: bpy.props.BoolProperty()
bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
show_actions =\
bpy.props.BoolProperty()
# Regex RNA path matchers # Regex RNA path matchers
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale') scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')

View File

@ -3,7 +3,7 @@ from bpy.app.handlers import persistent
from mathutils import Quaternion, Color from mathutils import Quaternion, Color
import math import math
import os.path import os.path
from .. import Nodegrid, swld from .. import swld
# Preview update func (for lighting preview) # Preview update func (for lighting preview)
def preview_update(self, context): def preview_update(self, context):
@ -13,30 +13,26 @@ def preview_update(self, context):
# Original Lightmaps # Original Lightmaps
if area_data.lightmap_mode == 'ORIGINAL': if area_data.lightmap_mode == 'ORIGINAL':
for material in bpy.data.materials: for material in bpy.data.materials:
if material.hecl_lightmap: if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
material.use_shadeless = False lm_node = material.node_tree.nodes['Lightmap']
# Reference original game lightmaps # Reference original game lightmaps
if material.hecl_lightmap in bpy.data.textures: if material.hecl_lightmap in bpy.data.images:
img_name = material.hecl_lightmap lm_node.image = bpy.data.images[material.hecl_lightmap]
if img_name in bpy.data.images: else:
bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name] lm_node.image = None
else:
bpy.data.textures[material.hecl_lightmap].image = None
# Cycles Lightmaps # Cycles Lightmaps
elif area_data.lightmap_mode == 'CYCLES': elif area_data.lightmap_mode == 'CYCLES':
for material in bpy.data.materials: for material in bpy.data.materials:
if material.hecl_lightmap: if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
material.use_shadeless = False lm_node = material.node_tree.nodes['Lightmap']
# Reference newly-generated lightmaps # Reference newly-generated lightmaps
if material.hecl_lightmap in bpy.data.textures: img_name = material.hecl_lightmap + '_CYCLES'
img_name = material.hecl_lightmap + '_CYCLES' if img_name in bpy.data.images:
if img_name in bpy.data.images: lm_node.image = bpy.data.images[img_name]
bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name] else:
else: lm_node.image = None
bpy.data.textures[material.hecl_lightmap].image = None
# White Lightmaps # White Lightmaps
elif area_data.lightmap_mode == 'NONE': elif area_data.lightmap_mode == 'NONE':
img_name = 'NONE' img_name = 'NONE'
@ -51,13 +47,11 @@ def preview_update(self, context):
img.file_format = 'PNG' img.file_format = 'PNG'
for material in bpy.data.materials: for material in bpy.data.materials:
if material.hecl_lightmap: if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
material.use_shadeless = False lm_node = material.node_tree.nodes['Lightmap']
# Reference NONE # Reference NONE
if material.hecl_lightmap in bpy.data.textures: lm_node.image = img
bpy.data.textures[material.hecl_lightmap].image = img
# Update lightmap output-resolution # Update lightmap output-resolution
def set_lightmap_resolution(self, context): def set_lightmap_resolution(self, context):
@ -153,17 +147,17 @@ def set_adjacent_area(self, context):
adjacent = dock_idx >= 0 adjacent = dock_idx >= 0
if len(context.scene.render.layers): if len(context.scene.render.layers):
context.scene.render.layers[0].use_sky = not adjacent context.scene.view_layers[0].use_sky = not adjacent
# Remove linked lamps and show/hide locals # Remove linked lamps and show/hide locals
for obj in bpy.data.objects: for obj in bpy.data.objects:
if obj.library is not None and (obj.type == 'LAMP' or obj.type == 'MESH'): if obj.library is not None and (obj.type == 'LIGHT' or obj.type == 'MESH'):
try: try:
context.scene.objects.unlink(obj) context.scene.collection.children.unlink(obj)
except: except:
pass pass
continue continue
if obj.type == 'LAMP': if obj.type == 'LIGHT':
obj.hide_render = adjacent obj.hide_render = adjacent
# Remove linked scenes # Remove linked scenes
@ -200,8 +194,8 @@ def set_adjacent_area(self, context):
bpy.data.scenes.remove(other_scene) bpy.data.scenes.remove(other_scene)
return return
for obj in other_scene.objects: for obj in other_scene.objects:
if (obj.type == 'LAMP' or obj.type == 'MESH') and obj.layers[0]: if (obj.type == 'LIGHT' or obj.type == 'MESH') and obj.layers[0]:
context.scene.objects.link(obj) context.scene.collection.objects.link(obj)
obj.hide_render = False obj.hide_render = False
# Ensure filepaths target the current dock index # Ensure filepaths target the current dock index
@ -212,7 +206,7 @@ def set_adjacent_area(self, context):
# Area data class # Area data class
class SREAData(bpy.types.PropertyGroup): class SREAData(bpy.types.PropertyGroup):
lightmap_resolution = bpy.props.EnumProperty(name="HECL Area Lightmap Resolution", lightmap_resolution: bpy.props.EnumProperty(name="HECL Area Lightmap Resolution",
description="Set square resolution to use when rendering new lightmaps", description="Set square resolution to use when rendering new lightmaps",
items=[ items=[
('256', "256", "256x256 (original quality)"), ('256', "256", "256x256 (original quality)"),
@ -223,7 +217,7 @@ class SREAData(bpy.types.PropertyGroup):
update=set_lightmap_resolution, update=set_lightmap_resolution,
default='1024') default='1024')
lightmap_mode = bpy.props.EnumProperty(name="HECL Area Lightmap Mode", lightmap_mode: bpy.props.EnumProperty(name="HECL Area Lightmap Mode",
description="Simple way to manipulate all lightmap-using materials", description="Simple way to manipulate all lightmap-using materials",
items=[ items=[
('NONE', "None", "Pure white lightmaps"), ('NONE', "None", "Pure white lightmaps"),
@ -232,7 +226,7 @@ class SREAData(bpy.types.PropertyGroup):
update=preview_update, update=preview_update,
default='ORIGINAL') default='ORIGINAL')
adjacent_area = bpy.props.IntProperty(name="HECL Adjacent Area Lightmap", adjacent_area: bpy.props.IntProperty(name="HECL Adjacent Area Lightmap",
description="Dock index of adjacent area to render, or -1 for local lights", description="Dock index of adjacent area to render, or -1 for local lights",
update=set_adjacent_area, update=set_adjacent_area,
default=-1, default=-1,
@ -296,179 +290,6 @@ def get_de_sockets(chain):
return found_mul, found_add return found_mul, found_add
# Get texture node from node
def tex_node_from_node(node):
if node.type == 'TEXTURE':
return node
elif node.type == 'MIX_RGB':
if node.inputs[1].is_linked and node.inputs[1].links[0].from_node.type == 'TEXTURE':
return node.inputs[1].links[0].from_node
if node.inputs[2].is_linked and node.inputs[2].links[0].from_node.type == 'TEXTURE':
return node.inputs[2].links[0].from_node
return None
# Delete existing cycles nodes and convert from GLSL nodes
CYCLES_TYPES = {'OUTPUT_MATERIAL', 'ADD_SHADER', 'BSDF_DIFFUSE', 'BSDF_TRANSPARENT',
'EMISSION', 'MIX_SHADER', 'TEX_IMAGE'}
def initialize_nodetree_cycles(mat, area_data):
nt = mat.node_tree
to_remove = set()
for node in nt.nodes:
if node.type in CYCLES_TYPES:
to_remove.add(node)
if node.parent:
to_remove.add(node.parent)
for node in to_remove:
nt.nodes.remove(node)
gridder = Nodegrid.Nodegrid(nt, cycles=True)
if mat.hecl_lightmap and not mat.library:
# Get name of lightmap texture
if mat.hecl_lightmap in bpy.data.textures:
img_name = mat.hecl_lightmap
if img_name in bpy.data.images:
bpy.data.textures[mat.hecl_lightmap].image = bpy.data.images[img_name]
else:
bpy.data.textures[mat.hecl_lightmap].image = None
# Get image already established or make new one
new_image = make_or_load_cycles_image(mat, area_data)
image_out_node = nt.nodes.new('ShaderNodeTexImage')
image_out_node.name = 'CYCLES_OUT'
gridder.place_node(image_out_node, 3)
image_out_node.image = new_image
if mat.game_settings.alpha_blend == 'ADD':
transp = nt.nodes.new('ShaderNodeBsdfTransparent')
gridder.place_node(transp, 2)
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
gridder.place_node(material_output, 3)
nt.links.new(transp.outputs[0], material_output.inputs[0])
elif mat.game_settings.alpha_blend == 'ALPHA':
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
gridder.place_node(diffuse, 2)
transp = nt.nodes.new('ShaderNodeBsdfTransparent')
gridder.place_node(transp, 2)
mix_shader = nt.nodes.new('ShaderNodeMixShader')
gridder.place_node(mix_shader, 2)
nt.links.new(transp.outputs[0], mix_shader.inputs[1])
nt.links.new(diffuse.outputs[0], mix_shader.inputs[2])
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
gridder.place_node(material_output, 3)
nt.links.new(mix_shader.outputs[0], material_output.inputs[0])
# Classify connected transparent textures
chain = recursive_build_material_chain(nt.nodes['Output'])
if chain:
diffuse_soc, emissive_soc = get_de_sockets(chain)
tex_node = tex_node_from_node(diffuse_soc.node)
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
diffuse_image_node = nt.nodes.new('ShaderNodeTexImage')
gridder.place_node(diffuse_image_node, 1)
diffuse_image_node.image = tex_node.texture.image
mixrgb_node = nt.nodes.new('ShaderNodeMixRGB')
gridder.place_node(mixrgb_node, 1)
mixrgb_node.inputs[1].default_value = (1.0,1.0,1.0,1.0)
mapping = nt.nodes.new('ShaderNodeUVMap')
gridder.place_node(mapping, 1)
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
nt.links.new(mapping.outputs[0], diffuse_image_node.inputs[0])
light_path = nt.nodes.new('ShaderNodeLightPath')
gridder.place_node(light_path, 2)
mixrgb_reflect = nt.nodes.new('ShaderNodeMixRGB')
gridder.place_node(mixrgb_reflect, 2)
nt.links.new(light_path.outputs['Is Reflection Ray'], mixrgb_reflect.inputs[0])
mixrgb_reflect.inputs[1].default_value = (1.0,1.0,1.0,1.0)
nt.links.new(diffuse_image_node.outputs[0], mixrgb_reflect.inputs[2])
nt.links.new(mixrgb_reflect.outputs[0], diffuse.inputs[0])
nt.links.new(mixrgb_reflect.outputs[0], mixrgb_node.inputs[2])
if nt.nodes['Output'].inputs[1].is_linked:
nt.links.new(nt.nodes['Output'].inputs[1].links[0].from_socket, mix_shader.inputs[0])
nt.links.new(nt.nodes['Output'].inputs[1].links[0].from_socket, mixrgb_node.inputs[0])
nt.links.new(mixrgb_node.outputs[0], transp.inputs[0])
else:
# Classify connected opaque textures
chain = recursive_build_material_chain(nt.nodes['Output'])
if chain:
diffuse = None
emissive = None
diffuse_soc, emissive_soc = get_de_sockets(chain)
if diffuse_soc:
tex_node = tex_node_from_node(diffuse_soc.node)
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
diffuse_image_node = nt.nodes.new('ShaderNodeTexImage')
gridder.place_node(diffuse_image_node, 1)
diffuse_image_node.image = tex_node.texture.image
mapping = nt.nodes.new('ShaderNodeUVMap')
gridder.place_node(mapping, 1)
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
nt.links.new(mapping.outputs[0], diffuse_image_node.inputs[0])
light_path = nt.nodes.new('ShaderNodeLightPath')
gridder.place_node(light_path, 2)
mixrgb_reflect = nt.nodes.new('ShaderNodeMixRGB')
gridder.place_node(mixrgb_reflect, 2)
nt.links.new(light_path.outputs['Is Reflection Ray'], mixrgb_reflect.inputs[0])
mixrgb_reflect.inputs[1].default_value = (1.0,1.0,1.0,1.0)
nt.links.new(diffuse_image_node.outputs[0], mixrgb_reflect.inputs[2])
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
gridder.place_node(diffuse, 2)
nt.links.new(mixrgb_reflect.outputs[0], diffuse.inputs[0])
else:
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
gridder.place_node(diffuse, 2)
if emissive_soc:
tex_node = tex_node_from_node(emissive_soc.node)
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
emissive_image_node = nt.nodes.new('ShaderNodeTexImage')
gridder.place_node(emissive_image_node, 1)
emissive_image_node.image = tex_node.texture.image
mapping = nt.nodes.new('ShaderNodeUVMap')
gridder.place_node(mapping, 1)
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
nt.links.new(mapping.outputs[0], emissive_image_node.inputs[0])
emissive = nt.nodes.new('ShaderNodeEmission')
gridder.place_node(emissive, 2)
nt.links.new(emissive_image_node.outputs[0], emissive.inputs[0])
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
gridder.place_node(material_output, 3)
if diffuse and emissive:
shader_add = nt.nodes.new('ShaderNodeAddShader')
gridder.place_node(shader_add, 2)
nt.links.new(diffuse.outputs[0], shader_add.inputs[0])
nt.links.new(emissive.outputs[0], shader_add.inputs[1])
nt.links.new(shader_add.outputs[0], material_output.inputs[0])
elif diffuse_soc:
nt.links.new(diffuse.outputs[0], material_output.inputs[0])
elif emissive_soc:
nt.links.new(emissive.outputs[0], material_output.inputs[0])
# Lightmap setup operator
class SREAInitializeCycles(bpy.types.Operator):
bl_idname = "scene.hecl_area_initialize_cycles"
bl_label = "HECL Initialize Cycles"
bl_description = "Initialize Cycles nodes for lightmap baking (WILL DELETE EXISTING CYCLES NODES!)"
@classmethod
def poll(cls, context):
return context.scene is not None and context.scene.hecl_type == 'AREA'
def execute(self, context):
area_data = context.scene.hecl_srea_data
# Iterate materials and setup cycles
for mat in bpy.data.materials:
if mat.use_nodes:
initialize_nodetree_cycles(mat, area_data)
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_confirm(self, event)
# Lookup the directory name of other area via dock link # Lookup the directory name of other area via dock link
def get_other_area_name(op, bg_scene, dock_idx): def get_other_area_name(op, bg_scene, dock_idx):
dock_conns = swld.build_dock_connections(bg_scene) dock_conns = swld.build_dock_connections(bg_scene)
@ -501,7 +322,6 @@ def render_lightmaps(context):
pixel_size = int(area_data.lightmap_resolution) pixel_size = int(area_data.lightmap_resolution)
# Mmm Cycles # Mmm Cycles
context.scene.render.engine = 'CYCLES'
context.scene.render.bake.margin = pixel_size // 256 context.scene.render.bake.margin = pixel_size // 256
# Iterate materials and setup cycles # Iterate materials and setup cycles
@ -547,8 +367,8 @@ class SREARenderLightmaps(bpy.types.Operator):
if not context.selected_objects: if not context.selected_objects:
for obj in context.scene.objects: for obj in context.scene.objects:
if obj.type == 'MESH' and not obj.library: if obj.type == 'MESH' and not obj.library:
obj.select = True obj.select_set(True)
context.scene.objects.active = obj context.view_layer.objects.active = obj
render_lightmaps(context) render_lightmaps(context)
@ -559,7 +379,6 @@ def shadeless_material(idx):
if name in bpy.data.materials: if name in bpy.data.materials:
return bpy.data.materials[name] return bpy.data.materials[name]
mat = bpy.data.materials.new(name) mat = bpy.data.materials.new(name)
mat.use_shadeless = True
r = idx % 256 r = idx % 256
g = (idx % 65536) // 256 g = (idx % 65536) // 256
b = idx // 65536 b = idx // 65536
@ -567,11 +386,11 @@ def shadeless_material(idx):
return mat return mat
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0)) look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0)) look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0)) look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0)) look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0)) look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0)) look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right) look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
# Render PVS for location # Render PVS for location
@ -585,7 +404,6 @@ def render_pvs(pathOut, location):
bpy.context.scene.render.use_sss = False bpy.context.scene.render.use_sss = False
bpy.context.scene.render.use_envmaps = False bpy.context.scene.render.use_envmaps = False
bpy.context.scene.render.use_raytrace = False bpy.context.scene.render.use_raytrace = False
bpy.context.scene.render.engine = 'BLENDER_RENDER'
bpy.context.scene.display_settings.display_device = 'None' bpy.context.scene.display_settings.display_device = 'None'
bpy.context.scene.render.image_settings.file_format = 'PNG' bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0)) bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
@ -593,7 +411,7 @@ def render_pvs(pathOut, location):
cam = bpy.data.cameras.new('CUBIC_CAM') cam = bpy.data.cameras.new('CUBIC_CAM')
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam) cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
bpy.context.scene.objects.link(cam_obj) bpy.context.scene.collection.objects.link(cam_obj)
bpy.context.scene.camera = cam_obj bpy.context.scene.camera = cam_obj
cam.lens_unit = 'FOV' cam.lens_unit = 'FOV'
cam.angle = math.radians(90.0) cam.angle = math.radians(90.0)
@ -617,7 +435,7 @@ def render_pvs(pathOut, location):
bpy.ops.render.render(write_still=True) bpy.ops.render.render(write_still=True)
bpy.context.scene.camera = None bpy.context.scene.camera = None
bpy.context.scene.objects.unlink(cam_obj) #bpy.context.scene.objects.unlink(cam_obj)
bpy.data.objects.remove(cam_obj) bpy.data.objects.remove(cam_obj)
bpy.data.cameras.remove(cam) bpy.data.cameras.remove(cam)
@ -634,16 +452,15 @@ def cook(writebuffunc, platform, endianchar):
# Panel draw # Panel draw
def draw(layout, context): def draw(layout, context):
area_data = context.scene.hecl_srea_data area_data = context.scene.hecl_srea_data
layout.label("Lighting:", icon='LAMP_SPOT') layout.label(text="Lighting:", icon='LIGHT')
light_row = layout.row(align=True) light_row = layout.row(align=True)
light_row.prop_enum(area_data, 'lightmap_mode', 'NONE') light_row.prop_enum(area_data, 'lightmap_mode', 'NONE')
light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL') light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL')
light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES') light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES')
layout.prop(area_data, 'lightmap_resolution', text="Resolution") layout.prop(area_data, 'lightmap_resolution', text="Resolution")
layout.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label) layout.popover("CYCLES_PT_sampling_presets", text=bpy.types.CYCLES_PT_sampling_presets.bl_label)
layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking") layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking")
layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='OOPS') layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='MOD_OPACITY')
layout.operator("scene.hecl_area_initialize_cycles", text="Initialize Cycles Nodes", icon='NODETREE')
layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL') layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL')
layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK') layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK')
@ -656,7 +473,6 @@ def scene_loaded(dummy):
# Registration # Registration
def register(): def register():
bpy.utils.register_class(SREAData) bpy.utils.register_class(SREAData)
bpy.utils.register_class(SREAInitializeCycles)
bpy.utils.register_class(SREARenderLightmaps) bpy.utils.register_class(SREARenderLightmaps)
bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData) bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData)
bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name') bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name')
@ -664,6 +480,5 @@ def register():
def unregister(): def unregister():
bpy.utils.unregister_class(SREAData) bpy.utils.unregister_class(SREAData)
bpy.utils.unregister_class(SREAInitializeCycles)
bpy.utils.unregister_class(SREARenderLightmaps) bpy.utils.unregister_class(SREARenderLightmaps)

View File

@ -62,9 +62,9 @@ def cook(writebuf):
for ch in dock_list: for ch in dock_list:
if len(ch.data.vertices) < 4: if len(ch.data.vertices) < 4:
raise RuntimeError('Not enough vertices in dock %s' % ch.name) raise RuntimeError('Not enough vertices in dock %s' % ch.name)
wmtx = wmtx_inv * ch.matrix_world wmtx = wmtx_inv @ ch.matrix_world
for vi in range(4): for vi in range(4):
v = wmtx * ch.data.vertices[vi].co v = wmtx @ ch.data.vertices[vi].co
writebuf(struct.pack('fff', v[0], v[1], v[2])) writebuf(struct.pack('fff', v[0], v[1], v[2]))
if ch.name in dock_conns: if ch.name in dock_conns:
conn_dock = dock_conns[ch.name] conn_dock = dock_conns[ch.name]

View File

@ -65,14 +65,14 @@ class PathHasher:
# If there's a third argument, use it as the .zip path containing the addon # If there's a third argument, use it as the .zip path containing the addon
did_install = False did_install = False
if len(args) >= 4 and args[3] != 'SKIPINSTALL': if len(args) >= 4 and args[3] != 'SKIPINSTALL':
bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[3]) bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=args[3])
bpy.ops.wm.addon_refresh() bpy.ops.preferences.addon_refresh()
did_install = True did_install = True
# Make addon available to commands # Make addon available to commands
if bpy.context.user_preferences.addons.find('hecl') == -1: if bpy.context.preferences.addons.find('hecl') == -1:
try: try:
bpy.ops.wm.addon_enable(module='hecl') bpy.ops.preferences.addon_enable(module='hecl')
bpy.ops.wm.save_userpref() bpy.ops.wm.save_userpref()
except: except:
pass pass
@ -93,16 +93,6 @@ ackbytes = readpipestr()
if ackbytes != b'ACK': if ackbytes != b'ACK':
quitblender() quitblender()
# slerp branch check
bpy.ops.mesh.primitive_cube_add()
orig_rot = bpy.context.object.rotation_mode
try:
bpy.context.object.rotation_mode = 'QUATERNION_SLERP'
writepipestr(b'SLERP1')
except:
writepipestr(b'SLERP0')
bpy.context.object.rotation_mode = orig_rot
# Count brackets # Count brackets
def count_brackets(linestr): def count_brackets(linestr):
bracket_count = 0 bracket_count = 0
@ -184,15 +174,15 @@ def writelight(obj):
if obj.data.type == 'POINT': if obj.data.type == 'POINT':
type = 2 type = 2
hasFalloff = True hasFalloff = True
castShadow = obj.data.shadow_method != 'NOSHADOW' castShadow = obj.data.use_shadow
elif obj.data.type == 'SPOT': elif obj.data.type == 'SPOT':
type = 3 type = 3
hasFalloff = True hasFalloff = True
spotCutoff = obj.data.spot_size spotCutoff = obj.data.spot_size
castShadow = obj.data.shadow_method != 'NOSHADOW' castShadow = obj.data.use_shadow
elif obj.data.type == 'SUN': elif obj.data.type == 'SUN':
type = 1 type = 1
castShadow = obj.data.shadow_method != 'NOSHADOW' castShadow = obj.data.use_shadow
constant = 1.0 constant = 1.0
linear = 0.0 linear = 0.0
@ -236,11 +226,11 @@ def dataout_loop():
elif cmdargs[0] == 'LIGHTLIST': elif cmdargs[0] == 'LIGHTLIST':
lightCount = 0 lightCount = 0
for obj in bpy.context.scene.objects: for obj in bpy.context.scene.objects:
if obj.type == 'LAMP' and not obj.library: if obj.type == 'LIGHT' and not obj.library:
lightCount += 1 lightCount += 1
writepipebuf(struct.pack('I', lightCount)) writepipebuf(struct.pack('I', lightCount))
for obj in bpy.context.scene.objects: for obj in bpy.context.scene.objects:
if obj.type == 'LAMP' and not obj.library: if obj.type == 'LIGHT' and not obj.library:
writepipestr(obj.name.encode()) writepipestr(obj.name.encode())
elif cmdargs[0] == 'MESHAABB': elif cmdargs[0] == 'MESHAABB':
@ -248,27 +238,24 @@ def dataout_loop():
hecl.mesh_aabb(writepipebuf) hecl.mesh_aabb(writepipebuf)
elif cmdargs[0] == 'MESHCOMPILE': elif cmdargs[0] == 'MESHCOMPILE':
maxSkinBanks = int(cmdargs[2])
meshName = bpy.context.scene.hecl_mesh_obj meshName = bpy.context.scene.hecl_mesh_obj
if meshName not in bpy.data.objects: if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode()) writepipestr(('mesh %s not found' % meshName).encode())
continue continue
writepipestr(b'OK') writepipestr(b'OK')
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[1], maxSkinBanks) hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName])
elif cmdargs[0] == 'MESHCOMPILENAME': elif cmdargs[0] == 'MESHCOMPILENAME':
meshName = cmdargs[1] meshName = cmdargs[1]
maxSkinBanks = int(cmdargs[3]) useLuv = int(cmdargs[2])
useLuv = int(cmdargs[4])
if meshName not in bpy.data.objects: if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode()) writepipestr(('mesh %s not found' % meshName).encode())
continue continue
writepipestr(b'OK') writepipestr(b'OK')
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[2], maxSkinBanks, useLuv) hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], useLuv)
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION': elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
meshName = cmdargs[1] meshName = cmdargs[1]
@ -293,25 +280,6 @@ def dataout_loop():
if obj.type == 'MESH' and not obj.library: if obj.type == 'MESH' and not obj.library:
hecl.hmdl.cookcol(writepipebuf, obj) hecl.hmdl.cookcol(writepipebuf, obj)
elif cmdargs[0] == 'MESHCOMPILEALL':
maxSkinBanks = int(cmdargs[2])
maxOctantLength = float(cmdargs[3])
bpy.ops.object.select_all(action='DESELECT')
join_mesh = bpy.data.meshes.new('JOIN_MESH')
join_obj = bpy.data.object.new(join_mesh.name, join_mesh)
bpy.context.scene.objects.link(join_obj)
bpy.ops.object.select_by_type(type='MESH')
bpy.context.scene.objects.active = join_obj
bpy.ops.object.join()
writepipestr(b'OK')
hecl.hmdl.cook(writepipebuf, join_obj, cmdargs[1], maxSkinBanks, maxOctantLength)
bpy.context.scene.objects.unlink(join_obj)
bpy.data.objects.remove(join_obj)
bpy.data.meshes.remove(join_mesh)
elif cmdargs[0] == 'MESHCOMPILEPATH': elif cmdargs[0] == 'MESHCOMPILEPATH':
meshName = bpy.context.scene.hecl_path_obj meshName = bpy.context.scene.hecl_path_obj
if meshName not in bpy.data.objects: if meshName not in bpy.data.objects:
@ -342,7 +310,7 @@ def dataout_loop():
lampCount = 0 lampCount = 0
firstSpot = None firstSpot = None
for obj in bpy.context.scene.objects: for obj in bpy.context.scene.objects:
if obj.type == 'LAMP': if obj.type == 'LIGHT':
lampCount += 1 lampCount += 1
if firstSpot is None and obj.data.type == 'SPOT': if firstSpot is None and obj.data.type == 'SPOT':
firstSpot = obj firstSpot = obj
@ -375,7 +343,7 @@ def dataout_loop():
# Lamp objects # Lamp objects
for obj in bpy.context.scene.objects: for obj in bpy.context.scene.objects:
if obj != firstSpot and obj.type == 'LAMP': if obj != firstSpot and obj.type == 'LIGHT':
writelight(obj) writelight(obj)
elif cmdargs[0] == 'GETTEXTURES': elif cmdargs[0] == 'GETTEXTURES':
@ -505,7 +473,7 @@ try:
else: else:
bpy.ops.wm.read_homefile() bpy.ops.wm.read_homefile()
loaded_blend = None loaded_blend = None
bpy.context.user_preferences.filepaths.save_version = 0 bpy.context.preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]): if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
bpy.ops.file.hecl_patching_load() bpy.ops.file.hecl_patching_load()
bpy.context.scene.hecl_type = cmdargs[2] bpy.context.scene.hecl_type = cmdargs[2]
@ -527,7 +495,7 @@ try:
writepipestr(b'FALSE') writepipestr(b'FALSE')
elif cmdargs[0] == 'SAVE': elif cmdargs[0] == 'SAVE':
bpy.context.user_preferences.filepaths.save_version = 0 bpy.context.preferences.filepaths.save_version = 0
print('SAVING %s' % loaded_blend) print('SAVING %s' % loaded_blend)
if loaded_blend: if loaded_blend:
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True): if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True):

2
hecl/extern/athena vendored

@ -1 +1 @@
Subproject commit b9de85440046dba056a55c85e9952ce36ccf8156 Subproject commit 9bbd7af9f6c5ff34b37752906b0d78295c44938d

2
hecl/extern/boo vendored

@ -1 +1 @@
Subproject commit 0f330c1f057ec3c190d6278370f6e1628df435ed Subproject commit 3ad748f28b5377027c900ded38eeccdfeb7fe7f6

@ -1 +1 @@
Subproject commit 96c520ed950682d113953f570fbd10a485cc0e8a Subproject commit 01bf9e65294f7abae61a84d5b855202565fe1267

View File

@ -1,16 +1,24 @@
#pragma once #pragma once
#include "hecl/Frontend.hpp" #include "boo/graphicsdev/IGraphicsDataFactory.hpp"
namespace hecl::Backend { namespace hecl::Backend {
struct ExtensionSlot; struct ExtensionSlot;
using namespace std::literals;
using IR = Frontend::IR; enum class TexCoordSource : uint8_t {
using Diagnostics = Frontend::Diagnostics; Invalid = 0xff,
using SourceLocation = Frontend::SourceLocation; Position = 0,
using ArithmeticOp = IR::Instruction::ArithmeticOpType; Normal = 1,
Tex0 = 2,
enum class TexGenSrc : uint8_t { Position, Normal, UV }; Tex1 = 3,
Tex2 = 4,
Tex3 = 5,
Tex4 = 6,
Tex5 = 7,
Tex6 = 8,
Tex7 = 9,
};
enum class BlendFactor : uint8_t { enum class BlendFactor : uint8_t {
Zero, Zero,
@ -28,25 +36,49 @@ enum class BlendFactor : uint8_t {
Original = 0xff Original = 0xff
}; };
constexpr std::string_view BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
switch (factor) {
case BlendFactor::Zero:
return "ZERO"sv;
case BlendFactor::One:
return "ONE"sv;
case BlendFactor::SrcColor:
return "SRCCOLOR"sv;
case BlendFactor::InvSrcColor:
return "INVSRCCOLOR"sv;
case BlendFactor::DstColor:
return "DSTCOLOR"sv;
case BlendFactor::InvDstColor:
return "INVDSTCOLOR"sv;
case BlendFactor::SrcAlpha:
return "SRCALPHA"sv;
case BlendFactor::InvSrcAlpha:
return "INVSRCALPHA"sv;
case BlendFactor::DstAlpha:
return "DSTALPHA"sv;
case BlendFactor::InvDstAlpha:
return "INVDSTALPHA"sv;
case BlendFactor::SrcColor1:
return "SRCCOLOR1"sv;
case BlendFactor::InvSrcColor1:
return "INVSRCCOLOR1"sv;
default:
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
}
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff }; enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff }; enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
struct TextureInfo { struct TextureInfo {
TexGenSrc src; TexCoordSource src;
int mapIdx; uint8_t mtxIdx;
int uvIdx;
int mtxIdx;
bool normalize; bool normalize;
}; };
enum class ReflectionType { None, Simple, Indirect }; enum class ReflectionType { None, Simple, Indirect };
class IBackend {
public:
virtual void reset(const IR& ir, Diagnostics& diag) = 0;
};
/** /**
* @brief Hash subclass for identifying shaders and their metadata * @brief Hash subclass for identifying shaders and their metadata
*/ */
@ -85,22 +117,6 @@ public:
m_alphaTest = alphaTest; m_alphaTest = alphaTest;
hash ^= m_meta; hash ^= m_meta;
} }
ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest)
: Hash(ir.m_hash) {
m_colorCount = c;
m_uvCount = u;
m_weightCount = w;
m_skinSlotCount = s;
m_primitiveType = uint8_t(pt);
m_reflectionType = uint8_t(reflectionType);
m_depthTest = depthTest;
m_depthWrite = depthWrite;
m_backfaceCulling = backfaceCulling;
m_alphaTest = alphaTest;
hash ^= m_meta;
}
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling, Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest) bool alphaTest)
@ -170,10 +186,7 @@ struct Function {
}; };
struct ExtensionSlot { struct ExtensionSlot {
Function lighting; const char* shaderMacro;
Function post;
size_t blockCount = 0;
const char** blockNames = nullptr;
size_t texCount = 0; size_t texCount = 0;
const Backend::TextureInfo* texs = nullptr; const Backend::TextureInfo* texs = nullptr;
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original; Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
@ -188,7 +201,7 @@ struct ExtensionSlot {
bool forceAlphaTest = false; bool forceAlphaTest = false;
bool diffuseOnly = false; bool diffuseOnly = false;
ExtensionSlot(size_t blockCount = 0, const char** blockNames = nullptr, size_t texCount = 0, ExtensionSlot(size_t texCount = 0,
const Backend::TextureInfo* texs = nullptr, const Backend::TextureInfo* texs = nullptr,
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original, Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original, Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
@ -196,9 +209,7 @@ struct ExtensionSlot {
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false, Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false, bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false) bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false)
: blockCount(blockCount) : texCount(texCount)
, blockNames(blockNames)
, texCount(texCount)
, texs(texs) , texs(texs)
, srcFactor(srcFactor) , srcFactor(srcFactor)
, dstFactor(dstFactor) , dstFactor(dstFactor)
@ -216,10 +227,7 @@ struct ExtensionSlot {
void calculateHash() const { void calculateHash() const {
XXH64_state_t st; XXH64_state_t st;
XXH64_reset(&st, 0); XXH64_reset(&st, 0);
if (!lighting.m_source.empty()) XXH64_update(&st, shaderMacro, strlen(shaderMacro));
XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size());
if (!post.m_source.empty())
XXH64_update(&st, post.m_source.data(), post.m_source.size());
for (size_t i = 0; i < texCount; ++i) { for (size_t i = 0; i < texCount; ++i) {
const Backend::TextureInfo& tinfo = texs[i]; const Backend::TextureInfo& tinfo = texs[i];
XXH64_update(&st, &tinfo, sizeof(tinfo)); XXH64_update(&st, &tinfo, sizeof(tinfo));

View File

@ -1,40 +0,0 @@
#pragma once
#include "ProgrammableCommon.hpp"
namespace hecl::Backend {
#define HECL_GLSL_VERT_UNIFORM_BLOCK_NAME "HECLVertUniform"
#define HECL_GLSL_TEXMTX_UNIFORM_BLOCK_NAME "HECLTexMtxUniform"
struct GLSL : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
size_t extTexCount, const TextureInfo* extTexs, bool diffuseOnly) const;
private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
return hecl::Format("vec3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
return hecl::Format("vec3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
};
} // namespace hecl::Backend

View File

@ -1,513 +0,0 @@
#pragma once
#include "Backend.hpp"
#include <athena/DNA.hpp>
#include <cstdint>
#include <cstdlib>
#include <algorithm>
#undef min
#undef max
namespace hecl::Backend {
struct GX final : IBackend {
enum AttrType { NONE, DIRECT, INDEX8, INDEX16 };
enum TevOp {
TEV_ADD = 0,
TEV_SUB = 1,
TEV_COMP_R8_GT = 8,
TEV_COMP_R8_EQ = 9,
TEV_COMP_GR16_GT = 10,
TEV_COMP_GR16_EQ = 11,
TEV_COMP_BGR24_GT = 12,
TEV_COMP_BGR24_EQ = 13,
TEV_COMP_RGB8_GT = 14,
TEV_COMP_RGB8_EQ = 15,
TEV_COMP_A8_GT = TEV_COMP_RGB8_GT, // for alpha channel
TEV_COMP_A8_EQ = TEV_COMP_RGB8_EQ // for alpha channel
};
enum TevBias {
TB_ZERO = 0,
TB_ADDHALF = 1,
TB_SUBHALF = 2,
};
enum TevScale { CS_SCALE_1 = 0, CS_SCALE_2 = 1, CS_SCALE_4 = 2, CS_DIVIDE_2 = 3 };
enum TexGenType {
TG_MTX3x4 = 0,
TG_MTX2x4,
TG_BUMP0,
TG_BUMP1,
TG_BUMP2,
TG_BUMP3,
TG_BUMP4,
TG_BUMP5,
TG_BUMP6,
TG_BUMP7,
TG_SRTG
};
enum TevRegID { TEVPREV = 0, TEVREG0 = 1, TEVREG1 = 2, TEVREG2 = 3, TEVLAZY = 5 };
enum TevColorArg {
CC_CPREV = 0, /*!< Use the color value from previous TEV stage */
CC_APREV = 1, /*!< Use the alpha value from previous TEV stage */
CC_C0 = 2, /*!< Use the color value from the color/output register 0 */
CC_A0 = 3, /*!< Use the alpha value from the color/output register 0 */
CC_C1 = 4, /*!< Use the color value from the color/output register 1 */
CC_A1 = 5, /*!< Use the alpha value from the color/output register 1 */
CC_C2 = 6, /*!< Use the color value from the color/output register 2 */
CC_A2 = 7, /*!< Use the alpha value from the color/output register 2 */
CC_TEXC = 8, /*!< Use the color value from texture */
CC_TEXA = 9, /*!< Use the alpha value from texture */
CC_RASC = 10, /*!< Use the color value from rasterizer */
CC_RASA = 11, /*!< Use the alpha value from rasterizer */
CC_ONE = 12,
CC_HALF = 13,
CC_KONST = 14,
CC_ZERO = 15, /*!< Use to pass zero value */
/* Non-GX */
CC_LAZY /*!< Lazy register allocation */
};
enum TevAlphaArg {
CA_APREV = 0, /*!< Use the alpha value from previous TEV stage */
CA_A0 = 1, /*!< Use the alpha value from the color/output register 0 */
CA_A1 = 2, /*!< Use the alpha value from the color/output register 1 */
CA_A2 = 3, /*!< Use the alpha value from the color/output register 2 */
CA_TEXA = 4, /*!< Use the alpha value from texture */
CA_RASA = 5, /*!< Use the alpha value from rasterizer */
CA_KONST = 6,
CA_ZERO = 7, /*!< Use to pass zero value */
/* Non-GX */
CA_LAZY /*!< Lazy register allocation */
};
enum TevKColorSel {
TEV_KCSEL_8_8 = 0x00,
TEV_KCSEL_7_8 = 0x01,
TEV_KCSEL_6_8 = 0x02,
TEV_KCSEL_5_8 = 0x03,
TEV_KCSEL_4_8 = 0x04,
TEV_KCSEL_3_8 = 0x05,
TEV_KCSEL_2_8 = 0x06,
TEV_KCSEL_1_8 = 0x07,
TEV_KCSEL_1 = TEV_KCSEL_8_8,
TEV_KCSEL_3_4 = TEV_KCSEL_6_8,
TEV_KCSEL_1_2 = TEV_KCSEL_4_8,
TEV_KCSEL_1_4 = TEV_KCSEL_2_8,
TEV_KCSEL_K0 = 0x0C,
TEV_KCSEL_K1 = 0x0D,
TEV_KCSEL_K2 = 0x0E,
TEV_KCSEL_K3 = 0x0F,
TEV_KCSEL_K0_R = 0x10,
TEV_KCSEL_K1_R = 0x11,
TEV_KCSEL_K2_R = 0x12,
TEV_KCSEL_K3_R = 0x13,
TEV_KCSEL_K0_G = 0x14,
TEV_KCSEL_K1_G = 0x15,
TEV_KCSEL_K2_G = 0x16,
TEV_KCSEL_K3_G = 0x17,
TEV_KCSEL_K0_B = 0x18,
TEV_KCSEL_K1_B = 0x19,
TEV_KCSEL_K2_B = 0x1A,
TEV_KCSEL_K3_B = 0x1B,
TEV_KCSEL_K0_A = 0x1C,
TEV_KCSEL_K1_A = 0x1D,
TEV_KCSEL_K2_A = 0x1E,
TEV_KCSEL_K3_A = 0x1F
};
enum TevKAlphaSel {
TEV_KASEL_8_8 = 0x00,
TEV_KASEL_7_8 = 0x01,
TEV_KASEL_6_8 = 0x02,
TEV_KASEL_5_8 = 0x03,
TEV_KASEL_4_8 = 0x04,
TEV_KASEL_3_8 = 0x05,
TEV_KASEL_2_8 = 0x06,
TEV_KASEL_1_8 = 0x07,
TEV_KASEL_1 = TEV_KASEL_8_8,
TEV_KASEL_3_4 = TEV_KASEL_6_8,
TEV_KASEL_1_2 = TEV_KASEL_4_8,
TEV_KASEL_1_4 = TEV_KASEL_2_8,
TEV_KASEL_K0_R = 0x10,
TEV_KASEL_K1_R = 0x11,
TEV_KASEL_K2_R = 0x12,
TEV_KASEL_K3_R = 0x13,
TEV_KASEL_K0_G = 0x14,
TEV_KASEL_K1_G = 0x15,
TEV_KASEL_K2_G = 0x16,
TEV_KASEL_K3_G = 0x17,
TEV_KASEL_K0_B = 0x18,
TEV_KASEL_K1_B = 0x19,
TEV_KASEL_K2_B = 0x1A,
TEV_KASEL_K3_B = 0x1B,
TEV_KASEL_K0_A = 0x1C,
TEV_KASEL_K1_A = 0x1D,
TEV_KASEL_K2_A = 0x1E,
TEV_KASEL_K3_A = 0x1F
};
enum ChannelID {
GX_COLOR0,
GX_COLOR1,
GX_ALPHA0,
GX_ALPHA1,
GX_COLOR0A0,
GX_COLOR1A1,
GX_COLOR_ZERO,
GX_ALPHA_BUMP,
GX_ALPHA_BUMPN,
GX_COLOR_NULL = 0xff
};
enum TexGenSrc {
TG_POS = 0,
TG_NRM,
TG_BINRM,
TG_TANGENT,
TG_TEX0,
TG_TEX1,
TG_TEX2,
TG_TEX3,
TG_TEX4,
TG_TEX5,
TG_TEX6,
TG_TEX7,
TG_TEXCOORD0,
TG_TEXCOORD1,
TG_TEXCOORD2,
TG_TEXCOORD3,
TG_TEXCOORD4,
TG_TEXCOORD5,
TG_TEXCOORD6,
TG_COLOR0,
TG_COLOR1
};
enum TexMtx {
TEXMTX0 = 30,
TEXMTX1 = 33,
TEXMTX2 = 36,
TEXMTX3 = 39,
TEXMTX4 = 42,
TEXMTX5 = 45,
TEXMTX6 = 48,
TEXMTX7 = 51,
TEXMTX8 = 54,
TEXMTX9 = 57,
IDENTITY = 60
};
enum PTTexMtx {
PTTEXMTX0 = 64,
PTTEXMTX1 = 67,
PTTEXMTX2 = 70,
PTTEXMTX3 = 73,
PTTEXMTX4 = 76,
PTTEXMTX5 = 79,
PTTEXMTX6 = 82,
PTTEXMTX7 = 85,
PTTEXMTX8 = 88,
PTTEXMTX9 = 91,
PTTEXMTX10 = 94,
PTTEXMTX11 = 97,
PTTEXMTX12 = 100,
PTTEXMTX13 = 103,
PTTEXMTX14 = 106,
PTTEXMTX15 = 109,
PTTEXMTX16 = 112,
PTTEXMTX17 = 115,
PTTEXMTX18 = 118,
PTTEXMTX19 = 121,
PTIDENTITY = 125
};
enum DiffuseFn { DF_NONE = 0, DF_SIGN, DF_CLAMP };
enum AttnFn { AF_SPEC = 0, AF_SPOT = 1, AF_NONE };
enum Primitive {
POINTS = 0xb8,
LINES = 0xa8,
LINESTRIP = 0xb0,
TRIANGLES = 0x90,
TRIANGLESTRIP = 0x98,
TRIANGLEFAN = 0xa0,
QUADS = 0x80
};
struct TexCoordGen {
TexGenSrc m_src = TG_TEX0;
TexMtx m_mtx = IDENTITY;
bool m_norm = false;
PTTexMtx m_pmtx = PTIDENTITY;
/* Not actually part of GX, but a way to relate out-of-band
* texmtx animation parameters */
std::string m_gameFunction;
std::vector<atVec4f> m_gameArgs;
};
unsigned m_tcgCount = 0;
TexCoordGen m_tcgs[8];
unsigned m_texMtxCount = 0;
TexCoordGen* m_texMtxRefs[8];
struct TEVStage {
TevOp m_cop = TEV_ADD;
TevOp m_aop = TEV_ADD;
TevColorArg m_color[4] = {CC_ZERO, CC_ZERO, CC_ZERO, CC_ZERO};
TevAlphaArg m_alpha[4] = {CA_ZERO, CA_ZERO, CA_ZERO, CA_ZERO};
TevKColorSel m_kColor = TEV_KCSEL_1;
TevKAlphaSel m_kAlpha = TEV_KASEL_1;
TevRegID m_cRegOut = TEVPREV;
TevRegID m_aRegOut = TEVPREV;
int m_lazyCInIdx = -1;
int m_lazyAInIdx = -1;
int m_lazyCOutIdx = -1;
int m_lazyAOutIdx = -1;
int m_texMapIdx = -1;
int m_texGenIdx = -1;
/* Convenience Links */
TEVStage* m_prev = nullptr;
TEVStage* m_next = nullptr;
/* Remember this for debugging */
SourceLocation m_loc;
};
unsigned m_tevCount = 0;
TEVStage m_tevs[16];
int getStageIdx(const TEVStage* stage) const {
for (int i = 0; i < int(m_tevCount); ++i)
if (&m_tevs[i] == stage)
return i;
return -1;
}
int m_cRegMask = 0;
int m_cRegLazy = 0;
int m_aRegMask = 0;
int m_aRegLazy = 0;
int pickCLazy(Diagnostics& diag, const SourceLocation& loc, int stageIdx) const {
int regMask = m_cRegMask;
for (int i = stageIdx + 1; i < int(m_tevCount); ++i) {
const TEVStage& stage = m_tevs[i];
for (int c = 0; c < 4; ++c) {
if (stage.m_color[c] == CC_C0)
regMask |= 1;
if (stage.m_color[c] == CC_C1)
regMask |= 2;
if (stage.m_color[c] == CC_C2)
regMask |= 4;
}
}
/* Allocate from back for compatibility with Retro's
* extended shader arithmetic use */
for (int i = 2; i >= 0; --i)
if (!(regMask & (1 << i)))
return i;
diag.reportBackendErr(loc, "TEV C Register overflow");
return -1;
}
int pickALazy(Diagnostics& diag, const SourceLocation& loc, int stageIdx) const {
int regMask = m_aRegMask;
for (int i = stageIdx + 1; i < int(m_tevCount); ++i) {
const TEVStage& stage = m_tevs[i];
for (int c = 0; c < 4; ++c) {
if (stage.m_color[c] == CC_A0 || stage.m_alpha[c] == CA_A0)
regMask |= 1;
if (stage.m_color[c] == CC_A1 || stage.m_alpha[c] == CA_A1)
regMask |= 2;
if (stage.m_color[c] == CC_A2 || stage.m_alpha[c] == CA_A2)
regMask |= 4;
}
}
/* Allocate from back for compatibility with Retro's
* extended shader arithmetic use */
for (int i = 2; i >= 0; --i)
if (!(regMask & (1 << i)))
return i;
diag.reportBackendErr(loc, "TEV A Register overflow");
return -1;
}
enum BlendFactor : uint16_t {
BL_ZERO,
BL_ONE,
BL_SRCCLR,
BL_INVSRCCLR,
BL_SRCALPHA,
BL_INVSRCALPHA,
BL_DSTALPHA,
BL_INVDSTALPHA
};
BlendFactor m_blendSrc;
BlendFactor m_blendDst;
struct Color : athena::io::DNA<athena::Big> {
union {
uint8_t color[4];
uint32_t num = 0;
};
Color() = default;
Color& operator=(const atVec4f& vec) {
athena::simd_floats f(vec.simd);
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
color[3] = uint8_t(std::min(std::max(f[3] * 255.f, 0.f), 255.f));
return *this;
}
Color& operator=(const atVec3f& vec) {
athena::simd_floats f(vec.simd);
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
color[3] = 0xff;
return *this;
}
Color& operator=(uint8_t val) {
color[0] = val;
color[1] = val;
color[2] = val;
color[3] = val;
return *this;
}
atVec4f toVec4f() const {
atVec4f out;
athena::simd_floats f;
f[0] = color[0] / 255.f;
f[1] = color[1] / 255.f;
f[2] = color[2] / 255.f;
f[3] = color[3] / 255.f;
out.simd.copy_from(f);
return out;
}
Color(const atVec4f& vec) { *this = vec; }
Color(const atVec3f& vec) { *this = vec; }
Color(uint8_t val) { *this = val; }
bool operator==(const Color& other) const { return num == other.num; }
bool operator!=(const Color& other) const { return num != other.num; }
uint8_t operator[](size_t idx) const { return color[idx]; }
uint8_t& operator[](size_t idx) { return color[idx]; }
AT_DECL_EXPLICIT_DNA
};
unsigned m_kcolorCount = 0;
Color m_kcolors[4];
int m_alphaTraceStage = -1;
bool operator==(const GX& other) const {
if (m_tcgCount != other.m_tcgCount)
return false;
if (m_tevCount != other.m_tevCount)
return false;
if (m_blendSrc != other.m_blendSrc)
return false;
if (m_blendDst != other.m_blendDst)
return false;
if (m_kcolorCount != other.m_kcolorCount)
return false;
for (unsigned i = 0; i < m_tcgCount; ++i) {
const TexCoordGen& a = m_tcgs[i];
const TexCoordGen& b = other.m_tcgs[i];
if (a.m_src != b.m_src)
return false;
if (a.m_mtx != b.m_mtx)
return false;
if (a.m_norm != b.m_norm)
return false;
if (a.m_pmtx != b.m_pmtx)
return false;
}
for (unsigned i = 0; i < m_tevCount; ++i) {
const TEVStage& a = m_tevs[i];
const TEVStage& b = other.m_tevs[i];
for (unsigned j = 0; j < 4; ++j)
if (a.m_color[j] != b.m_color[j])
return false;
for (unsigned j = 0; j < 4; ++j)
if (a.m_alpha[j] != b.m_alpha[j])
return false;
if (a.m_cop != b.m_cop)
return false;
if (a.m_aop != b.m_aop)
return false;
if (a.m_kColor != b.m_kColor)
return false;
if (a.m_kAlpha != b.m_kAlpha)
return false;
if (a.m_cRegOut != b.m_cRegOut)
return false;
if (a.m_aRegOut != b.m_aRegOut)
return false;
if (a.m_texMapIdx != b.m_texMapIdx)
return false;
if (a.m_texGenIdx != b.m_texGenIdx)
return false;
}
for (unsigned i = 0; i < m_kcolorCount; ++i) {
const Color& a = m_kcolors[i];
const Color& b = other.m_kcolors[i];
if (a.num != b.num)
return false;
}
return true;
}
bool operator!=(const GX& other) const { return !(*this == other); }
void reset(const IR& ir, Diagnostics& diag);
private:
struct TraceResult {
enum class Type { Invalid, TEVStage, TEVColorArg, TEVAlphaArg, TEVKColorSel, TEVKAlphaSel } type;
union {
GX::TEVStage* tevStage;
GX::TevColorArg tevColorArg;
GX::TevAlphaArg tevAlphaArg;
GX::TevKColorSel tevKColorSel;
GX::TevKAlphaSel tevKAlphaSel;
};
TraceResult() : type(Type::Invalid) {}
TraceResult(GX::TEVStage* stage) : type(Type::TEVStage), tevStage(stage) {}
TraceResult(GX::TevColorArg arg) : type(Type::TEVColorArg), tevColorArg(arg) {}
TraceResult(GX::TevAlphaArg arg) : type(Type::TEVAlphaArg), tevAlphaArg(arg) {}
TraceResult(GX::TevKColorSel arg) : type(Type::TEVKColorSel), tevKColorSel(arg) {}
TraceResult(GX::TevKAlphaSel arg) : type(Type::TEVKAlphaSel), tevKAlphaSel(arg) {}
};
unsigned addKColor(Diagnostics& diag, const SourceLocation& loc, const Color& color);
unsigned addKAlpha(Diagnostics& diag, const SourceLocation& loc, float alpha);
unsigned addTexCoordGen(Diagnostics& diag, const SourceLocation& loc, TexGenSrc src, TexMtx mtx, bool norm,
PTTexMtx pmtx);
TEVStage& addTEVStage(Diagnostics& diag, const SourceLocation& loc);
TEVStage& addAlphaTEVStage(Diagnostics& diag, const SourceLocation& loc);
TraceResult RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool swizzleAlpha = false);
TraceResult RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst);
unsigned RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, TexMtx mtx,
bool normalize, PTTexMtx pmtx);
};
} // namespace hecl::Backend

View File

@ -1,37 +0,0 @@
#pragma once
#include "ProgrammableCommon.hpp"
namespace hecl::Backend {
struct HLSL : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
size_t extTexCount, const TextureInfo* extTexs, bool diffuseOnly) const;
private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
};
} // namespace hecl::Backend

View File

@ -1,38 +0,0 @@
#pragma once
#include "ProgrammableCommon.hpp"
namespace hecl::Backend {
struct Metal : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
size_t extTexCount, const TextureInfo* extTexs, bool diffuseOnly) const;
private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots) const;
std::string GenerateFragOutStruct() const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
};
} // namespace hecl::Backend

View File

@ -1,92 +0,0 @@
#pragma once
#include "Backend.hpp"
#include "hecl/Runtime.hpp"
#include <athena/DNA.hpp>
#include <cstdint>
#include <cstdlib>
#include <algorithm>
namespace hecl::Backend {
struct ProgrammableCommon : IBackend {
std::string m_diffuseColorExpr;
std::string m_colorExpr;
std::string m_alphaExpr;
BlendFactor m_blendSrc;
BlendFactor m_blendDst;
bool m_lighting = false;
struct TexSampling {
int mapIdx = -1;
int tcgIdx = -1;
};
std::vector<TexSampling> m_texSamplings;
unsigned m_texMapEnd = 0;
unsigned m_extMapStart = 8;
struct TexCoordGen {
TexGenSrc m_src;
int m_uvIdx = 0;
int m_mtx = -1;
bool m_norm = false;
std::string m_gameFunction;
std::vector<atVec4f> m_gameArgs;
};
std::vector<TexCoordGen> m_tcgs;
std::vector<size_t> m_texMtxRefs;
using IBackend::reset;
void reset(const IR& ir, Diagnostics& diag, const char* backendName);
static const char* BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor);
private:
void resetResourceAccumulators();
unsigned addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize);
unsigned addTexSampling(unsigned mapIdx, unsigned tcgIdx);
std::string RecursiveTraceDiffuseColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle,
bool fallback);
std::string RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle);
std::string RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle);
unsigned RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, int mtx, bool normalize);
std::string EmitSamplingUseRaw(unsigned samplingIdx) const { return hecl::Format("sampling%u", samplingIdx); }
std::string EmitSamplingUseRGB(unsigned samplingIdx) const { return hecl::Format("sampling%u.rgb", samplingIdx); }
std::string EmitSamplingUseAlpha(unsigned samplingIdx) const { return hecl::Format("sampling%u.a", samplingIdx); }
std::string EmitColorRegUseRaw(unsigned idx) const { return hecl::Format("colorReg%u", idx); }
std::string EmitColorRegUseRGB(unsigned idx) const { return hecl::Format("colorReg%u.rgb", idx); }
std::string EmitColorRegUseAlpha(unsigned idx) const { return hecl::Format("colorReg%u.a", idx); }
std::string EmitLightingRaw() const { return std::string("lighting"); }
std::string EmitLightingRGB() const { return std::string("lighting.rgb"); }
std::string EmitLightingAlpha() const { return std::string("lighting.a"); }
virtual std::string EmitVec3(const atVec4f& vec) const = 0;
virtual std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const = 0;
std::string EmitVal(float val) const { return hecl::Format("%f", val); }
std::string EmitAdd(const std::string& a, const std::string& b) const { return '(' + a + '+' + b + ')'; }
std::string EmitSub(const std::string& a, const std::string& b) const { return '(' + a + '-' + b + ')'; }
std::string EmitMult(const std::string& a, const std::string& b) const { return '(' + a + '*' + b + ')'; }
std::string EmitDiv(const std::string& a, const std::string& b) const { return '(' + a + '/' + b + ')'; }
std::string EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const;
std::string EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const;
};
} // namespace hecl::Backend

View File

@ -20,15 +20,19 @@
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
#include <atomic> #include <atomic>
#include <variant>
#include "hecl/hecl.hpp" #include "hecl/hecl.hpp"
#include "hecl/HMDLMeta.hpp" #include "hecl/HMDLMeta.hpp"
#include "hecl/TypedVariant.hpp"
#include "hecl/Backend.hpp"
#include "athena/Types.hpp" #include "athena/Types.hpp"
#include "athena/MemoryWriter.hpp" #include "athena/MemoryWriter.hpp"
#include "optional.hpp" #include "optional.hpp"
#include "Token.hpp" #include "Token.hpp"
namespace hecl::blender { namespace hecl::blender {
using namespace std::literals;
extern logvisor::Module BlenderLog; extern logvisor::Module BlenderLog;
class HMDLBuffers; class HMDLBuffers;
@ -103,6 +107,10 @@ struct Vector2f {
void read(Connection& conn); void read(Connection& conn);
Vector2f(Connection& conn) { read(conn); } Vector2f(Connection& conn) { read(conn); }
operator const atVec2f&() const { return val; } operator const atVec2f&() const { return val; }
bool operator==(const Vector2f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1];
}
}; };
struct Vector3f { struct Vector3f {
atVec3f val; atVec3f val;
@ -110,6 +118,11 @@ struct Vector3f {
void read(Connection& conn); void read(Connection& conn);
Vector3f(Connection& conn) { read(conn); } Vector3f(Connection& conn) { read(conn); }
operator const atVec3f&() const { return val; } operator const atVec3f&() const { return val; }
bool operator==(const Vector3f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1] &&
val.simd[2] == other.val.simd[2];
}
}; };
struct Vector4f { struct Vector4f {
atVec4f val; atVec4f val;
@ -117,6 +130,12 @@ struct Vector4f {
void read(Connection& conn); void read(Connection& conn);
Vector4f(Connection& conn) { read(conn); } Vector4f(Connection& conn) { read(conn); }
operator const atVec4f&() const { return val; } operator const atVec4f&() const { return val; }
bool operator==(const Vector4f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1] &&
val.simd[2] == other.val.simd[2] &&
val.simd[3] == other.val.simd[3];
}
}; };
struct Matrix3f { struct Matrix3f {
atVec3f m[3]; atVec3f m[3];
@ -137,26 +156,137 @@ struct Index {
Index(Connection& conn) { read(conn); } Index(Connection& conn) { read(conn); }
operator const uint32_t&() const { return val; } operator const uint32_t&() const { return val; }
}; };
struct Float {
float val;
Float() = default;
void read(Connection& conn);
Float(Connection& conn) { read(conn); }
operator const float&() const { return val; }
};
struct Boolean {
bool val;
Boolean() = default;
void read(Connection& conn);
Boolean(Connection& conn) { read(conn); }
operator const bool&() const { return val; }
};
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec); atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec); atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec);
/** HECL source and metadata of each material */ /** Intermediate material representation */
struct Material { struct Material {
std::string name; enum class ShaderType : uint32_t {
std::string source; Invalid = 0,
std::vector<ProjectPath> texs; RetroShader = 'RSHD',
std::unordered_map<std::string, int32_t> iprops; RetroDynamicShader = 'RDYN',
bool transparent; RetroDynamicAlphaShader = 'RDAL',
RetroDynamicCharacterShader = 'RCHR',
};
enum class ChunkType : uint32_t {
Invalid = 0,
TexturePass = 'PASS',
ColorPass = 'CLR ',
};
enum class PassType : uint32_t {
Invalid = 0,
Lightmap = 'LMAP',
Diffuse = 'DIFF',
Emissive = 'EMIS',
Specular = 'SPEC',
ExtendedSpecular = 'ESPC',
Reflection = 'REFL',
IndirectTex = 'INDR',
Alpha = 'ALPH',
};
static constexpr std::string_view PassTypeToString(PassType tp) {
switch (tp) {
case PassType::Lightmap: return "lightmap"sv;
case PassType::Diffuse: return "diffuse"sv;
case PassType::Emissive: return "emissive"sv;
case PassType::Specular: return "specular"sv;
case PassType::ExtendedSpecular: return "extendedSpecular"sv;
case PassType::Reflection: return "reflection"sv;
case PassType::Alpha: return "alpha"sv;
default:
assert(false && "Unknown pass type");
return ""sv;
}
}
enum class UVAnimType : uint8_t {
MvInvNoTranslation,
MvInv,
Scroll,
Rotation,
HStrip,
VStrip,
Model,
CylinderEnvironment,
Eight,
Invalid = UINT8_MAX
};
using TexCoordSource = hecl::Backend::TexCoordSource;
struct PASS : hecl::TypedRecord<ChunkType::TexturePass> {
PassType type;
ProjectPath tex;
TexCoordSource source;
UVAnimType uvAnimType;
std::array<float, 9> uvAnimParms = {};
bool alpha;
explicit PASS(Connection& conn);
bool operator==(const PASS& other) const {
return type == other.type &&
tex == other.tex &&
source == other.source &&
uvAnimType == other.uvAnimType &&
uvAnimParms == other.uvAnimParms &&
alpha == other.alpha;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
XXH64_update(st, &source, sizeof(source));
XXH64_update(st, &uvAnimType, sizeof(uvAnimType));
XXH64_update(st, &alpha, sizeof(alpha));
}
};
struct CLR : hecl::TypedRecord<ChunkType::ColorPass> {
PassType type;
Vector4f color;
explicit CLR(Connection& conn);
bool operator==(const CLR& other) const {
return type == other.type && color == other.color;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
}
};
using Chunk = hecl::TypedVariant<PASS, CLR>;
Material(Connection& conn); enum class BlendMode : uint32_t {
Opaque = 0,
Alpha = 1,
Additive = 2
};
std::string name;
uint32_t passIndex;
ShaderType shaderType;
std::vector<Chunk> chunks;
std::unordered_map<std::string, int32_t> iprops;
BlendMode blendMode = BlendMode::Opaque;
explicit Material(Connection& conn);
bool operator==(const Material& other) const { bool operator==(const Material& other) const {
return source == other.source && texs == other.texs && iprops == other.iprops; return chunks == other.chunks && iprops == other.iprops && blendMode == other.blendMode;
} }
}; };
/** Intermediate mesh representation prepared by blender from a single mesh object */ /** Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh { struct Mesh {
static constexpr size_t MaxColorLayers = 4;
static constexpr size_t MaxUVLayers = 8;
static constexpr size_t MaxSkinEntries = 16;
HMDLTopology topology; HMDLTopology topology;
/* Object transform in scene */ /* Object transform in scene */
@ -181,19 +311,30 @@ struct Mesh {
/* Skinning data */ /* Skinning data */
std::vector<std::string> boneNames; std::vector<std::string> boneNames;
struct SkinBind { struct SkinBind {
uint32_t boneIdx; uint32_t vg_idx = UINT32_MAX;
float weight; float weight = 0.f;
SkinBind(Connection& conn); SkinBind() = default;
explicit SkinBind(Connection& conn);
operator bool() const { return vg_idx != UINT32_MAX; }
}; };
std::vector<std::vector<SkinBind>> skins; std::vector<std::array<SkinBind, MaxSkinEntries>> skins;
std::vector<size_t> contiguousSkinVertCounts; std::vector<size_t> contiguousSkinVertCounts;
static size_t countSkinBinds(const std::array<SkinBind, MaxSkinEntries>& arr) {
size_t ret = 0;
for (const auto& b : arr)
if (b)
++ret;
else
break;
return ret;
}
void normalizeSkinBinds(); void normalizeSkinBinds();
/** Islands of the same material/skinBank are represented here */ /** Islands of the same material/skinBank are represented here */
struct Surface { struct Surface {
Vector3f centroid; Vector3f centroid;
Index materialIdx; uint32_t materialIdx;
Vector3f aabbMin; Vector3f aabbMin;
Vector3f aabbMax; Vector3f aabbMax;
Vector3f reflectionNormal; Vector3f reflectionNormal;
@ -208,12 +349,9 @@ struct Mesh {
uint32_t iSkin = 0xffffffff; uint32_t iSkin = 0xffffffff;
uint32_t iBankSkin = 0xffffffff; uint32_t iBankSkin = 0xffffffff;
Vert(Connection& conn, const Mesh& parent);
bool operator==(const Vert& other) const; bool operator==(const Vert& other) const;
}; };
std::vector<Vert> verts; std::vector<Vert> verts;
Surface(Connection& conn, Mesh& parent, int skinSlotCount);
}; };
std::vector<Surface> surfaces; std::vector<Surface> surfaces;
@ -225,15 +363,13 @@ struct Mesh {
std::vector<uint32_t> m_boneIdxs; std::vector<uint32_t> m_boneIdxs;
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs); void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
size_t lookupLocalBoneIdx(uint32_t boneIdx) const;
}; };
std::vector<Bank> banks; std::vector<Bank> banks;
std::vector<Bank>::iterator addSkinBank(int skinSlotCount); std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount); uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
} skinBanks; } skinBanks;
using SurfProgFunc = std::function<void(int)>; Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, bool useLuvs = false);
Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, SurfProgFunc& surfProg);
Mesh getContiguousSkinningVersion() const; Mesh getContiguousSkinningVersion() const;
@ -486,11 +622,10 @@ public:
static const char* MeshOutputModeString(HMDLTopology topology); static const char* MeshOutputModeString(HMDLTopology topology);
/** Compile mesh by context (MESH blends only) */ /** Compile mesh by context (MESH blends only) */
Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10, Mesh::SurfProgFunc surfProg = [](int) {}); Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10);
/** Compile mesh by name (AREA blends only) */ /** Compile mesh by name (AREA blends only) */
Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false, Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false);
Mesh::SurfProgFunc surfProg = [](int) {});
/** Compile collision mesh by name (AREA blends only) */ /** Compile collision mesh by name (AREA blends only) */
ColMesh compileColMesh(std::string_view name); ColMesh compileColMesh(std::string_view name);
@ -498,10 +633,6 @@ public:
/** Compile all meshes as collision meshes (CMESH blends only) */ /** Compile all meshes as collision meshes (CMESH blends only) */
std::vector<ColMesh> compileColMeshes(); std::vector<ColMesh> compileColMeshes();
/** Compile all meshes into one (AREA blends only) */
Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount = 10, float maxOctantLength = 5.0,
Mesh::SurfProgFunc surfProg = [](int) {});
/** Compile world intermediate (WORLD blends only) */ /** Compile world intermediate (WORLD blends only) */
World compileWorld(); World compileWorld();
@ -558,6 +689,8 @@ class Connection {
friend struct Matrix3f; friend struct Matrix3f;
friend struct Matrix4f; friend struct Matrix4f;
friend struct Index; friend struct Index;
friend struct Float;
friend struct Boolean;
std::atomic_bool m_lock = {false}; std::atomic_bool m_lock = {false};
bool m_pyStreamActive = false; bool m_pyStreamActive = false;
@ -571,7 +704,6 @@ class Connection {
#endif #endif
int m_readpipe[2]; int m_readpipe[2];
int m_writepipe[2]; int m_writepipe[2];
bool m_hasSlerp;
BlendType m_loadedType = BlendType::None; BlendType m_loadedType = BlendType::None;
bool m_loadedRigged = false; bool m_loadedRigged = false;
ProjectPath m_loadedBlend; ProjectPath m_loadedBlend;
@ -595,8 +727,6 @@ public:
Connection(Connection&&) = delete; Connection(Connection&&) = delete;
Connection& operator=(Connection&&) = delete; Connection& operator=(Connection&&) = delete;
bool hasSLERP() const { return m_hasSlerp; }
bool createBlend(const ProjectPath& path, BlendType type); bool createBlend(const ProjectPath& path, BlendType type);
BlendType getBlendType() const { return m_loadedType; } BlendType getBlendType() const { return m_loadedType; }
const ProjectPath& getBlendPath() const { return m_loadedBlend; } const ProjectPath& getBlendPath() const { return m_loadedBlend; }
@ -659,3 +789,46 @@ public:
}; };
} // namespace hecl::blender } // namespace hecl::blender
namespace std {
template <>
struct hash<hecl::blender::Vector2f> {
size_t operator()(const hecl::blender::Vector2f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector3f> {
size_t operator()(const hecl::blender::Vector3f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector4f> {
size_t operator()(const hecl::blender::Vector4f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[3]));
return h;
}
};
template <>
struct hash<array<hecl::blender::Mesh::SkinBind, 16>> {
size_t operator()(const array<hecl::blender::Mesh::SkinBind, 16>& val) const noexcept {
size_t h = 0;
for (const auto& bind : val) {
if (!bind)
break;
hecl::hash_combine_impl(h, std::hash<float>()(bind.vg_idx));
hecl::hash_combine_impl(h, std::hash<float>()(bind.weight));
}
return h;
}
};
}

View File

@ -102,14 +102,13 @@ public:
(void)progress; (void)progress;
} }
virtual bool canCook(const ProjectPath& path, blender::Token& btok, int cookPass = -1) { virtual bool canCook(const ProjectPath& path, blender::Token& btok) {
(void)path; (void)path;
LogModule.report(logvisor::Error, "not implemented"); LogModule.report(logvisor::Error, "not implemented");
(void)cookPass;
return false; return false;
} }
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, const Database::DataSpecEntry* oldEntry, virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path,
blender::Token& btok) const { const Database::DataSpecEntry* oldEntry) const {
(void)path; (void)path;
return oldEntry; return oldEntry;
} }
@ -151,12 +150,11 @@ struct DataSpecEntry {
SystemStringView m_name; SystemStringView m_name;
SystemStringView m_desc; SystemStringView m_desc;
SystemStringView m_pakExt; SystemStringView m_pakExt;
int m_numCookPasses;
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory; std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses, DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory) std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses), m_factory(std::move(factory)) {} : m_name(name), m_desc(desc), m_pakExt(pakExt), m_factory(std::move(factory)) {}
}; };
/** /**
@ -399,10 +397,6 @@ public:
* @param fast enables faster (draft) extraction for supported data types * @param fast enables faster (draft) extraction for supported data types
* @param spec if non-null, cook using a manually-selected dataspec * @param spec if non-null, cook using a manually-selected dataspec
* @param cp if non-null, cook asynchronously via the ClientProcess * @param cp if non-null, cook asynchronously via the ClientProcess
* @param cookPass cookPath() should be called the number of times
* prescribed in DataSpecEntry at the root-most invocation.
* This value conveys the pass index through the call tree.
* Negative values mean "cook always".
* @return true on success * @return true on success
* *
* Object cooking is generally an expensive process for large projects. * Object cooking is generally an expensive process for large projects.
@ -410,8 +404,8 @@ public:
* feedback delivered via feedbackCb. * feedback delivered via feedbackCb.
*/ */
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false, bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr, bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr,
int cookPass = -1); ClientProcess* cp = nullptr);
/** /**
* @brief Begin package process for specified !world.blend or directory * @brief Begin package process for specified !world.blend or directory

View File

@ -22,11 +22,11 @@ protected:
}; };
public: public:
FourCC() /* Sentinel FourCC */ constexpr FourCC() /* Sentinel FourCC */
: num(0) {} : num(0) {}
FourCC(const FourCC& other) { num = other.num; } constexpr FourCC(const FourCC& other) : num(other.num) {}
FourCC(const char* name) : num(*(uint32_t*)name) {} constexpr FourCC(const char* name) : num(*(uint32_t*)name) {}
FourCC(uint32_t n) : num(n) {} constexpr FourCC(uint32_t n) : num(n) {}
bool operator==(const FourCC& other) const { return num == other.num; } bool operator==(const FourCC& other) const { return num == other.num; }
bool operator!=(const FourCC& other) const { return num != other.num; } bool operator!=(const FourCC& other) const { return num != other.num; }
bool operator==(const char* other) const { return num == *(uint32_t*)other; } bool operator==(const char* other) const { return num == *(uint32_t*)other; }

View File

@ -1,348 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <forward_list>
#include <athena/Types.hpp>
#include <athena/DNA.hpp>
#include <hecl/hecl.hpp>
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
namespace hecl::Frontend {
using namespace std::literals;
struct SourceLocation {
int line = -1;
int col = -1;
SourceLocation() = default;
SourceLocation(int l, int c) : line(l), col(c) {}
};
class Diagnostics {
std::string m_name;
std::string m_source;
std::string m_backend = "Backend";
std::string sourceDiagString(const SourceLocation& l, bool ansi = false) const;
public:
void reset(std::string_view name, std::string_view source) {
m_name = name;
m_source = source;
}
void reset(std::string_view name) {
m_name = name;
m_source.clear();
}
void setBackend(std::string_view backend) { m_backend = backend; }
void reportScannerErr(const SourceLocation& l, const char* format, ...);
void reportParserErr(const SourceLocation& l, const char* format, ...);
void reportBackendErr(const SourceLocation& l, const char* format, ...);
std::string_view getName() const { return m_name; }
std::string_view getSource() const { return m_source; }
};
struct Token {
enum class Kind { None, Eof, Lf, Plus, Minus, Times, Div, Lpar, Rpar, Comma, Period, Ident, Number };
static std::string_view KindToStr(Kind k) {
switch (k) {
case Kind::None:
default:
return "none"sv;
case Kind::Eof:
return "eof"sv;
case Kind::Lf:
return "lf"sv;
case Kind::Plus:
return "+"sv;
case Kind::Minus:
return "-"sv;
case Kind::Times:
return "*"sv;
case Kind::Div:
return "/"sv;
case Kind::Lpar:
return "("sv;
case Kind::Rpar:
return ")"sv;
case Kind::Comma:
return ","sv;
case Kind::Period:
return "."sv;
case Kind::Ident:
return "ident"sv;
case Kind::Number:
return "number"sv;
}
}
Kind kind = Kind::None;
std::string str;
SourceLocation loc;
Token() = default;
Token(Kind kind, const SourceLocation& loc) : kind(kind), loc(loc) {}
Token(Kind kind, std::string&& str, const SourceLocation& loc) : kind(kind), str(std::move(str)), loc(loc) {}
std::string toString() const {
if (str.empty())
return hecl::Format("%d:%d: %s", loc.line, loc.col, KindToStr(kind).data());
else
return hecl::Format("%d:%d: %s (%s)", loc.line, loc.col, KindToStr(kind).data(), str.c_str());
}
};
class Scanner {
friend class Parser;
static constexpr char LF = '\n';
static constexpr char COMMENT = '#';
Diagnostics& m_diag;
std::string_view m_source;
std::string_view::const_iterator m_sourceIt;
char ch;
SourceLocation loc;
int lfcol;
std::string lastLine;
std::string currentLine;
Token::Kind CharToTokenKind(char ch) {
switch (ch) {
case '(':
return Token::Kind::Lpar;
case ')':
return Token::Kind::Rpar;
case ',':
return Token::Kind::Comma;
case '.':
return Token::Kind::Period;
case '+':
return Token::Kind::Plus;
case '-':
return Token::Kind::Minus;
case '*':
return Token::Kind::Times;
case '/':
return Token::Kind::Div;
default:
return Token::Kind::None;
}
}
template <class... Args>
void error(const SourceLocation& loc, const char* s, Args&&... args) {
m_diag.reportScannerErr(loc, s, args...);
}
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
static bool isStartIdent(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_'); }
static bool isMidIdent(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') || isDigit(c);
}
int _read();
bool read();
static char chr(char c) { return (c < 32) ? '.' : c; }
public:
Scanner(Diagnostics& diag) : m_diag(diag) {}
void reset(std::string_view in) {
m_source = in;
m_sourceIt = in.cbegin();
ch = 0;
loc.line = 1;
loc.col = 0;
lfcol = 0;
lastLine = std::string();
currentLine = std::string();
read();
}
Token next();
};
struct IRNode {
friend struct IR;
enum class Op { Add, Sub, Mul, Div };
enum class Kind { None, Call, Imm, Binop, Swizzle };
Kind kind = Kind::None;
std::string str;
float val;
Op op;
std::unique_ptr<IRNode> left;
std::unique_ptr<IRNode> right;
std::list<IRNode> children;
SourceLocation loc;
static std::string_view OpToStr(Op op) {
switch (op) {
case Op::Add:
return "+"sv;
case Op::Sub:
return "-"sv;
case Op::Mul:
return "*"sv;
case Op::Div:
return "/"sv;
default:
return ""sv;
}
}
static std::string_view KindToStr(Kind k) {
switch (k) {
case Kind::None:
default:
return "none"sv;
case Kind::Call:
return "call"sv;
case Kind::Imm:
return "imm"sv;
case Kind::Binop:
return "binop"sv;
case Kind::Swizzle:
return "swizzle"sv;
}
}
IRNode() = default;
IRNode(Kind kind, std::string&& str, const SourceLocation& loc) : kind(kind), str(std::move(str)), loc(loc) {}
IRNode(Kind kind, float val, const SourceLocation& loc) : kind(kind), val(val), loc(loc) {}
IRNode(Kind kind, std::string&& str, std::list<IRNode>&& children, const SourceLocation& loc)
: kind(kind), str(std::move(str)), children(std::move(children)), loc(loc) {}
IRNode(Op op, IRNode&& left, IRNode&& right, const SourceLocation& loc)
: kind(Kind::Binop), op(op), left(new IRNode(std::move(left))), right(new IRNode(std::move(right))), loc(loc) {}
IRNode(Kind kind, std::string&& str, IRNode&& node, const SourceLocation& loc)
: kind(kind), str(std::move(str)), left(new IRNode(std::move(node))), loc(loc) {}
std::string toString(bool stripUVAnims = false) const { return fmt(0, stripUVAnims); }
private:
static std::string rep(int n, std::string_view s);
std::string fmt(int level, bool stripUVAnims) const;
std::string describe() const;
};
class Parser {
Scanner m_scanner;
Token t;
Token la;
Token::Kind sym;
void scan() {
t = la;
la = m_scanner.next();
sym = la.kind;
}
template <class... Args>
void error(const char* s, Args&&... args) {
m_scanner.m_diag.reportParserErr(la.loc, s, args...);
}
void check(Token::Kind expected);
IRNode call();
static bool imm(const IRNode& a, const IRNode& b);
IRNode expr();
IRNode sum();
IRNode factor();
IRNode value();
public:
Parser(Diagnostics& diag) : m_scanner(diag) {}
void reset(std::string_view in) {
la = Token();
m_scanner.reset(in);
}
std::list<IRNode> parse();
};
using BigDNA = athena::io::DNA<athena::Big>;
struct IR : BigDNA {
AT_DECL_EXPLICIT_DNA
enum OpType : uint8_t {
None, /**< NOP */
Call, /**< Deferred function insertion for HECL backend using specified I/O regs */
LoadImm, /**< Load a constant (numeric literal) into register */
Arithmetic, /**< Perform binary arithmetic between registers */
Swizzle /**< Vector insertion/extraction/swizzling operation */
};
using RegID = atUint16;
struct Instruction : BigDNA {
AT_DECL_EXPLICIT_DNA
OpType m_op = OpType::None;
RegID m_target = RegID(-1);
SourceLocation m_loc;
struct Call : BigDNA {
AT_DECL_DNA
String<-1> m_name;
Value<atUint16> m_argInstCount;
Vector<atUint16, AT_DNA_COUNT(m_argInstCount)> m_argInstIdxs;
} m_call;
struct LoadImm : BigDNA {
AT_DECL_DNA
Value<atVec4f> m_immVec = {};
} m_loadImm;
enum ArithmeticOpType : uint8_t { None, Add, Subtract, Multiply, Divide };
struct Arithmetic : BigDNA {
AT_DECL_DNA
Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
Value<atUint16> m_instIdxs[2];
} m_arithmetic;
struct Swizzle : BigDNA {
AT_DECL_DNA
Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
Value<atUint16> m_instIdx;
} m_swizzle;
Instruction(OpType type, RegID target, const SourceLocation& loc) : m_op(type), m_target(target), m_loc(loc) {}
int getChildCount() const;
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
const atVec4f& getImmVec() const;
Instruction(athena::io::IStreamReader& reader) { read(reader); }
};
atUint64 m_hash = 0;
atUint16 m_regCount = 0;
std::vector<Instruction> m_instructions;
boo::BlendFactor m_blendSrc = boo::BlendFactor::One;
boo::BlendFactor m_blendDst = boo::BlendFactor::Zero;
bool m_doAlpha = false;
static atInt8 swizzleCompIdx(char aChar);
int addInstruction(const IRNode& n, IR::RegID target);
};
class Frontend {
Diagnostics m_diag;
Parser m_parser;
public:
IR compileSource(std::string_view source, std::string_view diagName);
Diagnostics& getDiagnostics() { return m_diag; }
Frontend() : m_parser(m_diag) {}
};
} // namespace hecl::Frontend

View File

@ -3,9 +3,6 @@
#include <type_traits> #include <type_traits>
#include <cassert> #include <cassert>
#include "hecl/hecl.hpp" #include "hecl/hecl.hpp"
#include "hecl/Backend/GLSL.hpp"
#include "hecl/Backend/HLSL.hpp"
#include "hecl/Backend/Metal.hpp"
#include "PipelineBase.hpp" #include "PipelineBase.hpp"
/* CMake-curated rep classes for the application */ /* CMake-curated rep classes for the application */
@ -28,108 +25,7 @@ public:
} }
boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; } boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; }
}; };
#endif
class HECLIR : public PipelineRep<PlatformType::Null> {
const hecl::Backend::IR& m_ir;
const hecl::Backend::ShaderTag& m_tag;
const hecl::Backend::ExtensionSlot& m_extension;
uint64_t m_hash;
public:
HECLIR(const hecl::Backend::IR& ir, const hecl::Backend::ShaderTag& tag,
const hecl::Backend::ExtensionSlot& extension)
: m_ir(ir), m_tag(tag), m_extension(extension) {
m_hash = tag.val64();
m_hash ^= extension.hash();
}
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
const hecl::Backend::IR& ir() const { return m_ir; }
const hecl::Backend::ShaderTag& tag() const { return m_tag; }
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
};
template <typename P, class BackendTp>
class HECLBackendImpl : public PipelineRep<P> {
hecl::Backend::ShaderTag m_tag;
BackendTp m_backend;
const hecl::Backend::ExtensionSlot& m_extension;
public:
static constexpr bool HasHash = false;
HECLBackendImpl(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLIR& in)
: m_tag(in.tag()), m_extension(in.extension()) {
hecl::Backend::Diagnostics diag;
m_backend.reset(in.ir(), diag);
}
std::string makeVert() const {
return m_backend.makeVert(m_tag.getColorCount(), m_tag.getUvCount(), m_tag.getWeightCount(),
m_tag.getSkinSlotCount(), m_extension.texCount, m_extension.texs,
m_extension.noReflection ? Backend::ReflectionType::None : m_tag.getReflectionType());
}
std::string makeFrag() const {
return m_backend.makeFrag(m_extension.blockCount, m_extension.blockNames,
m_tag.getAlphaTest() || m_extension.forceAlphaTest,
m_extension.noReflection ? Backend::ReflectionType::None : m_tag.getReflectionType(),
m_backend.m_blendSrc, m_backend.m_blendDst, m_extension.lighting, m_extension.post,
m_extension.texCount, m_extension.texs, m_extension.diffuseOnly);
}
const hecl::Backend::ShaderTag& getTag() const { return m_tag; }
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
std::pair<hecl::Backend::BlendFactor, hecl::Backend::BlendFactor> blendFactors() const {
return {m_backend.m_blendSrc, m_backend.m_blendDst};
}
};
template <typename P>
class HECLBackend : public PipelineRep<P> {
public:
static constexpr bool HasHash = false;
};
template <>
class HECLBackend<PlatformType::OpenGL> : public HECLBackendImpl<PlatformType::OpenGL, hecl::Backend::GLSL> {
public:
using HECLBackendImpl::HECLBackendImpl;
};
template <>
class HECLBackend<PlatformType::Vulkan> : public HECLBackendImpl<PlatformType::Vulkan, hecl::Backend::GLSL> {
public:
using HECLBackendImpl::HECLBackendImpl;
};
template <>
class HECLBackend<PlatformType::D3D11> : public HECLBackendImpl<PlatformType::D3D11, hecl::Backend::HLSL> {
public:
using HECLBackendImpl::HECLBackendImpl;
};
template <>
class HECLBackend<PlatformType::Metal> : public HECLBackendImpl<PlatformType::Metal, hecl::Backend::Metal> {
public:
using HECLBackendImpl::HECLBackendImpl;
};
template <>
class HECLBackend<PlatformType::NX> : public HECLBackendImpl<PlatformType::NX, hecl::Backend::GLSL> {
public:
using HECLBackendImpl::HECLBackendImpl;
};
template <template <typename, typename> class T, typename P, typename... Rest>
StageCollection<T<P, Rest...>>::StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in) {
m_vertex = conv.getVertexConverter().convert(ctx, StageSourceText<P, PipelineStage::Vertex>(in.makeVert()));
m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText<P, PipelineStage::Fragment>(in.makeFrag()));
m_vtxFmtData = in.getTag().vertexFormat();
m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data());
m_additionalInfo = in.getTag().additionalInfo(in.extension(), in.blendFactors());
MakeHash();
}
#if HECL_RUNTIME
template <typename P> template <typename P>
class FinalPipeline : public PipelineRep<P> { class FinalPipeline : public PipelineRep<P> {
boo::ObjToken<boo::IShaderPipeline> m_pipeline; boo::ObjToken<boo::IShaderPipeline> m_pipeline;
@ -155,10 +51,9 @@ struct ShaderDB {};
#define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>, #define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>,
#define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \ #define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \
HECLBackend<P>, \
STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \ STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \
STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) FinalPipeline<P>, STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) FinalPipeline<P>,
#define PIPELINE_OFFLINE_SPECIALIZATIONS(P) HECLBackend<P>, STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) #define PIPELINE_OFFLINE_SPECIALIZATIONS(P) STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P)
#define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \ #define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \
StageBinary<P, S>, HECL_APPLICATION_STAGE_REPS(P, S) StageRuntimeObject<P, S>, StageBinary<P, S>, HECL_APPLICATION_STAGE_REPS(P, S) StageRuntimeObject<P, S>,
#define STAGE_OFFLINE_SPECIALIZATIONS(P, S) HECL_APPLICATION_STAGE_REPS(P, S) #define STAGE_OFFLINE_SPECIALIZATIONS(P, S) HECL_APPLICATION_STAGE_REPS(P, S)

View File

@ -88,9 +88,6 @@ public:
template <typename P> template <typename P>
class FinalPipeline; class FinalPipeline;
template <typename P>
class HECLBackend;
template <class T> template <class T>
using __IsStageSubclass = using __IsStageSubclass =
typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>, typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>,
@ -138,7 +135,6 @@ public:
m_hash ^= m_evaluation.Hash(); m_hash ^= m_evaluation.Hash();
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0); m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
} }
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in);
template <typename I> template <typename I>
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in, StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) { typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) {

View File

@ -0,0 +1,260 @@
#pragma once
#include <variant>
#include <cassert>
#include "athena/DNA.hpp"
/*
* The TypedVariant system is a type-safe union implementation capable of selecting
* a participating field type based on an enumeration constant. As an extension of
* std::variant, the usage pattern is similar to that of std::visit. A key difference
* with TypedVariant is the monostate is implicitly supplied as the first argument.
* The monostate will cause the visit implementation to function as a no-op, returning
* a specified default value.
*/
/* Example user code:
enum class ChunkType {
Invalid = 0,
TYP1 = SBIG('TYP1'),
TYP2 = SBIG('TYP2'),
TYP3 = SBIG('TYP3'),
};
struct TYP1 : TypedRecord<ChunkType::TYP1> {
static void PrintMe() { std::cout << "TYP1" << std::endl; }
};
struct TYP2 : TypedRecord<ChunkType::TYP2> {
static void PrintMe() { std::cout << "TYP2" << std::endl; }
};
struct TYP3 : TypedRecord<ChunkType::TYP3> {
static void PrintMe() { std::cout << "TYP3" << std::endl; }
};
using ChunkVariant = TypedVariant<TYP1, TYP2, TYP3>;
int main(int argc, char** argv) {
volatile ChunkType tp = ChunkType::TYP2;
auto var = ChunkVariant::Build(tp);
var.visit([](auto& arg) { arg.PrintMe(); });
return 0;
}
*/
namespace hecl {
template<auto _Type>
struct TypedRecord {
using TypedType = std::integral_constant<decltype(_Type), _Type>;
static constexpr auto variant_type() { return _Type; }
static constexpr bool is_monostate() { return false; }
};
template<typename _Enum>
struct TypedMonostate : TypedRecord<_Enum(0)> {
static constexpr bool is_monostate() { return true; }
bool operator==(const TypedMonostate& other) const { return true; }
bool operator!=(const TypedMonostate& other) const { return false; }
};
template<typename... _Types>
class _TypedVariant : public std::variant<_Types...> {
public:
using base = std::variant<_Types...>;
using EnumType = typename std::variant_alternative_t<0, std::variant<_Types...>>::TypedType::value_type;
private:
template<typename _Type, typename _Variant>
struct _Match;
template<typename _Type, typename _First, typename... _Rest>
struct _Match<_Type, std::variant<_First, _Rest...>>
: _Match<_Type, std::variant<_Rest...>> {};
template<typename _First, typename... _Rest>
struct _Match<typename _First::TypedType, std::variant<_First, _Rest...>>
{ using type = _First; };
public:
template<auto _Type>
using Match = typename _Match<std::integral_constant<decltype(_Type), _Type>, std::variant<_Types...>>::type;
private:
template<typename _First, typename... _Rest>
struct _Builder {
template<typename... _Args>
static constexpr _TypedVariant _Build(EnumType tp, _Args&&... args) {
//static_assert(std::is_constructible_v<_First, _Args...>,
// "All variant types must be constructible with the same parameters.");
if (_First::TypedType::value == tp) {
if constexpr (std::is_constructible_v<_First, _Args...>) {
return {_First(std::forward<_Args>(args)...)};
} else {
assert(false && "Variant not constructible with supplied args.");
return {};
}
}
else if constexpr (sizeof...(_Rest) > 0)
return _Builder<_Rest...>::template _Build<_Args...>(tp, std::forward<_Args>(args)...);
return {};
}
template<typename... _Args>
static constexpr _TypedVariant _BuildSkip(EnumType tp, _Args&&... args) {
/* This prevents selecting the monostate explicitly (so constructor arguments aren't passed) */
if constexpr (sizeof...(_Rest) > 0)
return _Builder<_Rest...>::template _Build<_Args...>(tp, std::forward<_Args>(args)...);
return {};
}
};
public:
template<typename... _Args>
static constexpr _TypedVariant Build(EnumType tp, _Args&&... args) {
return _TypedVariant::_Builder<_Types...>::template _BuildSkip<_Args...>(tp, std::forward<_Args>(args)...);
}
template<typename _Return, typename _Visitor>
constexpr auto visit(_Visitor&& visitor, _Return&& def) {
return std::visit([&](auto& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (!T::is_monostate())
return visitor(arg);
return def;
}, static_cast<base&>(*this));
}
template<typename _Visitor>
constexpr void visit(_Visitor&& visitor) {
std::visit([&](auto& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (!T::is_monostate())
visitor(arg);
}, static_cast<base&>(*this));
}
template<typename _Return, typename _Visitor>
constexpr auto visit(_Visitor&& visitor, _Return&& def) const {
return std::visit([&](const auto& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (!T::is_monostate())
return visitor(arg);
return def;
}, static_cast<const base&>(*this));
}
template<typename _Visitor>
constexpr void visit(_Visitor&& visitor) const {
std::visit([&](const auto& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (!T::is_monostate())
visitor(arg);
}, static_cast<const base&>(*this));
}
template<typename T>
constexpr T& get() { return std::get<T>(*this); }
template<typename T>
constexpr const T& get() const { return std::get<T>(*this); }
template<typename T>
constexpr std::add_pointer_t<T> get_if() noexcept { return std::get_if<T>(this); }
template<typename T>
constexpr std::add_pointer_t<const T> get_if() const noexcept { return std::get_if<T>(this); }
template<typename T>
constexpr bool holds_alternative() const noexcept { return std::holds_alternative<T>(*this); }
constexpr EnumType variant_type() const {
return std::visit([](const auto& arg) {
return arg.variant_type();
}, static_cast<const base&>(*this));
}
constexpr operator bool() const noexcept {
return !std::holds_alternative<typename std::variant_alternative_t<0, std::variant<_Types...>>>(*this);
}
};
template<typename... _Types>
using TypedVariant = _TypedVariant<TypedMonostate<typename std::variant_alternative_t
<0, std::variant<_Types...>>::TypedType::value_type>, _Types...>;
/* DNA support below here */
template<auto _Type>
struct TypedRecordBigDNA : BigDNA, TypedRecord<_Type> {};
template<typename... _Types>
struct TypedVariantBigDNA : BigDNA, TypedVariant<_Types...> {
AT_DECL_EXPLICIT_DNA_YAML
template<typename... _Args>
static constexpr TypedVariantBigDNA Build(typename TypedVariant<_Types...>::EnumType tp, _Args&&... args) {
return TypedVariantBigDNA(TypedVariant<_Types...>::Build(tp, std::forward<_Args>(args)...));
}
TypedVariantBigDNA() = default;
private:
TypedVariantBigDNA(TypedVariant<_Types...> var) : TypedVariant<_Types...>(std::move(var)) {}
};
#define AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(...) \
template <> \
template <> \
inline void hecl::TypedVariantBigDNA<__VA_ARGS__>::Enumerate<athena::io::DNA<athena::Big>::Read>(typename Read::StreamT & r) { \
EnumType variant_type = {}; \
Do<athena::io::DNA<athena::Big>::Read>({"variant_type"}, variant_type, r); \
static_cast<TypedVariant<__VA_ARGS__>&>(*this) = Build(variant_type); \
visit([&](auto& var) { var.read(r); }); \
} \
\
template <> \
template <> \
inline void hecl::TypedVariantBigDNA<__VA_ARGS__>::Enumerate<athena::io::DNA<athena::Big>::Write>(typename Write::StreamT & w) { \
visit([&](auto& var) { \
using T = std::decay_t<decltype(var)>; \
EnumType variant_type = T::variant_type(); \
Do<athena::io::DNA<athena::Big>::Write>({"variant_type"}, variant_type, w); \
var.write(w); \
}); \
} \
\
template <> \
template <> \
inline void hecl::TypedVariantBigDNA<__VA_ARGS__>::Enumerate<athena::io::DNA<athena::Big>::BinarySize>(typename BinarySize::StreamT & sz) { \
visit([&](auto& var) { \
using T = std::decay_t<decltype(var)>; \
EnumType variant_type = T::variant_type(); \
Do<athena::io::DNA<athena::Big>::BinarySize>({"variant_type"}, variant_type, sz); \
var.binarySize(sz); \
}); \
} \
template <> \
inline const char* hecl::TypedVariantBigDNA<__VA_ARGS__>::DNAType() { \
return "hecl::TypedVariantBigDNA<" #__VA_ARGS__ ">"; \
}
#define AT_SPECIALIZE_TYPED_VARIANT_BIGDNA_YAML(...) \
AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(__VA_ARGS__) \
template <> \
template <> \
inline void hecl::TypedVariantBigDNA<__VA_ARGS__>::Enumerate<athena::io::DNA<athena::Big>::ReadYaml>(typename ReadYaml::StreamT & r) { \
EnumType variant_type = {}; \
Do<athena::io::DNA<athena::Big>::ReadYaml>({"variant_type"}, variant_type, r); \
static_cast<TypedVariant<__VA_ARGS__>&>(*this) = Build(variant_type); \
visit([&](auto& var) { var.read(r); }); \
} \
\
template <> \
template <> \
inline void hecl::TypedVariantBigDNA<__VA_ARGS__>::Enumerate<athena::io::DNA<athena::Big>::WriteYaml>(typename WriteYaml::StreamT & w) { \
visit([&](auto& var) { \
using T = std::decay_t<decltype(var)>; \
EnumType variant_type = T::variant_type(); \
Do<athena::io::DNA<athena::Big>::WriteYaml>({"variant_type"}, variant_type, w); \
var.write(w); \
}); \
}
}

View File

@ -1313,6 +1313,11 @@ static inline double SBig(double val) { return val; }
#endif #endif
#endif #endif
template <typename SizeT>
constexpr void hash_combine_impl(SizeT& seed, SizeT value) {
seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
} // namespace hecl } // namespace hecl
namespace std { namespace std {

View File

@ -1,12 +0,0 @@
if(NOT WINDOWS_STORE)
list(APPEND PLAT_SRCS GLSL.cpp HLSL.cpp Metal.cpp)
else()
list(APPEND PLAT_SRCS HLSL.cpp)
endif()
set(BACKEND_SOURCES
GX.cpp
ProgrammableCommon.cpp
${PLAT_SRCS})
hecl_add_list(Backend BACKEND_SOURCES)

View File

@ -115,13 +115,13 @@ std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionC
if (reflectionCoords) if (reflectionCoords)
retval += retval +=
"UBINDING3 uniform HECLReflectMtx\n" "UBINDING3 uniform HECLReflectMtx\n"
"{\n" "{\n"
" mat4 indMtx;\n" " mat4 indMtx;\n"
" mat4 reflectMtx;\n" " mat4 reflectMtx;\n"
" float reflectAlpha;\n" " float reflectAlpha;\n"
"};\n" "};\n"
"\n"; "\n";
return retval; return retval;
} }

View File

@ -1,5 +1,7 @@
set(BLENDER_SOURCES set(BLENDER_SOURCES
Connection.cpp Connection.cpp
MeshOptimizer.hpp
MeshOptimizer.cpp
SDNARead.cpp SDNARead.cpp
HMDL.cpp) HMDL.cpp)

View File

@ -16,6 +16,7 @@
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
#include "hecl/SteamFinder.hpp" #include "hecl/SteamFinder.hpp"
#include "MeshOptimizer.hpp"
#if _WIN32 #if _WIN32
#include <io.h> #include <io.h>
@ -46,7 +47,7 @@ Token SharedBlenderToken;
#ifdef __APPLE__ #ifdef __APPLE__
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender" #define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
#else #else
#define DEFAULT_BLENDER_BIN "blender" #define DEFAULT_BLENDER_BIN "blender-2.8"
#endif #endif
extern "C" uint8_t HECL_BLENDERSHELL[]; extern "C" uint8_t HECL_BLENDERSHELL[];
@ -500,16 +501,6 @@ Connection::Connection(int verbosityLevel) {
} }
_writeStr("ACK"); _writeStr("ACK");
_readStr(lineBuf, 7);
if (!strcmp(lineBuf, "SLERP0"))
m_hasSlerp = false;
else if (!strcmp(lineBuf, "SLERP1"))
m_hasSlerp = true;
else {
_closePipe();
BlenderLog.report(logvisor::Fatal, "read '%s' from blender; expected 'SLERP(0|1)'", lineBuf);
}
break; break;
} }
#else #else
@ -524,6 +515,8 @@ void Vector3f::read(Connection& conn) { conn._readBuf(&val, 12); }
void Vector4f::read(Connection& conn) { conn._readBuf(&val, 16); } void Vector4f::read(Connection& conn) { conn._readBuf(&val, 16); }
void Matrix4f::read(Connection& conn) { conn._readBuf(&val, 64); } void Matrix4f::read(Connection& conn) { conn._readBuf(&val, 64); }
void Index::read(Connection& conn) { conn._readBuf(&val, 4); } void Index::read(Connection& conn) { conn._readBuf(&val, 4); }
void Float::read(Connection& conn) { conn._readBuf(&val, 4); }
void Boolean::read(Connection& conn) { conn._readBuf(&val, 1); }
std::streambuf::int_type PyOutStream::StreamBuf::overflow(int_type ch) { std::streambuf::int_type PyOutStream::StreamBuf::overflow(int_type ch) {
if (!m_parent.m_parent || !m_parent.m_parent->m_lock) if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
@ -755,10 +748,11 @@ void PyOutStream::AABBToBMesh(const atVec3f& min, const atVec3f& max) {
void PyOutStream::centerView() { void PyOutStream::centerView() {
*this << "for obj in bpy.context.scene.objects:\n" *this << "for obj in bpy.context.scene.objects:\n"
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n" " if obj.type == 'CAMERA' or obj.type == 'LIGHT':\n"
" obj.hide = True\n" " obj.hide_set(True)\n"
"\n" "\n"
"bpy.context.user_preferences.view.smooth_view = 0\n" "old_smooth_view = bpy.context.preferences.view.smooth_view\n"
"bpy.context.preferences.view.smooth_view = 0\n"
"for window in bpy.context.window_manager.windows:\n" "for window in bpy.context.window_manager.windows:\n"
" screen = window.screen\n" " screen = window.screen\n"
" for area in screen.areas:\n" " for area in screen.areas:\n"
@ -769,10 +763,11 @@ void PyOutStream::centerView() {
"area, 'region': region}\n" "area, 'region': region}\n"
" bpy.ops.view3d.view_all(override)\n" " bpy.ops.view3d.view_all(override)\n"
" break\n" " break\n"
"bpy.context.preferences.view.smooth_view = old_smooth_view\n"
"\n" "\n"
"for obj in bpy.context.scene.objects:\n" "for obj in bpy.context.scene.objects:\n"
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n" " if obj.type == 'CAMERA' or obj.type == 'LIGHT':\n"
" obj.hide = False\n"; " obj.hide_set(True)\n";
} }
ANIMOutStream::ANIMOutStream(Connection* parent) : m_parent(parent) { ANIMOutStream::ANIMOutStream(Connection* parent) : m_parent(parent) {
@ -821,118 +816,65 @@ void ANIMOutStream::write(unsigned frame, float val) {
BlenderLog.report(logvisor::Fatal, "ANIMOutStream keyCount overflow"); BlenderLog.report(logvisor::Fatal, "ANIMOutStream keyCount overflow");
} }
Mesh::SkinBind::SkinBind(Connection& conn) { conn._readBuf(&boneIdx, 8); } Mesh::SkinBind::SkinBind(Connection& conn) {
vg_idx = Index(conn).val;
weight = Float(conn).val;
}
void Mesh::normalizeSkinBinds() { void Mesh::normalizeSkinBinds() {
for (std::vector<SkinBind>& skin : skins) { for (auto& skin : skins) {
float accum = 0.f; float accum = 0.f;
for (const SkinBind& bind : skin) for (const SkinBind& bind : skin)
accum += bind.weight; if (bind)
accum += bind.weight;
if (accum > FLT_EPSILON) { if (accum > FLT_EPSILON) {
for (SkinBind& bind : skin) for (SkinBind& bind : skin)
bind.weight /= accum; if (bind)
bind.weight /= accum;
} }
} }
} }
Mesh::Mesh(Connection& conn, HMDLTopology topologyIn, int skinSlotCount, SurfProgFunc& surfProg) Mesh::Mesh(Connection& conn, HMDLTopology topologyIn, int skinSlotCount, bool useLuvs)
: topology(topologyIn), sceneXf(conn), aabbMin(conn), aabbMax(conn) { : topology(topologyIn), sceneXf(conn), aabbMin(conn), aabbMax(conn) {
uint32_t matSetCount; Index matSetCount(conn);
conn._readBuf(&matSetCount, 4); materialSets.reserve(matSetCount.val);
materialSets.reserve(matSetCount); for (uint32_t i = 0; i < matSetCount.val; ++i) {
for (uint32_t i = 0; i < matSetCount; ++i) {
materialSets.emplace_back(); materialSets.emplace_back();
std::vector<Material>& materials = materialSets.back(); std::vector<Material>& materials = materialSets.back();
uint32_t matCount; Index matCount(conn);
conn._readBuf(&matCount, 4); materials.reserve(matCount.val);
materials.reserve(matCount); for (uint32_t j = 0; j < matCount.val; ++j)
for (uint32_t i = 0; i < matCount; ++i)
materials.emplace_back(conn); materials.emplace_back(conn);
} }
uint32_t count; MeshOptimizer opt(conn, materialSets[0], useLuvs);
conn._readBuf(&count, 4); opt.optimize(*this, skinSlotCount);
pos.reserve(count);
for (uint32_t i = 0; i < count; ++i)
pos.emplace_back(conn);
conn._readBuf(&count, 4); Index count(conn);
norm.reserve(count); boneNames.reserve(count.val);
for (uint32_t i = 0; i < count; ++i)
norm.emplace_back(conn);
conn._readBuf(&colorLayerCount, 4);
if (colorLayerCount > 4)
LogModule.report(logvisor::Fatal, "mesh has %u color-layers; max 4", colorLayerCount);
conn._readBuf(&count, 4);
color.reserve(count);
for (uint32_t i = 0; i < count; ++i)
color.emplace_back(conn);
conn._readBuf(&uvLayerCount, 4);
if (uvLayerCount > 8)
LogModule.report(logvisor::Fatal, "mesh has %u UV-layers; max 8", uvLayerCount);
conn._readBuf(&count, 4);
uv.reserve(count);
for (uint32_t i = 0; i < count; ++i)
uv.emplace_back(conn);
conn._readBuf(&luvLayerCount, 4);
if (luvLayerCount > 1)
LogModule.report(logvisor::Fatal, "mesh has %u LUV-layers; max 1", luvLayerCount);
conn._readBuf(&count, 4);
luv.reserve(count);
for (uint32_t i = 0; i < count; ++i)
luv.emplace_back(conn);
conn._readBuf(&count, 4);
boneNames.reserve(count);
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
char name[128]; char name[128];
conn._readStr(name, 128); conn._readStr(name, 128);
boneNames.emplace_back(name); boneNames.emplace_back(name);
} }
conn._readBuf(&count, 4); if (boneNames.size())
skins.reserve(count); for (Surface& s : surfaces)
for (uint32_t i = 0; i < count; ++i) { s.skinBankIdx = skinBanks.addSurface(*this, s, skinSlotCount);
skins.emplace_back();
std::vector<SkinBind>& binds = skins.back();
uint32_t bindCount;
conn._readBuf(&bindCount, 4);
binds.reserve(bindCount);
for (uint32_t j = 0; j < bindCount; ++j)
binds.emplace_back(conn);
}
normalizeSkinBinds();
/* Assume 16 islands per material for reserve */
if (materialSets.size())
surfaces.reserve(materialSets.front().size() * 16);
uint8_t isSurf;
conn._readBuf(&isSurf, 1);
int prog = 0;
while (isSurf) {
surfaces.emplace_back(conn, *this, skinSlotCount);
surfProg(++prog);
conn._readBuf(&isSurf, 1);
}
/* Custom properties */ /* Custom properties */
uint32_t propCount; Index propCount(conn);
conn._readBuf(&propCount, 4);
std::string keyBuf; std::string keyBuf;
std::string valBuf; std::string valBuf;
for (uint32_t i = 0; i < propCount; ++i) { for (uint32_t i = 0; i < propCount.val; ++i) {
uint32_t kLen; Index kLen(conn);
conn._readBuf(&kLen, 4); keyBuf.assign(kLen.val, '\0');
keyBuf.assign(kLen, '\0'); conn._readBuf(&keyBuf[0], kLen.val);
conn._readBuf(&keyBuf[0], kLen);
uint32_t vLen; Index vLen(conn);
conn._readBuf(&vLen, 4); valBuf.assign(vLen.val, '\0');
valBuf.assign(vLen, '\0'); conn._readBuf(&valBuf[0], vLen.val);
conn._readBuf(&valBuf[0], vLen);
customProps[keyBuf] = valBuf; customProps[keyBuf] = valBuf;
} }
@ -991,28 +933,58 @@ Mesh Mesh::getContiguousSkinningVersion() const {
return newMesh; return newMesh;
} }
template <typename T>
static T SwapFourCC(T fcc) {
return T(hecl::SBig(std::underlying_type_t<T>(fcc)));
}
Material::PASS::PASS(Connection& conn) {
conn._readBuf(&type, 4);
type = SwapFourCC(type);
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
std::string readStr(bufSz, ' ');
conn._readBuf(&readStr[0], bufSz);
SystemStringConv absolute(readStr);
SystemString relative =
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
tex.assign(conn.getBlendPath().getProject().getProjectWorkingPath(), relative);
conn._readBuf(&source, 1);
conn._readBuf(&uvAnimType, 1);
uint32_t argCount;
conn._readBuf(&argCount, 4);
for (uint32_t i = 0; i < argCount; ++i)
conn._readBuf(&uvAnimParms[i], 4);
conn._readBuf(&alpha, 1);
}
Material::CLR::CLR(Connection& conn) {
conn._readBuf(&type, 4);
type = SwapFourCC(type);
color.read(conn);
}
Material::Material(Connection& conn) { Material::Material(Connection& conn) {
uint32_t bufSz; uint32_t bufSz;
conn._readBuf(&bufSz, 4); conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' '); name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz); conn._readBuf(&name[0], bufSz);
conn._readBuf(&bufSz, 4); conn._readBuf(&passIndex, 4);
source.assign(bufSz, ' '); conn._readBuf(&shaderType, 4);
conn._readBuf(&source[0], bufSz); shaderType = SwapFourCC(shaderType);
uint32_t texCount; uint32_t chunkCount;
conn._readBuf(&texCount, 4); conn._readBuf(&chunkCount, 4);
texs.reserve(texCount); chunks.reserve(chunkCount);
for (uint32_t i = 0; i < texCount; ++i) { for (uint32_t i = 0; i < chunkCount; ++i) {
conn._readBuf(&bufSz, 4); ChunkType type;
std::string readStr(bufSz, ' '); conn._readBuf(&type, 4);
conn._readBuf(&readStr[0], bufSz); type = SwapFourCC(type);
SystemStringConv absolute(readStr); chunks.push_back(Chunk::Build(type, conn));
SystemString relative =
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
texs.emplace_back(conn.getBlendPath().getProject().getProjectWorkingPath(), relative);
} }
uint32_t iPropCount; uint32_t iPropCount;
@ -1028,36 +1000,7 @@ Material::Material(Connection& conn) {
iprops[readStr] = val; iprops[readStr] = val;
} }
conn._readBuf(&transparent, 1); conn._readBuf(&blendMode, 4);
}
Mesh::Surface::Surface(Connection& conn, Mesh& parent, int skinSlotCount)
: centroid(conn), materialIdx(conn), aabbMin(conn), aabbMax(conn), reflectionNormal(conn) {
uint32_t countEstimate;
conn._readBuf(&countEstimate, 4);
verts.reserve(countEstimate);
uint8_t isVert;
conn._readBuf(&isVert, 1);
while (isVert) {
verts.emplace_back(conn, parent);
conn._readBuf(&isVert, 1);
}
if (parent.boneNames.size())
skinBankIdx = parent.skinBanks.addSurface(parent, *this, skinSlotCount);
}
Mesh::Surface::Vert::Vert(Connection& conn, const Mesh& parent) {
conn._readBuf(&iPos, 4);
if (iPos == 0xffffffff)
return;
conn._readBuf(&iNorm, 4);
for (uint32_t i = 0; i < parent.colorLayerCount; ++i)
conn._readBuf(&iColor[i], 4);
for (uint32_t i = 0; i < parent.uvLayerCount; ++i)
conn._readBuf(&iUv[i], 4);
conn._readBuf(&iSkin, 4);
} }
bool Mesh::Surface::Vert::operator==(const Vert& other) const { bool Mesh::Surface::Vert::operator==(const Vert& other) const {
@ -1087,26 +1030,21 @@ void Mesh::SkinBanks::Bank::addSkins(const Mesh& parent, const std::vector<uint3
for (uint32_t sidx : skinIdxs) { for (uint32_t sidx : skinIdxs) {
m_skinIdxs.push_back(sidx); m_skinIdxs.push_back(sidx);
for (const SkinBind& bind : parent.skins[sidx]) { for (const SkinBind& bind : parent.skins[sidx]) {
if (!bind)
break;
bool found = false; bool found = false;
for (uint32_t bidx : m_boneIdxs) { for (uint32_t bidx : m_boneIdxs) {
if (bidx == bind.boneIdx) { if (bidx == bind.vg_idx) {
found = true; found = true;
break; break;
} }
} }
if (!found) if (!found)
m_boneIdxs.push_back(bind.boneIdx); m_boneIdxs.push_back(bind.vg_idx);
} }
} }
} }
size_t Mesh::SkinBanks::Bank::lookupLocalBoneIdx(uint32_t boneIdx) const {
for (size_t i = 0; i < m_boneIdxs.size(); ++i)
if (m_boneIdxs[i] == boneIdx)
return i;
return -1;
}
std::vector<Mesh::SkinBanks::Bank>::iterator Mesh::SkinBanks::addSkinBank(int skinSlotCount) { std::vector<Mesh::SkinBanks::Bank>::iterator Mesh::SkinBanks::addSkinBank(int skinSlotCount) {
banks.emplace_back(); banks.emplace_back();
if (skinSlotCount > 0) if (skinSlotCount > 0)
@ -1631,32 +1569,28 @@ const char* DataStream::MeshOutputModeString(HMDLTopology topology) {
return STRS[int(topology)]; return STRS[int(topology)];
} }
Mesh DataStream::compileMesh(HMDLTopology topology, int skinSlotCount, Mesh::SurfProgFunc surfProg) { Mesh DataStream::compileMesh(HMDLTopology topology, int skinSlotCount) {
if (m_parent->getBlendType() != BlendType::Mesh) if (m_parent->getBlendType() != BlendType::Mesh)
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not a MESH blend"), BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not a MESH blend"),
m_parent->getBlendPath().getAbsolutePath().data()); m_parent->getBlendPath().getAbsolutePath().data());
char req[128]; m_parent->_writeStr("MESHCOMPILE");
snprintf(req, 128, "MESHCOMPILE %s %d", MeshOutputModeString(topology), skinSlotCount);
m_parent->_writeStr(req);
char readBuf[256]; char readBuf[256];
m_parent->_readStr(readBuf, 256); m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK")) if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook mesh: %s", readBuf); BlenderLog.report(logvisor::Fatal, "unable to cook mesh: %s", readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg); return Mesh(*m_parent, topology, skinSlotCount);
} }
Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount, bool useLuv, Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount, bool useLuv) {
Mesh::SurfProgFunc surfProg) {
if (m_parent->getBlendType() != BlendType::Area) if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"), BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
m_parent->getBlendPath().getAbsolutePath().data()); m_parent->getBlendPath().getAbsolutePath().data());
char req[128]; char req[128];
snprintf(req, 128, "MESHCOMPILENAME %s %s %d %d", name.data(), MeshOutputModeString(topology), skinSlotCount, snprintf(req, 128, "MESHCOMPILENAME %s %d", name.data(), int(useLuv));
int(useLuv));
m_parent->_writeStr(req); m_parent->_writeStr(req);
char readBuf[256]; char readBuf[256];
@ -1664,7 +1598,7 @@ Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int s
if (strcmp(readBuf, "OK")) if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook mesh '%s': %s", name.data(), readBuf); BlenderLog.report(logvisor::Fatal, "unable to cook mesh '%s': %s", name.data(), readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg); return Mesh(*m_parent, topology, skinSlotCount, useLuv);
} }
ColMesh DataStream::compileColMesh(std::string_view name) { ColMesh DataStream::compileColMesh(std::string_view name) {
@ -1710,24 +1644,6 @@ std::vector<ColMesh> DataStream::compileColMeshes() {
return ret; return ret;
} }
Mesh DataStream::compileAllMeshes(HMDLTopology topology, int skinSlotCount, float maxOctantLength,
Mesh::SurfProgFunc surfProg) {
if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
snprintf(req, 128, "MESHCOMPILEALL %s %d %f", MeshOutputModeString(topology), skinSlotCount, maxOctantLength);
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook all meshes: %s", readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
}
std::vector<Light> DataStream::compileLights() { std::vector<Light> DataStream::compileLights() {
if (m_parent->getBlendType() != BlendType::Area) if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"), BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),

View File

@ -138,7 +138,7 @@ HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinInd
if (weightVecCount) { if (weightVecCount) {
const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx]; const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx];
const std::vector<SkinBind>& binds = skins[v.iSkin]; const auto& binds = skins[v.iSkin];
auto it = bank.m_boneIdxs.cbegin(); auto it = bank.m_boneIdxs.cbegin();
for (size_t i = 0; i < weightVecCount; ++i) { for (size_t i = 0; i < weightVecCount; ++i) {
@ -146,11 +146,14 @@ HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinInd
for (size_t j = 0; j < 4; ++j) { for (size_t j = 0; j < 4; ++j) {
if (it == bank.m_boneIdxs.cend()) if (it == bank.m_boneIdxs.cend())
break; break;
for (const SkinBind& bind : binds) for (const SkinBind& bind : binds) {
if (bind.boneIdx == *it) { if (!bind)
break;
if (bind.vg_idx == *it) {
vec.simd[j] = bind.weight; vec.simd[j] = bind.weight;
break; break;
} }
}
++it; ++it;
} }
vboW.writeVec4fLittle(vec); vboW.writeVec4fLittle(vec);

View File

@ -0,0 +1,437 @@
#include "MeshOptimizer.hpp"
#include <numeric>
#include <cmath>
namespace hecl::blender {
logvisor::Module Log("MeshOptimizer");
template <typename T>
static void insert_unique_attr(std::unordered_map<T, uint32_t>& set, const T& attr) {
if (set.find(attr) == set.cend()) {
size_t sz = set.size();
set[attr] = sz;
}
}
template <typename T>
static std::vector<T> sort_unordered_map(const std::unordered_map<T, uint32_t>& map) {
struct SortableIterator {
typename std::unordered_map<T, uint32_t>::const_iterator it;
bool operator<(const SortableIterator& other) const { return it->second < other.it->second; }
explicit SortableIterator(typename std::unordered_map<T, uint32_t>::const_iterator i) : it(i) {}
};
std::vector<SortableIterator> to_sort;
to_sort.reserve(map.size());
for (auto I = map.cbegin(), E = map.cend(); I != E; ++I)
to_sort.emplace_back(I);
std::sort(to_sort.begin(), to_sort.end());
std::vector<T> ret;
ret.reserve(to_sort.size());
for (const auto& sit : to_sort)
ret.push_back(sit.it->first);
return ret;
}
static bool material_is_lightmapped(const Material& mat) {
auto search = mat.iprops.find("retro_lightmapped");
if (search != mat.iprops.cend())
return search->second;
return false;
}
MeshOptimizer::Vertex::Vertex(Connection& conn) {
co.read(conn);
Index skin_count(conn);
if (skin_count.val > MaxSkinEntries)
Log.report(logvisor::Fatal, "Skin entry overflow %u/%u", skin_count.val, MaxSkinEntries);
for (uint32_t i = 0; i < skin_count.val; ++i)
skin_ents[i] = Mesh::SkinBind(conn);
}
MeshOptimizer::Loop::Loop(Connection& conn, uint32_t color_count, uint32_t uv_count) {
normal.read(conn);
for (uint32_t i = 0; i < color_count; ++i)
colors[i].read(conn);
for (uint32_t i = 0; i < uv_count; ++i)
uvs[i].read(conn);
vert = Index(conn).val;
edge = Index(conn).val;
face = Index(conn).val;
link_loop_next = Index(conn).val;
link_loop_prev = Index(conn).val;
link_loop_radial_next = Index(conn).val;
link_loop_radial_prev = Index(conn).val;
}
MeshOptimizer::Edge::Edge(Connection& conn) {
for (uint32_t i = 0; i < 2; ++i)
verts[i] = Index(conn).val;
Index face_count(conn);
if (face_count > MaxLinkFaces)
Log.report(logvisor::Fatal, "Face overflow %u/%u", face_count.val, MaxLinkFaces);
for (uint32_t i = 0; i < face_count.val; ++i)
link_faces[i] = Index(conn).val;
is_contiguous = Boolean(conn).val;
}
MeshOptimizer::Face::Face(Connection& conn) {
normal.read(conn);
centroid.read(conn);
material_index = Index(conn).val;
for (uint32_t i = 0; i < 3; ++i)
loops[i] = Index(conn).val;
}
uint32_t MeshOptimizer::get_pos_idx(const Vertex& v) const {
auto search = b_pos.find(v.co);
if (search != b_pos.cend())
return search->second;
return UINT32_MAX;
}
uint32_t MeshOptimizer::get_norm_idx(const Loop& l) const {
auto search = b_norm.find(l.normal);
if (search != b_norm.cend())
return search->second;
return UINT32_MAX;
}
uint32_t MeshOptimizer::get_skin_idx(const Vertex& v) const {
auto search = b_skin.find(v.skin_ents);
if (search != b_skin.cend())
return search->second;
return UINT32_MAX;
}
uint32_t MeshOptimizer::get_color_idx(const Loop& l, uint32_t cidx) const {
auto search = b_color.find(l.colors[cidx]);
if (search != b_color.cend())
return search->second;
return UINT32_MAX;
}
uint32_t MeshOptimizer::get_uv_idx(const Loop& l, uint32_t uidx) const {
if (use_luvs && uidx == 0 && material_is_lightmapped(materials[faces[l.face].material_index])) {
auto search = b_luv.find(l.uvs[0]);
if (search != b_luv.cend())
return search->second;
return UINT32_MAX;
}
auto search = b_uv.find(l.uvs[uidx]);
if (search != b_uv.cend())
return search->second;
return UINT32_MAX;
}
bool MeshOptimizer::loops_contiguous(const Loop& la, const Loop& lb) const {
if (la.vert != lb.vert)
return false;
if (get_norm_idx(la) != get_norm_idx(lb))
return false;
for (uint32_t i = 0; i < color_count; ++i)
if (get_color_idx(la, i) != get_color_idx(lb, i))
return false;
for (uint32_t i = 0; i < uv_count; ++i)
if (get_uv_idx(la, i) != get_uv_idx(lb, i))
return false;
return true;
}
bool MeshOptimizer::splitable_edge(const Edge& e) const {
if (!e.is_contiguous)
return false;
for (uint32_t vidx : e.verts) {
const Loop* found = nullptr;
for (uint32_t fidx : e.link_faces) {
for (uint32_t lidx : faces[fidx].loops) {
if (loops[lidx].vert == vidx) {
if (!found) {
found = &loops[lidx];
break;
} else {
if (!loops_contiguous(*found, loops[lidx]))
return true;
break;
}
}
}
}
}
return false;
}
void MeshOptimizer::sort_faces_by_skin_group(std::vector<uint32_t>& sfaces) const {
std::vector<uint32_t> faces_out;
faces_out.reserve(sfaces.size());
std::unordered_set<uint32_t> done_sg;
uint32_t ref_sg = UINT32_MAX;
while (faces_out.size() < sfaces.size()) {
for (uint32_t f : sfaces) {
bool found = false;
for (uint32_t l : faces[f].loops) {
uint32_t skin_idx = get_skin_idx(verts[loops[l].vert]);
if (done_sg.find(skin_idx) == done_sg.end()) {
ref_sg = skin_idx;
done_sg.insert(skin_idx);
found = true;
break;
}
}
if (found)
break;
}
for (uint32_t f : sfaces) {
if (std::find(faces_out.begin(), faces_out.end(), f) != faces_out.end())
continue;
for (uint32_t l : faces[f].loops) {
uint32_t skin_idx = get_skin_idx(verts[loops[l].vert]);
if (skin_idx == ref_sg) {
faces_out.push_back(f);
break;
}
}
}
}
sfaces = std::move(faces_out);
}
std::pair<uint32_t, uint32_t> MeshOptimizer::strip_next_loop(uint32_t prev_loop, uint32_t out_count) const {
if (out_count & 0x1) {
uint32_t radial_loop = loops[prev_loop].link_loop_radial_next;
uint32_t loop = loops[radial_loop].link_loop_prev;
return {loop, loop};
} else {
uint32_t radial_loop = loops[prev_loop].link_loop_radial_prev;
uint32_t loop = loops[radial_loop].link_loop_next;
return {loops[loop].link_loop_next, loop};
}
}
static float Magnitude(const Vector3f& v) { return std::sqrt(v.val.simd.dot3(v.val.simd)); }
static void Normalize(Vector3f& v) {
float mag = 1.f / Magnitude(v);
v.val.simd *= athena::simd<float>(mag);
}
Mesh::Surface MeshOptimizer::generate_surface(std::vector<uint32_t>& island_faces, uint32_t mat_idx) const {
Mesh::Surface ret = {};
ret.materialIdx = mat_idx;
/* Centroid of surface */
for (const auto& f : island_faces)
ret.centroid.val.simd += faces[f].centroid.val.simd;
ret.centroid.val.simd /= athena::simd<float>(island_faces.size());
/* AABB of surface */
ret.aabbMin.val.simd = athena::simd<float>(FLT_MAX);
ret.aabbMax.val.simd = athena::simd<float>(-FLT_MAX);
for (const auto& f : island_faces) {
for (const auto& l : faces[f].loops) {
const Vertex& v = verts[loops[l].vert];
for (int c = 0; c < 3; ++c) {
if (v.co.val.simd[c] < ret.aabbMin.val.simd[c])
ret.aabbMin.val.simd[c] = v.co.val.simd[c];
if (v.co.val.simd[c] > ret.aabbMax.val.simd[c])
ret.aabbMax.val.simd[c] = v.co.val.simd[c];
}
}
}
/* Average normal of surface */
for (const auto& f : island_faces)
ret.reflectionNormal.val.simd += faces[f].normal.val.simd;
Normalize(ret.reflectionNormal);
/* Verts themselves */
uint32_t prev_loop_emit = UINT32_MAX;
std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> sel_lists_local;
sel_lists_local.reserve(loops.size());
while (island_faces.size()) {
sel_lists_local.clear();
for (uint32_t start_face : island_faces) {
for (uint32_t l : faces[start_face].loops) {
std::vector<uint32_t> island_local(island_faces);
uint32_t prev_loop = loops[l].link_loop_next;
uint32_t loop = loops[prev_loop].link_loop_next;
std::vector<uint32_t> sel_list;
sel_list.reserve(64);
sel_list.push_back(l);
sel_list.push_back(prev_loop);
sel_list.push_back(loop);
island_local.erase(std::find(island_local.begin(), island_local.end(), start_face));
while (true) {
const Edge& prev_edge = edges[loops[prev_loop].edge];
if (!prev_edge.is_contiguous || prev_edge.tag)
break;
std::tie(loop, prev_loop) = strip_next_loop(prev_loop, sel_list.size());
uint32_t face = loops[loop].face;
auto search = std::find(island_local.begin(), island_local.end(), face);
if (search == island_local.end())
break;
sel_list.push_back(loop);
island_local.erase(search);
}
sel_lists_local.emplace_back(std::move(sel_list), std::move(island_local));
}
}
uint32_t max_count = 0;
const std::vector<uint32_t>* max_sl = nullptr;
const std::vector<uint32_t>* max_island_faces = nullptr;
for (const auto& sl : sel_lists_local) {
if (sl.first.size() > max_count) {
max_count = sl.first.size();
max_sl = &sl.first;
max_island_faces = &sl.second;
}
}
assert(max_island_faces && "Should not be null");
assert(max_island_faces->size() < island_faces.size() && "Infinite loop condition");
island_faces = std::move(*max_island_faces);
if (prev_loop_emit != UINT32_MAX)
ret.verts.emplace_back();
for (uint32_t loop : *max_sl) {
ret.verts.emplace_back();
const auto& l = loops[loop];
auto& vert = ret.verts.back();
vert.iPos = get_pos_idx(verts[l.vert]);
vert.iNorm = get_norm_idx(l);
for (uint32_t i = 0; i < color_count; ++i)
vert.iColor[i] = get_color_idx(l, i);
for (uint32_t i = 0; i < uv_count; ++i)
vert.iUv[i] = get_uv_idx(l, i);
vert.iSkin = get_skin_idx(verts[l.vert]);
prev_loop_emit = loop;
}
}
return ret;
}
void MeshOptimizer::optimize(Mesh& mesh, int max_skin_banks) const {
mesh.topology = HMDLTopology::TriStrips;
mesh.pos = sort_unordered_map(b_pos);
mesh.norm = sort_unordered_map(b_norm);
mesh.colorLayerCount = color_count;
mesh.color = sort_unordered_map(b_color);
mesh.uvLayerCount = uv_count;
mesh.uv = sort_unordered_map(b_uv);
mesh.luv = sort_unordered_map(b_luv);
mesh.skins = sort_unordered_map(b_skin);
/* Sort materials by pass index */
std::vector<uint32_t> sorted_material_idxs(materials.size());
std::iota(sorted_material_idxs.begin(), sorted_material_idxs.end(), 0);
std::sort(sorted_material_idxs.begin(), sorted_material_idxs.end(),
[this](uint32_t a, uint32_t b) { return materials[a].passIndex < materials[b].passIndex; });
/* Generate island surfaces */
std::vector<uint32_t> mat_faces_rem, the_list;
mat_faces_rem.reserve(faces.size());
the_list.reserve(faces.size());
std::unordered_set<uint32_t> skin_slot_set;
skin_slot_set.reserve(b_skin.size());
for (uint32_t mat_idx : sorted_material_idxs) {
const auto& mat = materials[mat_idx];
mat_faces_rem.clear();
for (auto B = faces.begin(), I = B, E = faces.end(); I != E; ++I) {
if (I->material_index == mat_idx)
mat_faces_rem.push_back(I - B);
}
if (b_skin.size())
sort_faces_by_skin_group(mat_faces_rem);
size_t rem_count = mat_faces_rem.size();
while (rem_count) {
the_list.clear();
skin_slot_set.clear();
for (uint32_t& f : mat_faces_rem) {
if (f == UINT32_MAX)
continue;
if (b_skin.size()) {
bool brk = false;
for (const auto& l : faces[f].loops) {
const Vertex& v = verts[loops[l].vert];
uint32_t skin_idx = get_skin_idx(v);
if (skin_slot_set.find(skin_idx) == skin_slot_set.end()) {
if (max_skin_banks > 0 && skin_slot_set.size() == max_skin_banks) {
brk = true;
break;
}
skin_slot_set.insert(skin_idx);
}
}
if (brk)
break;
}
the_list.push_back(f);
f = UINT32_MAX;
--rem_count;
}
mesh.surfaces.push_back(generate_surface(the_list, mat_idx));
}
}
}
MeshOptimizer::MeshOptimizer(Connection& conn, const std::vector<Material>& materials, bool use_luvs)
: materials(materials), use_luvs(use_luvs) {
color_count = Index(conn).val;
if (color_count > MaxColorLayers)
Log.report(logvisor::Fatal, "Color layer overflow %u/%u", color_count, MaxColorLayers);
uv_count = Index(conn).val;
if (uv_count > MaxUVLayers)
Log.report(logvisor::Fatal, "UV layer overflow %u/%u", uv_count, MaxUVLayers);
/* Simultaneously load topology objects and build unique mapping indices */
Index vert_count(conn);
verts.reserve(vert_count.val);
b_pos.reserve(vert_count.val);
b_skin.reserve(vert_count.val * 4);
for (uint32_t i = 0; i < vert_count.val; ++i) {
verts.emplace_back(conn);
insert_unique_attr(b_pos, verts.back().co);
if (verts.back().skin_ents[0])
insert_unique_attr(b_skin, verts.back().skin_ents);
}
Index loop_count(conn);
loops.reserve(loop_count.val);
b_norm.reserve(loop_count.val);
if (use_luvs) {
b_uv.reserve(std::max(int(loop_count.val) - 1, 0) * uv_count);
b_luv.reserve(loop_count.val);
} else {
b_uv.reserve(loop_count.val * uv_count);
}
for (uint32_t i = 0; i < loop_count.val; ++i) {
loops.emplace_back(conn, color_count, uv_count);
insert_unique_attr(b_norm, loops.back().normal);
for (const auto& c : loops.back().colors)
insert_unique_attr(b_color, c);
if (use_luvs && material_is_lightmapped(materials[faces[loops.back().face].material_index])) {
insert_unique_attr(b_luv, loops.back().uvs[0]);
for (auto I = std::begin(loops.back().uvs) + 1, E = std::end(loops.back().uvs); I != E; ++I)
insert_unique_attr(b_uv, *I);
} else {
for (const auto& c : loops.back().uvs)
insert_unique_attr(b_uv, c);
}
}
Index edge_count(conn);
edges.reserve(edge_count.val);
for (uint32_t i = 0; i < edge_count.val; ++i)
edges.emplace_back(conn);
Index face_count(conn);
faces.reserve(face_count.val);
for (uint32_t i = 0; i < face_count.val; ++i)
faces.emplace_back(conn);
/* Cache edges that should block tristrip traversal */
for (auto& e : edges)
e.tag = splitable_edge(e);
}
}

View File

@ -0,0 +1,109 @@
#pragma once
#include "hecl/Blender/Connection.hpp"
#include <unordered_set>
namespace hecl::blender {
template <size_t S>
class IndexArray {
std::array<uint32_t, S> arr;
public:
IndexArray() { std::fill(arr.begin(), arr.end(), UINT32_MAX); }
class const_iterator {
typename std::array<uint32_t, S>::const_iterator it;
public:
explicit const_iterator(typename std::array<uint32_t, S>::const_iterator i) : it(i) {}
bool operator==(const_iterator other) const { return it == other.it; }
bool operator!=(const_iterator other) const { return it != other.it; }
const_iterator& operator++() { ++it; return *this; }
uint32_t operator*() const { return *it; }
};
const_iterator begin() const { return const_iterator(arr.cbegin()); }
const_iterator end() const {
typename std::array<uint32_t, S>::const_iterator I, E;
for (I = arr.cbegin(), E = arr.cend(); I != E && *I != UINT32_MAX; ++I) {}
return const_iterator(I);
}
uint32_t& operator[](size_t idx) { return arr[idx]; }
const uint32_t& operator[](size_t idx) const { return arr[idx]; }
static constexpr size_t capacity() { return S; }
size_t size() const { return end() - begin(); }
};
class MeshOptimizer {
static constexpr size_t MaxColorLayers = Mesh::MaxColorLayers;
static constexpr size_t MaxUVLayers = Mesh::MaxUVLayers;
static constexpr size_t MaxSkinEntries = Mesh::MaxSkinEntries;
const std::vector<Material>& materials;
bool use_luvs;
uint32_t color_count;
uint32_t uv_count;
struct Vertex {
Vector3f co = {};
std::array<Mesh::SkinBind, MaxSkinEntries> skin_ents = {};
explicit Vertex(Connection& conn);
};
std::vector<Vertex> verts;
struct Loop {
Vector3f normal = {};
std::array<Vector3f, MaxColorLayers> colors = {};
std::array<Vector2f, MaxUVLayers> uvs = {};
uint32_t vert = UINT32_MAX;
uint32_t edge = UINT32_MAX;
uint32_t face = UINT32_MAX;
uint32_t link_loop_next = UINT32_MAX;
uint32_t link_loop_prev = UINT32_MAX;
uint32_t link_loop_radial_next = UINT32_MAX;
uint32_t link_loop_radial_prev = UINT32_MAX;
explicit Loop(Connection& conn, uint32_t color_count, uint32_t uv_count);
};
std::vector<Loop> loops;
struct Edge {
static constexpr size_t MaxLinkFaces = 8;
IndexArray<2> verts;
IndexArray<MaxLinkFaces> link_faces;
bool is_contiguous = false;
bool tag = false;
explicit Edge(Connection& conn);
};
std::vector<Edge> edges;
struct Face {
Vector3f normal = {};
Vector3f centroid = {};
uint32_t material_index = UINT32_MAX;
IndexArray<3> loops;
explicit Face(Connection& conn);
};
std::vector<Face> faces;
std::unordered_map<Vector3f, uint32_t> b_pos;
std::unordered_map<Vector3f, uint32_t> b_norm;
std::unordered_map<std::array<Mesh::SkinBind, MaxSkinEntries>, uint32_t> b_skin;
std::unordered_map<Vector3f, uint32_t> b_color;
std::unordered_map<Vector2f, uint32_t> b_uv;
std::unordered_map<Vector2f, uint32_t> b_luv;
uint32_t get_pos_idx(const Vertex& v) const;
uint32_t get_norm_idx(const Loop& l) const;
uint32_t get_skin_idx(const Vertex& v) const;
uint32_t get_color_idx(const Loop& l, uint32_t cidx) const;
uint32_t get_uv_idx(const Loop& l, uint32_t uidx) const;
void sort_faces_by_skin_group(std::vector<uint32_t>& faces) const;
std::pair<uint32_t, uint32_t> strip_next_loop(uint32_t prev_loop, uint32_t out_count) const;
bool loops_contiguous(const Loop& la, const Loop& lb) const;
bool splitable_edge(const Edge& e) const;
Mesh::Surface generate_surface(std::vector<uint32_t>& island_faces, uint32_t mat_idx) const;
public:
explicit MeshOptimizer(Connection& conn, const std::vector<Material>& materials, bool use_luvs);
void optimize(Mesh& mesh, int max_skin_banks) const;
};
}

View File

@ -9,8 +9,6 @@ endmacro(hecl_add_list)
include_directories(../extern/boo/glslang ../extern/boo) include_directories(../extern/boo/glslang ../extern/boo)
add_subdirectory(Blender) add_subdirectory(Blender)
add_subdirectory(Backend)
add_subdirectory(Frontend)
add_subdirectory(Runtime) add_subdirectory(Runtime)
if(WIN32) if(WIN32)
@ -18,7 +16,6 @@ list(APPEND PLAT_SRCS winsupport.cpp ../include/hecl/winsupport.hpp)
endif() endif()
atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp) atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp)
atdna(atdna_Frontend.cpp ../include/hecl/Frontend.hpp)
atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp) atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp)
atdna(atdna_SDNARead.cpp ../include/hecl/Blender/SDNARead.hpp) atdna(atdna_SDNARead.cpp ../include/hecl/Blender/SDNARead.hpp)
@ -34,18 +31,13 @@ set(HECL_HEADERS
../include/hecl/hecl.hpp ../include/hecl/hecl.hpp
../include/hecl/MultiProgressPrinter.hpp ../include/hecl/MultiProgressPrinter.hpp
../include/hecl/FourCC.hpp ../include/hecl/FourCC.hpp
../include/hecl/TypedVariant.hpp
../include/hecl/HMDLMeta.hpp ../include/hecl/HMDLMeta.hpp
../include/hecl/Backend/Backend.hpp ../include/hecl/Backend.hpp
../include/hecl/Backend/GX.hpp
../include/hecl/Backend/ProgrammableCommon.hpp
../include/hecl/Backend/GLSL.hpp
../include/hecl/Backend/HLSL.hpp
../include/hecl/Backend/Metal.hpp
../include/hecl/Blender/Connection.hpp ../include/hecl/Blender/Connection.hpp
../include/hecl/Blender/SDNARead.hpp ../include/hecl/Blender/SDNARead.hpp
../include/hecl/Blender/Token.hpp ../include/hecl/Blender/Token.hpp
../include/hecl/SteamFinder.hpp ../include/hecl/SteamFinder.hpp
../include/hecl/Frontend.hpp
../include/hecl/Database.hpp ../include/hecl/Database.hpp
../include/hecl/Runtime.hpp ../include/hecl/Runtime.hpp
../include/hecl/ClientProcess.hpp ../include/hecl/ClientProcess.hpp
@ -72,7 +64,6 @@ set(COMMON_SOURCES
Compilers.cpp Compilers.cpp
Pipeline.cpp Pipeline.cpp
atdna_HMDLMeta.cpp atdna_HMDLMeta.cpp
atdna_Frontend.cpp
atdna_CVar.cpp) atdna_CVar.cpp)
if(UNIX) if(UNIX)
@ -80,7 +71,6 @@ if(UNIX)
endif() endif()
add_library(hecl-full add_library(hecl-full
${BACKEND_SOURCES}
${FRONTEND_SOURCES} ${FRONTEND_SOURCES}
${RUNTIME_SOURCES} ${RUNTIME_SOURCES}
${BLENDER_SOURCES} ${BLENDER_SOURCES}

View File

@ -166,7 +166,7 @@ ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func)
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force, bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force,
bool fast) { bool fast) {
if (spec->canCook(path, btok)) { if (spec->canCook(path, btok)) {
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok); const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry());
if (specEnt) { if (specEnt) {
hecl::ProjectPath cooked = path.getCookedPath(*specEnt); hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
if (fast) if (fast)

View File

@ -1,7 +0,0 @@
set(FRONTEND_SOURCES
Parser.cpp
Scanner.cpp
Diagnostics.cpp
Frontend.cpp)
hecl_add_list(Frontend FRONTEND_SOURCES)

View File

@ -1,104 +0,0 @@
#include "hecl/hecl.hpp"
#include "hecl/Frontend.hpp"
#include <cstdarg>
/* ANSI sequences */
#define RED "\x1b[1;31m"
#define YELLOW "\x1b[1;33m"
#define GREEN "\x1b[1;32m"
#define MAGENTA "\x1b[1;35m"
#define CYAN "\x1b[1;36m"
#define BOLD "\x1b[1m"
#define NORMAL "\x1b[0m"
namespace hecl::Frontend {
std::string Diagnostics::sourceDiagString(const SourceLocation& l, bool ansi) const {
std::string::const_iterator it = m_source.begin();
for (int i = 1; i < l.line; ++i) {
while (*it != '\n' && it != m_source.end())
++it;
if (*it == '\n')
++it;
}
std::string::const_iterator begin = it;
while (*it != '\n' && it != m_source.end())
++it;
std::string::const_iterator end = it;
std::string retval(begin, end);
retval += '\n';
for (int i = 1; i < l.col; ++i)
retval += ' ';
if (ansi)
retval += GREEN "^" NORMAL;
else
retval += '^';
return retval;
}
void Diagnostics::reportScannerErr(const SourceLocation& l, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
char* result = nullptr;
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
vasprintf(&result, fmt, ap);
#endif
va_end(ap);
if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[Scanner]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_name.c_str(),
l.line, l.col, result, sourceDiagString(l, true).c_str());
else
LogModule.report(logvisor::Fatal, "[Scanner] %s @%d:%d\n%s\n%s", m_name.c_str(), l.line, l.col, result,
sourceDiagString(l, false).c_str());
free(result);
}
void Diagnostics::reportParserErr(const SourceLocation& l, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
char* result = nullptr;
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
vasprintf(&result, fmt, ap);
#endif
va_end(ap);
if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[Parser]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_name.c_str(),
l.line, l.col, result, sourceDiagString(l, true).c_str());
else
LogModule.report(logvisor::Fatal, "[Parser] %s @%d:%d\n%s\n%s", m_name.c_str(), l.line, l.col, result,
sourceDiagString(l, false).c_str());
free(result);
}
void Diagnostics::reportBackendErr(const SourceLocation& l, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
char* result = nullptr;
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
vasprintf(&result, fmt, ap);
#endif
va_end(ap);
if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[%s]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_backend.c_str(),
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str());
else
LogModule.report(logvisor::Fatal, "[%s] %s @%d:%d\n%s\n%s", m_backend.c_str(), m_name.c_str(), l.line, l.col,
result, sourceDiagString(l, false).c_str());
free(result);
}
} // namespace hecl::Frontend

View File

@ -1,225 +0,0 @@
#include "hecl/Frontend.hpp"
namespace hecl::Frontend {
int IR::Instruction::getChildCount() const {
switch (m_op) {
case OpType::Call:
return m_call.m_argInstIdxs.size();
case OpType::Arithmetic:
return 2;
case OpType::Swizzle:
return 1;
default:
LogModule.report(logvisor::Fatal, "invalid op type");
}
return -1;
}
const IR::Instruction& IR::Instruction::getChildInst(const IR& ir, size_t idx) const {
switch (m_op) {
case OpType::Call:
return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
case OpType::Arithmetic:
if (idx > 1)
LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
case OpType::Swizzle:
if (idx > 0)
LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
return ir.m_instructions.at(m_swizzle.m_instIdx);
default:
LogModule.report(logvisor::Fatal, "invalid op type");
}
return *this;
}
const atVec4f& IR::Instruction::getImmVec() const {
if (m_op != OpType::LoadImm)
LogModule.report(logvisor::Fatal, "invalid op type");
return m_loadImm.m_immVec;
}
template <class Op>
void IR::Instruction::Enumerate(typename Op::StreamT& s) {
Do<Op>({"op"}, m_op, s);
Do<Op>({"target"}, m_target, s);
switch (m_op) {
default:
break;
case OpType::Call:
Do<Op>({"call"}, m_call, s);
break;
case OpType::LoadImm:
Do<Op>({"loadImm"}, m_loadImm, s);
break;
case OpType::Arithmetic:
Do<Op>({"arithmetic"}, m_arithmetic, s);
break;
case OpType::Swizzle:
Do<Op>({"swizzle"}, m_swizzle, s);
break;
}
}
atInt8 IR::swizzleCompIdx(char aChar) {
switch (aChar) {
case 'x':
case 'r':
return 0;
case 'y':
case 'g':
return 1;
case 'z':
case 'b':
return 2;
case 'w':
case 'a':
return 3;
default:
break;
}
return -1;
}
int IR::addInstruction(const IRNode& n, IR::RegID target) {
if (n.kind == IRNode::Kind::None)
return -1;
switch (n.kind) {
case IRNode::Kind::Call: {
if (!n.str.compare("vec3") && n.children.size() >= 3) {
atVec4f vec = {};
auto it = n.children.cbegin();
int i;
athena::simd_floats f;
for (i = 0; i < 3; ++i, ++it) {
if (it->kind != IRNode::Kind::Imm)
break;
f[i] = it->val;
}
vec.simd.copy_from(f);
if (i == 3) {
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec = vec;
return m_instructions.size() - 1;
}
} else if (!n.str.compare("vec4") && n.children.size() >= 4) {
atVec4f vec = {};
auto it = n.children.cbegin();
int i;
athena::simd_floats f;
for (i = 0; i < 4; ++i, ++it) {
if (it->kind != IRNode::Kind::Imm)
break;
f[i] = it->val;
}
vec.simd.copy_from(f);
if (i == 4) {
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec = vec;
return m_instructions.size() - 1;
}
}
std::vector<atUint16> argInstIdxs;
argInstIdxs.reserve(n.children.size());
IR::RegID tgt = target;
for (auto& c : n.children)
argInstIdxs.push_back(addInstruction(c, tgt++));
m_instructions.emplace_back(OpType::Call, target, n.loc);
Instruction::Call& inst = m_instructions.back().m_call;
inst.m_name = n.str;
inst.m_argInstCount = atUint16(argInstIdxs.size());
inst.m_argInstIdxs = argInstIdxs;
return m_instructions.size() - 1;
}
case IRNode::Kind::Imm: {
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec.simd = athena::simd<float>(n.val);
return m_instructions.size() - 1;
}
case IRNode::Kind::Binop: {
atUint16 left = addInstruction(*n.left, target);
atUint16 right = addInstruction(*n.right, target + 1);
m_instructions.emplace_back(OpType::Arithmetic, target, n.loc);
Instruction::Arithmetic& inst = m_instructions.back().m_arithmetic;
inst.m_op = Instruction::ArithmeticOpType(int(n.op) + 1);
inst.m_instIdxs[0] = left;
inst.m_instIdxs[1] = right;
return m_instructions.size() - 1;
}
case IRNode::Kind::Swizzle: {
atUint16 left = addInstruction(*n.left, target);
m_instructions.emplace_back(OpType::Swizzle, target, n.loc);
Instruction::Swizzle& inst = m_instructions.back().m_swizzle;
for (int i = 0; i < n.str.size() && i < 4; ++i)
inst.m_idxs[i] = swizzleCompIdx(n.str[i]);
inst.m_instIdx = left;
return m_instructions.size() - 1;
}
default:
return -1;
}
}
template <>
void IR::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
m_hash = reader.readUint64Big();
m_regCount = reader.readUint16Big();
atUint16 instCount = reader.readUint16Big();
m_instructions.clear();
m_instructions.reserve(instCount);
for (atUint16 i = 0; i < instCount; ++i)
m_instructions.emplace_back(reader);
/* Pre-resolve blending mode */
const IR::Instruction& rootCall = m_instructions.back();
m_doAlpha = false;
if (!rootCall.m_call.m_name.compare("HECLOpaque")) {
m_blendSrc = boo::BlendFactor::One;
m_blendDst = boo::BlendFactor::Zero;
} else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
m_blendSrc = boo::BlendFactor::SrcAlpha;
m_blendDst = boo::BlendFactor::InvSrcAlpha;
m_doAlpha = true;
} else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
m_blendSrc = boo::BlendFactor::SrcAlpha;
m_blendDst = boo::BlendFactor::One;
m_doAlpha = true;
}
}
template <>
void IR::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
writer.writeUint64Big(m_hash);
writer.writeUint16Big(m_regCount);
writer.writeUint16Big(m_instructions.size());
for (const Instruction& inst : m_instructions)
inst.write(writer);
}
template <>
void IR::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& sz) {
sz += 12;
for (const Instruction& inst : m_instructions)
inst.binarySize(sz);
}
IR Frontend::compileSource(std::string_view source, std::string_view diagName) {
m_diag.reset(diagName, source);
m_parser.reset(source);
auto insts = m_parser.parse();
IR ir;
std::string stripString;
if (!insts.empty())
stripString = insts.front().toString(true);
ir.m_hash = Hash(stripString).val64();
for (auto& inst : insts)
ir.addInstruction(inst, 0);
return ir;
}
} // namespace hecl::Frontend

View File

@ -1,258 +0,0 @@
#include "hecl/hecl.hpp"
#include "hecl/Frontend.hpp"
/* Syntatical token parsing system */
namespace hecl::Frontend {
/*
* hecl = { lf } call { lf { lf } call } { lf } .
* call = ident "(" [ expr { "," expr } ] ")" .
* expr = sum { ("+" | "-") sum } .
* sum = factor { ("*" | "/") factor } .
* factor = value | "(" expr ")" .
* value = ( call [ "." ident ] )
* | [ "-" ] number
* .
*/
std::string IRNode::rep(int n, std::string_view s) {
std::string buf;
buf.reserve(n * s.size());
for (int i = 0; i < n; i++)
buf.append(s);
return buf;
}
std::string IRNode::fmt(int level, bool stripUVAnims) const {
std::string buf;
auto indent = rep(level, "\t"sv);
switch (kind) {
case Kind::Call:
if (stripUVAnims && (str == "Texture" || str == "TextureD" || str == "TextureN" || str == "TextureDN") &&
children.size() >= 2) {
auto it = children.begin();
IRNode& uvNode = const_cast<IRNode&>(*++it);
if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View") {
std::string replacementName(str);
if (uvNode.str.back() == 'N' && replacementName.back() != 'N')
replacementName += 'N';
IRNode replacementNode(Kind::Call, std::move(replacementName), std::move(uvNode.children), loc);
auto ret = replacementNode.fmt(level, false);
uvNode.children = std::move(replacementNode.children);
return ret;
}
}
buf.append(indent);
buf.append("Call "sv).append(str);
if (!children.empty()) {
buf.append(" {\n"sv);
for (const IRNode& n : children) {
buf.append(n.fmt(level + 1, stripUVAnims));
buf.append("\n"sv);
}
buf.append(indent);
buf.append("}"sv);
}
break;
case Kind::Imm:
buf.append(indent);
buf.append("Imm "sv).append(hecl::Format("%f", val));
break;
case Kind::Binop:
buf.append(indent);
buf.append("Binop "sv).append(OpToStr(op)).append(" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(right->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
case Kind::Swizzle:
buf.append(indent);
buf.append("Swizzle \""sv).append(str);
buf.append("\" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
default:
break;
}
return buf;
}
std::string IRNode::describe() const {
std::vector<std::string> l;
l.push_back("kind="s + KindToStr(kind).data());
if (!str.empty())
l.push_back("str="s + str);
if (kind == Kind::Binop) {
l.push_back("op="s + OpToStr(op).data());
l.push_back("left="s + left->toString());
l.push_back("right="s + right->toString());
}
if (kind == Kind::Swizzle)
l.push_back("node="s + left->toString());
if (kind == Kind::Call) {
std::string str = "children=["s;
for (auto it = children.begin(); it != children.end(); ++it) {
str += it->toString();
if (&*it != &children.back())
str += ';';
}
str += ']';
l.push_back(str);
}
std::string str = "IRNode["s;
for (auto it = l.begin(); it != l.end(); ++it) {
str += *it;
if (&*it != &l.back())
str += ';';
}
str += ']';
return str;
}
void Parser::check(Token::Kind expected) {
if (sym == expected)
scan();
else
error("expected %s, was %s", Token::KindToStr(expected).data(), Token::KindToStr(sym).data());
}
IRNode Parser::call() {
check(Token::Kind::Ident);
std::string name = t.str;
std::list<IRNode> args;
check(Token::Kind::Lpar);
if (sym == Token::Kind::Lpar || sym == Token::Kind::Ident || sym == Token::Kind::Number ||
sym == Token::Kind::Minus) {
args.push_back(expr());
while (sym == Token::Kind::Comma) {
scan();
args.push_back(expr());
}
}
if (sym != Token::Kind::Rpar)
error("expected expr|rpar, was %s", Token::KindToStr(sym).data());
else
scan();
return IRNode(IRNode::Kind::Call, std::move(name), std::move(args), t.loc);
}
bool Parser::imm(const IRNode& a, const IRNode& b) {
return a.kind == IRNode::Kind::Imm && b.kind == IRNode::Kind::Imm;
}
IRNode Parser::expr() {
IRNode node = sum();
while (sym == Token::Kind::Plus || sym == Token::Kind::Minus) {
scan();
Token::Kind op = t.kind;
IRNode right = sum();
switch (op) {
case Token::Kind::Plus:
if (imm(node, right)) // constant folding
return IRNode(IRNode::Kind::Imm, node.val + right.val, t.loc);
else
node = IRNode(IRNode::Op::Add, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Minus:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val - right.val, t.loc);
else
node = IRNode(IRNode::Op::Sub, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::sum() {
IRNode node = factor();
while (sym == Token::Kind::Times || sym == Token::Kind::Div) {
scan();
Token::Kind op = t.kind;
IRNode right = factor();
switch (op) {
case Token::Kind::Times:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val * right.val, t.loc);
else
node = IRNode(IRNode::Op::Mul, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Div:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val / right.val, t.loc);
else
node = IRNode(IRNode::Op::Div, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::factor() {
if (sym == Token::Kind::Lpar) {
scan();
IRNode node = expr();
check(Token::Kind::Rpar);
return node;
} else
return value();
}
IRNode Parser::value() {
if (sym == Token::Kind::Number || sym == Token::Kind::Minus) {
scan();
bool neg = false;
if (t.kind == Token::Kind::Minus) {
neg = true;
check(Token::Kind::Number);
}
float val = strtof(((neg ? "-"s : ""s) + t.str).c_str(), nullptr);
return IRNode(IRNode::Kind::Imm, val, t.loc);
} else if (sym == Token::Kind::Ident) {
IRNode call = Parser::call();
if (sym == Token::Kind::Period) {
scan();
check(Token::Kind::Ident);
return IRNode(IRNode::Kind::Swizzle, std::string(t.str), std::move(call), t.loc);
}
return call;
} else {
error("expected number|call, was %s", Token::KindToStr(sym).data());
return IRNode();
}
}
std::list<IRNode> Parser::parse() {
std::list<IRNode> result;
scan();
while (sym == Token::Kind::Lf)
scan();
result.push_back(call());
while (sym == Token::Kind::Lf) {
while (sym == Token::Kind::Lf)
scan();
if (sym != Token::Kind::Eof)
result.push_back(call());
}
while (sym == Token::Kind::Lf)
scan();
check(Token::Kind::Eof);
if (hecl::VerbosityLevel > 1)
for (auto& res : result)
printf("%s\n", res.toString().c_str());
return result;
}
} // namespace hecl::Frontend

View File

@ -1,104 +0,0 @@
#include "hecl/Frontend.hpp"
namespace hecl::Frontend {
int Scanner::_read() {
if (m_sourceIt == m_source.end())
return -1;
return *m_sourceIt++;
}
bool Scanner::read() {
if (ch == EOF)
return false;
if (ch == LF) {
lastLine = std::move(currentLine);
currentLine = std::string();
}
int c = _read();
ch = char(c);
if (ch == LF) {
loc.line++;
lfcol = loc.col;
loc.col = 0;
} else if (c != EOF) {
currentLine += ch;
loc.col++;
}
return c != EOF;
}
Token Scanner::next() {
if (ch == EOF)
return Token(Token::Kind::Eof, loc);
char c = ch;
int tline = loc.line;
int tcol = loc.col;
int tlfcol = lfcol;
read();
// skip comments and newlines
while (c != EOF && (c == COMMENT || isspace(c))) {
if (c == COMMENT) {
while (c != LF && c != EOF) {
tline = loc.line;
tcol = loc.col;
tlfcol = lfcol;
c = ch;
read();
}
}
while (c != EOF && isspace(c)) {
if (c == LF)
return Token(Token::Kind::Lf, {tline - 1, tlfcol + 1});
tline = loc.line;
tcol = loc.col;
tlfcol = lfcol;
c = ch;
read();
}
}
Token::Kind kind = CharToTokenKind(c);
if (kind != Token::Kind::None)
return Token(kind, {tline, tcol});
if (ch == EOF)
return Token(Token::Kind::Eof, {tline, tcol});
// ident or number
if (isDigit(c)) {
std::string buf;
buf += c;
while (isDigit(ch)) {
buf += ch;
read();
}
if (ch == '.') { // float
buf += ch;
read();
while (isDigit(ch)) {
buf += ch;
read();
}
}
return Token(Token::Kind::Number, std::move(buf), {tline, tcol});
}
if (isStartIdent(c)) {
std::string buf;
buf += c;
while (isMidIdent(ch)) {
buf += ch;
read();
}
return Token(Token::Kind::Ident, std::move(buf), {tline, tcol});
}
error({tline, tcol}, "unexpected character '%c' (X'%02X')", chr(c), int(c));
return Token(Token::Kind::None, {tline, tcol});
}
} // namespace hecl::Frontend

View File

@ -331,15 +331,13 @@ public:
}; };
static void VisitFile(const ProjectPath& path, bool force, bool fast, static void VisitFile(const ProjectPath& path, bool force, bool fast,
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, ClientProcess* cp, std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, ClientProcess* cp) {
int cookPass) {
for (auto& spec : specInsts) { for (auto& spec : specInsts) {
if (spec->canCook(path, hecl::blender::SharedBlenderToken, cookPass)) { if (spec->canCook(path, hecl::blender::SharedBlenderToken)) {
if (cp) { if (cp) {
cp->addCookTransaction(path, force, fast, spec.get()); cp->addCookTransaction(path, force, fast, spec.get());
} else { } else {
const DataSpecEntry* override = const DataSpecEntry* override = spec->overrideDataSpec(path, spec->getDataSpecEntry());
spec->overrideDataSpec(path, spec->getDataSpecEntry(), hecl::blender::SharedBlenderToken);
if (!override) if (!override)
continue; continue;
ProjectPath cooked = path.getCookedPath(*override); ProjectPath cooked = path.getCookedPath(*override);
@ -357,14 +355,14 @@ static void VisitFile(const ProjectPath& path, bool force, bool fast,
static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, bool fast, static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, bool fast,
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress,
ClientProcess* cp, int cookPass) { ClientProcess* cp) {
if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.')) if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.'))
return; return;
if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() && if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() &&
hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) { hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) {
/* Handle AudioGroup case */ /* Handle AudioGroup case */
VisitFile(dir, force, fast, specInsts, progress, cp, cookPass); VisitFile(dir, force, fast, specInsts, progress, cp);
return; return;
} }
@ -384,7 +382,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
for (auto& child : children) { for (auto& child : children) {
if (child.second.getPathType() == ProjectPath::Type::File) { if (child.second.getPathType() == ProjectPath::Type::File) {
progress.changeFile(child.first.c_str(), progNum++ / progDenom); progress.changeFile(child.first.c_str(), progNum++ / progDenom);
VisitFile(child.second, force, fast, specInsts, progress, cp, cookPass); VisitFile(child.second, force, fast, specInsts, progress, cp);
} }
} }
progress.reportDirComplete(); progress.reportDirComplete();
@ -394,7 +392,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
for (auto& child : children) { for (auto& child : children) {
switch (child.second.getPathType()) { switch (child.second.getPathType()) {
case ProjectPath::Type::Directory: { case ProjectPath::Type::Directory: {
VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp, cookPass); VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp);
break; break;
} }
default: default:
@ -405,7 +403,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
} }
bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool recursive, bool force, bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool recursive, bool force,
bool fast, const DataSpecEntry* spec, ClientProcess* cp, int cookPass) { bool fast, const DataSpecEntry* spec, ClientProcess* cp) {
/* Construct DataSpec instances for cooking */ /* Construct DataSpec instances for cooking */
if (spec) { if (spec) {
if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) { if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) {
@ -426,11 +424,11 @@ bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter
case ProjectPath::Type::File: case ProjectPath::Type::File:
case ProjectPath::Type::Glob: { case ProjectPath::Type::Glob: {
cookProg.changeFile(path.getLastComponent().data(), 0.f); cookProg.changeFile(path.getLastComponent().data(), 0.f);
VisitFile(path, force, fast, m_cookSpecs, cookProg, cp, cookPass); VisitFile(path, force, fast, m_cookSpecs, cookProg, cp);
break; break;
} }
case ProjectPath::Type::Directory: { case ProjectPath::Type::Directory: {
VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp, cookPass); VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp);
break; break;
} }
default: default: