diff --git a/hecl/blender/hecl/Nodegrid.py b/hecl/blender/hecl/Nodegrid.py index 0db8b3281..4dfe25d0f 100644 --- a/hecl/blender/hecl/Nodegrid.py +++ b/hecl/blender/hecl/Nodegrid.py @@ -1,11 +1,11 @@ # Node Grid Arranger Class NODE_PADDING = 80 -FRAME_NAMES = ['Dynamics','Textures','Combiners','Output'] -FRAME_WIDTHS = [250, 250, 800, 180] +FRAME_NAMES = ['Textures','Output'] +FRAME_WIDTHS = [400, 180] TOTAL_WIDTH = 0.0 for width in FRAME_WIDTHS: 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: def __init__(self, nodetree, cycles=False): diff --git a/hecl/blender/hecl/Patching.py b/hecl/blender/hecl/Patching.py index 702917a83..054b0296c 100644 --- a/hecl/blender/hecl/Patching.py +++ b/hecl/blender/hecl/Patching.py @@ -134,11 +134,11 @@ def load_func(self, context): def register(): bpy.utils.register_class(FILE_OT_hecl_patching_save) bpy.utils.register_class(FILE_OT_hecl_patching_load) - bpy.types.INFO_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(load_func) + bpy.types.TOPBAR_MT_file_external_data.append(save_func) def unregister(): bpy.utils.unregister_class(FILE_OT_hecl_patching_save) bpy.utils.unregister_class(FILE_OT_hecl_patching_load) - bpy.types.INFO_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(load_func) + bpy.types.TOPBAR_MT_file_external_data.remove(save_func) diff --git a/hecl/blender/hecl/__init__.py b/hecl/blender/hecl/__init__.py index dc39b7784..318182944 100644 --- a/hecl/blender/hecl/__init__.py +++ b/hecl/blender/hecl/__init__.py @@ -2,7 +2,7 @@ bl_info = { "name": "HECL", "author": "Jack Andersen ", "version": (1, 0), - "blender": (2, 78), + "blender": (2, 80, 0), "tracker_url": "https://github.com/AxioDL/hecl/issues/new", "location": "Properties > Scene > HECL", "description": "Enables blender to gather meshes, materials, and textures for hecl", @@ -11,11 +11,10 @@ bl_info = { # Package import from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching Nodegrid = Nodegrid.Nodegrid -import bpy, os, sys, struct -from bpy.app.handlers import persistent +parent_armature = sact.SACTSubtype.parent_armature +import bpy, os, sys, struct, math from mathutils import Vector - # Appendable list allowing external addons to register additional resource types hecl_typeS = [ ('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.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: if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]): exp_type[3](self.layout, context) 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 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_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 from bpy.app.handlers import persistent @persistent @@ -114,13 +143,13 @@ def scene_loaded(dummy): # Hide everything from an external library for o in bpy.context.scene.objects: if o.library: - o.hide = True + o.hide_set(True) # Show PATH library objects as wireframes if bpy.context.scene.hecl_type == 'PATH': if bpy.context.scene.background_set: 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: path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj] path_obj.show_wire = True @@ -134,14 +163,11 @@ def scene_loaded(dummy): mesh_obj = bpy.data.objects[subtype.linked_mesh] if subtype.linked_armature in bpy.data.objects: arm_obj = bpy.data.objects[subtype.linked_armature] - mesh_obj.parent = arm_obj - mesh_obj.parent_type = 'ARMATURE' + parent_armature(mesh_obj, arm_obj) for overlay in subtype.overlays: if overlay.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[overlay.linked_mesh] - mesh_obj.parent = arm_obj - mesh_obj.parent_type = 'ARMATURE' - + parent_armature(mesh_obj, arm_obj) # Show only the active mesh and action if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context): @@ -149,6 +175,28 @@ def scene_loaded(dummy): if sact.SACTAction.SACTAction_load.poll(bpy.context): 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 def register(): @@ -161,7 +209,33 @@ def register(): mapu.register() path.register() 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.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) Patching.register() @@ -172,6 +246,7 @@ def unregister(): srea.unregister() path.unregister() bpy.utils.unregister_class(hecl_scene_panel) + bpy.utils.unregister_class(hecl_light_panel) Patching.unregister() if __name__ == "__main__": diff --git a/hecl/blender/hecl/frme.py b/hecl/blender/hecl/frme.py index 424737677..76c25ba5d 100644 --- a/hecl/blender/hecl/frme.py +++ b/hecl/blender/hecl/frme.py @@ -4,7 +4,7 @@ from mathutils import Quaternion def draw(layout, context): if 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_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent') 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_worker_count', text='Worker Count') 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.label("Angular Falloff:", icon='LAMP_SPOT') + layout.label(text="Angular Falloff:", icon='LIGHT') row = layout.row(align=True) row.prop(obj.data, 'retro_light_angle_constant', text='Constant') 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 if obj.data.type == 'POINT': type_enum = 4 - elif obj.data.type == 'HEMI': + elif obj.data.type == 'SUN': type_enum = 2 elif obj.data.type == 'SPOT': type_enum = 0 @@ -231,10 +231,10 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name): path_hash = 0xffffffff if len(obj.data.materials): material = obj.data.materials[0] - if len(material.texture_slots) and material.texture_slots[0]: - tex_slot = material.texture_slots[0] - if tex_slot.texture.type == 'IMAGE' and tex_slot.texture.image: - image = tex_slot.texture.image + if 'Image Texture' in material.node_tree.nodes: + image_node = material.node_tree.nodes['Image Texture'] + if image_node.image: + image = image_node.image path = bpy.path.abspath(image.filepath) path_hash = path_hasher.hashpath32(path) @@ -254,7 +254,7 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name): else: 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', obj.matrix_local[0][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_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.Lamp.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.Lamp.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0) + bpy.types.Light.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0) + bpy.types.Light.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', 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.Light.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0) diff --git a/hecl/blender/hecl/hmdl/HMDLMesh.py b/hecl/blender/hecl/hmdl/HMDLMesh.py index fb5612b70..612f43798 100644 --- a/hecl/blender/hecl/hmdl/HMDLMesh.py +++ b/hecl/blender/hecl/hmdl/HMDLMesh.py @@ -1,5 +1,4 @@ import bpy, bmesh, operator, struct -from mathutils import Vector # Function to quantize normals to 15-bit precision def quant_norm(n): @@ -15,348 +14,87 @@ def quant_luv(n): uf[i] = int(uf[i] * 32768) / 32768.0 return uf.freeze() -# Class for building unique sets of vertex attributes for VBO generation -class VertPool: +# Function to output all mesh attribute values +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 - 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 + clays = [] + for cl in range(len(bm.loops.layers.color)): + clays.append(bm.loops.layers.color[cl]) + writebuf(struct.pack('I', len(clays))) - dlay = None - if len(bm.verts.layers.deform): - dlay = bm.verts.layers.deform[0] - self.dlay = dlay + luvlay = None + if use_luv: + luvlay = bm.loops.layers.uv[0] + ulays = [] + for ul in range(len(bm.loops.layers.uv)): + ulays.append(bm.loops.layers.uv[ul]) + writebuf(struct.pack('I', len(ulays))) - 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) + # Verts + writebuf(struct.pack('I', len(bm.verts))) + for v in bm.verts: + writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2])) + if dlay: + sf = tuple(sorted(v[dlay].items())) + writebuf(struct.pack('I', len(sf))) + total_len = 0.0 + for ent in sf: + total_len += ent[1] + for ent in sf: + writebuf(struct.pack('If', ent[0], ent[1] / total_len)) else: - nf = quant_norm(loop.vert.normal) - return self.norm[nf] + writebuf(struct.pack('I', 0)) - def get_skin_idx(self, vert): - if not self.dlay: - return 0 - sf = tuple(sorted(vert[self.dlay].items())) - return self.skin[sf] + # Loops + loop_count = 0 + for f in bm.faces: + for l in f.loops: + 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): - 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 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 + # Edges + writebuf(struct.pack('I', len(bm.edges))) + for e in bm.edges: + for v in e.verts: + writebuf(struct.pack('I', v.index)) + writebuf(struct.pack('I', len(e.link_faces))) for f in e.link_faces: - if f == face: - continue - next_faces.append(f) - return next_faces + writebuf(struct.pack('I', f.index)) + writebuf(struct.pack('b', e.is_contiguous)) -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 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)) + # Faces + writebuf(struct.pack('I', len(bm.faces))) + for f in bm.faces: + norm = f.normal + writebuf(struct.pack('fff', norm[0], norm[1], norm[2])) + centroid = f.calc_center_bounds() + writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2])) + writebuf(struct.pack('I', f.material_index)) + for l in f.loops: + writebuf(struct.pack('I', l.index)) diff --git a/hecl/blender/hecl/hmdl/HMDLShader.py b/hecl/blender/hecl/hmdl/HMDLShader.py index e35e76f38..4e94e4c5a 100644 --- a/hecl/blender/hecl/hmdl/HMDLShader.py +++ b/hecl/blender/hecl/hmdl/HMDLShader.py @@ -1,394 +1,162 @@ -import bpy, bpy.path, os.path +import bpy, bpy.path, os.path, struct -def get_texmap_idx(tex_list, name): - for i in range(len(tex_list)): - if tex_list[i] == name: - return i - retval = len(tex_list) - tex_list.append(name) - return retval +def get_texture_path(image): + return os.path.normpath(bpy.path.abspath(image.filepath)) -def get_texture_path(name): - if name not in bpy.data.textures: - raise RuntimeError('unable to find %s texture' % name) - tex = bpy.data.textures[name] - if tex.type != 'IMAGE': - 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)) +SHADER_TYPES = { + 'RetroShader': b'RSHD', + 'RetroDynamicShader': b'RDYN', + 'RetroDynamicAlphaShader': b'RDAL', + 'RetroDynamicCharacterShader': b'RCHR', +} -# Trace color node structure -def recursive_color_trace(mat_obj, mesh_obj, tex_list, node, socket=None): +PASS_TYPE = { + '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': - 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): +def write_chunks(writebuf, mat_obj, mesh_obj): if not mat_obj.use_nodes: 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': - raise RuntimeError("HMDL *requires* that an OUTPUT shader node named 'Output' is present") + if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'GROUP': + raise RuntimeError("HMDL *requires* that an group shader node named 'Output' is present") # Root (output) node 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 - tex_list = [] - color_trace_result = recursive_color_trace(mat_obj, mesh_obj, tex_list, output_node) - alpha_trace_result = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, output_node) + # Count sockets + chunk_count = 0 + for inp in output_node.inputs: + 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 - 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) + writebuf(struct.pack('I', chunk_count)) - # Resolve texture paths - tex_paths = [get_texture_path(name) for name in tex_list] + # Enumerate sockets + 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': - return "HECLAlpha(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths - 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 + if not node.image: + raise RuntimeError("HMDL texture nodes must specify an image object") -# DEBUG operator -import bpy -class hecl_shader_operator(bpy.types.Operator): - bl_idname = "scene.hecl_shader" - bl_label = "DEBUG HECL shader maker" - bl_description = "Test shader generation utility" + if not node.inputs['Vector'].is_linked: + raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked") - @classmethod - def poll(cls, context): - return context.object and context.object.type == 'MESH' + # Determine matrix generator type + tex_coord_source = 0xff + uv_anim_type = 0xff + uv_anim_args = [] + soc_from = node.inputs['Vector'].links[0].from_socket - def execute(self, context): - shad, texs = shader(context.object.active_material, context.object) - - vs = bpy.data.texts.new('HECL SHADER') - vs.write((shad + '\n')) - for tex in texs: - vs.write(tex + '\n') + if soc_from.node.type == 'GROUP': + if soc_from.node.node_tree.name.startswith('RetroUVMode'): + uv_anim_type = int(soc_from.node.node_tree.name[11:12]) + if len(soc_from.node.inputs)-1: + 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': + 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])) diff --git a/hecl/blender/hecl/hmdl/__init__.py b/hecl/blender/hecl/hmdl/__init__.py index 4a588da1b..08e3c5b13 100644 --- a/hecl/blender/hecl/hmdl/__init__.py +++ b/hecl/blender/hecl/hmdl/__init__.py @@ -1,17 +1,13 @@ import struct, bpy, bmesh -from mathutils import Vector from . import HMDLShader, HMDLMesh def write_out_material(writebuf, mat, mesh_obj): - hecl_str, texs = HMDLShader.shader(mat, mesh_obj) writebuf(struct.pack('I', len(mat.name))) writebuf(mat.name.encode()) - writebuf(struct.pack('I', len(hecl_str))) - writebuf(hecl_str.encode()) - writebuf(struct.pack('I', len(texs))) - for tex in texs: - writebuf(struct.pack('I', len(tex))) - writebuf(tex.encode()) + + writebuf(struct.pack('I', mat.pass_index)) + + HMDLShader.write_chunks(writebuf, mat, mesh_obj) prop_count = 0 for prop in mat.items(): @@ -24,22 +20,16 @@ def write_out_material(writebuf, mat, mesh_obj): writebuf(prop[0].encode()) writebuf(struct.pack('i', prop[1])) - transparent = False - if mat.game_settings.alpha_blend == 'ALPHA' or mat.game_settings.alpha_blend == 'ALPHA_SORT': - transparent = True - elif mat.game_settings.alpha_blend == 'ADD': - transparent = True - writebuf(struct.pack('b', int(transparent))) - -# 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'] + blend = 0 + if mat.blend_method == 'BLEND': + blend = 1 + elif mat.blend_method == 'ADD': + blend = 2 + writebuf(struct.pack('I', blend)) # Takes a Blender 'Mesh' object (not the datablock) # 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': 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_mesh = bpy.data.meshes.new(copy_name) 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_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.context.scene.objects.active = copy_obj - copy_obj.select = True + bpy.context.view_layer.objects.active = copy_obj + copy_obj.select_set(True) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') 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] writebuf(struct.pack('fff', pt[0], pt[1], pt[2])) - # Create master BMesh and VertPool + # Create master BMesh bm_master = bmesh.new() 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 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: write_out_material(writebuf, mat, mesh_obj) - # Output vert pool - vert_pool.write_out(writebuf, mesh_obj.vertex_groups) + # Output attribute lists + HMDLMesh.write_mesh_attrs(writebuf, bm_master, rna_loops, use_luv, mesh_obj.material_slots) - dlay = None - if len(bm_master.verts.layers.deform): - dlay = bm_master.verts.layers.deform[0] - - # Generate material meshes (if opaque) - 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)) + # Vertex groups + writebuf(struct.pack('I', len(mesh_obj.vertex_groups))) + for vgrp in mesh_obj.vertex_groups: + writebuf(struct.pack('I', len(vgrp.name))) + writebuf(vgrp.name.encode()) # Enumerate custom props 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 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.meshes.remove(copy_mesh) @@ -231,13 +134,13 @@ def cookcol(writebuf, mesh_obj): copy_name = mesh_obj.name + "_hmdltri" copy_mesh = bpy.data.meshes.new(copy_name) 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_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.context.scene.objects.active = copy_obj - copy_obj.select = True + bpy.context.view_layer.objects.active = copy_obj + copy_obj.select_set(True) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.quads_convert_to_tris() @@ -320,7 +223,7 @@ def cookcol(writebuf, mesh_obj): # Send verts writebuf(struct.pack('I', len(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])) # 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)) # 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.meshes.remove(copy_mesh) @@ -348,13 +251,13 @@ def cookcol(writebuf, mesh_obj): def draw(layout, context): layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects') 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: - 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: obj = context.scene.objects[context.scene.hecl_mesh_obj] 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_material_count') @@ -398,10 +301,8 @@ def register(): 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_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) pass def unregister(): - bpy.utils.unregister_class(HMDLShader.hecl_shader_operator) bpy.utils.unregister_class(hecl_mesh_operator) pass diff --git a/hecl/blender/hecl/mapa.py b/hecl/blender/hecl/mapa.py index b5c749e6e..a320b8083 100644 --- a/hecl/blender/hecl/mapa.py +++ b/hecl/blender/hecl/mapa.py @@ -1,8 +1,233 @@ -import bpy, struct, bmesh -from . import hmdl +import bpy, struct, bmesh, operator 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): if face not in rem_list: @@ -34,13 +259,13 @@ def cook(writebuf, mesh_obj): copy_name = mesh_obj.name + "_hmdltri" copy_mesh = bpy.data.meshes.new(copy_name) 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_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.context.scene.objects.active = copy_obj - copy_obj.select = True + bpy.context.view_layer.objects.active = copy_obj + copy_obj.select_set(True) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.quads_convert_to_tris() diff --git a/hecl/blender/hecl/path.py b/hecl/blender/hecl/path.py index 2d5c862e9..d6a350bed 100644 --- a/hecl/blender/hecl/path.py +++ b/hecl/blender/hecl/path.py @@ -1,5 +1,6 @@ -import bpy, bgl, sys, bmesh, struct +import bpy, gpu, sys, bmesh, struct 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 class HeightRef: @@ -44,14 +45,6 @@ def set_height(self, val): if ar.type == 'VIEW_3D': 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 class AABB: def __init__(self): @@ -226,34 +219,39 @@ def cook(writebuf, mesh_obj): writebuf(struct.pack('I', len(ba))) writebuf(ba) +try: + line_shader = gpu.shader.from_builtin('3D_FLAT_COLOR') +except: + pass + # Line draw helper -def draw_line_3d(color, start, end): - bgl.glColor4f(*color) - bgl.glBegin(bgl.GL_LINES) - bgl.glVertex3f(*start) - bgl.glVertex3f(*end) +def draw_line_3d(pos_vbo, color_vbo, color, start, end): + pos_vbo.append(start) + pos_vbo.append(end) + color_vbo.append(color) + color_vbo.append(color) # 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: - co0 = obj_mtx * obj.data.vertices[ek[0]].co - co1 = obj_mtx * obj.data.vertices[ek[1]].co - draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)), + co0 = obj_mtx @ obj.data.vertices[ek[0]].co + co1 = obj_mtx @ obj.data.vertices[ek[1]].co + draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)), co1 + Vector((0.0, 0.0, height))) for vk in p.vertices: - co = obj_mtx * obj.data.vertices[vk].co - draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height))) + co = obj_mtx @ obj.data.vertices[vk].co + draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height))) # 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: - co0 = obj_mtx * e.verts[0].co - co1 = obj_mtx * e.verts[1].co - draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)), + co0 = obj_mtx @ e.verts[0].co + co1 = obj_mtx @ e.verts[1].co + draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)), co1 + Vector((0.0, 0.0, height))) for v in f.verts: - co = obj_mtx * v.co - draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height))) + co = obj_mtx @ v.co + draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height))) # Viewport hook callback def draw_callback_3d(self, context): @@ -275,6 +273,9 @@ def draw_callback_3d(self, context): if 'Height' in obj.data.polygon_layers_float: height_lay = obj.data.polygon_layers_float['Height'] + pos_vbo = [] + color_vbo = [] + # Deselected colors top_color = (0.0, 0.0, 1.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: continue 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: for p in obj.data.polygons: height = 1.0 if height_lay is not None: height = height_lay.data[p.index].value - draw_poly(p, obj, obj_mtx, height, top_color, side_color) - bgl.glEnd() + draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color) # Selected colors if bm is not None: top_color = (1.0, 0.0, 1.0, 0.7) side_color = (0.0, 1.0, 0.0, 0.7) # Avoid z-fighting on selected lines - bgl.glDepthRange(-0.001, 0.999) + #bgl.glDepthRange(-0.001, 0.999) for f in bm.faces: if height_lay is not None: selected = f.select if not selected: continue height = f[height_lay] - draw_face(f, obj_mtx, height, top_color, side_color) - bgl.glEnd() - bgl.glDepthRange(0.0, 1.0) + draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color) + #bgl.glEnd() + #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 class PathHeightDrawOperator(bpy.types.Operator): @@ -346,17 +348,29 @@ class PathBackgroundWireframeOperator(bpy.types.Operator): if context.scene.background_set: to_wire = False for o in context.scene.background_set.objects: - if o.draw_type != 'WIRE': + if o.display_type != 'WIRE': to_wire = True break if to_wire: for o in context.scene.background_set.objects: - o.draw_type = 'WIRE' + o.display_type = 'WIRE' else: for o in context.scene.background_set.objects: - o.draw_type = 'TEXTURED' + o.display_type = 'TEXTURED' 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 def register(): bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask') diff --git a/hecl/blender/hecl/sact/ANIM.py b/hecl/blender/hecl/sact/ANIM.py index d006bbd2f..90d3a1cfc 100644 --- a/hecl/blender/hecl/sact/ANIM.py +++ b/hecl/blender/hecl/sact/ANIM.py @@ -4,7 +4,6 @@ in an interleaved, sparse array for use by the runtime ''' import re -import hashlib import struct import mathutils diff --git a/hecl/blender/hecl/sact/SACTAction.py b/hecl/blender/hecl/sact/SACTAction.py index 6a9ec6d3c..e2d5bd951 100644 --- a/hecl/blender/hecl/sact/SACTAction.py +++ b/hecl/blender/hecl/sact/SACTAction.py @@ -15,7 +15,7 @@ def action_type_update(self, context): # Actor action class class SACTAction(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name="Action Name") + name: bpy.props.StringProperty(name="Action Name") # Panel draw def draw(layout, context): @@ -30,8 +30,8 @@ def draw(layout, context): row.template_list("UI_UL_list", "SCENE_UL_SACTActions", actor_data, 'actions', actor_data, 'active_action') col = row.column(align=True) - col.operator("scene.sactaction_add", icon="ZOOMIN", text="") - col.operator("scene.sactaction_remove", icon="ZOOMOUT", text="") + col.operator("scene.sactaction_add", icon="ADD", text="") + col.operator("scene.sactaction_remove", icon="REMOVE", text="") if len(actor_data.actions) and actor_data.active_action >= 0: action = actor_data.actions[actor_data.active_action] @@ -48,7 +48,7 @@ def draw(layout, context): # Validate if linked_action is None: - layout.label("Source action not set", icon='ERROR') + layout.label(text="Source action not set", icon='ERROR') else: #layout.prop(linked_action, 'hecl_index', text="Index") #layout.prop(linked_action, 'hecl_anim_props', text="Props") diff --git a/hecl/blender/hecl/sact/SACTSubtype.py b/hecl/blender/hecl/sact/SACTSubtype.py index 656427d3e..6a8500c62 100644 --- a/hecl/blender/hecl/sact/SACTSubtype.py +++ b/hecl/blender/hecl/sact/SACTSubtype.py @@ -9,28 +9,26 @@ def active_subtype_update(self, context): # Actor subtype overlay class class SACTSubtypeOverlay(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name="Overlay Name") - 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) + name: bpy.props.StringProperty(name="Overlay Name") + 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) # Actor attachment class class SACTAttachment(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name="Attachment Name") - 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) - show_attachment = bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update) + name: bpy.props.StringProperty(name="Attachment Name") + 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) + show_attachment: bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update) # Actor subtype class class SACTSubtype(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name="Actor Mesh Name") - 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) - show_mesh = bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update) + name: bpy.props.StringProperty(name="Actor Mesh Name") + 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) + show_mesh: bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update) - overlays =\ - bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List") - active_overlay =\ - bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update) + overlays: bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List") + active_overlay: bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update) # Panel draw def draw(layout, context): @@ -45,8 +43,8 @@ def draw(layout, context): row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes", actor_data, 'subtypes', actor_data, 'active_subtype') col = row.column(align=True) - col.operator("scene.sactsubtype_add", icon="ZOOMIN", text="") - col.operator("scene.sactsubtype_remove", icon="ZOOMOUT", text="") + col.operator("scene.sactsubtype_add", icon="ADD", text="") + col.operator("scene.sactsubtype_remove", icon="REMOVE", text="") if len(actor_data.subtypes) and actor_data.active_subtype >= 0: subtype = actor_data.subtypes[actor_data.active_subtype] @@ -67,9 +65,9 @@ def draw(layout, context): # Validate 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': - 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 @@ -80,13 +78,13 @@ def draw(layout, context): layout.prop(subtype, 'show_mesh', text="Show Mesh") # Mesh overlays - layout.label("Overlay Meshes:") + layout.label(text="Overlay Meshes:") row = layout.row() row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays", subtype, 'overlays', subtype, 'active_overlay') col = row.column(align=True) - col.operator("scene.sactsubtypeoverlay_add", icon="ZOOMIN", text="") - col.operator("scene.sactsubtypeoverlay_remove", icon="ZOOMOUT", text="") + col.operator("scene.sactsubtypeoverlay_add", icon="ADD", text="") + col.operator("scene.sactsubtypeoverlay_remove", icon="REMOVE", text="") overlay_mesh = None 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") # Mesh attachments - layout.label("Attachment Meshes:") + layout.label(text="Attachment Meshes:") row = layout.row() row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments", actor_data, 'attachments', actor_data, 'active_attachment') col = row.column(align=True) - col.operator("scene.sactattachment_add", icon="ZOOMIN", text="") - col.operator("scene.sactattachment_remove", icon="ZOOMOUT", text="") + col.operator("scene.sactattachment_add", icon="ADD", text="") + col.operator("scene.sactattachment_remove", icon="REMOVE", text="") attachment_armature = linked_armature attachment_mesh = None @@ -121,27 +119,27 @@ def draw(layout, context): # Validate 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': - 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: layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR') 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.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': - 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.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: layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR') 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 @@ -195,6 +193,17 @@ class SACTSubtype_remove(bpy.types.Operator): 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 class SACTSubtype_load(bpy.types.Operator): bl_idname = "scene.sactsubtype_load" @@ -222,49 +231,46 @@ class SACTSubtype_load(bpy.types.Operator): # Hide armature children for object in linked_armature.children: if object.name in context.scene.objects: - object.hide = True + object.hide_set(True) # Hide all meshes (incl overlays) for subtype_data in actor_data.subtypes: if subtype_data.linked_mesh in bpy.data.objects: mesh = bpy.data.objects[subtype_data.linked_mesh] if mesh.name in context.scene.objects: - mesh.hide = True + mesh.hide_set(True) for overlay in subtype_data.overlays: if overlay.linked_mesh in bpy.data.objects: mesh = bpy.data.objects[overlay.linked_mesh] if mesh.name in context.scene.objects: - mesh.hide = True + mesh.hide_set(True) # Hide/Show selected attachment meshes for attachment in actor_data.attachments: if attachment.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[attachment.linked_mesh] 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 if attachment.linked_armature in bpy.data.objects: attachment_armature = bpy.data.objects[attachment.linked_armature] if mesh_obj != attachment_armature: - mesh_obj.parent = attachment_armature - mesh_obj.parent_type = 'ARMATURE' + parent_armature(mesh_obj, attachment_armature) # Show only the chosen subtype (and selected overlays) if subtype.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[subtype.linked_mesh] if subtype.show_mesh: - mesh_obj.hide = False + mesh_obj.hide_set(False) if mesh_obj != linked_armature: - mesh_obj.parent = linked_armature - mesh_obj.parent_type = 'ARMATURE' + parent_armature(mesh_obj, linked_armature) for overlay in subtype.overlays: if overlay.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[overlay.linked_mesh] if overlay.show_overlay: - mesh_obj.hide = False + mesh_obj.hide_set(False) if mesh_obj != linked_armature: - mesh_obj.parent = linked_armature - mesh_obj.parent_type = 'ARMATURE' + parent_armature(mesh_obj, linked_armature) return {'FINISHED'} diff --git a/hecl/blender/hecl/sact/__init__.py b/hecl/blender/hecl/sact/__init__.py index b65c0b5bd..b4a27d079 100644 --- a/hecl/blender/hecl/sact/__init__.py +++ b/hecl/blender/hecl/sact/__init__.py @@ -1,35 +1,24 @@ from . import SACTSubtype, SACTAction, ANIM -from .. import hmdl import bpy import bpy.path import re -import os.path -import posixpath import struct from mathutils import Vector, Quaternion, Euler # Actor data class class SACTData(bpy.types.PropertyGroup): - subtypes =\ - 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) - show_subtypes =\ - bpy.props.BoolProperty() + subtypes: 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) + show_subtypes: bpy.props.BoolProperty() - attachments = \ - bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List") - active_attachment = \ - bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update) + attachments: bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List") + active_attachment: bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update) - actions =\ - 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) - show_actions =\ - bpy.props.BoolProperty() + actions: 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) + show_actions: bpy.props.BoolProperty() # Regex RNA path matchers scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale') diff --git a/hecl/blender/hecl/srea/__init__.py b/hecl/blender/hecl/srea/__init__.py index e971409f3..7974b7590 100644 --- a/hecl/blender/hecl/srea/__init__.py +++ b/hecl/blender/hecl/srea/__init__.py @@ -3,7 +3,7 @@ from bpy.app.handlers import persistent from mathutils import Quaternion, Color import math import os.path -from .. import Nodegrid, swld +from .. import swld # Preview update func (for lighting preview) def preview_update(self, context): @@ -13,30 +13,26 @@ def preview_update(self, context): # Original Lightmaps if area_data.lightmap_mode == 'ORIGINAL': for material in bpy.data.materials: - if material.hecl_lightmap: - material.use_shadeless = False - + if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes: + lm_node = material.node_tree.nodes['Lightmap'] # Reference original game lightmaps - if material.hecl_lightmap in bpy.data.textures: - img_name = material.hecl_lightmap - if img_name in bpy.data.images: - bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name] - else: - bpy.data.textures[material.hecl_lightmap].image = None + if material.hecl_lightmap in bpy.data.images: + lm_node.image = bpy.data.images[material.hecl_lightmap] + else: + lm_node.image = None # Cycles Lightmaps elif area_data.lightmap_mode == 'CYCLES': for material in bpy.data.materials: - if material.hecl_lightmap: - material.use_shadeless = False - + if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes: + lm_node = material.node_tree.nodes['Lightmap'] # Reference newly-generated lightmaps - if material.hecl_lightmap in bpy.data.textures: - img_name = material.hecl_lightmap + '_CYCLES' - if img_name in bpy.data.images: - bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name] - else: - bpy.data.textures[material.hecl_lightmap].image = None + img_name = material.hecl_lightmap + '_CYCLES' + if img_name in bpy.data.images: + lm_node.image = bpy.data.images[img_name] + else: + lm_node.image = None + # White Lightmaps elif area_data.lightmap_mode == 'NONE': img_name = 'NONE' @@ -51,13 +47,11 @@ def preview_update(self, context): img.file_format = 'PNG' for material in bpy.data.materials: - if material.hecl_lightmap: - material.use_shadeless = False - + if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes: + lm_node = material.node_tree.nodes['Lightmap'] # Reference NONE - if material.hecl_lightmap in bpy.data.textures: - bpy.data.textures[material.hecl_lightmap].image = img - + lm_node.image = img + # Update lightmap output-resolution def set_lightmap_resolution(self, context): @@ -153,17 +147,17 @@ def set_adjacent_area(self, context): adjacent = dock_idx >= 0 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 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: - context.scene.objects.unlink(obj) + context.scene.collection.children.unlink(obj) except: pass continue - if obj.type == 'LAMP': + if obj.type == 'LIGHT': obj.hide_render = adjacent # Remove linked scenes @@ -200,8 +194,8 @@ def set_adjacent_area(self, context): bpy.data.scenes.remove(other_scene) return for obj in other_scene.objects: - if (obj.type == 'LAMP' or obj.type == 'MESH') and obj.layers[0]: - context.scene.objects.link(obj) + if (obj.type == 'LIGHT' or obj.type == 'MESH') and obj.layers[0]: + context.scene.collection.objects.link(obj) obj.hide_render = False # Ensure filepaths target the current dock index @@ -212,7 +206,7 @@ def set_adjacent_area(self, context): # Area data class 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", items=[ ('256', "256", "256x256 (original quality)"), @@ -223,7 +217,7 @@ class SREAData(bpy.types.PropertyGroup): update=set_lightmap_resolution, 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", items=[ ('NONE', "None", "Pure white lightmaps"), @@ -232,7 +226,7 @@ class SREAData(bpy.types.PropertyGroup): update=preview_update, 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", update=set_adjacent_area, default=-1, @@ -296,179 +290,6 @@ def get_de_sockets(chain): 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 def get_other_area_name(op, bg_scene, dock_idx): dock_conns = swld.build_dock_connections(bg_scene) @@ -501,7 +322,6 @@ def render_lightmaps(context): pixel_size = int(area_data.lightmap_resolution) # Mmm Cycles - context.scene.render.engine = 'CYCLES' context.scene.render.bake.margin = pixel_size // 256 # Iterate materials and setup cycles @@ -547,8 +367,8 @@ class SREARenderLightmaps(bpy.types.Operator): if not context.selected_objects: for obj in context.scene.objects: if obj.type == 'MESH' and not obj.library: - obj.select = True - context.scene.objects.active = obj + obj.select_set(True) + context.view_layer.objects.active = obj render_lightmaps(context) @@ -559,7 +379,6 @@ def shadeless_material(idx): if name in bpy.data.materials: return bpy.data.materials[name] mat = bpy.data.materials.new(name) - mat.use_shadeless = True r = idx % 256 g = (idx % 65536) // 256 b = idx // 65536 @@ -567,11 +386,11 @@ def shadeless_material(idx): return mat 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_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_right = 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_list = (look_forward, look_backward, look_up, look_down, look_left, look_right) # 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_envmaps = 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.render.image_settings.file_format = 'PNG' 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_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 cam.lens_unit = 'FOV' cam.angle = math.radians(90.0) @@ -617,7 +435,7 @@ def render_pvs(pathOut, location): bpy.ops.render.render(write_still=True) 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.cameras.remove(cam) @@ -634,16 +452,15 @@ def cook(writebuffunc, platform, endianchar): # Panel draw def draw(layout, context): 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.prop_enum(area_data, 'lightmap_mode', 'NONE') light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL') light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES') 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(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='OOPS') - layout.operator("scene.hecl_area_initialize_cycles", text="Initialize Cycles Nodes", icon='NODETREE') + layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='MOD_OPACITY') 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') @@ -656,7 +473,6 @@ def scene_loaded(dummy): # Registration def register(): bpy.utils.register_class(SREAData) - bpy.utils.register_class(SREAInitializeCycles) bpy.utils.register_class(SREARenderLightmaps) bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData) bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name') @@ -664,6 +480,5 @@ def register(): def unregister(): bpy.utils.unregister_class(SREAData) - bpy.utils.unregister_class(SREAInitializeCycles) bpy.utils.unregister_class(SREARenderLightmaps) diff --git a/hecl/blender/hecl/swld/__init__.py b/hecl/blender/hecl/swld/__init__.py index 6ebece8d9..d656e2b8a 100644 --- a/hecl/blender/hecl/swld/__init__.py +++ b/hecl/blender/hecl/swld/__init__.py @@ -62,9 +62,9 @@ def cook(writebuf): for ch in dock_list: if len(ch.data.vertices) < 4: 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): - v = wmtx * ch.data.vertices[vi].co + v = wmtx @ ch.data.vertices[vi].co writebuf(struct.pack('fff', v[0], v[1], v[2])) if ch.name in dock_conns: conn_dock = dock_conns[ch.name] diff --git a/hecl/blender/hecl_blendershell.py b/hecl/blender/hecl_blendershell.py index f548e8ca9..04470707c 100644 --- a/hecl/blender/hecl_blendershell.py +++ b/hecl/blender/hecl_blendershell.py @@ -65,14 +65,14 @@ class PathHasher: # If there's a third argument, use it as the .zip path containing the addon did_install = False if len(args) >= 4 and args[3] != 'SKIPINSTALL': - bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[3]) - bpy.ops.wm.addon_refresh() + bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=args[3]) + bpy.ops.preferences.addon_refresh() did_install = True # Make addon available to commands -if bpy.context.user_preferences.addons.find('hecl') == -1: +if bpy.context.preferences.addons.find('hecl') == -1: try: - bpy.ops.wm.addon_enable(module='hecl') + bpy.ops.preferences.addon_enable(module='hecl') bpy.ops.wm.save_userpref() except: pass @@ -93,16 +93,6 @@ ackbytes = readpipestr() if ackbytes != b'ACK': 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 def count_brackets(linestr): bracket_count = 0 @@ -184,15 +174,15 @@ def writelight(obj): if obj.data.type == 'POINT': type = 2 hasFalloff = True - castShadow = obj.data.shadow_method != 'NOSHADOW' + castShadow = obj.data.use_shadow elif obj.data.type == 'SPOT': type = 3 hasFalloff = True spotCutoff = obj.data.spot_size - castShadow = obj.data.shadow_method != 'NOSHADOW' + castShadow = obj.data.use_shadow elif obj.data.type == 'SUN': type = 1 - castShadow = obj.data.shadow_method != 'NOSHADOW' + castShadow = obj.data.use_shadow constant = 1.0 linear = 0.0 @@ -236,11 +226,11 @@ def dataout_loop(): elif cmdargs[0] == 'LIGHTLIST': lightCount = 0 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 writepipebuf(struct.pack('I', lightCount)) 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()) elif cmdargs[0] == 'MESHAABB': @@ -248,27 +238,24 @@ def dataout_loop(): hecl.mesh_aabb(writepipebuf) elif cmdargs[0] == 'MESHCOMPILE': - maxSkinBanks = int(cmdargs[2]) - meshName = bpy.context.scene.hecl_mesh_obj if meshName not in bpy.data.objects: writepipestr(('mesh %s not found' % meshName).encode()) continue 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': meshName = cmdargs[1] - maxSkinBanks = int(cmdargs[3]) - useLuv = int(cmdargs[4]) + useLuv = int(cmdargs[2]) if meshName not in bpy.data.objects: writepipestr(('mesh %s not found' % meshName).encode()) continue 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': meshName = cmdargs[1] @@ -293,25 +280,6 @@ def dataout_loop(): if obj.type == 'MESH' and not obj.library: 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': meshName = bpy.context.scene.hecl_path_obj if meshName not in bpy.data.objects: @@ -342,7 +310,7 @@ def dataout_loop(): lampCount = 0 firstSpot = None for obj in bpy.context.scene.objects: - if obj.type == 'LAMP': + if obj.type == 'LIGHT': lampCount += 1 if firstSpot is None and obj.data.type == 'SPOT': firstSpot = obj @@ -375,7 +343,7 @@ def dataout_loop(): # Lamp objects for obj in bpy.context.scene.objects: - if obj != firstSpot and obj.type == 'LAMP': + if obj != firstSpot and obj.type == 'LIGHT': writelight(obj) elif cmdargs[0] == 'GETTEXTURES': @@ -505,7 +473,7 @@ try: else: bpy.ops.wm.read_homefile() 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]): bpy.ops.file.hecl_patching_load() bpy.context.scene.hecl_type = cmdargs[2] @@ -527,7 +495,7 @@ try: writepipestr(b'FALSE') elif cmdargs[0] == 'SAVE': - bpy.context.user_preferences.filepaths.save_version = 0 + bpy.context.preferences.filepaths.save_version = 0 print('SAVING %s' % loaded_blend) if loaded_blend: if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True): diff --git a/hecl/extern/athena b/hecl/extern/athena index b9de85440..9bbd7af9f 160000 --- a/hecl/extern/athena +++ b/hecl/extern/athena @@ -1 +1 @@ -Subproject commit b9de85440046dba056a55c85e9952ce36ccf8156 +Subproject commit 9bbd7af9f6c5ff34b37752906b0d78295c44938d diff --git a/hecl/extern/boo b/hecl/extern/boo index 0f330c1f0..3ad748f28 160000 --- a/hecl/extern/boo +++ b/hecl/extern/boo @@ -1 +1 @@ -Subproject commit 0f330c1f057ec3c190d6278370f6e1628df435ed +Subproject commit 3ad748f28b5377027c900ded38eeccdfeb7fe7f6 diff --git a/hecl/extern/libSquish b/hecl/extern/libSquish index 96c520ed9..01bf9e652 160000 --- a/hecl/extern/libSquish +++ b/hecl/extern/libSquish @@ -1 +1 @@ -Subproject commit 96c520ed950682d113953f570fbd10a485cc0e8a +Subproject commit 01bf9e65294f7abae61a84d5b855202565fe1267 diff --git a/hecl/include/hecl/Backend/Backend.hpp b/hecl/include/hecl/Backend.hpp similarity index 84% rename from hecl/include/hecl/Backend/Backend.hpp rename to hecl/include/hecl/Backend.hpp index 7b7f11cd6..13ac4e3b3 100644 --- a/hecl/include/hecl/Backend/Backend.hpp +++ b/hecl/include/hecl/Backend.hpp @@ -1,16 +1,24 @@ #pragma once -#include "hecl/Frontend.hpp" +#include "boo/graphicsdev/IGraphicsDataFactory.hpp" namespace hecl::Backend { struct ExtensionSlot; +using namespace std::literals; -using IR = Frontend::IR; -using Diagnostics = Frontend::Diagnostics; -using SourceLocation = Frontend::SourceLocation; -using ArithmeticOp = IR::Instruction::ArithmeticOpType; - -enum class TexGenSrc : uint8_t { Position, Normal, UV }; +enum class TexCoordSource : uint8_t { + Invalid = 0xff, + Position = 0, + Normal = 1, + Tex0 = 2, + Tex1 = 3, + Tex2 = 4, + Tex3 = 5, + Tex4 = 6, + Tex5 = 7, + Tex6 = 8, + Tex7 = 9, +}; enum class BlendFactor : uint8_t { Zero, @@ -28,25 +36,49 @@ enum class BlendFactor : uint8_t { 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 CullMode : uint8_t { None, Backface, Frontface, Original = 0xff }; struct TextureInfo { - TexGenSrc src; - int mapIdx; - int uvIdx; - int mtxIdx; + TexCoordSource src; + uint8_t mtxIdx; bool normalize; }; 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 */ @@ -85,22 +117,6 @@ public: m_alphaTest = alphaTest; 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, Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling, bool alphaTest) @@ -170,10 +186,7 @@ struct Function { }; struct ExtensionSlot { - Function lighting; - Function post; - size_t blockCount = 0; - const char** blockNames = nullptr; + const char* shaderMacro; size_t texCount = 0; const Backend::TextureInfo* texs = nullptr; Backend::BlendFactor srcFactor = Backend::BlendFactor::Original; @@ -188,7 +201,7 @@ struct ExtensionSlot { bool forceAlphaTest = 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, Backend::BlendFactor srcFactor = Backend::BlendFactor::Original, Backend::BlendFactor dstFactor = Backend::BlendFactor::Original, @@ -196,9 +209,7 @@ struct ExtensionSlot { Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false, bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false, bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false) - : blockCount(blockCount) - , blockNames(blockNames) - , texCount(texCount) + : texCount(texCount) , texs(texs) , srcFactor(srcFactor) , dstFactor(dstFactor) @@ -216,10 +227,7 @@ struct ExtensionSlot { void calculateHash() const { XXH64_state_t st; XXH64_reset(&st, 0); - if (!lighting.m_source.empty()) - 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()); + XXH64_update(&st, shaderMacro, strlen(shaderMacro)); for (size_t i = 0; i < texCount; ++i) { const Backend::TextureInfo& tinfo = texs[i]; XXH64_update(&st, &tinfo, sizeof(tinfo)); diff --git a/hecl/include/hecl/Backend/GLSL.hpp b/hecl/include/hecl/Backend/GLSL.hpp deleted file mode 100644 index 54ad60744..000000000 --- a/hecl/include/hecl/Backend/GLSL.hpp +++ /dev/null @@ -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 diff --git a/hecl/include/hecl/Backend/GX.hpp b/hecl/include/hecl/Backend/GX.hpp deleted file mode 100644 index 1fa294bf9..000000000 --- a/hecl/include/hecl/Backend/GX.hpp +++ /dev/null @@ -1,513 +0,0 @@ -#pragma once - -#include "Backend.hpp" -#include -#include -#include -#include - -#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 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 { - 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 diff --git a/hecl/include/hecl/Backend/HLSL.hpp b/hecl/include/hecl/Backend/HLSL.hpp deleted file mode 100644 index 1c6cc47c2..000000000 --- a/hecl/include/hecl/Backend/HLSL.hpp +++ /dev/null @@ -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 diff --git a/hecl/include/hecl/Backend/Metal.hpp b/hecl/include/hecl/Backend/Metal.hpp deleted file mode 100644 index bf29a0ce1..000000000 --- a/hecl/include/hecl/Backend/Metal.hpp +++ /dev/null @@ -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 diff --git a/hecl/include/hecl/Backend/ProgrammableCommon.hpp b/hecl/include/hecl/Backend/ProgrammableCommon.hpp deleted file mode 100644 index 83b2ba13c..000000000 --- a/hecl/include/hecl/Backend/ProgrammableCommon.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include "Backend.hpp" -#include "hecl/Runtime.hpp" -#include -#include -#include -#include - -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 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 m_gameArgs; - }; - std::vector m_tcgs; - std::vector 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 diff --git a/hecl/include/hecl/Blender/Connection.hpp b/hecl/include/hecl/Blender/Connection.hpp index a56f10777..5a1c86d40 100644 --- a/hecl/include/hecl/Blender/Connection.hpp +++ b/hecl/include/hecl/Blender/Connection.hpp @@ -20,15 +20,19 @@ #include #include #include +#include #include "hecl/hecl.hpp" #include "hecl/HMDLMeta.hpp" +#include "hecl/TypedVariant.hpp" +#include "hecl/Backend.hpp" #include "athena/Types.hpp" #include "athena/MemoryWriter.hpp" #include "optional.hpp" #include "Token.hpp" namespace hecl::blender { +using namespace std::literals; extern logvisor::Module BlenderLog; class HMDLBuffers; @@ -103,6 +107,10 @@ struct Vector2f { void read(Connection& conn); Vector2f(Connection& conn) { read(conn); } 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 { atVec3f val; @@ -110,6 +118,11 @@ struct Vector3f { void read(Connection& conn); Vector3f(Connection& conn) { read(conn); } 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 { atVec4f val; @@ -117,6 +130,12 @@ struct Vector4f { void read(Connection& conn); Vector4f(Connection& conn) { read(conn); } 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 { atVec3f m[3]; @@ -137,26 +156,137 @@ struct Index { Index(Connection& conn) { read(conn); } 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 MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec); -/** HECL source and metadata of each material */ +/** Intermediate material representation */ struct Material { - std::string name; - std::string source; - std::vector texs; - std::unordered_map iprops; - bool transparent; + enum class ShaderType : uint32_t { + Invalid = 0, + RetroShader = 'RSHD', + RetroDynamicShader = 'RDYN', + 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 { + PassType type; + ProjectPath tex; + TexCoordSource source; + UVAnimType uvAnimType; + std::array 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 { + 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; - Material(Connection& conn); + enum class BlendMode : uint32_t { + Opaque = 0, + Alpha = 1, + Additive = 2 + }; + + std::string name; + uint32_t passIndex; + ShaderType shaderType; + std::vector chunks; + std::unordered_map iprops; + BlendMode blendMode = BlendMode::Opaque; + + explicit Material(Connection& conn); 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 */ struct Mesh { + static constexpr size_t MaxColorLayers = 4; + static constexpr size_t MaxUVLayers = 8; + static constexpr size_t MaxSkinEntries = 16; + HMDLTopology topology; /* Object transform in scene */ @@ -181,19 +311,30 @@ struct Mesh { /* Skinning data */ std::vector boneNames; struct SkinBind { - uint32_t boneIdx; - float weight; - SkinBind(Connection& conn); + uint32_t vg_idx = UINT32_MAX; + float weight = 0.f; + SkinBind() = default; + explicit SkinBind(Connection& conn); + operator bool() const { return vg_idx != UINT32_MAX; } }; - std::vector> skins; + std::vector> skins; std::vector contiguousSkinVertCounts; + static size_t countSkinBinds(const std::array& arr) { + size_t ret = 0; + for (const auto& b : arr) + if (b) + ++ret; + else + break; + return ret; + } void normalizeSkinBinds(); /** Islands of the same material/skinBank are represented here */ struct Surface { Vector3f centroid; - Index materialIdx; + uint32_t materialIdx; Vector3f aabbMin; Vector3f aabbMax; Vector3f reflectionNormal; @@ -208,12 +349,9 @@ struct Mesh { uint32_t iSkin = 0xffffffff; uint32_t iBankSkin = 0xffffffff; - Vert(Connection& conn, const Mesh& parent); bool operator==(const Vert& other) const; }; std::vector verts; - - Surface(Connection& conn, Mesh& parent, int skinSlotCount); }; std::vector surfaces; @@ -225,15 +363,13 @@ struct Mesh { std::vector m_boneIdxs; void addSkins(const Mesh& parent, const std::vector& skinIdxs); - size_t lookupLocalBoneIdx(uint32_t boneIdx) const; }; std::vector banks; std::vector::iterator addSkinBank(int skinSlotCount); uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount); } skinBanks; - using SurfProgFunc = std::function; - Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, SurfProgFunc& surfProg); + Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, bool useLuvs = false); Mesh getContiguousSkinningVersion() const; @@ -486,11 +622,10 @@ public: static const char* MeshOutputModeString(HMDLTopology topology); /** 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) */ - Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false, - Mesh::SurfProgFunc surfProg = [](int) {}); + Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false); /** Compile collision mesh by name (AREA blends only) */ ColMesh compileColMesh(std::string_view name); @@ -498,10 +633,6 @@ public: /** Compile all meshes as collision meshes (CMESH blends only) */ std::vector 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) */ World compileWorld(); @@ -558,6 +689,8 @@ class Connection { friend struct Matrix3f; friend struct Matrix4f; friend struct Index; + friend struct Float; + friend struct Boolean; std::atomic_bool m_lock = {false}; bool m_pyStreamActive = false; @@ -571,7 +704,6 @@ class Connection { #endif int m_readpipe[2]; int m_writepipe[2]; - bool m_hasSlerp; BlendType m_loadedType = BlendType::None; bool m_loadedRigged = false; ProjectPath m_loadedBlend; @@ -595,8 +727,6 @@ public: Connection(Connection&&) = delete; Connection& operator=(Connection&&) = delete; - bool hasSLERP() const { return m_hasSlerp; } - bool createBlend(const ProjectPath& path, BlendType type); BlendType getBlendType() const { return m_loadedType; } const ProjectPath& getBlendPath() const { return m_loadedBlend; } @@ -659,3 +789,46 @@ public: }; } // namespace hecl::blender + +namespace std { +template <> +struct hash { + size_t operator()(const hecl::blender::Vector2f& val) const noexcept { + size_t h = std::hash()(val.val.simd[0]); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[1])); + return h; + } +}; +template <> +struct hash { + size_t operator()(const hecl::blender::Vector3f& val) const noexcept { + size_t h = std::hash()(val.val.simd[0]); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[1])); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[2])); + return h; + } +}; +template <> +struct hash { + size_t operator()(const hecl::blender::Vector4f& val) const noexcept { + size_t h = std::hash()(val.val.simd[0]); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[1])); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[2])); + hecl::hash_combine_impl(h, std::hash()(val.val.simd[3])); + return h; + } +}; +template <> +struct hash> { + size_t operator()(const array& val) const noexcept { + size_t h = 0; + for (const auto& bind : val) { + if (!bind) + break; + hecl::hash_combine_impl(h, std::hash()(bind.vg_idx)); + hecl::hash_combine_impl(h, std::hash()(bind.weight)); + } + return h; + } +}; +} diff --git a/hecl/include/hecl/Database.hpp b/hecl/include/hecl/Database.hpp index d0822b1fb..a4a3793ce 100644 --- a/hecl/include/hecl/Database.hpp +++ b/hecl/include/hecl/Database.hpp @@ -102,14 +102,13 @@ public: (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; LogModule.report(logvisor::Error, "not implemented"); - (void)cookPass; return false; } - virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, const Database::DataSpecEntry* oldEntry, - blender::Token& btok) const { + virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, + const Database::DataSpecEntry* oldEntry) const { (void)path; return oldEntry; } @@ -151,12 +150,11 @@ struct DataSpecEntry { SystemStringView m_name; SystemStringView m_desc; SystemStringView m_pakExt; - int m_numCookPasses; std::function(Project&, DataSpecTool)> m_factory; - DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses, + DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, std::function(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 spec if non-null, cook using a manually-selected dataspec * @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 * * Object cooking is generally an expensive process for large projects. @@ -410,8 +404,8 @@ public: * feedback delivered via feedbackCb. */ bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false, - bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr, - int cookPass = -1); + bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr, + ClientProcess* cp = nullptr); /** * @brief Begin package process for specified !world.blend or directory diff --git a/hecl/include/hecl/FourCC.hpp b/hecl/include/hecl/FourCC.hpp index 9d5c3f91f..a668a6df6 100644 --- a/hecl/include/hecl/FourCC.hpp +++ b/hecl/include/hecl/FourCC.hpp @@ -22,11 +22,11 @@ protected: }; public: - FourCC() /* Sentinel FourCC */ + constexpr FourCC() /* Sentinel FourCC */ : num(0) {} - FourCC(const FourCC& other) { num = other.num; } - FourCC(const char* name) : num(*(uint32_t*)name) {} - FourCC(uint32_t n) : num(n) {} + constexpr FourCC(const FourCC& other) : num(other.num) {} + constexpr FourCC(const char* name) : num(*(uint32_t*)name) {} + 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 char* other) const { return num == *(uint32_t*)other; } diff --git a/hecl/include/hecl/Frontend.hpp b/hecl/include/hecl/Frontend.hpp deleted file mode 100644 index ff2a4e785..000000000 --- a/hecl/include/hecl/Frontend.hpp +++ /dev/null @@ -1,348 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -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 - 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 left; - std::unique_ptr right; - std::list 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&& 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 - 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 parse(); -}; - -using BigDNA = athena::io::DNA; - -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 m_argInstCount; - Vector m_argInstIdxs; - } m_call; - - struct LoadImm : BigDNA { - AT_DECL_DNA - Value m_immVec = {}; - } m_loadImm; - - enum ArithmeticOpType : uint8_t { None, Add, Subtract, Multiply, Divide }; - - struct Arithmetic : BigDNA { - AT_DECL_DNA - Value m_op = ArithmeticOpType::None; - Value m_instIdxs[2]; - } m_arithmetic; - - struct Swizzle : BigDNA { - AT_DECL_DNA - Value m_idxs[4] = {-1, -1, -1, -1}; - Value 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 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 diff --git a/hecl/include/hecl/Pipeline.hpp b/hecl/include/hecl/Pipeline.hpp index 613e31077..48c12ee35 100644 --- a/hecl/include/hecl/Pipeline.hpp +++ b/hecl/include/hecl/Pipeline.hpp @@ -3,9 +3,6 @@ #include #include #include "hecl/hecl.hpp" -#include "hecl/Backend/GLSL.hpp" -#include "hecl/Backend/HLSL.hpp" -#include "hecl/Backend/Metal.hpp" #include "PipelineBase.hpp" /* CMake-curated rep classes for the application */ @@ -28,108 +25,7 @@ public: } boo::ObjToken stage() const { return m_stage; } }; -#endif -class HECLIR : public PipelineRep { - 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 -class HECLBackendImpl : public PipelineRep

{ - hecl::Backend::ShaderTag m_tag; - BackendTp m_backend; - const hecl::Backend::ExtensionSlot& m_extension; - -public: - static constexpr bool HasHash = false; - HECLBackendImpl(PipelineConverter

& 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 blendFactors() const { - return {m_backend.m_blendSrc, m_backend.m_blendDst}; - } -}; - -template -class HECLBackend : public PipelineRep

{ -public: - static constexpr bool HasHash = false; -}; - -template <> -class HECLBackend : public HECLBackendImpl { -public: - using HECLBackendImpl::HECLBackendImpl; -}; - -template <> -class HECLBackend : public HECLBackendImpl { -public: - using HECLBackendImpl::HECLBackendImpl; -}; - -template <> -class HECLBackend : public HECLBackendImpl { -public: - using HECLBackendImpl::HECLBackendImpl; -}; - -template <> -class HECLBackend : public HECLBackendImpl { -public: - using HECLBackendImpl::HECLBackendImpl; -}; - -template <> -class HECLBackend : public HECLBackendImpl { -public: - using HECLBackendImpl::HECLBackendImpl; -}; - -template