Blender 2.8 refactor

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

View File

@ -1,11 +1,11 @@
# Node Grid Arranger Class
NODE_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):

View File

@ -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)

View File

@ -2,7 +2,7 @@ bl_info = {
"name": "HECL",
"author": "Jack Andersen <jackoalan@gmail.com>",
"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__":

View File

@ -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)

View File

@ -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:
# 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
# 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]
self.dlay = dlay
clays = []
for cl in range(len(bm.loops.layers.color)):
clays.append(bm.loops.layers.color[cl])
self.clays = clays
writebuf(struct.pack('I', len(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
writebuf(struct.pack('I', len(ulays)))
# Per-vert pool attributes
# Verts
writebuf(struct.pack('I', len(bm.verts)))
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)
writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
if dlay:
sf = tuple(sorted(v[dlay].items()))
if sf not in self.skin:
self.skin[sf] = len(self.skin)
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:
writebuf(struct.pack('I', 0))
# Per-loop pool attributes
# 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:
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)
else:
nf = quant_norm(l.vert.normal)
writebuf(struct.pack('fff', nf[0], nf[1], nf[2]))
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)
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:
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
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:
if not self.loops_contiguous(found, l):
return True
break
return False
writebuf(struct.pack('II', 0xffffffff, 0xffffffff))
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)
# 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]))
# 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:
writebuf(struct.pack('I', f.material_index))
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))
writebuf(struct.pack('I', l.index))

View File

@ -1,394 +1,162 @@
import bpy, bpy.path, os.path
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(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))
# Trace color node structure
def recursive_color_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
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):
import bpy, bpy.path, os.path, struct
def get_texture_path(image):
return os.path.normpath(bpy.path.abspath(image.filepath))
SHADER_TYPES = {
'RetroShader': b'RSHD',
'RetroDynamicShader': b'RDYN',
'RetroDynamicAlphaShader': b'RDAL',
'RetroDynamicCharacterShader': b'RCHR',
}
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',
}
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)
# 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)
# Resolve texture paths
tex_paths = [get_texture_path(name) for name in tex_list]
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
# 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:
return "HECLOpaque(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
# 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
# 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"
writebuf(struct.pack('I', chunk_count))
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'MESH'
# 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")
def execute(self, context):
shad, texs = shader(context.object.active_material, context.object)
if not node.image:
raise RuntimeError("HMDL texture nodes must specify an image object")
vs = bpy.data.texts.new('HECL SHADER')
vs.write((shad + '\n'))
for tex in texs:
vs.write(tex + '\n')
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")
return {'FINISHED'}
# Determine matrix generator type
tex_coord_source = 0xff
uv_anim_type = 0xff
uv_anim_args = []
soc_from = node.inputs['Vector'].links[0].from_socket
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
elif soc_from.node.type == 'UVMAP' or soc_from.node.type == 'TEX_COORD':
pass
else:
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
if soc_from.node.type != 'UVMAP' and soc_from.node.type != 'TEX_COORD':
raise RuntimeError("Matrix animator nodes must connect to 'Texture Coordinate' or 'UV Map' node")
# Resolve map and matrix index
node_label = soc_from.node.label
matrix_idx = None
if node_label.startswith('MTX_'):
matrix_idx = int(node_label[4:])
if soc_from.name == 'UV':
if hasattr(soc_from.node, 'uv_map'):
uv_name = soc_from.node.uv_map
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
if uv_idx == -1:
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
tex_coord_source = uv_idx + 2
else:
tex_coord_source = 2
elif soc_from.name == 'Normal':
tex_coord_source = 1
elif soc_from.name == 'Window':
tex_coord_source = 0
else:
raise RuntimeError("Only the 'UV', 'Normal' and 'Window' sockets may be used from 'Texture Coordinate' nodes")
alpha = False
if socket.name == 'Alpha':
alpha = True
writebuf(b'PASS')
writebuf(pass_fourcc)
path = get_texture_path(node.image)
writebuf(struct.pack('I', len(path)))
writebuf(path.encode())
writebuf(struct.pack('B', tex_coord_source))
writebuf(struct.pack('B', uv_anim_type))
writebuf(struct.pack('I', len(uv_anim_args)))
for arg in uv_anim_args:
writebuf(struct.pack('f', arg))
writebuf(struct.pack('B', alpha))
else:
# Color pass
color_set = False
if inp.type == 'VALUE':
color_set = bool(inp.default_value)
else:
for comp in inp.default_value:
color_set |= bool(comp)
if color_set:
writebuf(b'CLR ')
writebuf(pass_fourcc)
if inp.type == 'VALUE':
writebuf(struct.pack('ffff', inp.default_value, inp.default_value,
inp.default_value, inp.default_value))
else:
writebuf(struct.pack('ffff', inp.default_value[0], inp.default_value[1],
inp.default_value[2], inp.default_value[3]))

View File

@ -1,17 +1,13 @@
import struct, bpy, bmesh
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

View File

@ -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()

View File

@ -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')

View File

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

View File

@ -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")

View File

@ -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'}

View File

@ -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')

View File

@ -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]
if material.hecl_lightmap in bpy.data.images:
lm_node.image = bpy.data.images[material.hecl_lightmap]
else:
bpy.data.textures[material.hecl_lightmap].image = None
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]
lm_node.image = bpy.data.images[img_name]
else:
bpy.data.textures[material.hecl_lightmap].image = None
lm_node.image = None
# White Lightmaps
elif area_data.lightmap_mode == 'NONE':
img_name = 'NONE'
@ -51,12 +47,10 @@ 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
@ -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)

View File

@ -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]

View File

@ -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):

2
hecl/extern/athena vendored

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

2
hecl/extern/boo vendored

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

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

View File

@ -1,16 +1,24 @@
#pragma once
#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));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,15 +20,19 @@
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <variant>
#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<ProjectPath> texs;
std::unordered_map<std::string, int32_t> 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<ChunkType::TexturePass> {
PassType type;
ProjectPath tex;
TexCoordSource source;
UVAnimType uvAnimType;
std::array<float, 9> uvAnimParms = {};
bool alpha;
explicit PASS(Connection& conn);
bool operator==(const PASS& other) const {
return type == other.type &&
tex == other.tex &&
source == other.source &&
uvAnimType == other.uvAnimType &&
uvAnimParms == other.uvAnimParms &&
alpha == other.alpha;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
XXH64_update(st, &source, sizeof(source));
XXH64_update(st, &uvAnimType, sizeof(uvAnimType));
XXH64_update(st, &alpha, sizeof(alpha));
}
};
struct CLR : hecl::TypedRecord<ChunkType::ColorPass> {
PassType type;
Vector4f color;
explicit CLR(Connection& conn);
bool operator==(const CLR& other) const {
return type == other.type && color == other.color;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
}
};
using Chunk = hecl::TypedVariant<PASS, CLR>;
Material(Connection& conn);
enum class BlendMode : uint32_t {
Opaque = 0,
Alpha = 1,
Additive = 2
};
std::string name;
uint32_t passIndex;
ShaderType shaderType;
std::vector<Chunk> chunks;
std::unordered_map<std::string, int32_t> iprops;
BlendMode blendMode = BlendMode::Opaque;
explicit Material(Connection& conn);
bool operator==(const Material& other) const {
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<std::string> 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<std::vector<SkinBind>> skins;
std::vector<std::array<SkinBind, MaxSkinEntries>> skins;
std::vector<size_t> contiguousSkinVertCounts;
static size_t countSkinBinds(const std::array<SkinBind, MaxSkinEntries>& arr) {
size_t ret = 0;
for (const auto& b : arr)
if (b)
++ret;
else
break;
return ret;
}
void normalizeSkinBinds();
/** 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<Vert> verts;
Surface(Connection& conn, Mesh& parent, int skinSlotCount);
};
std::vector<Surface> surfaces;
@ -225,15 +363,13 @@ struct Mesh {
std::vector<uint32_t> m_boneIdxs;
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
size_t lookupLocalBoneIdx(uint32_t boneIdx) const;
};
std::vector<Bank> banks;
std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
} skinBanks;
using SurfProgFunc = std::function<void(int)>;
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<ColMesh> compileColMeshes();
/** Compile all meshes into one (AREA blends only) */
Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount = 10, float maxOctantLength = 5.0,
Mesh::SurfProgFunc surfProg = [](int) {});
/** Compile world intermediate (WORLD blends only) */
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<hecl::blender::Vector2f> {
size_t operator()(const hecl::blender::Vector2f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector3f> {
size_t operator()(const hecl::blender::Vector3f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector4f> {
size_t operator()(const hecl::blender::Vector4f& val) const noexcept {
size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[3]));
return h;
}
};
template <>
struct hash<array<hecl::blender::Mesh::SkinBind, 16>> {
size_t operator()(const array<hecl::blender::Mesh::SkinBind, 16>& val) const noexcept {
size_t h = 0;
for (const auto& bind : val) {
if (!bind)
break;
hecl::hash_combine_impl(h, std::hash<float>()(bind.vg_idx));
hecl::hash_combine_impl(h, std::hash<float>()(bind.weight));
}
return h;
}
};
}

View File

@ -102,14 +102,13 @@ public:
(void)progress;
}
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<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses,
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: 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

View File

@ -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; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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