mirror of https://github.com/AxioDL/metaforce.git
Blender 2.8 refactor
This commit is contained in:
parent
8b1b674a7d
commit
5c59acddf2
|
@ -1,11 +1,11 @@
|
||||||
# Node Grid Arranger Class
|
# Node Grid Arranger Class
|
||||||
NODE_PADDING = 80
|
NODE_PADDING = 80
|
||||||
FRAME_NAMES = ['Dynamics','Textures','Combiners','Output']
|
FRAME_NAMES = ['Textures','Output']
|
||||||
FRAME_WIDTHS = [250, 250, 800, 180]
|
FRAME_WIDTHS = [400, 180]
|
||||||
TOTAL_WIDTH = 0.0
|
TOTAL_WIDTH = 0.0
|
||||||
for width in FRAME_WIDTHS:
|
for width in FRAME_WIDTHS:
|
||||||
TOTAL_WIDTH += width + NODE_PADDING
|
TOTAL_WIDTH += width + NODE_PADDING
|
||||||
FRAME_COLORS = [(0.6,0.46,0.6),(0.6,0.48,0.44),(0.33,0.48,0.6),(0.53,0.6,0.47)]
|
FRAME_COLORS = [(0.6,0.48,0.44),(0.53,0.6,0.47)]
|
||||||
class Nodegrid:
|
class Nodegrid:
|
||||||
|
|
||||||
def __init__(self, nodetree, cycles=False):
|
def __init__(self, nodetree, cycles=False):
|
||||||
|
|
|
@ -134,11 +134,11 @@ def load_func(self, context):
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(FILE_OT_hecl_patching_save)
|
bpy.utils.register_class(FILE_OT_hecl_patching_save)
|
||||||
bpy.utils.register_class(FILE_OT_hecl_patching_load)
|
bpy.utils.register_class(FILE_OT_hecl_patching_load)
|
||||||
bpy.types.INFO_MT_file_external_data.append(load_func)
|
bpy.types.TOPBAR_MT_file_external_data.append(load_func)
|
||||||
bpy.types.INFO_MT_file_external_data.append(save_func)
|
bpy.types.TOPBAR_MT_file_external_data.append(save_func)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(FILE_OT_hecl_patching_save)
|
bpy.utils.unregister_class(FILE_OT_hecl_patching_save)
|
||||||
bpy.utils.unregister_class(FILE_OT_hecl_patching_load)
|
bpy.utils.unregister_class(FILE_OT_hecl_patching_load)
|
||||||
bpy.types.INFO_MT_file_external_data.remove(load_func)
|
bpy.types.TOPBAR_MT_file_external_data.remove(load_func)
|
||||||
bpy.types.INFO_MT_file_external_data.remove(save_func)
|
bpy.types.TOPBAR_MT_file_external_data.remove(save_func)
|
||||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "HECL",
|
"name": "HECL",
|
||||||
"author": "Jack Andersen <jackoalan@gmail.com>",
|
"author": "Jack Andersen <jackoalan@gmail.com>",
|
||||||
"version": (1, 0),
|
"version": (1, 0),
|
||||||
"blender": (2, 78),
|
"blender": (2, 80, 0),
|
||||||
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
|
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
|
||||||
"location": "Properties > Scene > HECL",
|
"location": "Properties > Scene > HECL",
|
||||||
"description": "Enables blender to gather meshes, materials, and textures for hecl",
|
"description": "Enables blender to gather meshes, materials, and textures for hecl",
|
||||||
|
@ -11,11 +11,10 @@ bl_info = {
|
||||||
# Package import
|
# Package import
|
||||||
from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching
|
from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching
|
||||||
Nodegrid = Nodegrid.Nodegrid
|
Nodegrid = Nodegrid.Nodegrid
|
||||||
import bpy, os, sys, struct
|
parent_armature = sact.SACTSubtype.parent_armature
|
||||||
from bpy.app.handlers import persistent
|
import bpy, os, sys, struct, math
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
|
|
||||||
# Appendable list allowing external addons to register additional resource types
|
# Appendable list allowing external addons to register additional resource types
|
||||||
hecl_typeS = [
|
hecl_typeS = [
|
||||||
('NONE', "None", "Active scene not using HECL", None),
|
('NONE', "None", "Active scene not using HECL", None),
|
||||||
|
@ -46,11 +45,33 @@ class hecl_scene_panel(bpy.types.Panel):
|
||||||
type_row = layout.row(align=True)
|
type_row = layout.row(align=True)
|
||||||
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
|
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
|
||||||
|
|
||||||
|
if context.scene.hecl_type == 'MESH' or context.scene.hecl_type == 'AREA' or context.scene.hecl_type == 'ACTOR':
|
||||||
|
sm_row = layout.row(align=True)
|
||||||
|
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'ORIGINAL')
|
||||||
|
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'PBR')
|
||||||
|
|
||||||
for exp_type in hecl_typeS:
|
for exp_type in hecl_typeS:
|
||||||
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
|
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
|
||||||
exp_type[3](self.layout, context)
|
exp_type[3](self.layout, context)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Light Panel
|
||||||
|
class hecl_light_panel(bpy.types.Panel):
|
||||||
|
bl_idname = "DATA_PT_hecl_light"
|
||||||
|
bl_label = "HECL"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.light
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(context.light, 'hecl_falloff_constant')
|
||||||
|
layout.prop(context.light, 'hecl_falloff_linear')
|
||||||
|
layout.prop(context.light, 'hecl_falloff_quadratic')
|
||||||
|
|
||||||
# Blender export-type registration
|
# Blender export-type registration
|
||||||
def register_export_type_enum():
|
def register_export_type_enum():
|
||||||
|
@ -107,6 +128,14 @@ def mesh_aabb(writepipebuf):
|
||||||
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
|
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
|
||||||
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
|
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
|
||||||
|
|
||||||
|
def shader_model_update(self, context):
|
||||||
|
value = 0.0
|
||||||
|
if self.hecl_shader_model == 'PBR':
|
||||||
|
value = 1.0
|
||||||
|
for shad in ('RetroShader', 'RetroDynamicShader', 'RetroDynamicAlphaShader', 'RetroDynamicCharacterShader'):
|
||||||
|
if shad in bpy.data.node_groups and 'NewShaderModel' in bpy.data.node_groups[shad].nodes:
|
||||||
|
bpy.data.node_groups[shad].nodes['NewShaderModel'].outputs[0].default_value = value
|
||||||
|
|
||||||
# Load scene callback
|
# Load scene callback
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
@persistent
|
@persistent
|
||||||
|
@ -114,13 +143,13 @@ def scene_loaded(dummy):
|
||||||
# Hide everything from an external library
|
# Hide everything from an external library
|
||||||
for o in bpy.context.scene.objects:
|
for o in bpy.context.scene.objects:
|
||||||
if o.library:
|
if o.library:
|
||||||
o.hide = True
|
o.hide_set(True)
|
||||||
|
|
||||||
# Show PATH library objects as wireframes
|
# Show PATH library objects as wireframes
|
||||||
if bpy.context.scene.hecl_type == 'PATH':
|
if bpy.context.scene.hecl_type == 'PATH':
|
||||||
if bpy.context.scene.background_set:
|
if bpy.context.scene.background_set:
|
||||||
for o in bpy.context.scene.background_set.objects:
|
for o in bpy.context.scene.background_set.objects:
|
||||||
o.draw_type = 'WIRE'
|
o.display_type = 'WIRE'
|
||||||
if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects:
|
if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects:
|
||||||
path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj]
|
path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj]
|
||||||
path_obj.show_wire = True
|
path_obj.show_wire = True
|
||||||
|
@ -134,14 +163,11 @@ def scene_loaded(dummy):
|
||||||
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
if subtype.linked_armature in bpy.data.objects:
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
arm_obj = bpy.data.objects[subtype.linked_armature]
|
arm_obj = bpy.data.objects[subtype.linked_armature]
|
||||||
mesh_obj.parent = arm_obj
|
parent_armature(mesh_obj, arm_obj)
|
||||||
mesh_obj.parent_type = 'ARMATURE'
|
|
||||||
for overlay in subtype.overlays:
|
for overlay in subtype.overlays:
|
||||||
if overlay.linked_mesh in bpy.data.objects:
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
||||||
mesh_obj.parent = arm_obj
|
parent_armature(mesh_obj, arm_obj)
|
||||||
mesh_obj.parent_type = 'ARMATURE'
|
|
||||||
|
|
||||||
|
|
||||||
# Show only the active mesh and action
|
# Show only the active mesh and action
|
||||||
if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context):
|
if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context):
|
||||||
|
@ -149,6 +175,28 @@ def scene_loaded(dummy):
|
||||||
if sact.SACTAction.SACTAction_load.poll(bpy.context):
|
if sact.SACTAction.SACTAction_load.poll(bpy.context):
|
||||||
bpy.ops.scene.sactaction_load()
|
bpy.ops.scene.sactaction_load()
|
||||||
|
|
||||||
|
shader_model_update(bpy.context.scene, bpy.context)
|
||||||
|
|
||||||
|
def power_of_distance(context, light, dist):
|
||||||
|
color = light.color
|
||||||
|
return dist * dist * context.scene.eevee.light_threshold / max(color[0], max(color[1], color[2]))
|
||||||
|
|
||||||
|
def power_of_coefficients(context, light):
|
||||||
|
epsilon = 1.19e-07
|
||||||
|
if light.hecl_falloff_linear < epsilon and light.hecl_falloff_quadratic < epsilon:
|
||||||
|
return 0.0
|
||||||
|
color = light.color
|
||||||
|
intens = max(color[0], max(color[1], color[2]))
|
||||||
|
if light.hecl_falloff_quadratic > epsilon:
|
||||||
|
if intens <= epsilon:
|
||||||
|
return 0.0
|
||||||
|
return power_of_distance(context, light, math.sqrt(intens / (0.0588235 * light.hecl_falloff_quadratic)))
|
||||||
|
if light.hecl_falloff_linear > epsilon:
|
||||||
|
return power_of_distance(context, light, intens / (0.0588235 * light.hecl_falloff_linear))
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def set_light_falloff(self, context):
|
||||||
|
self.energy = power_of_coefficients(context, self)
|
||||||
|
|
||||||
# Registration
|
# Registration
|
||||||
def register():
|
def register():
|
||||||
|
@ -161,7 +209,33 @@ def register():
|
||||||
mapu.register()
|
mapu.register()
|
||||||
path.register()
|
path.register()
|
||||||
bpy.utils.register_class(hecl_scene_panel)
|
bpy.utils.register_class(hecl_scene_panel)
|
||||||
|
bpy.utils.register_class(hecl_light_panel)
|
||||||
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
|
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
|
||||||
|
bpy.types.Light.hecl_falloff_constant = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Constant",
|
||||||
|
description="Constant falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=1.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Light.hecl_falloff_linear = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Linear",
|
||||||
|
description="Linear falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=0.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Light.hecl_falloff_quadratic = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Quadratic",
|
||||||
|
description="Quadratic falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=0.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Scene.hecl_shader_model = bpy.props.EnumProperty(name="HECL Shader Model",
|
||||||
|
description="Which shader model to use for rendering",
|
||||||
|
items=[
|
||||||
|
('ORIGINAL', "Original", "Close approximation of GameCube materials"),
|
||||||
|
('PBR', "PBR", "Hybrid PBR materials replacing original reflection")],
|
||||||
|
update=shader_model_update,
|
||||||
|
default='ORIGINAL')
|
||||||
bpy.app.handlers.load_post.append(scene_loaded)
|
bpy.app.handlers.load_post.append(scene_loaded)
|
||||||
Patching.register()
|
Patching.register()
|
||||||
|
|
||||||
|
@ -172,6 +246,7 @@ def unregister():
|
||||||
srea.unregister()
|
srea.unregister()
|
||||||
path.unregister()
|
path.unregister()
|
||||||
bpy.utils.unregister_class(hecl_scene_panel)
|
bpy.utils.unregister_class(hecl_scene_panel)
|
||||||
|
bpy.utils.unregister_class(hecl_light_panel)
|
||||||
Patching.unregister()
|
Patching.unregister()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -4,7 +4,7 @@ from mathutils import Quaternion
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
if bpy.context.active_object:
|
if bpy.context.active_object:
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
layout.label("Widget Settings:", icon='OBJECT_DATA')
|
layout.label(text="Widget Settings:", icon='OBJECT_DATA')
|
||||||
layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type')
|
layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type')
|
||||||
#layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent')
|
#layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent')
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
@ -56,9 +56,9 @@ def draw(layout, context):
|
||||||
layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity')
|
layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity')
|
||||||
layout.prop(obj, 'retro_meter_worker_count', text='Worker Count')
|
layout.prop(obj, 'retro_meter_worker_count', text='Worker Count')
|
||||||
elif obj.retro_widget_type == 'RETRO_LITE':
|
elif obj.retro_widget_type == 'RETRO_LITE':
|
||||||
if obj.data and obj.type == 'LAMP':
|
if obj.data and obj.type == 'LIGHT':
|
||||||
layout.prop(obj.data, 'retro_light_index', text='Index')
|
layout.prop(obj.data, 'retro_light_index', text='Index')
|
||||||
layout.label("Angular Falloff:", icon='LAMP_SPOT')
|
layout.label(text="Angular Falloff:", icon='LIGHT')
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(obj.data, 'retro_light_angle_constant', text='Constant')
|
row.prop(obj.data, 'retro_light_angle_constant', text='Constant')
|
||||||
row.prop(obj.data, 'retro_light_angle_linear', text='Linear')
|
row.prop(obj.data, 'retro_light_angle_linear', text='Linear')
|
||||||
|
@ -201,7 +201,7 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
|
||||||
cutoff = 0.0
|
cutoff = 0.0
|
||||||
if obj.data.type == 'POINT':
|
if obj.data.type == 'POINT':
|
||||||
type_enum = 4
|
type_enum = 4
|
||||||
elif obj.data.type == 'HEMI':
|
elif obj.data.type == 'SUN':
|
||||||
type_enum = 2
|
type_enum = 2
|
||||||
elif obj.data.type == 'SPOT':
|
elif obj.data.type == 'SPOT':
|
||||||
type_enum = 0
|
type_enum = 0
|
||||||
|
@ -231,10 +231,10 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
|
||||||
path_hash = 0xffffffff
|
path_hash = 0xffffffff
|
||||||
if len(obj.data.materials):
|
if len(obj.data.materials):
|
||||||
material = obj.data.materials[0]
|
material = obj.data.materials[0]
|
||||||
if len(material.texture_slots) and material.texture_slots[0]:
|
if 'Image Texture' in material.node_tree.nodes:
|
||||||
tex_slot = material.texture_slots[0]
|
image_node = material.node_tree.nodes['Image Texture']
|
||||||
if tex_slot.texture.type == 'IMAGE' and tex_slot.texture.image:
|
if image_node.image:
|
||||||
image = tex_slot.texture.image
|
image = image_node.image
|
||||||
path = bpy.path.abspath(image.filepath)
|
path = bpy.path.abspath(image.filepath)
|
||||||
path_hash = path_hasher.hashpath32(path)
|
path_hash = path_hasher.hashpath32(path)
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ def recursive_cook(buffer, obj, version, path_hasher, parent_name):
|
||||||
else:
|
else:
|
||||||
buffer += struct.pack('>b', False)
|
buffer += struct.pack('>b', False)
|
||||||
|
|
||||||
angMtx = angle.to_matrix() * obj.matrix_local.to_3x3()
|
angMtx = angle.to_matrix() @ obj.matrix_local.to_3x3()
|
||||||
buffer += struct.pack('>fffffffffffffffIH',
|
buffer += struct.pack('>fffffffffffffffIH',
|
||||||
obj.matrix_local[0][3],
|
obj.matrix_local[0][3],
|
||||||
obj.matrix_local[1][3],
|
obj.matrix_local[1][3],
|
||||||
|
@ -384,8 +384,8 @@ def register():
|
||||||
bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100)
|
bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100)
|
||||||
bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1)
|
bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1)
|
||||||
|
|
||||||
bpy.types.Lamp.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0)
|
bpy.types.Light.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0)
|
||||||
bpy.types.Lamp.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0)
|
bpy.types.Light.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0)
|
||||||
bpy.types.Lamp.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0)
|
bpy.types.Light.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0)
|
||||||
bpy.types.Lamp.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0)
|
bpy.types.Light.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import bpy, bmesh, operator, struct
|
import bpy, bmesh, operator, struct
|
||||||
from mathutils import Vector
|
|
||||||
|
|
||||||
# Function to quantize normals to 15-bit precision
|
# Function to quantize normals to 15-bit precision
|
||||||
def quant_norm(n):
|
def quant_norm(n):
|
||||||
|
@ -15,348 +14,87 @@ def quant_luv(n):
|
||||||
uf[i] = int(uf[i] * 32768) / 32768.0
|
uf[i] = int(uf[i] * 32768) / 32768.0
|
||||||
return uf.freeze()
|
return uf.freeze()
|
||||||
|
|
||||||
# Class for building unique sets of vertex attributes for VBO generation
|
# Function to output all mesh attribute values
|
||||||
class VertPool:
|
def write_mesh_attrs(writebuf, bm, rna_loops, use_luv, material_slots):
|
||||||
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
|
||||||
# Initialize hash-unique index for each available attribute
|
clays = []
|
||||||
def __init__(self, bm, rna_loops, use_luv, material_slots):
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
self.bm = bm
|
clays.append(bm.loops.layers.color[cl])
|
||||||
self.rna_loops = rna_loops
|
writebuf(struct.pack('I', len(clays)))
|
||||||
self.material_slots = material_slots
|
|
||||||
self.pos = {}
|
|
||||||
self.norm = {}
|
|
||||||
self.skin = {}
|
|
||||||
self.color = {}
|
|
||||||
self.uv = {}
|
|
||||||
self.luv = {}
|
|
||||||
self.dlay = None
|
|
||||||
self.clays = []
|
|
||||||
self.ulays = []
|
|
||||||
self.luvlay = None
|
|
||||||
|
|
||||||
dlay = None
|
luvlay = None
|
||||||
if len(bm.verts.layers.deform):
|
if use_luv:
|
||||||
dlay = bm.verts.layers.deform[0]
|
luvlay = bm.loops.layers.uv[0]
|
||||||
self.dlay = dlay
|
ulays = []
|
||||||
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
writebuf(struct.pack('I', len(ulays)))
|
||||||
|
|
||||||
clays = []
|
# Verts
|
||||||
for cl in range(len(bm.loops.layers.color)):
|
writebuf(struct.pack('I', len(bm.verts)))
|
||||||
clays.append(bm.loops.layers.color[cl])
|
for v in bm.verts:
|
||||||
self.clays = clays
|
writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
|
||||||
|
if dlay:
|
||||||
luvlay = None
|
sf = tuple(sorted(v[dlay].items()))
|
||||||
if use_luv:
|
writebuf(struct.pack('I', len(sf)))
|
||||||
luvlay = bm.loops.layers.uv[0]
|
total_len = 0.0
|
||||||
self.luvlay = luvlay
|
for ent in sf:
|
||||||
ulays = []
|
total_len += ent[1]
|
||||||
for ul in range(len(bm.loops.layers.uv)):
|
for ent in sf:
|
||||||
ulays.append(bm.loops.layers.uv[ul])
|
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
||||||
self.ulays = ulays
|
|
||||||
|
|
||||||
# Per-vert pool attributes
|
|
||||||
for v in bm.verts:
|
|
||||||
pf = v.co.copy().freeze()
|
|
||||||
if pf not in self.pos:
|
|
||||||
self.pos[pf] = len(self.pos)
|
|
||||||
if not rna_loops:
|
|
||||||
nf = quant_norm(v.normal)
|
|
||||||
if nf not in self.norm:
|
|
||||||
self.norm[nf] = len(self.norm)
|
|
||||||
if dlay:
|
|
||||||
sf = tuple(sorted(v[dlay].items()))
|
|
||||||
if sf not in self.skin:
|
|
||||||
self.skin[sf] = len(self.skin)
|
|
||||||
|
|
||||||
# Per-loop pool attributes
|
|
||||||
for f in bm.faces:
|
|
||||||
lightmapped = f.material_index < len(material_slots) and \
|
|
||||||
material_slots[f.material_index].material['retro_lightmapped']
|
|
||||||
for l in f.loops:
|
|
||||||
if rna_loops:
|
|
||||||
nf = quant_norm(rna_loops[l.index].normal)
|
|
||||||
if nf not in self.norm:
|
|
||||||
self.norm[nf] = len(self.norm)
|
|
||||||
for cl in range(len(clays)):
|
|
||||||
cf = l[clays[cl]].copy().freeze()
|
|
||||||
if cf not in self.color:
|
|
||||||
self.color[cf] = len(self.color)
|
|
||||||
start_uvlay = 0
|
|
||||||
if use_luv and lightmapped:
|
|
||||||
start_uvlay = 1
|
|
||||||
uf = quant_luv(l[luvlay].uv)
|
|
||||||
if uf not in self.luv:
|
|
||||||
self.luv[uf] = len(self.luv)
|
|
||||||
for ul in range(start_uvlay, len(ulays)):
|
|
||||||
uf = l[ulays[ul]].uv.copy().freeze()
|
|
||||||
if uf not in self.uv:
|
|
||||||
self.uv[uf] = len(self.uv)
|
|
||||||
|
|
||||||
def write_out(self, writebuf, vert_groups):
|
|
||||||
writebuf(struct.pack('I', len(self.pos)))
|
|
||||||
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
|
||||||
|
|
||||||
writebuf(struct.pack('I', len(self.norm)))
|
|
||||||
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
|
|
||||||
|
|
||||||
writebuf(struct.pack('II', len(self.clays), len(self.color)))
|
|
||||||
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
|
|
||||||
|
|
||||||
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
|
|
||||||
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
|
||||||
|
|
||||||
luv_count = 0
|
|
||||||
if self.luvlay is not None:
|
|
||||||
luv_count = 1
|
|
||||||
writebuf(struct.pack('II', luv_count, len(self.luv)))
|
|
||||||
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
|
||||||
|
|
||||||
writebuf(struct.pack('I', len(vert_groups)))
|
|
||||||
for vgrp in vert_groups:
|
|
||||||
writebuf(struct.pack('I', len(vgrp.name)))
|
|
||||||
writebuf(vgrp.name.encode())
|
|
||||||
|
|
||||||
writebuf(struct.pack('I', len(self.skin)))
|
|
||||||
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
|
|
||||||
entries = s[0]
|
|
||||||
writebuf(struct.pack('I', len(entries)))
|
|
||||||
if len(entries):
|
|
||||||
total_len = 0.0
|
|
||||||
for ent in entries:
|
|
||||||
total_len += ent[1]
|
|
||||||
for ent in entries:
|
|
||||||
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
|
||||||
|
|
||||||
def write_out_map(self, writebuf):
|
|
||||||
writebuf(struct.pack('I', len(self.pos)))
|
|
||||||
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
|
||||||
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
|
||||||
|
|
||||||
def get_pos_idx(self, vert):
|
|
||||||
pf = vert.co.copy().freeze()
|
|
||||||
return self.pos[pf]
|
|
||||||
|
|
||||||
def get_norm_idx(self, loop):
|
|
||||||
if self.rna_loops:
|
|
||||||
nf = quant_norm(self.rna_loops[loop.index].normal)
|
|
||||||
else:
|
else:
|
||||||
nf = quant_norm(loop.vert.normal)
|
writebuf(struct.pack('I', 0))
|
||||||
return self.norm[nf]
|
|
||||||
|
|
||||||
def get_skin_idx(self, vert):
|
# Loops
|
||||||
if not self.dlay:
|
loop_count = 0
|
||||||
return 0
|
for f in bm.faces:
|
||||||
sf = tuple(sorted(vert[self.dlay].items()))
|
for l in f.loops:
|
||||||
return self.skin[sf]
|
loop_count += 1
|
||||||
|
writebuf(struct.pack('I', loop_count))
|
||||||
|
for f in bm.faces:
|
||||||
|
for l in f.loops:
|
||||||
|
if rna_loops:
|
||||||
|
nf = quant_norm(rna_loops[l.index].normal)
|
||||||
|
else:
|
||||||
|
nf = quant_norm(l.vert.normal)
|
||||||
|
writebuf(struct.pack('fff', nf[0], nf[1], nf[2]))
|
||||||
|
for cl in range(len(clays)):
|
||||||
|
col = l[clays[cl]]
|
||||||
|
writebuf(struct.pack('fff', col[0], col[1], col[2]))
|
||||||
|
for cl in range(len(ulays)):
|
||||||
|
if luvlay and cl == 0 and material_slots[l.face.material_index].material['retro_lightmapped']:
|
||||||
|
uv = quant_luv(l[luvlay].uv)
|
||||||
|
else:
|
||||||
|
uv = l[ulays[cl]].uv
|
||||||
|
writebuf(struct.pack('ff', uv[0], uv[1]))
|
||||||
|
writebuf(struct.pack('IIIII', l.vert.index, l.edge.index, l.face.index,
|
||||||
|
l.link_loop_next.index, l.link_loop_prev.index))
|
||||||
|
if l.edge.is_contiguous:
|
||||||
|
writebuf(struct.pack('II', l.link_loop_radial_next.index, l.link_loop_radial_prev.index))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('II', 0xffffffff, 0xffffffff))
|
||||||
|
|
||||||
def get_color_idx(self, loop, cidx):
|
# Edges
|
||||||
cf = loop[self.clays[cidx]].copy().freeze()
|
writebuf(struct.pack('I', len(bm.edges)))
|
||||||
return self.color[cf]
|
for e in bm.edges:
|
||||||
|
for v in e.verts:
|
||||||
def get_uv_idx(self, loop, uidx):
|
writebuf(struct.pack('I', v.index))
|
||||||
if self.luvlay is not None and uidx == 0:
|
writebuf(struct.pack('I', len(e.link_faces)))
|
||||||
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
|
|
||||||
uf = quant_luv(loop[self.luvlay].uv)
|
|
||||||
return self.luv[uf]
|
|
||||||
uf = loop[self.ulays[uidx]].uv.copy().freeze()
|
|
||||||
return self.uv[uf]
|
|
||||||
|
|
||||||
def loops_contiguous(self, la, lb):
|
|
||||||
if la.vert != lb.vert:
|
|
||||||
return False
|
|
||||||
if self.get_norm_idx(la) != self.get_norm_idx(lb):
|
|
||||||
return False
|
|
||||||
for cl in range(len(self.clays)):
|
|
||||||
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
|
|
||||||
return False
|
|
||||||
for ul in range(len(self.ulays)):
|
|
||||||
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def splitable_edge(self, edge):
|
|
||||||
if len(edge.link_faces) < 2:
|
|
||||||
return False
|
|
||||||
for v in edge.verts:
|
|
||||||
found = None
|
|
||||||
for f in edge.link_faces:
|
|
||||||
for l in f.loops:
|
|
||||||
if l.vert == v:
|
|
||||||
if not found:
|
|
||||||
found = l
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if not self.loops_contiguous(found, l):
|
|
||||||
return True
|
|
||||||
break
|
|
||||||
return False
|
|
||||||
|
|
||||||
def loop_out(self, writebuf, loop):
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
|
|
||||||
for cl in range(len(self.clays)):
|
|
||||||
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
|
|
||||||
for ul in range(len(self.ulays)):
|
|
||||||
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
|
|
||||||
sp = struct.pack('I', self.get_skin_idx(loop.vert))
|
|
||||||
writebuf(sp)
|
|
||||||
|
|
||||||
def null_loop_out(self, writebuf):
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
writebuf(struct.pack('I', 0xffffffff))
|
|
||||||
|
|
||||||
def loop_out_map(self, writebuf, loop):
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
|
|
||||||
|
|
||||||
def vert_out_map(self, writebuf, vert):
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
writebuf(struct.pack('I', self.get_pos_idx(vert)))
|
|
||||||
|
|
||||||
|
|
||||||
def sort_faces_by_skin_group(dlay, faces):
|
|
||||||
faces_out = []
|
|
||||||
done_sg = set()
|
|
||||||
ref_sg = None
|
|
||||||
while len(faces_out) < len(faces):
|
|
||||||
for f in faces:
|
|
||||||
found = False
|
|
||||||
for v in f.verts:
|
|
||||||
sg = tuple(sorted(v[dlay].items()))
|
|
||||||
if sg not in done_sg:
|
|
||||||
ref_sg = sg
|
|
||||||
done_sg.add(ref_sg)
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if found:
|
|
||||||
break
|
|
||||||
|
|
||||||
for f in faces:
|
|
||||||
if f in faces_out:
|
|
||||||
continue
|
|
||||||
for v in f.verts:
|
|
||||||
sg = tuple(sorted(v[dlay].items()))
|
|
||||||
if sg == ref_sg:
|
|
||||||
faces_out.append(f)
|
|
||||||
break
|
|
||||||
|
|
||||||
return faces_out
|
|
||||||
|
|
||||||
def recursive_faces_islands(dlay, list_out, rem_list, skin_slot_set, skin_slot_count, face):
|
|
||||||
if face not in rem_list:
|
|
||||||
return []
|
|
||||||
|
|
||||||
if dlay:
|
|
||||||
for v in face.verts:
|
|
||||||
sg = tuple(sorted(v[dlay].items()))
|
|
||||||
if sg not in skin_slot_set:
|
|
||||||
if skin_slot_count > 0 and len(skin_slot_set) == skin_slot_count:
|
|
||||||
return False
|
|
||||||
skin_slot_set.add(sg)
|
|
||||||
|
|
||||||
list_out.append(face)
|
|
||||||
rem_list.remove(face)
|
|
||||||
next_faces = []
|
|
||||||
for e in face.edges:
|
|
||||||
if not e.is_contiguous:
|
|
||||||
continue
|
|
||||||
for f in e.link_faces:
|
for f in e.link_faces:
|
||||||
if f == face:
|
writebuf(struct.pack('I', f.index))
|
||||||
continue
|
writebuf(struct.pack('b', e.is_contiguous))
|
||||||
next_faces.append(f)
|
|
||||||
return next_faces
|
|
||||||
|
|
||||||
def strip_next_loop(prev_loop, out_count):
|
# Faces
|
||||||
if out_count & 1:
|
writebuf(struct.pack('I', len(bm.faces)))
|
||||||
radial_loop = prev_loop.link_loop_radial_next
|
for f in bm.faces:
|
||||||
loop = radial_loop.link_loop_prev
|
norm = f.normal
|
||||||
return loop, loop
|
writebuf(struct.pack('fff', norm[0], norm[1], norm[2]))
|
||||||
else:
|
centroid = f.calc_center_bounds()
|
||||||
radial_loop = prev_loop.link_loop_radial_prev
|
writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
|
||||||
loop = radial_loop.link_loop_next
|
writebuf(struct.pack('I', f.material_index))
|
||||||
return loop.link_loop_next, loop
|
for l in f.loops:
|
||||||
|
writebuf(struct.pack('I', l.index))
|
||||||
def write_out_surface(writebuf, output_mode, vert_pool, island_faces, mat_idx):
|
|
||||||
|
|
||||||
# Centroid of surface
|
|
||||||
centroid = Vector()
|
|
||||||
for f in island_faces:
|
|
||||||
centroid += f.calc_center_bounds()
|
|
||||||
centroid /= len(island_faces)
|
|
||||||
writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
|
|
||||||
|
|
||||||
# Material index
|
|
||||||
writebuf(struct.pack('I', mat_idx))
|
|
||||||
|
|
||||||
# AABB of surface
|
|
||||||
aabb_min = Vector((9999999, 9999999, 9999999))
|
|
||||||
aabb_max = Vector((-9999999, -9999999, -9999999))
|
|
||||||
for f in island_faces:
|
|
||||||
for v in f.verts:
|
|
||||||
for c in range(3):
|
|
||||||
if v.co[c] < aabb_min[c]:
|
|
||||||
aabb_min[c] = v.co[c]
|
|
||||||
if v.co[c] > aabb_max[c]:
|
|
||||||
aabb_max[c] = v.co[c]
|
|
||||||
writebuf(struct.pack('fff', aabb_min[0], aabb_min[1], aabb_min[2]))
|
|
||||||
writebuf(struct.pack('fff', aabb_max[0], aabb_max[1], aabb_max[2]))
|
|
||||||
|
|
||||||
# Average normal of surface
|
|
||||||
avg_norm = Vector()
|
|
||||||
for f in island_faces:
|
|
||||||
avg_norm += f.normal
|
|
||||||
avg_norm.normalize()
|
|
||||||
writebuf(struct.pack('fff', avg_norm[0], avg_norm[1], avg_norm[2]))
|
|
||||||
|
|
||||||
# Count estimate (as raw triangles)
|
|
||||||
writebuf(struct.pack('I', len(island_faces) * 3))
|
|
||||||
|
|
||||||
# Verts themselves
|
|
||||||
if output_mode == 'TRIANGLES':
|
|
||||||
for f in island_faces:
|
|
||||||
for l in f.loops:
|
|
||||||
vert_pool.loop_out(writebuf, l)
|
|
||||||
|
|
||||||
elif output_mode == 'TRISTRIPS':
|
|
||||||
prev_loop_emit = None
|
|
||||||
while len(island_faces):
|
|
||||||
sel_lists_local = []
|
|
||||||
for start_face in island_faces:
|
|
||||||
for l in start_face.loops:
|
|
||||||
island_local = list(island_faces)
|
|
||||||
prev_loop = l.link_loop_next
|
|
||||||
loop = prev_loop.link_loop_next
|
|
||||||
sel_list = [l, prev_loop, loop]
|
|
||||||
island_local.remove(start_face)
|
|
||||||
while True:
|
|
||||||
if not prev_loop.edge.is_contiguous or prev_loop.edge.tag:
|
|
||||||
break
|
|
||||||
loop, prev_loop = strip_next_loop(prev_loop, len(sel_list))
|
|
||||||
face = loop.face
|
|
||||||
if face not in island_local:
|
|
||||||
break
|
|
||||||
sel_list.append(loop)
|
|
||||||
island_local.remove(face)
|
|
||||||
sel_lists_local.append((sel_list, island_local))
|
|
||||||
|
|
||||||
max_count = 0
|
|
||||||
max_sl = None
|
|
||||||
max_island_faces = None
|
|
||||||
for sl in sel_lists_local:
|
|
||||||
if len(sl[0]) > max_count:
|
|
||||||
max_count = len(sl[0])
|
|
||||||
max_sl = sl[0]
|
|
||||||
max_island_faces = sl[1]
|
|
||||||
island_faces = max_island_faces
|
|
||||||
if prev_loop_emit:
|
|
||||||
vert_pool.null_loop_out(writebuf)
|
|
||||||
for loop in max_sl:
|
|
||||||
vert_pool.loop_out(writebuf, loop)
|
|
||||||
prev_loop_emit = loop
|
|
||||||
|
|
||||||
writebuf(struct.pack('B', 0))
|
|
||||||
|
|
||||||
|
|
|
@ -1,394 +1,162 @@
|
||||||
import bpy, bpy.path, os.path
|
import bpy, bpy.path, os.path, struct
|
||||||
|
|
||||||
def get_texmap_idx(tex_list, name):
|
def get_texture_path(image):
|
||||||
for i in range(len(tex_list)):
|
return os.path.normpath(bpy.path.abspath(image.filepath))
|
||||||
if tex_list[i] == name:
|
|
||||||
return i
|
|
||||||
retval = len(tex_list)
|
|
||||||
tex_list.append(name)
|
|
||||||
return retval
|
|
||||||
|
|
||||||
def get_texture_path(name):
|
SHADER_TYPES = {
|
||||||
if name not in bpy.data.textures:
|
'RetroShader': b'RSHD',
|
||||||
raise RuntimeError('unable to find %s texture' % name)
|
'RetroDynamicShader': b'RDYN',
|
||||||
tex = bpy.data.textures[name]
|
'RetroDynamicAlphaShader': b'RDAL',
|
||||||
if tex.type != 'IMAGE':
|
'RetroDynamicCharacterShader': b'RCHR',
|
||||||
raise RuntimeError('%s texture unsupported for %s, please save as IMAGE' % (tex.type, name))
|
}
|
||||||
img = tex.image
|
|
||||||
if not img:
|
|
||||||
raise RuntimeError('image not set in %s' % name)
|
|
||||||
return os.path.normpath(bpy.path.abspath(img.filepath))
|
|
||||||
|
|
||||||
# Trace color node structure
|
PASS_TYPE = {
|
||||||
def recursive_color_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
|
'Lightmap': b'LMAP',
|
||||||
|
'Diffuse': b'DIFF',
|
||||||
|
'Emissive': b'EMIS',
|
||||||
|
'Specular': b'SPEC',
|
||||||
|
'ExtendedSpecular': b'ESPC',
|
||||||
|
'Reflection': b'REFL',
|
||||||
|
'IndirectTex': b'INDR',
|
||||||
|
'Alpha': b'ALPH',
|
||||||
|
}
|
||||||
|
|
||||||
if node.type == 'OUTPUT':
|
def write_chunks(writebuf, mat_obj, mesh_obj):
|
||||||
if node.inputs['Color'].is_linked:
|
|
||||||
return recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs['Color'].links[0].from_node, node.inputs['Color'].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
return 'vec3(%g,%g,%g)' % (node.inputs['Color'].default_value[0],
|
|
||||||
node.inputs['Color'].default_value[1],
|
|
||||||
node.inputs['Color'].default_value[2])
|
|
||||||
|
|
||||||
elif node.type == 'MIX_RGB':
|
|
||||||
|
|
||||||
if node.inputs[1].is_linked:
|
|
||||||
a_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
a_input = 'vec3(%g,%g,%g)' % (node.inputs[1].default_value[0],
|
|
||||||
node.inputs[1].default_value[1],
|
|
||||||
node.inputs[1].default_value[2])
|
|
||||||
|
|
||||||
if node.inputs[2].is_linked:
|
|
||||||
b_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[2].links[0].from_node, node.inputs[2].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
b_input = 'vec3(%g,%g,%g)' % (node.inputs[2].default_value[0],
|
|
||||||
node.inputs[2].default_value[1],
|
|
||||||
node.inputs[2].default_value[2])
|
|
||||||
|
|
||||||
if node.blend_type == 'MULTIPLY':
|
|
||||||
if a_input == 'vec3(1,1,1)':
|
|
||||||
return b_input
|
|
||||||
elif b_input == 'vec3(1,1,1)':
|
|
||||||
return a_input
|
|
||||||
return '(%s * %s)' % (a_input, b_input)
|
|
||||||
elif node.blend_type == 'ADD':
|
|
||||||
if a_input == 'vec3(0,0,0)':
|
|
||||||
return b_input
|
|
||||||
elif b_input == 'vec3(0,0,0)':
|
|
||||||
return a_input
|
|
||||||
return '(%s + %s)' % (a_input, b_input)
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL does not support shaders with '{0}' blending modes".format(node.blend_type))
|
|
||||||
|
|
||||||
elif node.type == 'TEXTURE':
|
|
||||||
|
|
||||||
if not node.texture or not hasattr(node.texture, 'name'):
|
|
||||||
raise RuntimeError("HMDL texture nodes must specify a texture object")
|
|
||||||
|
|
||||||
if not node.inputs['Vector'].is_linked:
|
|
||||||
raise RuntimeError("HMDL texture nodes must have a 'Geometry' or 'Group' UV modifier node linked")
|
|
||||||
|
|
||||||
# Determine matrix generator type
|
|
||||||
matrix_str = None
|
|
||||||
soc_from = node.inputs['Vector'].links[0].from_socket
|
|
||||||
|
|
||||||
if soc_from.node.type == 'GROUP':
|
|
||||||
matrix_str = '%s(%%s' % soc_from.node.node_tree.name
|
|
||||||
if len(soc_from.node.inputs)-1:
|
|
||||||
matrix_str += ', '
|
|
||||||
for s in range(len(soc_from.node.inputs)-1):
|
|
||||||
soc = soc_from.node.inputs[s+1]
|
|
||||||
if len(soc.links):
|
|
||||||
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
|
|
||||||
if soc.type == 'VALUE':
|
|
||||||
matrix_str += '%g' % soc.default_value
|
|
||||||
else:
|
|
||||||
ncomps = len(soc.default_value)
|
|
||||||
matrix_str += 'vec%d(' % ncomps
|
|
||||||
for c in range(ncomps-1):
|
|
||||||
matrix_str += '%g,' % soc.default_value[c]
|
|
||||||
matrix_str += '%g)' % soc.default_value[ncomps-1]
|
|
||||||
|
|
||||||
if s == len(soc_from.node.inputs)-2:
|
|
||||||
matrix_str += ')'
|
|
||||||
else:
|
|
||||||
matrix_str += ', '
|
|
||||||
else:
|
|
||||||
matrix_str += ')'
|
|
||||||
|
|
||||||
soc_from = soc_from.node.inputs[0].links[0].from_socket
|
|
||||||
|
|
||||||
elif soc_from.node.type == 'GEOMETRY':
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL texture nodes must have a 'Geometry', 'Group' UV modifier node linked")
|
|
||||||
|
|
||||||
if soc_from.node.type != 'GEOMETRY':
|
|
||||||
raise RuntimeError("Matrix animator nodes must connect to 'Geometry' node")
|
|
||||||
|
|
||||||
|
|
||||||
# Resolve map and matrix index
|
|
||||||
node_label = soc_from.node.label
|
|
||||||
if not matrix_str and node_label.startswith('MTX_'):
|
|
||||||
matrix_str = 'TexMtx(%%s, %d)' % int(node_label[4:])
|
|
||||||
|
|
||||||
if soc_from.name == 'UV':
|
|
||||||
uv_name = soc_from.node.uv_layer
|
|
||||||
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
|
||||||
if uv_idx == -1:
|
|
||||||
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
|
||||||
uvsource_str = 'UV(%d)' % uv_idx
|
|
||||||
|
|
||||||
elif soc_from.name == 'Normal':
|
|
||||||
uvsource_str = 'Normal()'
|
|
||||||
|
|
||||||
elif soc_from.name == 'View':
|
|
||||||
uvsource_str = 'View()'
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
|
||||||
|
|
||||||
call_name = 'Texture'
|
|
||||||
if node.label.startswith('Diffuse'):
|
|
||||||
call_name = 'TextureD'
|
|
||||||
|
|
||||||
if socket.name == 'Value':
|
|
||||||
if matrix_str:
|
|
||||||
uvsource_str = matrix_str % uvsource_str
|
|
||||||
return '%s(%d, %s).aaa' % (call_name, get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
|
||||||
if socket.name == 'Color':
|
|
||||||
if matrix_str:
|
|
||||||
uvsource_str = matrix_str % uvsource_str
|
|
||||||
return '%s(%d, %s)' % (call_name, get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Only the 'Value' or 'Color' output sockets may be used from Texture nodes")
|
|
||||||
|
|
||||||
elif node.type == 'GROUP':
|
|
||||||
|
|
||||||
group_str = '%s(' % node.node_tree.name
|
|
||||||
did_first = False
|
|
||||||
for input in node.inputs:
|
|
||||||
if input.type == 'RGBA':
|
|
||||||
if did_first:
|
|
||||||
group_str += ', '
|
|
||||||
if input.is_linked:
|
|
||||||
group_str += recursive_color_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
|
|
||||||
else:
|
|
||||||
group_str += 'vec3(%g,%g,%g)' % (input.default_value[0],
|
|
||||||
input.default_value[1],
|
|
||||||
input.default_value[2])
|
|
||||||
did_first = True
|
|
||||||
group_str += ')'
|
|
||||||
return group_str
|
|
||||||
|
|
||||||
elif node.type == 'RGB':
|
|
||||||
|
|
||||||
if node.label.startswith('DYNAMIC_'):
|
|
||||||
dynamic_index = int(node.label[8:])
|
|
||||||
return 'ColorReg(%d)' % dynamic_index
|
|
||||||
|
|
||||||
return 'vec3(%g,%g,%g)' % (node.outputs['Color'].default_value[0],
|
|
||||||
node.outputs['Color'].default_value[1],
|
|
||||||
node.outputs['Color'].default_value[2])
|
|
||||||
|
|
||||||
elif node.type == 'MATERIAL':
|
|
||||||
|
|
||||||
if mat_obj.use_shadeless:
|
|
||||||
return 'vec3(1.0)'
|
|
||||||
else:
|
|
||||||
return 'Lighting()'
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Trace alpha node structure
|
|
||||||
def recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
|
|
||||||
|
|
||||||
if node.type == 'OUTPUT':
|
|
||||||
if node.inputs['Alpha'].is_linked:
|
|
||||||
return recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs['Alpha'].links[0].from_node, node.inputs['Alpha'].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
return '%g' % node.inputs['Alpha'].default_value
|
|
||||||
|
|
||||||
elif node.type == 'MATH':
|
|
||||||
|
|
||||||
if node.inputs[0].is_linked:
|
|
||||||
a_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[0].links[0].from_node, node.inputs[0].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
a_input = '%g' % node.inputs[0].default_value
|
|
||||||
|
|
||||||
if node.inputs[1].is_linked:
|
|
||||||
b_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
|
||||||
else:
|
|
||||||
b_input = '%g' % node.inputs[1].default_value
|
|
||||||
|
|
||||||
if node.operation == 'MULTIPLY':
|
|
||||||
if a_input == '1':
|
|
||||||
return b_input
|
|
||||||
elif b_input == '1':
|
|
||||||
return a_input
|
|
||||||
return '(%s * %s)' % (a_input, b_input)
|
|
||||||
elif node.operation == 'ADD':
|
|
||||||
if a_input == '0':
|
|
||||||
return b_input
|
|
||||||
elif b_input == '0':
|
|
||||||
return a_input
|
|
||||||
return '(%s + %s)' % (a_input, b_input)
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL does not support shaders with '{0}' blending modes".format(node.operation))
|
|
||||||
|
|
||||||
elif node.type == 'TEXTURE':
|
|
||||||
|
|
||||||
if not node.texture or not hasattr(node.texture, 'name'):
|
|
||||||
raise RuntimeError("HMDL texture nodes must specify a texture object")
|
|
||||||
|
|
||||||
if not node.inputs['Vector'].is_linked:
|
|
||||||
raise RuntimeError("HMDL texture nodes must have a 'Geometry' or 'Group' UV modifier node linked")
|
|
||||||
|
|
||||||
# Determine matrix generator type
|
|
||||||
matrix_str = None
|
|
||||||
soc_from = node.inputs['Vector'].links[0].from_socket
|
|
||||||
|
|
||||||
if soc_from.node.type == 'GROUP':
|
|
||||||
matrix_str = '%s(%%s' % soc_from.node.node_tree.name
|
|
||||||
if len(soc_from.node.inputs)-1:
|
|
||||||
matrix_str += ', '
|
|
||||||
for s in range(len(soc_from.node.inputs)-1):
|
|
||||||
soc = soc_from.node.inputs[s+1]
|
|
||||||
if len(soc.links):
|
|
||||||
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
|
|
||||||
if soc.type == 'VALUE':
|
|
||||||
matrix_str += '%g' % soc.default_value
|
|
||||||
else:
|
|
||||||
ncomps = len(soc.default_value)
|
|
||||||
matrix_str += 'vec%d(' % ncomps
|
|
||||||
for c in range(ncomps-1):
|
|
||||||
matrix_str += '%g,' % soc.default_value[c]
|
|
||||||
matrix_str += '%g)' % soc.default_value[ncomps-1]
|
|
||||||
|
|
||||||
if s == len(soc_from.node.inputs)-2:
|
|
||||||
matrix_str += ')'
|
|
||||||
else:
|
|
||||||
matrix_str += ', '
|
|
||||||
else:
|
|
||||||
matrix_str += ')'
|
|
||||||
|
|
||||||
soc_from = soc_from.node.inputs[0].links[0].from_socket
|
|
||||||
|
|
||||||
elif soc_from.node.type == 'GEOMETRY':
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL texture nodes must have a 'Geometry', 'Group' UV modifier node linked")
|
|
||||||
|
|
||||||
if soc_from.node.type != 'GEOMETRY':
|
|
||||||
raise RuntimeError("Matrix animator nodes must connect to 'Geometry' node")
|
|
||||||
|
|
||||||
|
|
||||||
# Resolve map and matrix index
|
|
||||||
node_label = soc_from.node.label
|
|
||||||
if not matrix_str and node_label.startswith('MTX_'):
|
|
||||||
matrix_str = 'TexMtx(%%s, %d)' % int(node_label[4:])
|
|
||||||
|
|
||||||
if soc_from.name == 'UV':
|
|
||||||
uv_name = soc_from.node.uv_layer
|
|
||||||
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
|
||||||
if uv_idx == -1:
|
|
||||||
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
|
||||||
uvsource_str = 'UV(%d)' % uv_idx
|
|
||||||
|
|
||||||
elif soc_from.name == 'Normal':
|
|
||||||
uvsource_str = 'Normal()'
|
|
||||||
|
|
||||||
elif soc_from.name == 'View':
|
|
||||||
uvsource_str = 'View()'
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
|
||||||
|
|
||||||
if socket.name == 'Value':
|
|
||||||
if matrix_str:
|
|
||||||
uvsource_str = matrix_str % uvsource_str
|
|
||||||
return 'Texture(%d, %s).aaa' % (get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Only the 'Value' output sockets may be used from Texture nodes")
|
|
||||||
|
|
||||||
elif node.type == 'GROUP':
|
|
||||||
|
|
||||||
group_str = '%s(' % node.node_tree.name
|
|
||||||
did_first = False
|
|
||||||
for input in node.inputs:
|
|
||||||
if input.type == 'VALUE':
|
|
||||||
if did_first:
|
|
||||||
group_str += ', '
|
|
||||||
if input.is_linked:
|
|
||||||
group_str += recursive_alpha_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
|
|
||||||
else:
|
|
||||||
group_str += '%g' % input.default_value
|
|
||||||
did_first = True
|
|
||||||
group_str += ')'
|
|
||||||
return group_str
|
|
||||||
|
|
||||||
elif node.type == 'VALUE':
|
|
||||||
|
|
||||||
if node.label.startswith('DYNAMIC_'):
|
|
||||||
dynamic_index = int(node.label[8:])
|
|
||||||
return 'ColorReg(%d).aaa' % dynamic_index
|
|
||||||
|
|
||||||
return '%g' % node.outputs['Value'].default_value
|
|
||||||
|
|
||||||
elif node.type == 'MATERIAL':
|
|
||||||
|
|
||||||
return '1.0'
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
|
|
||||||
|
|
||||||
|
|
||||||
# Trace indirect node structure
|
|
||||||
def indirect_trace(mat_obj, mesh_obj, tex_list, node):
|
|
||||||
if node.type == 'OUTPUT':
|
|
||||||
if node.inputs['Color'].is_linked:
|
|
||||||
tex_node = node.inputs['Color'].links[0].from_node
|
|
||||||
if tex_node.type != 'TEXTURE':
|
|
||||||
raise RuntimeError("HMDL *requires* that an indirect output node is directly connected to TEXTURE")
|
|
||||||
if not tex_node.texture or not hasattr(tex_node.texture, 'name'):
|
|
||||||
raise RuntimeError("HMDL texture nodes must specify a texture object")
|
|
||||||
get_texmap_idx(tex_list, tex_node.texture.name)
|
|
||||||
|
|
||||||
|
|
||||||
def shader(mat_obj, mesh_obj):
|
|
||||||
|
|
||||||
if not mat_obj.use_nodes:
|
if not mat_obj.use_nodes:
|
||||||
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
|
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
|
||||||
|
|
||||||
if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'OUTPUT':
|
if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'GROUP':
|
||||||
raise RuntimeError("HMDL *requires* that an OUTPUT shader node named 'Output' is present")
|
raise RuntimeError("HMDL *requires* that an group shader node named 'Output' is present")
|
||||||
|
|
||||||
# Root (output) node
|
# Root (output) node
|
||||||
output_node = mat_obj.node_tree.nodes['Output']
|
output_node = mat_obj.node_tree.nodes['Output']
|
||||||
|
if output_node.node_tree.name not in SHADER_TYPES:
|
||||||
|
raise RuntimeError("HMDL *requires* one of the RetroShader group nodes for the 'Output' node")
|
||||||
|
writebuf(SHADER_TYPES[output_node.node_tree.name])
|
||||||
|
|
||||||
# Trace nodes and build result
|
# Count sockets
|
||||||
tex_list = []
|
chunk_count = 0
|
||||||
color_trace_result = recursive_color_trace(mat_obj, mesh_obj, tex_list, output_node)
|
for inp in output_node.inputs:
|
||||||
alpha_trace_result = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, output_node)
|
if inp.name in PASS_TYPE:
|
||||||
|
if inp.is_linked:
|
||||||
|
chunk_count += 1
|
||||||
|
else:
|
||||||
|
# Color pass
|
||||||
|
color_set = False
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
color_set = bool(inp.default_value)
|
||||||
|
else:
|
||||||
|
for comp in inp.default_value:
|
||||||
|
color_set |= bool(comp)
|
||||||
|
if color_set:
|
||||||
|
chunk_count += 1
|
||||||
|
|
||||||
# Trace indirect reflection texture
|
writebuf(struct.pack('I', chunk_count))
|
||||||
if 'IndirectOutput' in mat_obj.node_tree.nodes:
|
|
||||||
ind_out_node = mat_obj.node_tree.nodes['IndirectOutput']
|
|
||||||
indirect_trace(mat_obj, mesh_obj, tex_list, ind_out_node)
|
|
||||||
|
|
||||||
# Resolve texture paths
|
# Enumerate sockets
|
||||||
tex_paths = [get_texture_path(name) for name in tex_list]
|
for inp in output_node.inputs:
|
||||||
|
if inp.name in PASS_TYPE:
|
||||||
|
pass_fourcc = PASS_TYPE[inp.name]
|
||||||
|
if inp.is_linked:
|
||||||
|
socket = inp.links[0].from_socket
|
||||||
|
node = socket.node
|
||||||
|
if node.type != 'TEX_IMAGE':
|
||||||
|
raise RuntimeError("HMDL requires all group node inputs connect to Image Texture nodes")
|
||||||
|
|
||||||
if mat_obj.game_settings.alpha_blend == 'ALPHA' or mat_obj.game_settings.alpha_blend == 'ALPHA_SORT':
|
if not node.image:
|
||||||
return "HECLAlpha(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
|
raise RuntimeError("HMDL texture nodes must specify an image object")
|
||||||
elif mat_obj.game_settings.alpha_blend == 'ADD':
|
|
||||||
return "HECLAdditive(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
|
|
||||||
else:
|
|
||||||
return "HECLOpaque(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
|
|
||||||
|
|
||||||
# DEBUG operator
|
if not node.inputs['Vector'].is_linked:
|
||||||
import bpy
|
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
|
||||||
class hecl_shader_operator(bpy.types.Operator):
|
|
||||||
bl_idname = "scene.hecl_shader"
|
|
||||||
bl_label = "DEBUG HECL shader maker"
|
|
||||||
bl_description = "Test shader generation utility"
|
|
||||||
|
|
||||||
@classmethod
|
# Determine matrix generator type
|
||||||
def poll(cls, context):
|
tex_coord_source = 0xff
|
||||||
return context.object and context.object.type == 'MESH'
|
uv_anim_type = 0xff
|
||||||
|
uv_anim_args = []
|
||||||
|
soc_from = node.inputs['Vector'].links[0].from_socket
|
||||||
|
|
||||||
def execute(self, context):
|
if soc_from.node.type == 'GROUP':
|
||||||
shad, texs = shader(context.object.active_material, context.object)
|
if soc_from.node.node_tree.name.startswith('RetroUVMode'):
|
||||||
|
uv_anim_type = int(soc_from.node.node_tree.name[11:12])
|
||||||
vs = bpy.data.texts.new('HECL SHADER')
|
if len(soc_from.node.inputs)-1:
|
||||||
vs.write((shad + '\n'))
|
for s in range(len(soc_from.node.inputs)-1):
|
||||||
for tex in texs:
|
soc = soc_from.node.inputs[s+1]
|
||||||
vs.write(tex + '\n')
|
if len(soc.links):
|
||||||
|
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
|
||||||
|
if soc.type == 'VALUE':
|
||||||
|
uv_anim_args.append(soc.default_value)
|
||||||
|
else:
|
||||||
|
uv_anim_args.append(soc.default_value[0])
|
||||||
|
uv_anim_args.append(soc.default_value[1])
|
||||||
|
soc_from = soc_from.node.inputs[0].links[0].from_socket
|
||||||
|
|
||||||
return {'FINISHED'}
|
elif soc_from.node.type == 'UVMAP' or soc_from.node.type == 'TEX_COORD':
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
|
||||||
|
|
||||||
|
if soc_from.node.type != 'UVMAP' and soc_from.node.type != 'TEX_COORD':
|
||||||
|
raise RuntimeError("Matrix animator nodes must connect to 'Texture Coordinate' or 'UV Map' node")
|
||||||
|
|
||||||
|
|
||||||
|
# Resolve map and matrix index
|
||||||
|
node_label = soc_from.node.label
|
||||||
|
matrix_idx = None
|
||||||
|
if node_label.startswith('MTX_'):
|
||||||
|
matrix_idx = int(node_label[4:])
|
||||||
|
|
||||||
|
if soc_from.name == 'UV':
|
||||||
|
if hasattr(soc_from.node, 'uv_map'):
|
||||||
|
uv_name = soc_from.node.uv_map
|
||||||
|
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
||||||
|
if uv_idx == -1:
|
||||||
|
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
||||||
|
tex_coord_source = uv_idx + 2
|
||||||
|
else:
|
||||||
|
tex_coord_source = 2
|
||||||
|
|
||||||
|
elif soc_from.name == 'Normal':
|
||||||
|
tex_coord_source = 1
|
||||||
|
|
||||||
|
elif soc_from.name == 'Window':
|
||||||
|
tex_coord_source = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Only the 'UV', 'Normal' and 'Window' sockets may be used from 'Texture Coordinate' nodes")
|
||||||
|
|
||||||
|
alpha = False
|
||||||
|
if socket.name == 'Alpha':
|
||||||
|
alpha = True
|
||||||
|
|
||||||
|
writebuf(b'PASS')
|
||||||
|
writebuf(pass_fourcc)
|
||||||
|
path = get_texture_path(node.image)
|
||||||
|
writebuf(struct.pack('I', len(path)))
|
||||||
|
writebuf(path.encode())
|
||||||
|
writebuf(struct.pack('B', tex_coord_source))
|
||||||
|
writebuf(struct.pack('B', uv_anim_type))
|
||||||
|
writebuf(struct.pack('I', len(uv_anim_args)))
|
||||||
|
for arg in uv_anim_args:
|
||||||
|
writebuf(struct.pack('f', arg))
|
||||||
|
writebuf(struct.pack('B', alpha))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Color pass
|
||||||
|
color_set = False
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
color_set = bool(inp.default_value)
|
||||||
|
else:
|
||||||
|
for comp in inp.default_value:
|
||||||
|
color_set |= bool(comp)
|
||||||
|
|
||||||
|
if color_set:
|
||||||
|
writebuf(b'CLR ')
|
||||||
|
writebuf(pass_fourcc)
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
writebuf(struct.pack('ffff', inp.default_value, inp.default_value,
|
||||||
|
inp.default_value, inp.default_value))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('ffff', inp.default_value[0], inp.default_value[1],
|
||||||
|
inp.default_value[2], inp.default_value[3]))
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
import struct, bpy, bmesh
|
import struct, bpy, bmesh
|
||||||
from mathutils import Vector
|
|
||||||
from . import HMDLShader, HMDLMesh
|
from . import HMDLShader, HMDLMesh
|
||||||
|
|
||||||
def write_out_material(writebuf, mat, mesh_obj):
|
def write_out_material(writebuf, mat, mesh_obj):
|
||||||
hecl_str, texs = HMDLShader.shader(mat, mesh_obj)
|
|
||||||
writebuf(struct.pack('I', len(mat.name)))
|
writebuf(struct.pack('I', len(mat.name)))
|
||||||
writebuf(mat.name.encode())
|
writebuf(mat.name.encode())
|
||||||
writebuf(struct.pack('I', len(hecl_str)))
|
|
||||||
writebuf(hecl_str.encode())
|
writebuf(struct.pack('I', mat.pass_index))
|
||||||
writebuf(struct.pack('I', len(texs)))
|
|
||||||
for tex in texs:
|
HMDLShader.write_chunks(writebuf, mat, mesh_obj)
|
||||||
writebuf(struct.pack('I', len(tex)))
|
|
||||||
writebuf(tex.encode())
|
|
||||||
|
|
||||||
prop_count = 0
|
prop_count = 0
|
||||||
for prop in mat.items():
|
for prop in mat.items():
|
||||||
|
@ -24,22 +20,16 @@ def write_out_material(writebuf, mat, mesh_obj):
|
||||||
writebuf(prop[0].encode())
|
writebuf(prop[0].encode())
|
||||||
writebuf(struct.pack('i', prop[1]))
|
writebuf(struct.pack('i', prop[1]))
|
||||||
|
|
||||||
transparent = False
|
blend = 0
|
||||||
if mat.game_settings.alpha_blend == 'ALPHA' or mat.game_settings.alpha_blend == 'ALPHA_SORT':
|
if mat.blend_method == 'BLEND':
|
||||||
transparent = True
|
blend = 1
|
||||||
elif mat.game_settings.alpha_blend == 'ADD':
|
elif mat.blend_method == 'ADD':
|
||||||
transparent = True
|
blend = 2
|
||||||
writebuf(struct.pack('b', int(transparent)))
|
writebuf(struct.pack('I', blend))
|
||||||
|
|
||||||
# If this returns true, the material geometry will be split into contiguous faces
|
|
||||||
def should_split_into_contiguous_faces(mat):
|
|
||||||
return False
|
|
||||||
#return mat.game_settings.alpha_blend != 'OPAQUE' and \
|
|
||||||
# 'retro_depth_sort' in mat and mat['retro_depth_sort']
|
|
||||||
|
|
||||||
# Takes a Blender 'Mesh' object (not the datablock)
|
# Takes a Blender 'Mesh' object (not the datablock)
|
||||||
# and performs a one-shot conversion process to HMDL
|
# and performs a one-shot conversion process to HMDL
|
||||||
def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
|
def cook(writebuf, mesh_obj, use_luv=False):
|
||||||
if mesh_obj.type != 'MESH':
|
if mesh_obj.type != 'MESH':
|
||||||
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
|
||||||
|
@ -47,13 +37,13 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
|
||||||
copy_name = mesh_obj.name + "_hmdltri"
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
copy_mesh = bpy.data.meshes.new(copy_name)
|
copy_mesh = bpy.data.meshes.new(copy_name)
|
||||||
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER')
|
copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
|
||||||
copy_mesh = copy_obj.data
|
copy_mesh = copy_obj.data
|
||||||
copy_obj.scale = mesh_obj.scale
|
copy_obj.scale = mesh_obj.scale
|
||||||
bpy.context.scene.objects.link(copy_obj)
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
bpy.context.scene.objects.active = copy_obj
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
copy_obj.select = True
|
copy_obj.select_set(True)
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_all(action='SELECT')
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
bpy.ops.mesh.quads_convert_to_tris()
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
@ -77,26 +67,9 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
|
||||||
pt = copy_obj.bound_box[6]
|
pt = copy_obj.bound_box[6]
|
||||||
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
|
||||||
# Create master BMesh and VertPool
|
# Create master BMesh
|
||||||
bm_master = bmesh.new()
|
bm_master = bmesh.new()
|
||||||
bm_master.from_mesh(copy_mesh)
|
bm_master.from_mesh(copy_mesh)
|
||||||
vert_pool = HMDLMesh.VertPool(bm_master, rna_loops, use_luv, mesh_obj.material_slots)
|
|
||||||
|
|
||||||
# Tag edges where there are distinctive loops
|
|
||||||
for e in bm_master.edges:
|
|
||||||
e.tag = vert_pool.splitable_edge(e)
|
|
||||||
|
|
||||||
# Sort materials by pass index first
|
|
||||||
sorted_material_idxs = []
|
|
||||||
source_mat_set = set(range(len(mesh_obj.data.materials)))
|
|
||||||
while len(source_mat_set):
|
|
||||||
min_mat_idx = source_mat_set.pop()
|
|
||||||
source_mat_set.add(min_mat_idx)
|
|
||||||
for mat_idx in source_mat_set:
|
|
||||||
if mesh_obj.data.materials[mat_idx].pass_index < mesh_obj.data.materials[min_mat_idx].pass_index:
|
|
||||||
min_mat_idx = mat_idx
|
|
||||||
sorted_material_idxs.append(min_mat_idx)
|
|
||||||
source_mat_set.discard(min_mat_idx)
|
|
||||||
|
|
||||||
# Generate shaders
|
# Generate shaders
|
||||||
if mesh_obj.data.hecl_material_count > 0:
|
if mesh_obj.data.hecl_material_count > 0:
|
||||||
|
@ -121,84 +94,14 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
|
||||||
for mat in mesh_obj.data.materials:
|
for mat in mesh_obj.data.materials:
|
||||||
write_out_material(writebuf, mat, mesh_obj)
|
write_out_material(writebuf, mat, mesh_obj)
|
||||||
|
|
||||||
# Output vert pool
|
# Output attribute lists
|
||||||
vert_pool.write_out(writebuf, mesh_obj.vertex_groups)
|
HMDLMesh.write_mesh_attrs(writebuf, bm_master, rna_loops, use_luv, mesh_obj.material_slots)
|
||||||
|
|
||||||
dlay = None
|
# Vertex groups
|
||||||
if len(bm_master.verts.layers.deform):
|
writebuf(struct.pack('I', len(mesh_obj.vertex_groups)))
|
||||||
dlay = bm_master.verts.layers.deform[0]
|
for vgrp in mesh_obj.vertex_groups:
|
||||||
|
writebuf(struct.pack('I', len(vgrp.name)))
|
||||||
# Generate material meshes (if opaque)
|
writebuf(vgrp.name.encode())
|
||||||
for mat_idx in sorted_material_idxs:
|
|
||||||
mat = mesh_obj.data.materials[mat_idx]
|
|
||||||
if should_split_into_contiguous_faces(mat):
|
|
||||||
continue
|
|
||||||
mat_faces_rem = []
|
|
||||||
for face in bm_master.faces:
|
|
||||||
if face.material_index == mat_idx:
|
|
||||||
mat_faces_rem.append(face)
|
|
||||||
if dlay:
|
|
||||||
mat_faces_rem = HMDLMesh.sort_faces_by_skin_group(dlay, mat_faces_rem)
|
|
||||||
while len(mat_faces_rem):
|
|
||||||
the_list = []
|
|
||||||
skin_slot_set = set()
|
|
||||||
faces = list(mat_faces_rem)
|
|
||||||
for f in faces:
|
|
||||||
if dlay:
|
|
||||||
ret_faces = None
|
|
||||||
for v in f.verts:
|
|
||||||
sg = tuple(sorted(v[dlay].items()))
|
|
||||||
if sg not in skin_slot_set:
|
|
||||||
if max_skin_banks > 0 and len(skin_slot_set) == max_skin_banks:
|
|
||||||
ret_faces = False
|
|
||||||
break
|
|
||||||
skin_slot_set.add(sg)
|
|
||||||
|
|
||||||
if ret_faces == False:
|
|
||||||
break
|
|
||||||
|
|
||||||
the_list.append(f)
|
|
||||||
mat_faces_rem.remove(f)
|
|
||||||
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
HMDLMesh.write_out_surface(writebuf, output_mode, vert_pool, the_list, mat_idx)
|
|
||||||
|
|
||||||
|
|
||||||
# Generate island meshes (if transparent)
|
|
||||||
for mat_idx in sorted_material_idxs:
|
|
||||||
mat = mesh_obj.data.materials[mat_idx]
|
|
||||||
if not should_split_into_contiguous_faces(mat):
|
|
||||||
continue
|
|
||||||
mat_faces_rem = []
|
|
||||||
for face in bm_master.faces:
|
|
||||||
if face.material_index == mat_idx:
|
|
||||||
mat_faces_rem.append(face)
|
|
||||||
if dlay:
|
|
||||||
mat_faces_rem = HMDLMesh.sort_faces_by_skin_group(dlay, mat_faces_rem)
|
|
||||||
while len(mat_faces_rem):
|
|
||||||
the_list = []
|
|
||||||
skin_slot_set = set()
|
|
||||||
faces = [mat_faces_rem[0]]
|
|
||||||
while len(faces):
|
|
||||||
next_faces = []
|
|
||||||
ret_faces = None
|
|
||||||
for f in faces:
|
|
||||||
ret_faces = HMDLMesh.recursive_faces_islands(dlay, the_list,
|
|
||||||
mat_faces_rem,
|
|
||||||
skin_slot_set,
|
|
||||||
max_skin_banks, f)
|
|
||||||
if ret_faces == False:
|
|
||||||
break
|
|
||||||
next_faces.extend(ret_faces)
|
|
||||||
if ret_faces == False:
|
|
||||||
break
|
|
||||||
faces = next_faces
|
|
||||||
|
|
||||||
writebuf(struct.pack('B', 1))
|
|
||||||
HMDLMesh.write_out_surface(writebuf, output_mode, vert_pool, the_list, mat_idx)
|
|
||||||
|
|
||||||
# No more surfaces
|
|
||||||
writebuf(struct.pack('B', 0))
|
|
||||||
|
|
||||||
# Enumerate custom props
|
# Enumerate custom props
|
||||||
writebuf(struct.pack('I', len(mesh_obj.keys())))
|
writebuf(struct.pack('I', len(mesh_obj.keys())))
|
||||||
|
@ -211,7 +114,7 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, use_luv=False):
|
||||||
|
|
||||||
# Delete copied mesh from scene
|
# Delete copied mesh from scene
|
||||||
bm_master.free()
|
bm_master.free()
|
||||||
bpy.context.scene.objects.unlink(copy_obj)
|
#bpy.context.scene.objects.unlink(copy_obj)
|
||||||
bpy.data.objects.remove(copy_obj)
|
bpy.data.objects.remove(copy_obj)
|
||||||
bpy.data.meshes.remove(copy_mesh)
|
bpy.data.meshes.remove(copy_mesh)
|
||||||
|
|
||||||
|
@ -231,13 +134,13 @@ def cookcol(writebuf, mesh_obj):
|
||||||
copy_name = mesh_obj.name + "_hmdltri"
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
copy_mesh = bpy.data.meshes.new(copy_name)
|
copy_mesh = bpy.data.meshes.new(copy_name)
|
||||||
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER')
|
copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
|
||||||
copy_mesh = copy_obj.data
|
copy_mesh = copy_obj.data
|
||||||
copy_obj.scale = mesh_obj.scale
|
copy_obj.scale = mesh_obj.scale
|
||||||
bpy.context.scene.objects.link(copy_obj)
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
bpy.context.scene.objects.active = copy_obj
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
copy_obj.select = True
|
copy_obj.select_set(True)
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_all(action='SELECT')
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
bpy.ops.mesh.quads_convert_to_tris()
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
@ -320,7 +223,7 @@ def cookcol(writebuf, mesh_obj):
|
||||||
# Send verts
|
# Send verts
|
||||||
writebuf(struct.pack('I', len(copy_mesh.vertices)))
|
writebuf(struct.pack('I', len(copy_mesh.vertices)))
|
||||||
for v in copy_mesh.vertices:
|
for v in copy_mesh.vertices:
|
||||||
xfVert = wmtx * v.co
|
xfVert = wmtx @ v.co
|
||||||
writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2]))
|
writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2]))
|
||||||
|
|
||||||
# Send edges
|
# Send edges
|
||||||
|
@ -340,7 +243,7 @@ def cookcol(writebuf, mesh_obj):
|
||||||
writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip))
|
writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip))
|
||||||
|
|
||||||
# Delete copied mesh from scene
|
# Delete copied mesh from scene
|
||||||
bpy.context.scene.objects.unlink(copy_obj)
|
#bpy.context.scene.objects.unlink(copy_obj)
|
||||||
bpy.data.objects.remove(copy_obj)
|
bpy.data.objects.remove(copy_obj)
|
||||||
bpy.data.meshes.remove(copy_mesh)
|
bpy.data.meshes.remove(copy_mesh)
|
||||||
|
|
||||||
|
@ -348,13 +251,13 @@ def cookcol(writebuf, mesh_obj):
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
||||||
if not len(context.scene.hecl_mesh_obj):
|
if not len(context.scene.hecl_mesh_obj):
|
||||||
layout.label("Mesh not specified", icon='ERROR')
|
layout.label(text="Mesh not specified", icon='ERROR')
|
||||||
elif context.scene.hecl_mesh_obj not in context.scene.objects:
|
elif context.scene.hecl_mesh_obj not in context.scene.objects:
|
||||||
layout.label("'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR')
|
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR')
|
||||||
else:
|
else:
|
||||||
obj = context.scene.objects[context.scene.hecl_mesh_obj]
|
obj = context.scene.objects[context.scene.hecl_mesh_obj]
|
||||||
if obj.type != 'MESH':
|
if obj.type != 'MESH':
|
||||||
layout.label("'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR')
|
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR')
|
||||||
layout.prop(obj.data, 'hecl_active_material')
|
layout.prop(obj.data, 'hecl_active_material')
|
||||||
layout.prop(obj.data, 'hecl_material_count')
|
layout.prop(obj.data, 'hecl_material_count')
|
||||||
|
|
||||||
|
@ -398,10 +301,8 @@ def register():
|
||||||
description='Blender Empty Object to export during HECL\'s cook process')
|
description='Blender Empty Object to export during HECL\'s cook process')
|
||||||
bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0)
|
bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0)
|
||||||
bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update)
|
bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update)
|
||||||
bpy.utils.register_class(HMDLShader.hecl_shader_operator)
|
|
||||||
bpy.utils.register_class(hecl_mesh_operator)
|
bpy.utils.register_class(hecl_mesh_operator)
|
||||||
pass
|
pass
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(HMDLShader.hecl_shader_operator)
|
|
||||||
bpy.utils.unregister_class(hecl_mesh_operator)
|
bpy.utils.unregister_class(hecl_mesh_operator)
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,8 +1,233 @@
|
||||||
import bpy, struct, bmesh
|
import bpy, struct, bmesh, operator
|
||||||
from . import hmdl
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
VertPool = hmdl.HMDLMesh.VertPool
|
|
||||||
strip_next_loop = hmdl.HMDLMesh.strip_next_loop
|
# Function to quantize normals to 15-bit precision
|
||||||
|
def quant_norm(n):
|
||||||
|
nf = n.copy()
|
||||||
|
for i in range(3):
|
||||||
|
nf[i] = int(nf[i] * 16384) / 16384.0
|
||||||
|
return nf.freeze()
|
||||||
|
|
||||||
|
# Function to quantize lightmap UVs to 15-bit precision
|
||||||
|
def quant_luv(n):
|
||||||
|
uf = n.copy()
|
||||||
|
for i in range(2):
|
||||||
|
uf[i] = int(uf[i] * 32768) / 32768.0
|
||||||
|
return uf.freeze()
|
||||||
|
|
||||||
|
# Class for building unique sets of vertex attributes for VBO generation
|
||||||
|
class VertPool:
|
||||||
|
|
||||||
|
# Initialize hash-unique index for each available attribute
|
||||||
|
def __init__(self, bm, rna_loops, use_luv, material_slots):
|
||||||
|
self.bm = bm
|
||||||
|
self.rna_loops = rna_loops
|
||||||
|
self.material_slots = material_slots
|
||||||
|
self.pos = {}
|
||||||
|
self.norm = {}
|
||||||
|
self.skin = {}
|
||||||
|
self.color = {}
|
||||||
|
self.uv = {}
|
||||||
|
self.luv = {}
|
||||||
|
self.dlay = None
|
||||||
|
self.clays = []
|
||||||
|
self.ulays = []
|
||||||
|
self.luvlay = None
|
||||||
|
|
||||||
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
self.dlay = dlay
|
||||||
|
|
||||||
|
clays = []
|
||||||
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
|
clays.append(bm.loops.layers.color[cl])
|
||||||
|
self.clays = clays
|
||||||
|
|
||||||
|
luvlay = None
|
||||||
|
if use_luv:
|
||||||
|
luvlay = bm.loops.layers.uv[0]
|
||||||
|
self.luvlay = luvlay
|
||||||
|
ulays = []
|
||||||
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
self.ulays = ulays
|
||||||
|
|
||||||
|
# Per-vert pool attributes
|
||||||
|
for v in bm.verts:
|
||||||
|
pf = v.co.copy().freeze()
|
||||||
|
if pf not in self.pos:
|
||||||
|
self.pos[pf] = len(self.pos)
|
||||||
|
if not rna_loops:
|
||||||
|
nf = quant_norm(v.normal)
|
||||||
|
if nf not in self.norm:
|
||||||
|
self.norm[nf] = len(self.norm)
|
||||||
|
if dlay:
|
||||||
|
sf = tuple(sorted(v[dlay].items()))
|
||||||
|
if sf not in self.skin:
|
||||||
|
self.skin[sf] = len(self.skin)
|
||||||
|
|
||||||
|
# Per-loop pool attributes
|
||||||
|
for f in bm.faces:
|
||||||
|
lightmapped = f.material_index < len(material_slots) and \
|
||||||
|
material_slots[f.material_index].material['retro_lightmapped']
|
||||||
|
for l in f.loops:
|
||||||
|
if rna_loops:
|
||||||
|
nf = quant_norm(rna_loops[l.index].normal)
|
||||||
|
if nf not in self.norm:
|
||||||
|
self.norm[nf] = len(self.norm)
|
||||||
|
for cl in range(len(clays)):
|
||||||
|
cf = l[clays[cl]].copy().freeze()
|
||||||
|
if cf not in self.color:
|
||||||
|
self.color[cf] = len(self.color)
|
||||||
|
start_uvlay = 0
|
||||||
|
if use_luv and lightmapped:
|
||||||
|
start_uvlay = 1
|
||||||
|
uf = quant_luv(l[luvlay].uv)
|
||||||
|
if uf not in self.luv:
|
||||||
|
self.luv[uf] = len(self.luv)
|
||||||
|
for ul in range(start_uvlay, len(ulays)):
|
||||||
|
uf = l[ulays[ul]].uv.copy().freeze()
|
||||||
|
if uf not in self.uv:
|
||||||
|
self.uv[uf] = len(self.uv)
|
||||||
|
|
||||||
|
def write_out(self, writebuf, vert_groups):
|
||||||
|
writebuf(struct.pack('I', len(self.pos)))
|
||||||
|
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(self.norm)))
|
||||||
|
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('II', len(self.clays), len(self.color)))
|
||||||
|
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
|
||||||
|
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
||||||
|
|
||||||
|
luv_count = 0
|
||||||
|
if self.luvlay is not None:
|
||||||
|
luv_count = 1
|
||||||
|
writebuf(struct.pack('II', luv_count, len(self.luv)))
|
||||||
|
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(vert_groups)))
|
||||||
|
for vgrp in vert_groups:
|
||||||
|
writebuf(struct.pack('I', len(vgrp.name)))
|
||||||
|
writebuf(vgrp.name.encode())
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(self.skin)))
|
||||||
|
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
|
||||||
|
entries = s[0]
|
||||||
|
writebuf(struct.pack('I', len(entries)))
|
||||||
|
if len(entries):
|
||||||
|
total_len = 0.0
|
||||||
|
for ent in entries:
|
||||||
|
total_len += ent[1]
|
||||||
|
for ent in entries:
|
||||||
|
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
||||||
|
|
||||||
|
def write_out_map(self, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(self.pos)))
|
||||||
|
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||||
|
|
||||||
|
def get_pos_idx(self, vert):
|
||||||
|
pf = vert.co.copy().freeze()
|
||||||
|
return self.pos[pf]
|
||||||
|
|
||||||
|
def get_norm_idx(self, loop):
|
||||||
|
if self.rna_loops:
|
||||||
|
nf = quant_norm(self.rna_loops[loop.index].normal)
|
||||||
|
else:
|
||||||
|
nf = quant_norm(loop.vert.normal)
|
||||||
|
return self.norm[nf]
|
||||||
|
|
||||||
|
def get_skin_idx(self, vert):
|
||||||
|
if not self.dlay:
|
||||||
|
return 0
|
||||||
|
sf = tuple(sorted(vert[self.dlay].items()))
|
||||||
|
return self.skin[sf]
|
||||||
|
|
||||||
|
def get_color_idx(self, loop, cidx):
|
||||||
|
cf = loop[self.clays[cidx]].copy().freeze()
|
||||||
|
return self.color[cf]
|
||||||
|
|
||||||
|
def get_uv_idx(self, loop, uidx):
|
||||||
|
if self.luvlay is not None and uidx == 0:
|
||||||
|
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
|
||||||
|
uf = quant_luv(loop[self.luvlay].uv)
|
||||||
|
return self.luv[uf]
|
||||||
|
uf = loop[self.ulays[uidx]].uv.copy().freeze()
|
||||||
|
return self.uv[uf]
|
||||||
|
|
||||||
|
def loops_contiguous(self, la, lb):
|
||||||
|
if la.vert != lb.vert:
|
||||||
|
return False
|
||||||
|
if self.get_norm_idx(la) != self.get_norm_idx(lb):
|
||||||
|
return False
|
||||||
|
for cl in range(len(self.clays)):
|
||||||
|
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
|
||||||
|
return False
|
||||||
|
for ul in range(len(self.ulays)):
|
||||||
|
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def splitable_edge(self, edge):
|
||||||
|
if len(edge.link_faces) < 2:
|
||||||
|
return False
|
||||||
|
for v in edge.verts:
|
||||||
|
found = None
|
||||||
|
for f in edge.link_faces:
|
||||||
|
for l in f.loops:
|
||||||
|
if l.vert == v:
|
||||||
|
if not found:
|
||||||
|
found = l
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not self.loops_contiguous(found, l):
|
||||||
|
return True
|
||||||
|
break
|
||||||
|
return False
|
||||||
|
|
||||||
|
def loop_out(self, writebuf, loop):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
|
||||||
|
for cl in range(len(self.clays)):
|
||||||
|
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
|
||||||
|
for ul in range(len(self.ulays)):
|
||||||
|
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
|
||||||
|
sp = struct.pack('I', self.get_skin_idx(loop.vert))
|
||||||
|
writebuf(sp)
|
||||||
|
|
||||||
|
def null_loop_out(self, writebuf):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', 0xffffffff))
|
||||||
|
|
||||||
|
def loop_out_map(self, writebuf, loop):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
|
||||||
|
|
||||||
|
def vert_out_map(self, writebuf, vert):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', self.get_pos_idx(vert)))
|
||||||
|
|
||||||
|
|
||||||
|
def strip_next_loop(prev_loop, out_count):
|
||||||
|
if out_count & 1:
|
||||||
|
radial_loop = prev_loop.link_loop_radial_next
|
||||||
|
loop = radial_loop.link_loop_prev
|
||||||
|
return loop, loop
|
||||||
|
else:
|
||||||
|
radial_loop = prev_loop.link_loop_radial_prev
|
||||||
|
loop = radial_loop.link_loop_next
|
||||||
|
return loop.link_loop_next, loop
|
||||||
|
|
||||||
|
|
||||||
def recursive_faces_islands(list_out, rem_list, face):
|
def recursive_faces_islands(list_out, rem_list, face):
|
||||||
if face not in rem_list:
|
if face not in rem_list:
|
||||||
|
@ -34,13 +259,13 @@ def cook(writebuf, mesh_obj):
|
||||||
copy_name = mesh_obj.name + "_hmdltri"
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
copy_mesh = bpy.data.meshes.new(copy_name)
|
copy_mesh = bpy.data.meshes.new(copy_name)
|
||||||
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER')
|
copy_obj.data = mesh_obj.to_mesh(bpy.context.depsgraph, True)
|
||||||
copy_mesh = copy_obj.data
|
copy_mesh = copy_obj.data
|
||||||
copy_obj.scale = mesh_obj.scale
|
copy_obj.scale = mesh_obj.scale
|
||||||
bpy.context.scene.objects.link(copy_obj)
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
bpy.context.scene.objects.active = copy_obj
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
copy_obj.select = True
|
copy_obj.select_set(True)
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_all(action='SELECT')
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
bpy.ops.mesh.quads_convert_to_tris()
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import bpy, bgl, sys, bmesh, struct
|
import bpy, gpu, sys, bmesh, struct
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
# Convenience class that automatically brings active edit mesh's face into scope for get/set
|
# Convenience class that automatically brings active edit mesh's face into scope for get/set
|
||||||
class HeightRef:
|
class HeightRef:
|
||||||
|
@ -44,14 +45,6 @@ def set_height(self, val):
|
||||||
if ar.type == 'VIEW_3D':
|
if ar.type == 'VIEW_3D':
|
||||||
ar.tag_redraw()
|
ar.tag_redraw()
|
||||||
|
|
||||||
# Edit panel
|
|
||||||
def draw(layout, context):
|
|
||||||
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
|
|
||||||
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='WIRE')
|
|
||||||
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon='MANIPUL')
|
|
||||||
if HeightRef().ready:
|
|
||||||
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
|
|
||||||
|
|
||||||
# Simple AABB class
|
# Simple AABB class
|
||||||
class AABB:
|
class AABB:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -226,34 +219,39 @@ def cook(writebuf, mesh_obj):
|
||||||
writebuf(struct.pack('I', len(ba)))
|
writebuf(struct.pack('I', len(ba)))
|
||||||
writebuf(ba)
|
writebuf(ba)
|
||||||
|
|
||||||
|
try:
|
||||||
|
line_shader = gpu.shader.from_builtin('3D_FLAT_COLOR')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
# Line draw helper
|
# Line draw helper
|
||||||
def draw_line_3d(color, start, end):
|
def draw_line_3d(pos_vbo, color_vbo, color, start, end):
|
||||||
bgl.glColor4f(*color)
|
pos_vbo.append(start)
|
||||||
bgl.glBegin(bgl.GL_LINES)
|
pos_vbo.append(end)
|
||||||
bgl.glVertex3f(*start)
|
color_vbo.append(color)
|
||||||
bgl.glVertex3f(*end)
|
color_vbo.append(color)
|
||||||
|
|
||||||
# Draw RNA polygon
|
# Draw RNA polygon
|
||||||
def draw_poly(p, obj, obj_mtx, height, top_color, side_color):
|
def draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color):
|
||||||
for ek in p.edge_keys:
|
for ek in p.edge_keys:
|
||||||
co0 = obj_mtx * obj.data.vertices[ek[0]].co
|
co0 = obj_mtx @ obj.data.vertices[ek[0]].co
|
||||||
co1 = obj_mtx * obj.data.vertices[ek[1]].co
|
co1 = obj_mtx @ obj.data.vertices[ek[1]].co
|
||||||
draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)),
|
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
|
||||||
co1 + Vector((0.0, 0.0, height)))
|
co1 + Vector((0.0, 0.0, height)))
|
||||||
for vk in p.vertices:
|
for vk in p.vertices:
|
||||||
co = obj_mtx * obj.data.vertices[vk].co
|
co = obj_mtx @ obj.data.vertices[vk].co
|
||||||
draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height)))
|
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
|
||||||
|
|
||||||
# Draw bmesh face
|
# Draw bmesh face
|
||||||
def draw_face(f, obj_mtx, height, top_color, side_color):
|
def draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color):
|
||||||
for e in f.edges:
|
for e in f.edges:
|
||||||
co0 = obj_mtx * e.verts[0].co
|
co0 = obj_mtx @ e.verts[0].co
|
||||||
co1 = obj_mtx * e.verts[1].co
|
co1 = obj_mtx @ e.verts[1].co
|
||||||
draw_line_3d(top_color, co0 + Vector((0.0, 0.0, height)),
|
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
|
||||||
co1 + Vector((0.0, 0.0, height)))
|
co1 + Vector((0.0, 0.0, height)))
|
||||||
for v in f.verts:
|
for v in f.verts:
|
||||||
co = obj_mtx * v.co
|
co = obj_mtx @ v.co
|
||||||
draw_line_3d(side_color, co, co + Vector((0.0, 0.0, height)))
|
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
|
||||||
|
|
||||||
# Viewport hook callback
|
# Viewport hook callback
|
||||||
def draw_callback_3d(self, context):
|
def draw_callback_3d(self, context):
|
||||||
|
@ -275,6 +273,9 @@ def draw_callback_3d(self, context):
|
||||||
if 'Height' in obj.data.polygon_layers_float:
|
if 'Height' in obj.data.polygon_layers_float:
|
||||||
height_lay = obj.data.polygon_layers_float['Height']
|
height_lay = obj.data.polygon_layers_float['Height']
|
||||||
|
|
||||||
|
pos_vbo = []
|
||||||
|
color_vbo = []
|
||||||
|
|
||||||
# Deselected colors
|
# Deselected colors
|
||||||
top_color = (0.0, 0.0, 1.0, 0.7)
|
top_color = (0.0, 0.0, 1.0, 0.7)
|
||||||
side_color = (1.0, 0.0, 0.0, 0.7)
|
side_color = (1.0, 0.0, 0.0, 0.7)
|
||||||
|
@ -286,33 +287,34 @@ def draw_callback_3d(self, context):
|
||||||
if selected:
|
if selected:
|
||||||
continue
|
continue
|
||||||
height = f[height_lay]
|
height = f[height_lay]
|
||||||
draw_face(f, obj_mtx, height, top_color, side_color)
|
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
|
||||||
else:
|
else:
|
||||||
for p in obj.data.polygons:
|
for p in obj.data.polygons:
|
||||||
height = 1.0
|
height = 1.0
|
||||||
if height_lay is not None:
|
if height_lay is not None:
|
||||||
height = height_lay.data[p.index].value
|
height = height_lay.data[p.index].value
|
||||||
draw_poly(p, obj, obj_mtx, height, top_color, side_color)
|
draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color)
|
||||||
bgl.glEnd()
|
|
||||||
|
|
||||||
# Selected colors
|
# Selected colors
|
||||||
if bm is not None:
|
if bm is not None:
|
||||||
top_color = (1.0, 0.0, 1.0, 0.7)
|
top_color = (1.0, 0.0, 1.0, 0.7)
|
||||||
side_color = (0.0, 1.0, 0.0, 0.7)
|
side_color = (0.0, 1.0, 0.0, 0.7)
|
||||||
# Avoid z-fighting on selected lines
|
# Avoid z-fighting on selected lines
|
||||||
bgl.glDepthRange(-0.001, 0.999)
|
#bgl.glDepthRange(-0.001, 0.999)
|
||||||
for f in bm.faces:
|
for f in bm.faces:
|
||||||
if height_lay is not None:
|
if height_lay is not None:
|
||||||
selected = f.select
|
selected = f.select
|
||||||
if not selected:
|
if not selected:
|
||||||
continue
|
continue
|
||||||
height = f[height_lay]
|
height = f[height_lay]
|
||||||
draw_face(f, obj_mtx, height, top_color, side_color)
|
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
|
||||||
bgl.glEnd()
|
#bgl.glEnd()
|
||||||
bgl.glDepthRange(0.0, 1.0)
|
#bgl.glDepthRange(0.0, 1.0)
|
||||||
|
|
||||||
|
line_shader.bind()
|
||||||
|
batch = batch_for_shader(line_shader, 'LINES', {"pos": pos_vbo, "color": color_vbo})
|
||||||
|
batch.draw(line_shader)
|
||||||
|
|
||||||
# restore opengl defaults
|
|
||||||
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
|
|
||||||
|
|
||||||
# Toggle height viz button
|
# Toggle height viz button
|
||||||
class PathHeightDrawOperator(bpy.types.Operator):
|
class PathHeightDrawOperator(bpy.types.Operator):
|
||||||
|
@ -346,17 +348,29 @@ class PathBackgroundWireframeOperator(bpy.types.Operator):
|
||||||
if context.scene.background_set:
|
if context.scene.background_set:
|
||||||
to_wire = False
|
to_wire = False
|
||||||
for o in context.scene.background_set.objects:
|
for o in context.scene.background_set.objects:
|
||||||
if o.draw_type != 'WIRE':
|
if o.display_type != 'WIRE':
|
||||||
to_wire = True
|
to_wire = True
|
||||||
break
|
break
|
||||||
if to_wire:
|
if to_wire:
|
||||||
for o in context.scene.background_set.objects:
|
for o in context.scene.background_set.objects:
|
||||||
o.draw_type = 'WIRE'
|
o.display_type = 'WIRE'
|
||||||
else:
|
else:
|
||||||
for o in context.scene.background_set.objects:
|
for o in context.scene.background_set.objects:
|
||||||
o.draw_type = 'TEXTURED'
|
o.display_type = 'TEXTURED'
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Edit panel
|
||||||
|
def draw(layout, context):
|
||||||
|
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
|
||||||
|
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='SHADING_WIRE')
|
||||||
|
if PathHeightDrawOperator._handle_3d:
|
||||||
|
icon = 'HIDE_OFF'
|
||||||
|
else:
|
||||||
|
icon = 'HIDE_ON'
|
||||||
|
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon=icon)
|
||||||
|
if HeightRef().ready:
|
||||||
|
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
|
||||||
|
|
||||||
# Registration
|
# Registration
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')
|
bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')
|
||||||
|
|
|
@ -4,7 +4,6 @@ in an interleaved, sparse array for use by the runtime
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import hashlib
|
|
||||||
import struct
|
import struct
|
||||||
import mathutils
|
import mathutils
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ def action_type_update(self, context):
|
||||||
|
|
||||||
# Actor action class
|
# Actor action class
|
||||||
class SACTAction(bpy.types.PropertyGroup):
|
class SACTAction(bpy.types.PropertyGroup):
|
||||||
name = bpy.props.StringProperty(name="Action Name")
|
name: bpy.props.StringProperty(name="Action Name")
|
||||||
|
|
||||||
# Panel draw
|
# Panel draw
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
|
@ -30,8 +30,8 @@ def draw(layout, context):
|
||||||
row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
|
row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
|
||||||
actor_data, 'actions', actor_data, 'active_action')
|
actor_data, 'actions', actor_data, 'active_action')
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("scene.sactaction_add", icon="ZOOMIN", text="")
|
col.operator("scene.sactaction_add", icon="ADD", text="")
|
||||||
col.operator("scene.sactaction_remove", icon="ZOOMOUT", text="")
|
col.operator("scene.sactaction_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
if len(actor_data.actions) and actor_data.active_action >= 0:
|
if len(actor_data.actions) and actor_data.active_action >= 0:
|
||||||
action = actor_data.actions[actor_data.active_action]
|
action = actor_data.actions[actor_data.active_action]
|
||||||
|
@ -48,7 +48,7 @@ def draw(layout, context):
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
if linked_action is None:
|
if linked_action is None:
|
||||||
layout.label("Source action not set", icon='ERROR')
|
layout.label(text="Source action not set", icon='ERROR')
|
||||||
else:
|
else:
|
||||||
#layout.prop(linked_action, 'hecl_index', text="Index")
|
#layout.prop(linked_action, 'hecl_index', text="Index")
|
||||||
#layout.prop(linked_action, 'hecl_anim_props', text="Props")
|
#layout.prop(linked_action, 'hecl_anim_props', text="Props")
|
||||||
|
|
|
@ -9,28 +9,26 @@ def active_subtype_update(self, context):
|
||||||
|
|
||||||
# Actor subtype overlay class
|
# Actor subtype overlay class
|
||||||
class SACTSubtypeOverlay(bpy.types.PropertyGroup):
|
class SACTSubtypeOverlay(bpy.types.PropertyGroup):
|
||||||
name = bpy.props.StringProperty(name="Overlay Name")
|
name: bpy.props.StringProperty(name="Overlay Name")
|
||||||
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
show_overlay = bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update)
|
show_overlay: bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update)
|
||||||
|
|
||||||
# Actor attachment class
|
# Actor attachment class
|
||||||
class SACTAttachment(bpy.types.PropertyGroup):
|
class SACTAttachment(bpy.types.PropertyGroup):
|
||||||
name = bpy.props.StringProperty(name="Attachment Name")
|
name: bpy.props.StringProperty(name="Attachment Name")
|
||||||
linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
||||||
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
show_attachment = bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update)
|
show_attachment: bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update)
|
||||||
|
|
||||||
# Actor subtype class
|
# Actor subtype class
|
||||||
class SACTSubtype(bpy.types.PropertyGroup):
|
class SACTSubtype(bpy.types.PropertyGroup):
|
||||||
name = bpy.props.StringProperty(name="Actor Mesh Name")
|
name: bpy.props.StringProperty(name="Actor Mesh Name")
|
||||||
linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
||||||
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
show_mesh = bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update)
|
show_mesh: bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update)
|
||||||
|
|
||||||
overlays =\
|
overlays: bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List")
|
||||||
bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List")
|
active_overlay: bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
|
||||||
active_overlay =\
|
|
||||||
bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
|
|
||||||
|
|
||||||
# Panel draw
|
# Panel draw
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
|
@ -45,8 +43,8 @@ def draw(layout, context):
|
||||||
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
|
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
|
||||||
actor_data, 'subtypes', actor_data, 'active_subtype')
|
actor_data, 'subtypes', actor_data, 'active_subtype')
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("scene.sactsubtype_add", icon="ZOOMIN", text="")
|
col.operator("scene.sactsubtype_add", icon="ADD", text="")
|
||||||
col.operator("scene.sactsubtype_remove", icon="ZOOMOUT", text="")
|
col.operator("scene.sactsubtype_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
|
if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
|
||||||
subtype = actor_data.subtypes[actor_data.active_subtype]
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
@ -67,9 +65,9 @@ def draw(layout, context):
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
if linked_armature is None:
|
if linked_armature is None:
|
||||||
layout.label("Source armature not set", icon='ERROR')
|
layout.label(text="Source armature not set", icon='ERROR')
|
||||||
elif linked_armature is not None and linked_armature.type != 'ARMATURE':
|
elif linked_armature is not None and linked_armature.type != 'ARMATURE':
|
||||||
layout.label("Source armature is not an 'ARMATURE'", icon='ERROR')
|
layout.label(text="Source armature is not an 'ARMATURE'", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
# Link external mesh search
|
# Link external mesh search
|
||||||
|
@ -80,13 +78,13 @@ def draw(layout, context):
|
||||||
layout.prop(subtype, 'show_mesh', text="Show Mesh")
|
layout.prop(subtype, 'show_mesh', text="Show Mesh")
|
||||||
|
|
||||||
# Mesh overlays
|
# Mesh overlays
|
||||||
layout.label("Overlay Meshes:")
|
layout.label(text="Overlay Meshes:")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays",
|
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays",
|
||||||
subtype, 'overlays', subtype, 'active_overlay')
|
subtype, 'overlays', subtype, 'active_overlay')
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("scene.sactsubtypeoverlay_add", icon="ZOOMIN", text="")
|
col.operator("scene.sactsubtypeoverlay_add", icon="ADD", text="")
|
||||||
col.operator("scene.sactsubtypeoverlay_remove", icon="ZOOMOUT", text="")
|
col.operator("scene.sactsubtypeoverlay_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
overlay_mesh = None
|
overlay_mesh = None
|
||||||
if len(subtype.overlays) and subtype.active_overlay >= 0:
|
if len(subtype.overlays) and subtype.active_overlay >= 0:
|
||||||
|
@ -98,13 +96,13 @@ def draw(layout, context):
|
||||||
layout.prop(overlay, 'show_overlay', text="Show Overlay")
|
layout.prop(overlay, 'show_overlay', text="Show Overlay")
|
||||||
|
|
||||||
# Mesh attachments
|
# Mesh attachments
|
||||||
layout.label("Attachment Meshes:")
|
layout.label(text="Attachment Meshes:")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments",
|
row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments",
|
||||||
actor_data, 'attachments', actor_data, 'active_attachment')
|
actor_data, 'attachments', actor_data, 'active_attachment')
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("scene.sactattachment_add", icon="ZOOMIN", text="")
|
col.operator("scene.sactattachment_add", icon="ADD", text="")
|
||||||
col.operator("scene.sactattachment_remove", icon="ZOOMOUT", text="")
|
col.operator("scene.sactattachment_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
attachment_armature = linked_armature
|
attachment_armature = linked_armature
|
||||||
attachment_mesh = None
|
attachment_mesh = None
|
||||||
|
@ -121,27 +119,27 @@ def draw(layout, context):
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
if linked_mesh is None:
|
if linked_mesh is None:
|
||||||
layout.label("Source mesh not set", icon='ERROR')
|
layout.label(text="Source mesh not set", icon='ERROR')
|
||||||
elif linked_mesh.type != 'MESH':
|
elif linked_mesh.type != 'MESH':
|
||||||
layout.label("Source mesh not 'MESH'", icon='ERROR')
|
layout.label(text="Source mesh not 'MESH'", icon='ERROR')
|
||||||
elif linked_armature is not None and linked_mesh not in linked_armature.children:
|
elif linked_armature is not None and linked_mesh not in linked_armature.children:
|
||||||
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
|
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
|
||||||
elif linked_mesh.parent_type != 'ARMATURE':
|
elif linked_mesh.parent_type != 'ARMATURE':
|
||||||
layout.label("Source mesh not 'ARMATURE' parent type", icon='ERROR')
|
layout.label(text="Source mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
if overlay_mesh:
|
if overlay_mesh:
|
||||||
if overlay_mesh.type != 'MESH':
|
if overlay_mesh.type != 'MESH':
|
||||||
layout.label("Overlay mesh not 'MESH'", icon='ERROR')
|
layout.label(text="Overlay mesh not 'MESH'", icon='ERROR')
|
||||||
elif overlay_mesh.parent_type != 'ARMATURE':
|
elif overlay_mesh.parent_type != 'ARMATURE':
|
||||||
layout.label("Overlay mesh not 'ARMATURE' parent type", icon='ERROR')
|
layout.label(text="Overlay mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
if attachment_mesh:
|
if attachment_mesh:
|
||||||
if attachment_mesh.type != 'MESH':
|
if attachment_mesh.type != 'MESH':
|
||||||
layout.label("Attachment mesh not 'MESH'", icon='ERROR')
|
layout.label(text="Attachment mesh not 'MESH'", icon='ERROR')
|
||||||
elif attachment_armature is not None and attachment_mesh not in attachment_armature.children:
|
elif attachment_armature is not None and attachment_mesh not in attachment_armature.children:
|
||||||
layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR')
|
layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR')
|
||||||
elif attachment_mesh.parent_type != 'ARMATURE':
|
elif attachment_mesh.parent_type != 'ARMATURE':
|
||||||
layout.label("Attachment mesh not 'ARMATURE' parent type", icon='ERROR')
|
layout.label(text="Attachment mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
# Subtype 'add' operator
|
# Subtype 'add' operator
|
||||||
|
@ -195,6 +193,17 @@ class SACTSubtype_remove(bpy.types.Operator):
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
def parent_armature(mesh_obj, arm_obj):
|
||||||
|
mesh_obj.parent = None
|
||||||
|
for mod in mesh_obj.modifiers:
|
||||||
|
if mod.type == 'ARMATURE':
|
||||||
|
mod.object = arm_obj
|
||||||
|
return
|
||||||
|
mod = mesh_obj.modifiers.new('Parent', 'ARMATURE')
|
||||||
|
mod.object = arm_obj
|
||||||
|
#mesh_obj.parent = arm_obj
|
||||||
|
#mesh_obj.parent_type = 'ARMATURE'
|
||||||
|
|
||||||
# Subtype 'load' operator
|
# Subtype 'load' operator
|
||||||
class SACTSubtype_load(bpy.types.Operator):
|
class SACTSubtype_load(bpy.types.Operator):
|
||||||
bl_idname = "scene.sactsubtype_load"
|
bl_idname = "scene.sactsubtype_load"
|
||||||
|
@ -222,49 +231,46 @@ class SACTSubtype_load(bpy.types.Operator):
|
||||||
# Hide armature children
|
# Hide armature children
|
||||||
for object in linked_armature.children:
|
for object in linked_armature.children:
|
||||||
if object.name in context.scene.objects:
|
if object.name in context.scene.objects:
|
||||||
object.hide = True
|
object.hide_set(True)
|
||||||
|
|
||||||
# Hide all meshes (incl overlays)
|
# Hide all meshes (incl overlays)
|
||||||
for subtype_data in actor_data.subtypes:
|
for subtype_data in actor_data.subtypes:
|
||||||
if subtype_data.linked_mesh in bpy.data.objects:
|
if subtype_data.linked_mesh in bpy.data.objects:
|
||||||
mesh = bpy.data.objects[subtype_data.linked_mesh]
|
mesh = bpy.data.objects[subtype_data.linked_mesh]
|
||||||
if mesh.name in context.scene.objects:
|
if mesh.name in context.scene.objects:
|
||||||
mesh.hide = True
|
mesh.hide_set(True)
|
||||||
for overlay in subtype_data.overlays:
|
for overlay in subtype_data.overlays:
|
||||||
if overlay.linked_mesh in bpy.data.objects:
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
mesh = bpy.data.objects[overlay.linked_mesh]
|
mesh = bpy.data.objects[overlay.linked_mesh]
|
||||||
if mesh.name in context.scene.objects:
|
if mesh.name in context.scene.objects:
|
||||||
mesh.hide = True
|
mesh.hide_set(True)
|
||||||
|
|
||||||
# Hide/Show selected attachment meshes
|
# Hide/Show selected attachment meshes
|
||||||
for attachment in actor_data.attachments:
|
for attachment in actor_data.attachments:
|
||||||
if attachment.linked_mesh in bpy.data.objects:
|
if attachment.linked_mesh in bpy.data.objects:
|
||||||
mesh_obj = bpy.data.objects[attachment.linked_mesh]
|
mesh_obj = bpy.data.objects[attachment.linked_mesh]
|
||||||
if mesh_obj.name in context.scene.objects:
|
if mesh_obj.name in context.scene.objects:
|
||||||
mesh_obj.hide = not attachment.show_attachment
|
mesh_obj.hide_set(not attachment.show_attachment)
|
||||||
attachment_armature = linked_armature
|
attachment_armature = linked_armature
|
||||||
if attachment.linked_armature in bpy.data.objects:
|
if attachment.linked_armature in bpy.data.objects:
|
||||||
attachment_armature = bpy.data.objects[attachment.linked_armature]
|
attachment_armature = bpy.data.objects[attachment.linked_armature]
|
||||||
if mesh_obj != attachment_armature:
|
if mesh_obj != attachment_armature:
|
||||||
mesh_obj.parent = attachment_armature
|
parent_armature(mesh_obj, attachment_armature)
|
||||||
mesh_obj.parent_type = 'ARMATURE'
|
|
||||||
|
|
||||||
# Show only the chosen subtype (and selected overlays)
|
# Show only the chosen subtype (and selected overlays)
|
||||||
if subtype.linked_mesh in bpy.data.objects:
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
if subtype.show_mesh:
|
if subtype.show_mesh:
|
||||||
mesh_obj.hide = False
|
mesh_obj.hide_set(False)
|
||||||
if mesh_obj != linked_armature:
|
if mesh_obj != linked_armature:
|
||||||
mesh_obj.parent = linked_armature
|
parent_armature(mesh_obj, linked_armature)
|
||||||
mesh_obj.parent_type = 'ARMATURE'
|
|
||||||
for overlay in subtype.overlays:
|
for overlay in subtype.overlays:
|
||||||
if overlay.linked_mesh in bpy.data.objects:
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
||||||
if overlay.show_overlay:
|
if overlay.show_overlay:
|
||||||
mesh_obj.hide = False
|
mesh_obj.hide_set(False)
|
||||||
if mesh_obj != linked_armature:
|
if mesh_obj != linked_armature:
|
||||||
mesh_obj.parent = linked_armature
|
parent_armature(mesh_obj, linked_armature)
|
||||||
mesh_obj.parent_type = 'ARMATURE'
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,24 @@
|
||||||
from . import SACTSubtype, SACTAction, ANIM
|
from . import SACTSubtype, SACTAction, ANIM
|
||||||
from .. import hmdl
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import bpy.path
|
import bpy.path
|
||||||
import re
|
import re
|
||||||
import os.path
|
|
||||||
import posixpath
|
|
||||||
import struct
|
import struct
|
||||||
from mathutils import Vector, Quaternion, Euler
|
from mathutils import Vector, Quaternion, Euler
|
||||||
|
|
||||||
# Actor data class
|
# Actor data class
|
||||||
class SACTData(bpy.types.PropertyGroup):
|
class SACTData(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
subtypes =\
|
subtypes: bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
|
||||||
bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
|
active_subtype: bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
|
||||||
active_subtype =\
|
show_subtypes: bpy.props.BoolProperty()
|
||||||
bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
|
|
||||||
show_subtypes =\
|
|
||||||
bpy.props.BoolProperty()
|
|
||||||
|
|
||||||
attachments = \
|
attachments: bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List")
|
||||||
bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List")
|
active_attachment: bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
|
||||||
active_attachment = \
|
|
||||||
bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
|
|
||||||
|
|
||||||
actions =\
|
actions: bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
|
||||||
bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
|
active_action: bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
|
||||||
active_action =\
|
show_actions: bpy.props.BoolProperty()
|
||||||
bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
|
|
||||||
show_actions =\
|
|
||||||
bpy.props.BoolProperty()
|
|
||||||
|
|
||||||
# Regex RNA path matchers
|
# Regex RNA path matchers
|
||||||
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from bpy.app.handlers import persistent
|
||||||
from mathutils import Quaternion, Color
|
from mathutils import Quaternion, Color
|
||||||
import math
|
import math
|
||||||
import os.path
|
import os.path
|
||||||
from .. import Nodegrid, swld
|
from .. import swld
|
||||||
|
|
||||||
# Preview update func (for lighting preview)
|
# Preview update func (for lighting preview)
|
||||||
def preview_update(self, context):
|
def preview_update(self, context):
|
||||||
|
@ -13,30 +13,26 @@ def preview_update(self, context):
|
||||||
# Original Lightmaps
|
# Original Lightmaps
|
||||||
if area_data.lightmap_mode == 'ORIGINAL':
|
if area_data.lightmap_mode == 'ORIGINAL':
|
||||||
for material in bpy.data.materials:
|
for material in bpy.data.materials:
|
||||||
if material.hecl_lightmap:
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
material.use_shadeless = False
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
|
||||||
# Reference original game lightmaps
|
# Reference original game lightmaps
|
||||||
if material.hecl_lightmap in bpy.data.textures:
|
if material.hecl_lightmap in bpy.data.images:
|
||||||
img_name = material.hecl_lightmap
|
lm_node.image = bpy.data.images[material.hecl_lightmap]
|
||||||
if img_name in bpy.data.images:
|
else:
|
||||||
bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name]
|
lm_node.image = None
|
||||||
else:
|
|
||||||
bpy.data.textures[material.hecl_lightmap].image = None
|
|
||||||
|
|
||||||
# Cycles Lightmaps
|
# Cycles Lightmaps
|
||||||
elif area_data.lightmap_mode == 'CYCLES':
|
elif area_data.lightmap_mode == 'CYCLES':
|
||||||
for material in bpy.data.materials:
|
for material in bpy.data.materials:
|
||||||
if material.hecl_lightmap:
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
material.use_shadeless = False
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
|
||||||
# Reference newly-generated lightmaps
|
# Reference newly-generated lightmaps
|
||||||
if material.hecl_lightmap in bpy.data.textures:
|
img_name = material.hecl_lightmap + '_CYCLES'
|
||||||
img_name = material.hecl_lightmap + '_CYCLES'
|
if img_name in bpy.data.images:
|
||||||
if img_name in bpy.data.images:
|
lm_node.image = bpy.data.images[img_name]
|
||||||
bpy.data.textures[material.hecl_lightmap].image = bpy.data.images[img_name]
|
else:
|
||||||
else:
|
lm_node.image = None
|
||||||
bpy.data.textures[material.hecl_lightmap].image = None
|
|
||||||
# White Lightmaps
|
# White Lightmaps
|
||||||
elif area_data.lightmap_mode == 'NONE':
|
elif area_data.lightmap_mode == 'NONE':
|
||||||
img_name = 'NONE'
|
img_name = 'NONE'
|
||||||
|
@ -51,13 +47,11 @@ def preview_update(self, context):
|
||||||
img.file_format = 'PNG'
|
img.file_format = 'PNG'
|
||||||
|
|
||||||
for material in bpy.data.materials:
|
for material in bpy.data.materials:
|
||||||
if material.hecl_lightmap:
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
material.use_shadeless = False
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
|
||||||
# Reference NONE
|
# Reference NONE
|
||||||
if material.hecl_lightmap in bpy.data.textures:
|
lm_node.image = img
|
||||||
bpy.data.textures[material.hecl_lightmap].image = img
|
|
||||||
|
|
||||||
|
|
||||||
# Update lightmap output-resolution
|
# Update lightmap output-resolution
|
||||||
def set_lightmap_resolution(self, context):
|
def set_lightmap_resolution(self, context):
|
||||||
|
@ -153,17 +147,17 @@ def set_adjacent_area(self, context):
|
||||||
|
|
||||||
adjacent = dock_idx >= 0
|
adjacent = dock_idx >= 0
|
||||||
if len(context.scene.render.layers):
|
if len(context.scene.render.layers):
|
||||||
context.scene.render.layers[0].use_sky = not adjacent
|
context.scene.view_layers[0].use_sky = not adjacent
|
||||||
|
|
||||||
# Remove linked lamps and show/hide locals
|
# Remove linked lamps and show/hide locals
|
||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
if obj.library is not None and (obj.type == 'LAMP' or obj.type == 'MESH'):
|
if obj.library is not None and (obj.type == 'LIGHT' or obj.type == 'MESH'):
|
||||||
try:
|
try:
|
||||||
context.scene.objects.unlink(obj)
|
context.scene.collection.children.unlink(obj)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
continue
|
continue
|
||||||
if obj.type == 'LAMP':
|
if obj.type == 'LIGHT':
|
||||||
obj.hide_render = adjacent
|
obj.hide_render = adjacent
|
||||||
|
|
||||||
# Remove linked scenes
|
# Remove linked scenes
|
||||||
|
@ -200,8 +194,8 @@ def set_adjacent_area(self, context):
|
||||||
bpy.data.scenes.remove(other_scene)
|
bpy.data.scenes.remove(other_scene)
|
||||||
return
|
return
|
||||||
for obj in other_scene.objects:
|
for obj in other_scene.objects:
|
||||||
if (obj.type == 'LAMP' or obj.type == 'MESH') and obj.layers[0]:
|
if (obj.type == 'LIGHT' or obj.type == 'MESH') and obj.layers[0]:
|
||||||
context.scene.objects.link(obj)
|
context.scene.collection.objects.link(obj)
|
||||||
obj.hide_render = False
|
obj.hide_render = False
|
||||||
|
|
||||||
# Ensure filepaths target the current dock index
|
# Ensure filepaths target the current dock index
|
||||||
|
@ -212,7 +206,7 @@ def set_adjacent_area(self, context):
|
||||||
|
|
||||||
# Area data class
|
# Area data class
|
||||||
class SREAData(bpy.types.PropertyGroup):
|
class SREAData(bpy.types.PropertyGroup):
|
||||||
lightmap_resolution = bpy.props.EnumProperty(name="HECL Area Lightmap Resolution",
|
lightmap_resolution: bpy.props.EnumProperty(name="HECL Area Lightmap Resolution",
|
||||||
description="Set square resolution to use when rendering new lightmaps",
|
description="Set square resolution to use when rendering new lightmaps",
|
||||||
items=[
|
items=[
|
||||||
('256', "256", "256x256 (original quality)"),
|
('256', "256", "256x256 (original quality)"),
|
||||||
|
@ -223,7 +217,7 @@ class SREAData(bpy.types.PropertyGroup):
|
||||||
update=set_lightmap_resolution,
|
update=set_lightmap_resolution,
|
||||||
default='1024')
|
default='1024')
|
||||||
|
|
||||||
lightmap_mode = bpy.props.EnumProperty(name="HECL Area Lightmap Mode",
|
lightmap_mode: bpy.props.EnumProperty(name="HECL Area Lightmap Mode",
|
||||||
description="Simple way to manipulate all lightmap-using materials",
|
description="Simple way to manipulate all lightmap-using materials",
|
||||||
items=[
|
items=[
|
||||||
('NONE', "None", "Pure white lightmaps"),
|
('NONE', "None", "Pure white lightmaps"),
|
||||||
|
@ -232,7 +226,7 @@ class SREAData(bpy.types.PropertyGroup):
|
||||||
update=preview_update,
|
update=preview_update,
|
||||||
default='ORIGINAL')
|
default='ORIGINAL')
|
||||||
|
|
||||||
adjacent_area = bpy.props.IntProperty(name="HECL Adjacent Area Lightmap",
|
adjacent_area: bpy.props.IntProperty(name="HECL Adjacent Area Lightmap",
|
||||||
description="Dock index of adjacent area to render, or -1 for local lights",
|
description="Dock index of adjacent area to render, or -1 for local lights",
|
||||||
update=set_adjacent_area,
|
update=set_adjacent_area,
|
||||||
default=-1,
|
default=-1,
|
||||||
|
@ -296,179 +290,6 @@ def get_de_sockets(chain):
|
||||||
|
|
||||||
return found_mul, found_add
|
return found_mul, found_add
|
||||||
|
|
||||||
# Get texture node from node
|
|
||||||
def tex_node_from_node(node):
|
|
||||||
if node.type == 'TEXTURE':
|
|
||||||
return node
|
|
||||||
elif node.type == 'MIX_RGB':
|
|
||||||
if node.inputs[1].is_linked and node.inputs[1].links[0].from_node.type == 'TEXTURE':
|
|
||||||
return node.inputs[1].links[0].from_node
|
|
||||||
if node.inputs[2].is_linked and node.inputs[2].links[0].from_node.type == 'TEXTURE':
|
|
||||||
return node.inputs[2].links[0].from_node
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Delete existing cycles nodes and convert from GLSL nodes
|
|
||||||
CYCLES_TYPES = {'OUTPUT_MATERIAL', 'ADD_SHADER', 'BSDF_DIFFUSE', 'BSDF_TRANSPARENT',
|
|
||||||
'EMISSION', 'MIX_SHADER', 'TEX_IMAGE'}
|
|
||||||
def initialize_nodetree_cycles(mat, area_data):
|
|
||||||
nt = mat.node_tree
|
|
||||||
to_remove = set()
|
|
||||||
for node in nt.nodes:
|
|
||||||
if node.type in CYCLES_TYPES:
|
|
||||||
to_remove.add(node)
|
|
||||||
if node.parent:
|
|
||||||
to_remove.add(node.parent)
|
|
||||||
for node in to_remove:
|
|
||||||
nt.nodes.remove(node)
|
|
||||||
|
|
||||||
gridder = Nodegrid.Nodegrid(nt, cycles=True)
|
|
||||||
|
|
||||||
if mat.hecl_lightmap and not mat.library:
|
|
||||||
# Get name of lightmap texture
|
|
||||||
if mat.hecl_lightmap in bpy.data.textures:
|
|
||||||
img_name = mat.hecl_lightmap
|
|
||||||
if img_name in bpy.data.images:
|
|
||||||
bpy.data.textures[mat.hecl_lightmap].image = bpy.data.images[img_name]
|
|
||||||
else:
|
|
||||||
bpy.data.textures[mat.hecl_lightmap].image = None
|
|
||||||
|
|
||||||
# Get image already established or make new one
|
|
||||||
new_image = make_or_load_cycles_image(mat, area_data)
|
|
||||||
|
|
||||||
image_out_node = nt.nodes.new('ShaderNodeTexImage')
|
|
||||||
image_out_node.name = 'CYCLES_OUT'
|
|
||||||
gridder.place_node(image_out_node, 3)
|
|
||||||
image_out_node.image = new_image
|
|
||||||
|
|
||||||
if mat.game_settings.alpha_blend == 'ADD':
|
|
||||||
transp = nt.nodes.new('ShaderNodeBsdfTransparent')
|
|
||||||
gridder.place_node(transp, 2)
|
|
||||||
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
|
|
||||||
gridder.place_node(material_output, 3)
|
|
||||||
nt.links.new(transp.outputs[0], material_output.inputs[0])
|
|
||||||
|
|
||||||
elif mat.game_settings.alpha_blend == 'ALPHA':
|
|
||||||
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
|
|
||||||
gridder.place_node(diffuse, 2)
|
|
||||||
transp = nt.nodes.new('ShaderNodeBsdfTransparent')
|
|
||||||
gridder.place_node(transp, 2)
|
|
||||||
mix_shader = nt.nodes.new('ShaderNodeMixShader')
|
|
||||||
gridder.place_node(mix_shader, 2)
|
|
||||||
nt.links.new(transp.outputs[0], mix_shader.inputs[1])
|
|
||||||
nt.links.new(diffuse.outputs[0], mix_shader.inputs[2])
|
|
||||||
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
|
|
||||||
gridder.place_node(material_output, 3)
|
|
||||||
nt.links.new(mix_shader.outputs[0], material_output.inputs[0])
|
|
||||||
|
|
||||||
# Classify connected transparent textures
|
|
||||||
chain = recursive_build_material_chain(nt.nodes['Output'])
|
|
||||||
if chain:
|
|
||||||
diffuse_soc, emissive_soc = get_de_sockets(chain)
|
|
||||||
tex_node = tex_node_from_node(diffuse_soc.node)
|
|
||||||
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
|
|
||||||
diffuse_image_node = nt.nodes.new('ShaderNodeTexImage')
|
|
||||||
gridder.place_node(diffuse_image_node, 1)
|
|
||||||
diffuse_image_node.image = tex_node.texture.image
|
|
||||||
mixrgb_node = nt.nodes.new('ShaderNodeMixRGB')
|
|
||||||
gridder.place_node(mixrgb_node, 1)
|
|
||||||
mixrgb_node.inputs[1].default_value = (1.0,1.0,1.0,1.0)
|
|
||||||
mapping = nt.nodes.new('ShaderNodeUVMap')
|
|
||||||
gridder.place_node(mapping, 1)
|
|
||||||
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
|
|
||||||
nt.links.new(mapping.outputs[0], diffuse_image_node.inputs[0])
|
|
||||||
light_path = nt.nodes.new('ShaderNodeLightPath')
|
|
||||||
gridder.place_node(light_path, 2)
|
|
||||||
mixrgb_reflect = nt.nodes.new('ShaderNodeMixRGB')
|
|
||||||
gridder.place_node(mixrgb_reflect, 2)
|
|
||||||
nt.links.new(light_path.outputs['Is Reflection Ray'], mixrgb_reflect.inputs[0])
|
|
||||||
mixrgb_reflect.inputs[1].default_value = (1.0,1.0,1.0,1.0)
|
|
||||||
nt.links.new(diffuse_image_node.outputs[0], mixrgb_reflect.inputs[2])
|
|
||||||
nt.links.new(mixrgb_reflect.outputs[0], diffuse.inputs[0])
|
|
||||||
nt.links.new(mixrgb_reflect.outputs[0], mixrgb_node.inputs[2])
|
|
||||||
if nt.nodes['Output'].inputs[1].is_linked:
|
|
||||||
nt.links.new(nt.nodes['Output'].inputs[1].links[0].from_socket, mix_shader.inputs[0])
|
|
||||||
nt.links.new(nt.nodes['Output'].inputs[1].links[0].from_socket, mixrgb_node.inputs[0])
|
|
||||||
nt.links.new(mixrgb_node.outputs[0], transp.inputs[0])
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Classify connected opaque textures
|
|
||||||
chain = recursive_build_material_chain(nt.nodes['Output'])
|
|
||||||
if chain:
|
|
||||||
diffuse = None
|
|
||||||
emissive = None
|
|
||||||
diffuse_soc, emissive_soc = get_de_sockets(chain)
|
|
||||||
if diffuse_soc:
|
|
||||||
tex_node = tex_node_from_node(diffuse_soc.node)
|
|
||||||
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
|
|
||||||
diffuse_image_node = nt.nodes.new('ShaderNodeTexImage')
|
|
||||||
gridder.place_node(diffuse_image_node, 1)
|
|
||||||
diffuse_image_node.image = tex_node.texture.image
|
|
||||||
mapping = nt.nodes.new('ShaderNodeUVMap')
|
|
||||||
gridder.place_node(mapping, 1)
|
|
||||||
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
|
|
||||||
nt.links.new(mapping.outputs[0], diffuse_image_node.inputs[0])
|
|
||||||
light_path = nt.nodes.new('ShaderNodeLightPath')
|
|
||||||
gridder.place_node(light_path, 2)
|
|
||||||
mixrgb_reflect = nt.nodes.new('ShaderNodeMixRGB')
|
|
||||||
gridder.place_node(mixrgb_reflect, 2)
|
|
||||||
nt.links.new(light_path.outputs['Is Reflection Ray'], mixrgb_reflect.inputs[0])
|
|
||||||
mixrgb_reflect.inputs[1].default_value = (1.0,1.0,1.0,1.0)
|
|
||||||
nt.links.new(diffuse_image_node.outputs[0], mixrgb_reflect.inputs[2])
|
|
||||||
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
|
|
||||||
gridder.place_node(diffuse, 2)
|
|
||||||
nt.links.new(mixrgb_reflect.outputs[0], diffuse.inputs[0])
|
|
||||||
else:
|
|
||||||
diffuse = nt.nodes.new('ShaderNodeBsdfDiffuse')
|
|
||||||
gridder.place_node(diffuse, 2)
|
|
||||||
if emissive_soc:
|
|
||||||
tex_node = tex_node_from_node(emissive_soc.node)
|
|
||||||
if tex_node and tex_node.inputs[0].links[0].from_socket.name == 'UV':
|
|
||||||
emissive_image_node = nt.nodes.new('ShaderNodeTexImage')
|
|
||||||
gridder.place_node(emissive_image_node, 1)
|
|
||||||
emissive_image_node.image = tex_node.texture.image
|
|
||||||
mapping = nt.nodes.new('ShaderNodeUVMap')
|
|
||||||
gridder.place_node(mapping, 1)
|
|
||||||
mapping.uv_map = tex_node.inputs[0].links[0].from_node.uv_layer
|
|
||||||
nt.links.new(mapping.outputs[0], emissive_image_node.inputs[0])
|
|
||||||
emissive = nt.nodes.new('ShaderNodeEmission')
|
|
||||||
gridder.place_node(emissive, 2)
|
|
||||||
nt.links.new(emissive_image_node.outputs[0], emissive.inputs[0])
|
|
||||||
|
|
||||||
material_output = nt.nodes.new('ShaderNodeOutputMaterial')
|
|
||||||
gridder.place_node(material_output, 3)
|
|
||||||
if diffuse and emissive:
|
|
||||||
shader_add = nt.nodes.new('ShaderNodeAddShader')
|
|
||||||
gridder.place_node(shader_add, 2)
|
|
||||||
nt.links.new(diffuse.outputs[0], shader_add.inputs[0])
|
|
||||||
nt.links.new(emissive.outputs[0], shader_add.inputs[1])
|
|
||||||
nt.links.new(shader_add.outputs[0], material_output.inputs[0])
|
|
||||||
elif diffuse_soc:
|
|
||||||
nt.links.new(diffuse.outputs[0], material_output.inputs[0])
|
|
||||||
elif emissive_soc:
|
|
||||||
nt.links.new(emissive.outputs[0], material_output.inputs[0])
|
|
||||||
|
|
||||||
# Lightmap setup operator
|
|
||||||
class SREAInitializeCycles(bpy.types.Operator):
|
|
||||||
bl_idname = "scene.hecl_area_initialize_cycles"
|
|
||||||
bl_label = "HECL Initialize Cycles"
|
|
||||||
bl_description = "Initialize Cycles nodes for lightmap baking (WILL DELETE EXISTING CYCLES NODES!)"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return context.scene is not None and context.scene.hecl_type == 'AREA'
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
area_data = context.scene.hecl_srea_data
|
|
||||||
|
|
||||||
# Iterate materials and setup cycles
|
|
||||||
for mat in bpy.data.materials:
|
|
||||||
if mat.use_nodes:
|
|
||||||
initialize_nodetree_cycles(mat, area_data)
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
return context.window_manager.invoke_confirm(self, event)
|
|
||||||
|
|
||||||
# Lookup the directory name of other area via dock link
|
# Lookup the directory name of other area via dock link
|
||||||
def get_other_area_name(op, bg_scene, dock_idx):
|
def get_other_area_name(op, bg_scene, dock_idx):
|
||||||
dock_conns = swld.build_dock_connections(bg_scene)
|
dock_conns = swld.build_dock_connections(bg_scene)
|
||||||
|
@ -501,7 +322,6 @@ def render_lightmaps(context):
|
||||||
pixel_size = int(area_data.lightmap_resolution)
|
pixel_size = int(area_data.lightmap_resolution)
|
||||||
|
|
||||||
# Mmm Cycles
|
# Mmm Cycles
|
||||||
context.scene.render.engine = 'CYCLES'
|
|
||||||
context.scene.render.bake.margin = pixel_size // 256
|
context.scene.render.bake.margin = pixel_size // 256
|
||||||
|
|
||||||
# Iterate materials and setup cycles
|
# Iterate materials and setup cycles
|
||||||
|
@ -547,8 +367,8 @@ class SREARenderLightmaps(bpy.types.Operator):
|
||||||
if not context.selected_objects:
|
if not context.selected_objects:
|
||||||
for obj in context.scene.objects:
|
for obj in context.scene.objects:
|
||||||
if obj.type == 'MESH' and not obj.library:
|
if obj.type == 'MESH' and not obj.library:
|
||||||
obj.select = True
|
obj.select_set(True)
|
||||||
context.scene.objects.active = obj
|
context.view_layer.objects.active = obj
|
||||||
|
|
||||||
render_lightmaps(context)
|
render_lightmaps(context)
|
||||||
|
|
||||||
|
@ -559,7 +379,6 @@ def shadeless_material(idx):
|
||||||
if name in bpy.data.materials:
|
if name in bpy.data.materials:
|
||||||
return bpy.data.materials[name]
|
return bpy.data.materials[name]
|
||||||
mat = bpy.data.materials.new(name)
|
mat = bpy.data.materials.new(name)
|
||||||
mat.use_shadeless = True
|
|
||||||
r = idx % 256
|
r = idx % 256
|
||||||
g = (idx % 65536) // 256
|
g = (idx % 65536) // 256
|
||||||
b = idx // 65536
|
b = idx // 65536
|
||||||
|
@ -567,11 +386,11 @@ def shadeless_material(idx):
|
||||||
return mat
|
return mat
|
||||||
|
|
||||||
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
|
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
|
||||||
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
|
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
|
||||||
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
|
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
|
||||||
|
|
||||||
# Render PVS for location
|
# Render PVS for location
|
||||||
|
@ -585,7 +404,6 @@ def render_pvs(pathOut, location):
|
||||||
bpy.context.scene.render.use_sss = False
|
bpy.context.scene.render.use_sss = False
|
||||||
bpy.context.scene.render.use_envmaps = False
|
bpy.context.scene.render.use_envmaps = False
|
||||||
bpy.context.scene.render.use_raytrace = False
|
bpy.context.scene.render.use_raytrace = False
|
||||||
bpy.context.scene.render.engine = 'BLENDER_RENDER'
|
|
||||||
bpy.context.scene.display_settings.display_device = 'None'
|
bpy.context.scene.display_settings.display_device = 'None'
|
||||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||||
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
|
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
|
||||||
|
@ -593,7 +411,7 @@ def render_pvs(pathOut, location):
|
||||||
|
|
||||||
cam = bpy.data.cameras.new('CUBIC_CAM')
|
cam = bpy.data.cameras.new('CUBIC_CAM')
|
||||||
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
|
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
|
||||||
bpy.context.scene.objects.link(cam_obj)
|
bpy.context.scene.collection.objects.link(cam_obj)
|
||||||
bpy.context.scene.camera = cam_obj
|
bpy.context.scene.camera = cam_obj
|
||||||
cam.lens_unit = 'FOV'
|
cam.lens_unit = 'FOV'
|
||||||
cam.angle = math.radians(90.0)
|
cam.angle = math.radians(90.0)
|
||||||
|
@ -617,7 +435,7 @@ def render_pvs(pathOut, location):
|
||||||
bpy.ops.render.render(write_still=True)
|
bpy.ops.render.render(write_still=True)
|
||||||
|
|
||||||
bpy.context.scene.camera = None
|
bpy.context.scene.camera = None
|
||||||
bpy.context.scene.objects.unlink(cam_obj)
|
#bpy.context.scene.objects.unlink(cam_obj)
|
||||||
bpy.data.objects.remove(cam_obj)
|
bpy.data.objects.remove(cam_obj)
|
||||||
bpy.data.cameras.remove(cam)
|
bpy.data.cameras.remove(cam)
|
||||||
|
|
||||||
|
@ -634,16 +452,15 @@ def cook(writebuffunc, platform, endianchar):
|
||||||
# Panel draw
|
# Panel draw
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
area_data = context.scene.hecl_srea_data
|
area_data = context.scene.hecl_srea_data
|
||||||
layout.label("Lighting:", icon='LAMP_SPOT')
|
layout.label(text="Lighting:", icon='LIGHT')
|
||||||
light_row = layout.row(align=True)
|
light_row = layout.row(align=True)
|
||||||
light_row.prop_enum(area_data, 'lightmap_mode', 'NONE')
|
light_row.prop_enum(area_data, 'lightmap_mode', 'NONE')
|
||||||
light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL')
|
light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL')
|
||||||
light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES')
|
light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES')
|
||||||
layout.prop(area_data, 'lightmap_resolution', text="Resolution")
|
layout.prop(area_data, 'lightmap_resolution', text="Resolution")
|
||||||
layout.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label)
|
layout.popover("CYCLES_PT_sampling_presets", text=bpy.types.CYCLES_PT_sampling_presets.bl_label)
|
||||||
layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking")
|
layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking")
|
||||||
layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='OOPS')
|
layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='MOD_OPACITY')
|
||||||
layout.operator("scene.hecl_area_initialize_cycles", text="Initialize Cycles Nodes", icon='NODETREE')
|
|
||||||
layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL')
|
layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL')
|
||||||
layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK')
|
layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK')
|
||||||
|
|
||||||
|
@ -656,7 +473,6 @@ def scene_loaded(dummy):
|
||||||
# Registration
|
# Registration
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(SREAData)
|
bpy.utils.register_class(SREAData)
|
||||||
bpy.utils.register_class(SREAInitializeCycles)
|
|
||||||
bpy.utils.register_class(SREARenderLightmaps)
|
bpy.utils.register_class(SREARenderLightmaps)
|
||||||
bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData)
|
bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData)
|
||||||
bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name')
|
bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name')
|
||||||
|
@ -664,6 +480,5 @@ def register():
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(SREAData)
|
bpy.utils.unregister_class(SREAData)
|
||||||
bpy.utils.unregister_class(SREAInitializeCycles)
|
|
||||||
bpy.utils.unregister_class(SREARenderLightmaps)
|
bpy.utils.unregister_class(SREARenderLightmaps)
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,9 @@ def cook(writebuf):
|
||||||
for ch in dock_list:
|
for ch in dock_list:
|
||||||
if len(ch.data.vertices) < 4:
|
if len(ch.data.vertices) < 4:
|
||||||
raise RuntimeError('Not enough vertices in dock %s' % ch.name)
|
raise RuntimeError('Not enough vertices in dock %s' % ch.name)
|
||||||
wmtx = wmtx_inv * ch.matrix_world
|
wmtx = wmtx_inv @ ch.matrix_world
|
||||||
for vi in range(4):
|
for vi in range(4):
|
||||||
v = wmtx * ch.data.vertices[vi].co
|
v = wmtx @ ch.data.vertices[vi].co
|
||||||
writebuf(struct.pack('fff', v[0], v[1], v[2]))
|
writebuf(struct.pack('fff', v[0], v[1], v[2]))
|
||||||
if ch.name in dock_conns:
|
if ch.name in dock_conns:
|
||||||
conn_dock = dock_conns[ch.name]
|
conn_dock = dock_conns[ch.name]
|
||||||
|
|
|
@ -65,14 +65,14 @@ class PathHasher:
|
||||||
# If there's a third argument, use it as the .zip path containing the addon
|
# If there's a third argument, use it as the .zip path containing the addon
|
||||||
did_install = False
|
did_install = False
|
||||||
if len(args) >= 4 and args[3] != 'SKIPINSTALL':
|
if len(args) >= 4 and args[3] != 'SKIPINSTALL':
|
||||||
bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[3])
|
bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=args[3])
|
||||||
bpy.ops.wm.addon_refresh()
|
bpy.ops.preferences.addon_refresh()
|
||||||
did_install = True
|
did_install = True
|
||||||
|
|
||||||
# Make addon available to commands
|
# Make addon available to commands
|
||||||
if bpy.context.user_preferences.addons.find('hecl') == -1:
|
if bpy.context.preferences.addons.find('hecl') == -1:
|
||||||
try:
|
try:
|
||||||
bpy.ops.wm.addon_enable(module='hecl')
|
bpy.ops.preferences.addon_enable(module='hecl')
|
||||||
bpy.ops.wm.save_userpref()
|
bpy.ops.wm.save_userpref()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
@ -93,16 +93,6 @@ ackbytes = readpipestr()
|
||||||
if ackbytes != b'ACK':
|
if ackbytes != b'ACK':
|
||||||
quitblender()
|
quitblender()
|
||||||
|
|
||||||
# slerp branch check
|
|
||||||
bpy.ops.mesh.primitive_cube_add()
|
|
||||||
orig_rot = bpy.context.object.rotation_mode
|
|
||||||
try:
|
|
||||||
bpy.context.object.rotation_mode = 'QUATERNION_SLERP'
|
|
||||||
writepipestr(b'SLERP1')
|
|
||||||
except:
|
|
||||||
writepipestr(b'SLERP0')
|
|
||||||
bpy.context.object.rotation_mode = orig_rot
|
|
||||||
|
|
||||||
# Count brackets
|
# Count brackets
|
||||||
def count_brackets(linestr):
|
def count_brackets(linestr):
|
||||||
bracket_count = 0
|
bracket_count = 0
|
||||||
|
@ -184,15 +174,15 @@ def writelight(obj):
|
||||||
if obj.data.type == 'POINT':
|
if obj.data.type == 'POINT':
|
||||||
type = 2
|
type = 2
|
||||||
hasFalloff = True
|
hasFalloff = True
|
||||||
castShadow = obj.data.shadow_method != 'NOSHADOW'
|
castShadow = obj.data.use_shadow
|
||||||
elif obj.data.type == 'SPOT':
|
elif obj.data.type == 'SPOT':
|
||||||
type = 3
|
type = 3
|
||||||
hasFalloff = True
|
hasFalloff = True
|
||||||
spotCutoff = obj.data.spot_size
|
spotCutoff = obj.data.spot_size
|
||||||
castShadow = obj.data.shadow_method != 'NOSHADOW'
|
castShadow = obj.data.use_shadow
|
||||||
elif obj.data.type == 'SUN':
|
elif obj.data.type == 'SUN':
|
||||||
type = 1
|
type = 1
|
||||||
castShadow = obj.data.shadow_method != 'NOSHADOW'
|
castShadow = obj.data.use_shadow
|
||||||
|
|
||||||
constant = 1.0
|
constant = 1.0
|
||||||
linear = 0.0
|
linear = 0.0
|
||||||
|
@ -236,11 +226,11 @@ def dataout_loop():
|
||||||
elif cmdargs[0] == 'LIGHTLIST':
|
elif cmdargs[0] == 'LIGHTLIST':
|
||||||
lightCount = 0
|
lightCount = 0
|
||||||
for obj in bpy.context.scene.objects:
|
for obj in bpy.context.scene.objects:
|
||||||
if obj.type == 'LAMP' and not obj.library:
|
if obj.type == 'LIGHT' and not obj.library:
|
||||||
lightCount += 1
|
lightCount += 1
|
||||||
writepipebuf(struct.pack('I', lightCount))
|
writepipebuf(struct.pack('I', lightCount))
|
||||||
for obj in bpy.context.scene.objects:
|
for obj in bpy.context.scene.objects:
|
||||||
if obj.type == 'LAMP' and not obj.library:
|
if obj.type == 'LIGHT' and not obj.library:
|
||||||
writepipestr(obj.name.encode())
|
writepipestr(obj.name.encode())
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHAABB':
|
elif cmdargs[0] == 'MESHAABB':
|
||||||
|
@ -248,27 +238,24 @@ def dataout_loop():
|
||||||
hecl.mesh_aabb(writepipebuf)
|
hecl.mesh_aabb(writepipebuf)
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILE':
|
elif cmdargs[0] == 'MESHCOMPILE':
|
||||||
maxSkinBanks = int(cmdargs[2])
|
|
||||||
|
|
||||||
meshName = bpy.context.scene.hecl_mesh_obj
|
meshName = bpy.context.scene.hecl_mesh_obj
|
||||||
if meshName not in bpy.data.objects:
|
if meshName not in bpy.data.objects:
|
||||||
writepipestr(('mesh %s not found' % meshName).encode())
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
writepipestr(b'OK')
|
writepipestr(b'OK')
|
||||||
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[1], maxSkinBanks)
|
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName])
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILENAME':
|
elif cmdargs[0] == 'MESHCOMPILENAME':
|
||||||
meshName = cmdargs[1]
|
meshName = cmdargs[1]
|
||||||
maxSkinBanks = int(cmdargs[3])
|
useLuv = int(cmdargs[2])
|
||||||
useLuv = int(cmdargs[4])
|
|
||||||
|
|
||||||
if meshName not in bpy.data.objects:
|
if meshName not in bpy.data.objects:
|
||||||
writepipestr(('mesh %s not found' % meshName).encode())
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
writepipestr(b'OK')
|
writepipestr(b'OK')
|
||||||
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[2], maxSkinBanks, useLuv)
|
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], useLuv)
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
|
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
|
||||||
meshName = cmdargs[1]
|
meshName = cmdargs[1]
|
||||||
|
@ -293,25 +280,6 @@ def dataout_loop():
|
||||||
if obj.type == 'MESH' and not obj.library:
|
if obj.type == 'MESH' and not obj.library:
|
||||||
hecl.hmdl.cookcol(writepipebuf, obj)
|
hecl.hmdl.cookcol(writepipebuf, obj)
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILEALL':
|
|
||||||
maxSkinBanks = int(cmdargs[2])
|
|
||||||
maxOctantLength = float(cmdargs[3])
|
|
||||||
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
|
||||||
join_mesh = bpy.data.meshes.new('JOIN_MESH')
|
|
||||||
join_obj = bpy.data.object.new(join_mesh.name, join_mesh)
|
|
||||||
bpy.context.scene.objects.link(join_obj)
|
|
||||||
bpy.ops.object.select_by_type(type='MESH')
|
|
||||||
bpy.context.scene.objects.active = join_obj
|
|
||||||
bpy.ops.object.join()
|
|
||||||
|
|
||||||
writepipestr(b'OK')
|
|
||||||
hecl.hmdl.cook(writepipebuf, join_obj, cmdargs[1], maxSkinBanks, maxOctantLength)
|
|
||||||
|
|
||||||
bpy.context.scene.objects.unlink(join_obj)
|
|
||||||
bpy.data.objects.remove(join_obj)
|
|
||||||
bpy.data.meshes.remove(join_mesh)
|
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILEPATH':
|
elif cmdargs[0] == 'MESHCOMPILEPATH':
|
||||||
meshName = bpy.context.scene.hecl_path_obj
|
meshName = bpy.context.scene.hecl_path_obj
|
||||||
if meshName not in bpy.data.objects:
|
if meshName not in bpy.data.objects:
|
||||||
|
@ -342,7 +310,7 @@ def dataout_loop():
|
||||||
lampCount = 0
|
lampCount = 0
|
||||||
firstSpot = None
|
firstSpot = None
|
||||||
for obj in bpy.context.scene.objects:
|
for obj in bpy.context.scene.objects:
|
||||||
if obj.type == 'LAMP':
|
if obj.type == 'LIGHT':
|
||||||
lampCount += 1
|
lampCount += 1
|
||||||
if firstSpot is None and obj.data.type == 'SPOT':
|
if firstSpot is None and obj.data.type == 'SPOT':
|
||||||
firstSpot = obj
|
firstSpot = obj
|
||||||
|
@ -375,7 +343,7 @@ def dataout_loop():
|
||||||
|
|
||||||
# Lamp objects
|
# Lamp objects
|
||||||
for obj in bpy.context.scene.objects:
|
for obj in bpy.context.scene.objects:
|
||||||
if obj != firstSpot and obj.type == 'LAMP':
|
if obj != firstSpot and obj.type == 'LIGHT':
|
||||||
writelight(obj)
|
writelight(obj)
|
||||||
|
|
||||||
elif cmdargs[0] == 'GETTEXTURES':
|
elif cmdargs[0] == 'GETTEXTURES':
|
||||||
|
@ -505,7 +473,7 @@ try:
|
||||||
else:
|
else:
|
||||||
bpy.ops.wm.read_homefile()
|
bpy.ops.wm.read_homefile()
|
||||||
loaded_blend = None
|
loaded_blend = None
|
||||||
bpy.context.user_preferences.filepaths.save_version = 0
|
bpy.context.preferences.filepaths.save_version = 0
|
||||||
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
|
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
|
||||||
bpy.ops.file.hecl_patching_load()
|
bpy.ops.file.hecl_patching_load()
|
||||||
bpy.context.scene.hecl_type = cmdargs[2]
|
bpy.context.scene.hecl_type = cmdargs[2]
|
||||||
|
@ -527,7 +495,7 @@ try:
|
||||||
writepipestr(b'FALSE')
|
writepipestr(b'FALSE')
|
||||||
|
|
||||||
elif cmdargs[0] == 'SAVE':
|
elif cmdargs[0] == 'SAVE':
|
||||||
bpy.context.user_preferences.filepaths.save_version = 0
|
bpy.context.preferences.filepaths.save_version = 0
|
||||||
print('SAVING %s' % loaded_blend)
|
print('SAVING %s' % loaded_blend)
|
||||||
if loaded_blend:
|
if loaded_blend:
|
||||||
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True):
|
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True):
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit b9de85440046dba056a55c85e9952ce36ccf8156
|
Subproject commit 9bbd7af9f6c5ff34b37752906b0d78295c44938d
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0f330c1f057ec3c190d6278370f6e1628df435ed
|
Subproject commit 3ad748f28b5377027c900ded38eeccdfeb7fe7f6
|
|
@ -1 +1 @@
|
||||||
Subproject commit 96c520ed950682d113953f570fbd10a485cc0e8a
|
Subproject commit 01bf9e65294f7abae61a84d5b855202565fe1267
|
|
@ -1,16 +1,24 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "hecl/Frontend.hpp"
|
#include "boo/graphicsdev/IGraphicsDataFactory.hpp"
|
||||||
|
|
||||||
namespace hecl::Backend {
|
namespace hecl::Backend {
|
||||||
struct ExtensionSlot;
|
struct ExtensionSlot;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
using IR = Frontend::IR;
|
enum class TexCoordSource : uint8_t {
|
||||||
using Diagnostics = Frontend::Diagnostics;
|
Invalid = 0xff,
|
||||||
using SourceLocation = Frontend::SourceLocation;
|
Position = 0,
|
||||||
using ArithmeticOp = IR::Instruction::ArithmeticOpType;
|
Normal = 1,
|
||||||
|
Tex0 = 2,
|
||||||
enum class TexGenSrc : uint8_t { Position, Normal, UV };
|
Tex1 = 3,
|
||||||
|
Tex2 = 4,
|
||||||
|
Tex3 = 5,
|
||||||
|
Tex4 = 6,
|
||||||
|
Tex5 = 7,
|
||||||
|
Tex6 = 8,
|
||||||
|
Tex7 = 9,
|
||||||
|
};
|
||||||
|
|
||||||
enum class BlendFactor : uint8_t {
|
enum class BlendFactor : uint8_t {
|
||||||
Zero,
|
Zero,
|
||||||
|
@ -28,25 +36,49 @@ enum class BlendFactor : uint8_t {
|
||||||
Original = 0xff
|
Original = 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr std::string_view BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
|
||||||
|
switch (factor) {
|
||||||
|
case BlendFactor::Zero:
|
||||||
|
return "ZERO"sv;
|
||||||
|
case BlendFactor::One:
|
||||||
|
return "ONE"sv;
|
||||||
|
case BlendFactor::SrcColor:
|
||||||
|
return "SRCCOLOR"sv;
|
||||||
|
case BlendFactor::InvSrcColor:
|
||||||
|
return "INVSRCCOLOR"sv;
|
||||||
|
case BlendFactor::DstColor:
|
||||||
|
return "DSTCOLOR"sv;
|
||||||
|
case BlendFactor::InvDstColor:
|
||||||
|
return "INVDSTCOLOR"sv;
|
||||||
|
case BlendFactor::SrcAlpha:
|
||||||
|
return "SRCALPHA"sv;
|
||||||
|
case BlendFactor::InvSrcAlpha:
|
||||||
|
return "INVSRCALPHA"sv;
|
||||||
|
case BlendFactor::DstAlpha:
|
||||||
|
return "DSTALPHA"sv;
|
||||||
|
case BlendFactor::InvDstAlpha:
|
||||||
|
return "INVDSTALPHA"sv;
|
||||||
|
case BlendFactor::SrcColor1:
|
||||||
|
return "SRCCOLOR1"sv;
|
||||||
|
case BlendFactor::InvSrcColor1:
|
||||||
|
return "INVSRCCOLOR1"sv;
|
||||||
|
default:
|
||||||
|
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
|
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
|
||||||
|
|
||||||
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
|
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
|
||||||
|
|
||||||
struct TextureInfo {
|
struct TextureInfo {
|
||||||
TexGenSrc src;
|
TexCoordSource src;
|
||||||
int mapIdx;
|
uint8_t mtxIdx;
|
||||||
int uvIdx;
|
|
||||||
int mtxIdx;
|
|
||||||
bool normalize;
|
bool normalize;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ReflectionType { None, Simple, Indirect };
|
enum class ReflectionType { None, Simple, Indirect };
|
||||||
|
|
||||||
class IBackend {
|
|
||||||
public:
|
|
||||||
virtual void reset(const IR& ir, Diagnostics& diag) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hash subclass for identifying shaders and their metadata
|
* @brief Hash subclass for identifying shaders and their metadata
|
||||||
*/
|
*/
|
||||||
|
@ -85,22 +117,6 @@ public:
|
||||||
m_alphaTest = alphaTest;
|
m_alphaTest = alphaTest;
|
||||||
hash ^= m_meta;
|
hash ^= m_meta;
|
||||||
}
|
}
|
||||||
ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
|
|
||||||
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
|
|
||||||
bool alphaTest)
|
|
||||||
: Hash(ir.m_hash) {
|
|
||||||
m_colorCount = c;
|
|
||||||
m_uvCount = u;
|
|
||||||
m_weightCount = w;
|
|
||||||
m_skinSlotCount = s;
|
|
||||||
m_primitiveType = uint8_t(pt);
|
|
||||||
m_reflectionType = uint8_t(reflectionType);
|
|
||||||
m_depthTest = depthTest;
|
|
||||||
m_depthWrite = depthWrite;
|
|
||||||
m_backfaceCulling = backfaceCulling;
|
|
||||||
m_alphaTest = alphaTest;
|
|
||||||
hash ^= m_meta;
|
|
||||||
}
|
|
||||||
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
|
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
|
||||||
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
|
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
|
||||||
bool alphaTest)
|
bool alphaTest)
|
||||||
|
@ -170,10 +186,7 @@ struct Function {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExtensionSlot {
|
struct ExtensionSlot {
|
||||||
Function lighting;
|
const char* shaderMacro;
|
||||||
Function post;
|
|
||||||
size_t blockCount = 0;
|
|
||||||
const char** blockNames = nullptr;
|
|
||||||
size_t texCount = 0;
|
size_t texCount = 0;
|
||||||
const Backend::TextureInfo* texs = nullptr;
|
const Backend::TextureInfo* texs = nullptr;
|
||||||
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
|
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
|
||||||
|
@ -188,7 +201,7 @@ struct ExtensionSlot {
|
||||||
bool forceAlphaTest = false;
|
bool forceAlphaTest = false;
|
||||||
bool diffuseOnly = false;
|
bool diffuseOnly = false;
|
||||||
|
|
||||||
ExtensionSlot(size_t blockCount = 0, const char** blockNames = nullptr, size_t texCount = 0,
|
ExtensionSlot(size_t texCount = 0,
|
||||||
const Backend::TextureInfo* texs = nullptr,
|
const Backend::TextureInfo* texs = nullptr,
|
||||||
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
|
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
|
||||||
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
|
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
|
||||||
|
@ -196,9 +209,7 @@ struct ExtensionSlot {
|
||||||
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
|
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
|
||||||
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
|
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
|
||||||
bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false)
|
bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false)
|
||||||
: blockCount(blockCount)
|
: texCount(texCount)
|
||||||
, blockNames(blockNames)
|
|
||||||
, texCount(texCount)
|
|
||||||
, texs(texs)
|
, texs(texs)
|
||||||
, srcFactor(srcFactor)
|
, srcFactor(srcFactor)
|
||||||
, dstFactor(dstFactor)
|
, dstFactor(dstFactor)
|
||||||
|
@ -216,10 +227,7 @@ struct ExtensionSlot {
|
||||||
void calculateHash() const {
|
void calculateHash() const {
|
||||||
XXH64_state_t st;
|
XXH64_state_t st;
|
||||||
XXH64_reset(&st, 0);
|
XXH64_reset(&st, 0);
|
||||||
if (!lighting.m_source.empty())
|
XXH64_update(&st, shaderMacro, strlen(shaderMacro));
|
||||||
XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size());
|
|
||||||
if (!post.m_source.empty())
|
|
||||||
XXH64_update(&st, post.m_source.data(), post.m_source.size());
|
|
||||||
for (size_t i = 0; i < texCount; ++i) {
|
for (size_t i = 0; i < texCount; ++i) {
|
||||||
const Backend::TextureInfo& tinfo = texs[i];
|
const Backend::TextureInfo& tinfo = texs[i];
|
||||||
XXH64_update(&st, &tinfo, sizeof(tinfo));
|
XXH64_update(&st, &tinfo, sizeof(tinfo));
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -20,15 +20,19 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "hecl/hecl.hpp"
|
#include "hecl/hecl.hpp"
|
||||||
#include "hecl/HMDLMeta.hpp"
|
#include "hecl/HMDLMeta.hpp"
|
||||||
|
#include "hecl/TypedVariant.hpp"
|
||||||
|
#include "hecl/Backend.hpp"
|
||||||
#include "athena/Types.hpp"
|
#include "athena/Types.hpp"
|
||||||
#include "athena/MemoryWriter.hpp"
|
#include "athena/MemoryWriter.hpp"
|
||||||
#include "optional.hpp"
|
#include "optional.hpp"
|
||||||
#include "Token.hpp"
|
#include "Token.hpp"
|
||||||
|
|
||||||
namespace hecl::blender {
|
namespace hecl::blender {
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
extern logvisor::Module BlenderLog;
|
extern logvisor::Module BlenderLog;
|
||||||
class HMDLBuffers;
|
class HMDLBuffers;
|
||||||
|
@ -103,6 +107,10 @@ struct Vector2f {
|
||||||
void read(Connection& conn);
|
void read(Connection& conn);
|
||||||
Vector2f(Connection& conn) { read(conn); }
|
Vector2f(Connection& conn) { read(conn); }
|
||||||
operator const atVec2f&() const { return val; }
|
operator const atVec2f&() const { return val; }
|
||||||
|
bool operator==(const Vector2f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct Vector3f {
|
struct Vector3f {
|
||||||
atVec3f val;
|
atVec3f val;
|
||||||
|
@ -110,6 +118,11 @@ struct Vector3f {
|
||||||
void read(Connection& conn);
|
void read(Connection& conn);
|
||||||
Vector3f(Connection& conn) { read(conn); }
|
Vector3f(Connection& conn) { read(conn); }
|
||||||
operator const atVec3f&() const { return val; }
|
operator const atVec3f&() const { return val; }
|
||||||
|
bool operator==(const Vector3f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1] &&
|
||||||
|
val.simd[2] == other.val.simd[2];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct Vector4f {
|
struct Vector4f {
|
||||||
atVec4f val;
|
atVec4f val;
|
||||||
|
@ -117,6 +130,12 @@ struct Vector4f {
|
||||||
void read(Connection& conn);
|
void read(Connection& conn);
|
||||||
Vector4f(Connection& conn) { read(conn); }
|
Vector4f(Connection& conn) { read(conn); }
|
||||||
operator const atVec4f&() const { return val; }
|
operator const atVec4f&() const { return val; }
|
||||||
|
bool operator==(const Vector4f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1] &&
|
||||||
|
val.simd[2] == other.val.simd[2] &&
|
||||||
|
val.simd[3] == other.val.simd[3];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct Matrix3f {
|
struct Matrix3f {
|
||||||
atVec3f m[3];
|
atVec3f m[3];
|
||||||
|
@ -137,26 +156,137 @@ struct Index {
|
||||||
Index(Connection& conn) { read(conn); }
|
Index(Connection& conn) { read(conn); }
|
||||||
operator const uint32_t&() const { return val; }
|
operator const uint32_t&() const { return val; }
|
||||||
};
|
};
|
||||||
|
struct Float {
|
||||||
|
float val;
|
||||||
|
Float() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Float(Connection& conn) { read(conn); }
|
||||||
|
operator const float&() const { return val; }
|
||||||
|
};
|
||||||
|
struct Boolean {
|
||||||
|
bool val;
|
||||||
|
Boolean() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Boolean(Connection& conn) { read(conn); }
|
||||||
|
operator const bool&() const { return val; }
|
||||||
|
};
|
||||||
|
|
||||||
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
|
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
|
||||||
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec);
|
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec);
|
||||||
|
|
||||||
/** HECL source and metadata of each material */
|
/** Intermediate material representation */
|
||||||
struct Material {
|
struct Material {
|
||||||
std::string name;
|
enum class ShaderType : uint32_t {
|
||||||
std::string source;
|
Invalid = 0,
|
||||||
std::vector<ProjectPath> texs;
|
RetroShader = 'RSHD',
|
||||||
std::unordered_map<std::string, int32_t> iprops;
|
RetroDynamicShader = 'RDYN',
|
||||||
bool transparent;
|
RetroDynamicAlphaShader = 'RDAL',
|
||||||
|
RetroDynamicCharacterShader = 'RCHR',
|
||||||
|
};
|
||||||
|
enum class ChunkType : uint32_t {
|
||||||
|
Invalid = 0,
|
||||||
|
TexturePass = 'PASS',
|
||||||
|
ColorPass = 'CLR ',
|
||||||
|
};
|
||||||
|
enum class PassType : uint32_t {
|
||||||
|
Invalid = 0,
|
||||||
|
Lightmap = 'LMAP',
|
||||||
|
Diffuse = 'DIFF',
|
||||||
|
Emissive = 'EMIS',
|
||||||
|
Specular = 'SPEC',
|
||||||
|
ExtendedSpecular = 'ESPC',
|
||||||
|
Reflection = 'REFL',
|
||||||
|
IndirectTex = 'INDR',
|
||||||
|
Alpha = 'ALPH',
|
||||||
|
};
|
||||||
|
static constexpr std::string_view PassTypeToString(PassType tp) {
|
||||||
|
switch (tp) {
|
||||||
|
case PassType::Lightmap: return "lightmap"sv;
|
||||||
|
case PassType::Diffuse: return "diffuse"sv;
|
||||||
|
case PassType::Emissive: return "emissive"sv;
|
||||||
|
case PassType::Specular: return "specular"sv;
|
||||||
|
case PassType::ExtendedSpecular: return "extendedSpecular"sv;
|
||||||
|
case PassType::Reflection: return "reflection"sv;
|
||||||
|
case PassType::Alpha: return "alpha"sv;
|
||||||
|
default:
|
||||||
|
assert(false && "Unknown pass type");
|
||||||
|
return ""sv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum class UVAnimType : uint8_t {
|
||||||
|
MvInvNoTranslation,
|
||||||
|
MvInv,
|
||||||
|
Scroll,
|
||||||
|
Rotation,
|
||||||
|
HStrip,
|
||||||
|
VStrip,
|
||||||
|
Model,
|
||||||
|
CylinderEnvironment,
|
||||||
|
Eight,
|
||||||
|
Invalid = UINT8_MAX
|
||||||
|
};
|
||||||
|
using TexCoordSource = hecl::Backend::TexCoordSource;
|
||||||
|
struct PASS : hecl::TypedRecord<ChunkType::TexturePass> {
|
||||||
|
PassType type;
|
||||||
|
ProjectPath tex;
|
||||||
|
TexCoordSource source;
|
||||||
|
UVAnimType uvAnimType;
|
||||||
|
std::array<float, 9> uvAnimParms = {};
|
||||||
|
bool alpha;
|
||||||
|
explicit PASS(Connection& conn);
|
||||||
|
bool operator==(const PASS& other) const {
|
||||||
|
return type == other.type &&
|
||||||
|
tex == other.tex &&
|
||||||
|
source == other.source &&
|
||||||
|
uvAnimType == other.uvAnimType &&
|
||||||
|
uvAnimParms == other.uvAnimParms &&
|
||||||
|
alpha == other.alpha;
|
||||||
|
}
|
||||||
|
void hash(XXH64_state_t* st) const {
|
||||||
|
XXH64_update(st, &type, sizeof(type));
|
||||||
|
XXH64_update(st, &source, sizeof(source));
|
||||||
|
XXH64_update(st, &uvAnimType, sizeof(uvAnimType));
|
||||||
|
XXH64_update(st, &alpha, sizeof(alpha));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct CLR : hecl::TypedRecord<ChunkType::ColorPass> {
|
||||||
|
PassType type;
|
||||||
|
Vector4f color;
|
||||||
|
explicit CLR(Connection& conn);
|
||||||
|
bool operator==(const CLR& other) const {
|
||||||
|
return type == other.type && color == other.color;
|
||||||
|
}
|
||||||
|
void hash(XXH64_state_t* st) const {
|
||||||
|
XXH64_update(st, &type, sizeof(type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using Chunk = hecl::TypedVariant<PASS, CLR>;
|
||||||
|
|
||||||
Material(Connection& conn);
|
enum class BlendMode : uint32_t {
|
||||||
|
Opaque = 0,
|
||||||
|
Alpha = 1,
|
||||||
|
Additive = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
uint32_t passIndex;
|
||||||
|
ShaderType shaderType;
|
||||||
|
std::vector<Chunk> chunks;
|
||||||
|
std::unordered_map<std::string, int32_t> iprops;
|
||||||
|
BlendMode blendMode = BlendMode::Opaque;
|
||||||
|
|
||||||
|
explicit Material(Connection& conn);
|
||||||
bool operator==(const Material& other) const {
|
bool operator==(const Material& other) const {
|
||||||
return source == other.source && texs == other.texs && iprops == other.iprops;
|
return chunks == other.chunks && iprops == other.iprops && blendMode == other.blendMode;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Intermediate mesh representation prepared by blender from a single mesh object */
|
/** Intermediate mesh representation prepared by blender from a single mesh object */
|
||||||
struct Mesh {
|
struct Mesh {
|
||||||
|
static constexpr size_t MaxColorLayers = 4;
|
||||||
|
static constexpr size_t MaxUVLayers = 8;
|
||||||
|
static constexpr size_t MaxSkinEntries = 16;
|
||||||
|
|
||||||
HMDLTopology topology;
|
HMDLTopology topology;
|
||||||
|
|
||||||
/* Object transform in scene */
|
/* Object transform in scene */
|
||||||
|
@ -181,19 +311,30 @@ struct Mesh {
|
||||||
/* Skinning data */
|
/* Skinning data */
|
||||||
std::vector<std::string> boneNames;
|
std::vector<std::string> boneNames;
|
||||||
struct SkinBind {
|
struct SkinBind {
|
||||||
uint32_t boneIdx;
|
uint32_t vg_idx = UINT32_MAX;
|
||||||
float weight;
|
float weight = 0.f;
|
||||||
SkinBind(Connection& conn);
|
SkinBind() = default;
|
||||||
|
explicit SkinBind(Connection& conn);
|
||||||
|
operator bool() const { return vg_idx != UINT32_MAX; }
|
||||||
};
|
};
|
||||||
std::vector<std::vector<SkinBind>> skins;
|
std::vector<std::array<SkinBind, MaxSkinEntries>> skins;
|
||||||
std::vector<size_t> contiguousSkinVertCounts;
|
std::vector<size_t> contiguousSkinVertCounts;
|
||||||
|
|
||||||
|
static size_t countSkinBinds(const std::array<SkinBind, MaxSkinEntries>& arr) {
|
||||||
|
size_t ret = 0;
|
||||||
|
for (const auto& b : arr)
|
||||||
|
if (b)
|
||||||
|
++ret;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
void normalizeSkinBinds();
|
void normalizeSkinBinds();
|
||||||
|
|
||||||
/** Islands of the same material/skinBank are represented here */
|
/** Islands of the same material/skinBank are represented here */
|
||||||
struct Surface {
|
struct Surface {
|
||||||
Vector3f centroid;
|
Vector3f centroid;
|
||||||
Index materialIdx;
|
uint32_t materialIdx;
|
||||||
Vector3f aabbMin;
|
Vector3f aabbMin;
|
||||||
Vector3f aabbMax;
|
Vector3f aabbMax;
|
||||||
Vector3f reflectionNormal;
|
Vector3f reflectionNormal;
|
||||||
|
@ -208,12 +349,9 @@ struct Mesh {
|
||||||
uint32_t iSkin = 0xffffffff;
|
uint32_t iSkin = 0xffffffff;
|
||||||
uint32_t iBankSkin = 0xffffffff;
|
uint32_t iBankSkin = 0xffffffff;
|
||||||
|
|
||||||
Vert(Connection& conn, const Mesh& parent);
|
|
||||||
bool operator==(const Vert& other) const;
|
bool operator==(const Vert& other) const;
|
||||||
};
|
};
|
||||||
std::vector<Vert> verts;
|
std::vector<Vert> verts;
|
||||||
|
|
||||||
Surface(Connection& conn, Mesh& parent, int skinSlotCount);
|
|
||||||
};
|
};
|
||||||
std::vector<Surface> surfaces;
|
std::vector<Surface> surfaces;
|
||||||
|
|
||||||
|
@ -225,15 +363,13 @@ struct Mesh {
|
||||||
std::vector<uint32_t> m_boneIdxs;
|
std::vector<uint32_t> m_boneIdxs;
|
||||||
|
|
||||||
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
|
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
|
||||||
size_t lookupLocalBoneIdx(uint32_t boneIdx) const;
|
|
||||||
};
|
};
|
||||||
std::vector<Bank> banks;
|
std::vector<Bank> banks;
|
||||||
std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
|
std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
|
||||||
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
|
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
|
||||||
} skinBanks;
|
} skinBanks;
|
||||||
|
|
||||||
using SurfProgFunc = std::function<void(int)>;
|
Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, bool useLuvs = false);
|
||||||
Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, SurfProgFunc& surfProg);
|
|
||||||
|
|
||||||
Mesh getContiguousSkinningVersion() const;
|
Mesh getContiguousSkinningVersion() const;
|
||||||
|
|
||||||
|
@ -486,11 +622,10 @@ public:
|
||||||
static const char* MeshOutputModeString(HMDLTopology topology);
|
static const char* MeshOutputModeString(HMDLTopology topology);
|
||||||
|
|
||||||
/** Compile mesh by context (MESH blends only) */
|
/** Compile mesh by context (MESH blends only) */
|
||||||
Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10, Mesh::SurfProgFunc surfProg = [](int) {});
|
Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10);
|
||||||
|
|
||||||
/** Compile mesh by name (AREA blends only) */
|
/** Compile mesh by name (AREA blends only) */
|
||||||
Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false,
|
Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false);
|
||||||
Mesh::SurfProgFunc surfProg = [](int) {});
|
|
||||||
|
|
||||||
/** Compile collision mesh by name (AREA blends only) */
|
/** Compile collision mesh by name (AREA blends only) */
|
||||||
ColMesh compileColMesh(std::string_view name);
|
ColMesh compileColMesh(std::string_view name);
|
||||||
|
@ -498,10 +633,6 @@ public:
|
||||||
/** Compile all meshes as collision meshes (CMESH blends only) */
|
/** Compile all meshes as collision meshes (CMESH blends only) */
|
||||||
std::vector<ColMesh> compileColMeshes();
|
std::vector<ColMesh> compileColMeshes();
|
||||||
|
|
||||||
/** Compile all meshes into one (AREA blends only) */
|
|
||||||
Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount = 10, float maxOctantLength = 5.0,
|
|
||||||
Mesh::SurfProgFunc surfProg = [](int) {});
|
|
||||||
|
|
||||||
/** Compile world intermediate (WORLD blends only) */
|
/** Compile world intermediate (WORLD blends only) */
|
||||||
World compileWorld();
|
World compileWorld();
|
||||||
|
|
||||||
|
@ -558,6 +689,8 @@ class Connection {
|
||||||
friend struct Matrix3f;
|
friend struct Matrix3f;
|
||||||
friend struct Matrix4f;
|
friend struct Matrix4f;
|
||||||
friend struct Index;
|
friend struct Index;
|
||||||
|
friend struct Float;
|
||||||
|
friend struct Boolean;
|
||||||
|
|
||||||
std::atomic_bool m_lock = {false};
|
std::atomic_bool m_lock = {false};
|
||||||
bool m_pyStreamActive = false;
|
bool m_pyStreamActive = false;
|
||||||
|
@ -571,7 +704,6 @@ class Connection {
|
||||||
#endif
|
#endif
|
||||||
int m_readpipe[2];
|
int m_readpipe[2];
|
||||||
int m_writepipe[2];
|
int m_writepipe[2];
|
||||||
bool m_hasSlerp;
|
|
||||||
BlendType m_loadedType = BlendType::None;
|
BlendType m_loadedType = BlendType::None;
|
||||||
bool m_loadedRigged = false;
|
bool m_loadedRigged = false;
|
||||||
ProjectPath m_loadedBlend;
|
ProjectPath m_loadedBlend;
|
||||||
|
@ -595,8 +727,6 @@ public:
|
||||||
Connection(Connection&&) = delete;
|
Connection(Connection&&) = delete;
|
||||||
Connection& operator=(Connection&&) = delete;
|
Connection& operator=(Connection&&) = delete;
|
||||||
|
|
||||||
bool hasSLERP() const { return m_hasSlerp; }
|
|
||||||
|
|
||||||
bool createBlend(const ProjectPath& path, BlendType type);
|
bool createBlend(const ProjectPath& path, BlendType type);
|
||||||
BlendType getBlendType() const { return m_loadedType; }
|
BlendType getBlendType() const { return m_loadedType; }
|
||||||
const ProjectPath& getBlendPath() const { return m_loadedBlend; }
|
const ProjectPath& getBlendPath() const { return m_loadedBlend; }
|
||||||
|
@ -659,3 +789,46 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace hecl::blender
|
} // namespace hecl::blender
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector2f> {
|
||||||
|
size_t operator()(const hecl::blender::Vector2f& val) const noexcept {
|
||||||
|
size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector3f> {
|
||||||
|
size_t operator()(const hecl::blender::Vector3f& val) const noexcept {
|
||||||
|
size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector4f> {
|
||||||
|
size_t operator()(const hecl::blender::Vector4f& val) const noexcept {
|
||||||
|
size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[3]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<array<hecl::blender::Mesh::SkinBind, 16>> {
|
||||||
|
size_t operator()(const array<hecl::blender::Mesh::SkinBind, 16>& val) const noexcept {
|
||||||
|
size_t h = 0;
|
||||||
|
for (const auto& bind : val) {
|
||||||
|
if (!bind)
|
||||||
|
break;
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(bind.vg_idx));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(bind.weight));
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -102,14 +102,13 @@ public:
|
||||||
(void)progress;
|
(void)progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool canCook(const ProjectPath& path, blender::Token& btok, int cookPass = -1) {
|
virtual bool canCook(const ProjectPath& path, blender::Token& btok) {
|
||||||
(void)path;
|
(void)path;
|
||||||
LogModule.report(logvisor::Error, "not implemented");
|
LogModule.report(logvisor::Error, "not implemented");
|
||||||
(void)cookPass;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, const Database::DataSpecEntry* oldEntry,
|
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path,
|
||||||
blender::Token& btok) const {
|
const Database::DataSpecEntry* oldEntry) const {
|
||||||
(void)path;
|
(void)path;
|
||||||
return oldEntry;
|
return oldEntry;
|
||||||
}
|
}
|
||||||
|
@ -151,12 +150,11 @@ struct DataSpecEntry {
|
||||||
SystemStringView m_name;
|
SystemStringView m_name;
|
||||||
SystemStringView m_desc;
|
SystemStringView m_desc;
|
||||||
SystemStringView m_pakExt;
|
SystemStringView m_pakExt;
|
||||||
int m_numCookPasses;
|
|
||||||
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
|
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
|
||||||
|
|
||||||
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses,
|
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt,
|
||||||
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
|
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
|
||||||
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses), m_factory(std::move(factory)) {}
|
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_factory(std::move(factory)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -399,10 +397,6 @@ public:
|
||||||
* @param fast enables faster (draft) extraction for supported data types
|
* @param fast enables faster (draft) extraction for supported data types
|
||||||
* @param spec if non-null, cook using a manually-selected dataspec
|
* @param spec if non-null, cook using a manually-selected dataspec
|
||||||
* @param cp if non-null, cook asynchronously via the ClientProcess
|
* @param cp if non-null, cook asynchronously via the ClientProcess
|
||||||
* @param cookPass cookPath() should be called the number of times
|
|
||||||
* prescribed in DataSpecEntry at the root-most invocation.
|
|
||||||
* This value conveys the pass index through the call tree.
|
|
||||||
* Negative values mean "cook always".
|
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*
|
*
|
||||||
* Object cooking is generally an expensive process for large projects.
|
* Object cooking is generally an expensive process for large projects.
|
||||||
|
@ -410,8 +404,8 @@ public:
|
||||||
* feedback delivered via feedbackCb.
|
* feedback delivered via feedbackCb.
|
||||||
*/
|
*/
|
||||||
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
|
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
|
||||||
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr,
|
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr,
|
||||||
int cookPass = -1);
|
ClientProcess* cp = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Begin package process for specified !world.blend or directory
|
* @brief Begin package process for specified !world.blend or directory
|
||||||
|
|
|
@ -22,11 +22,11 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FourCC() /* Sentinel FourCC */
|
constexpr FourCC() /* Sentinel FourCC */
|
||||||
: num(0) {}
|
: num(0) {}
|
||||||
FourCC(const FourCC& other) { num = other.num; }
|
constexpr FourCC(const FourCC& other) : num(other.num) {}
|
||||||
FourCC(const char* name) : num(*(uint32_t*)name) {}
|
constexpr FourCC(const char* name) : num(*(uint32_t*)name) {}
|
||||||
FourCC(uint32_t n) : num(n) {}
|
constexpr FourCC(uint32_t n) : num(n) {}
|
||||||
bool operator==(const FourCC& other) const { return num == other.num; }
|
bool operator==(const FourCC& other) const { return num == other.num; }
|
||||||
bool operator!=(const FourCC& other) const { return num != other.num; }
|
bool operator!=(const FourCC& other) const { return num != other.num; }
|
||||||
bool operator==(const char* other) const { return num == *(uint32_t*)other; }
|
bool operator==(const char* other) const { return num == *(uint32_t*)other; }
|
||||||
|
|
|
@ -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
|
|
|
@ -3,9 +3,6 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "hecl/hecl.hpp"
|
#include "hecl/hecl.hpp"
|
||||||
#include "hecl/Backend/GLSL.hpp"
|
|
||||||
#include "hecl/Backend/HLSL.hpp"
|
|
||||||
#include "hecl/Backend/Metal.hpp"
|
|
||||||
#include "PipelineBase.hpp"
|
#include "PipelineBase.hpp"
|
||||||
|
|
||||||
/* CMake-curated rep classes for the application */
|
/* CMake-curated rep classes for the application */
|
||||||
|
@ -28,108 +25,7 @@ public:
|
||||||
}
|
}
|
||||||
boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; }
|
boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; }
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
class HECLIR : public PipelineRep<PlatformType::Null> {
|
|
||||||
const hecl::Backend::IR& m_ir;
|
|
||||||
const hecl::Backend::ShaderTag& m_tag;
|
|
||||||
const hecl::Backend::ExtensionSlot& m_extension;
|
|
||||||
uint64_t m_hash;
|
|
||||||
|
|
||||||
public:
|
|
||||||
HECLIR(const hecl::Backend::IR& ir, const hecl::Backend::ShaderTag& tag,
|
|
||||||
const hecl::Backend::ExtensionSlot& extension)
|
|
||||||
: m_ir(ir), m_tag(tag), m_extension(extension) {
|
|
||||||
m_hash = tag.val64();
|
|
||||||
m_hash ^= extension.hash();
|
|
||||||
}
|
|
||||||
static constexpr bool HasHash = true;
|
|
||||||
uint64_t Hash() const { return m_hash; }
|
|
||||||
|
|
||||||
const hecl::Backend::IR& ir() const { return m_ir; }
|
|
||||||
const hecl::Backend::ShaderTag& tag() const { return m_tag; }
|
|
||||||
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename P, class BackendTp>
|
|
||||||
class HECLBackendImpl : public PipelineRep<P> {
|
|
||||||
hecl::Backend::ShaderTag m_tag;
|
|
||||||
BackendTp m_backend;
|
|
||||||
const hecl::Backend::ExtensionSlot& m_extension;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr bool HasHash = false;
|
|
||||||
HECLBackendImpl(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLIR& in)
|
|
||||||
: m_tag(in.tag()), m_extension(in.extension()) {
|
|
||||||
hecl::Backend::Diagnostics diag;
|
|
||||||
m_backend.reset(in.ir(), diag);
|
|
||||||
}
|
|
||||||
std::string makeVert() const {
|
|
||||||
return m_backend.makeVert(m_tag.getColorCount(), m_tag.getUvCount(), m_tag.getWeightCount(),
|
|
||||||
m_tag.getSkinSlotCount(), m_extension.texCount, m_extension.texs,
|
|
||||||
m_extension.noReflection ? Backend::ReflectionType::None : m_tag.getReflectionType());
|
|
||||||
}
|
|
||||||
std::string makeFrag() const {
|
|
||||||
return m_backend.makeFrag(m_extension.blockCount, m_extension.blockNames,
|
|
||||||
m_tag.getAlphaTest() || m_extension.forceAlphaTest,
|
|
||||||
m_extension.noReflection ? Backend::ReflectionType::None : m_tag.getReflectionType(),
|
|
||||||
m_backend.m_blendSrc, m_backend.m_blendDst, m_extension.lighting, m_extension.post,
|
|
||||||
m_extension.texCount, m_extension.texs, m_extension.diffuseOnly);
|
|
||||||
}
|
|
||||||
const hecl::Backend::ShaderTag& getTag() const { return m_tag; }
|
|
||||||
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
|
|
||||||
std::pair<hecl::Backend::BlendFactor, hecl::Backend::BlendFactor> blendFactors() const {
|
|
||||||
return {m_backend.m_blendSrc, m_backend.m_blendDst};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename P>
|
|
||||||
class HECLBackend : public PipelineRep<P> {
|
|
||||||
public:
|
|
||||||
static constexpr bool HasHash = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class HECLBackend<PlatformType::OpenGL> : public HECLBackendImpl<PlatformType::OpenGL, hecl::Backend::GLSL> {
|
|
||||||
public:
|
|
||||||
using HECLBackendImpl::HECLBackendImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class HECLBackend<PlatformType::Vulkan> : public HECLBackendImpl<PlatformType::Vulkan, hecl::Backend::GLSL> {
|
|
||||||
public:
|
|
||||||
using HECLBackendImpl::HECLBackendImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class HECLBackend<PlatformType::D3D11> : public HECLBackendImpl<PlatformType::D3D11, hecl::Backend::HLSL> {
|
|
||||||
public:
|
|
||||||
using HECLBackendImpl::HECLBackendImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class HECLBackend<PlatformType::Metal> : public HECLBackendImpl<PlatformType::Metal, hecl::Backend::Metal> {
|
|
||||||
public:
|
|
||||||
using HECLBackendImpl::HECLBackendImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class HECLBackend<PlatformType::NX> : public HECLBackendImpl<PlatformType::NX, hecl::Backend::GLSL> {
|
|
||||||
public:
|
|
||||||
using HECLBackendImpl::HECLBackendImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <template <typename, typename> class T, typename P, typename... Rest>
|
|
||||||
StageCollection<T<P, Rest...>>::StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in) {
|
|
||||||
m_vertex = conv.getVertexConverter().convert(ctx, StageSourceText<P, PipelineStage::Vertex>(in.makeVert()));
|
|
||||||
m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText<P, PipelineStage::Fragment>(in.makeFrag()));
|
|
||||||
m_vtxFmtData = in.getTag().vertexFormat();
|
|
||||||
m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data());
|
|
||||||
m_additionalInfo = in.getTag().additionalInfo(in.extension(), in.blendFactors());
|
|
||||||
MakeHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HECL_RUNTIME
|
|
||||||
template <typename P>
|
template <typename P>
|
||||||
class FinalPipeline : public PipelineRep<P> {
|
class FinalPipeline : public PipelineRep<P> {
|
||||||
boo::ObjToken<boo::IShaderPipeline> m_pipeline;
|
boo::ObjToken<boo::IShaderPipeline> m_pipeline;
|
||||||
|
@ -155,10 +51,9 @@ struct ShaderDB {};
|
||||||
|
|
||||||
#define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>,
|
#define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>,
|
||||||
#define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \
|
#define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \
|
||||||
HECLBackend<P>, \
|
|
||||||
STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \
|
STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \
|
||||||
STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) FinalPipeline<P>,
|
STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) FinalPipeline<P>,
|
||||||
#define PIPELINE_OFFLINE_SPECIALIZATIONS(P) HECLBackend<P>, STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P)
|
#define PIPELINE_OFFLINE_SPECIALIZATIONS(P) STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P)
|
||||||
#define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \
|
#define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \
|
||||||
StageBinary<P, S>, HECL_APPLICATION_STAGE_REPS(P, S) StageRuntimeObject<P, S>,
|
StageBinary<P, S>, HECL_APPLICATION_STAGE_REPS(P, S) StageRuntimeObject<P, S>,
|
||||||
#define STAGE_OFFLINE_SPECIALIZATIONS(P, S) HECL_APPLICATION_STAGE_REPS(P, S)
|
#define STAGE_OFFLINE_SPECIALIZATIONS(P, S) HECL_APPLICATION_STAGE_REPS(P, S)
|
||||||
|
|
|
@ -88,9 +88,6 @@ public:
|
||||||
template <typename P>
|
template <typename P>
|
||||||
class FinalPipeline;
|
class FinalPipeline;
|
||||||
|
|
||||||
template <typename P>
|
|
||||||
class HECLBackend;
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using __IsStageSubclass =
|
using __IsStageSubclass =
|
||||||
typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>,
|
typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>,
|
||||||
|
@ -138,7 +135,6 @@ public:
|
||||||
m_hash ^= m_evaluation.Hash();
|
m_hash ^= m_evaluation.Hash();
|
||||||
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
|
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
|
||||||
}
|
}
|
||||||
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in);
|
|
||||||
template <typename I>
|
template <typename I>
|
||||||
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
|
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
|
||||||
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) {
|
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) {
|
||||||
|
|
|
@ -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); \
|
||||||
|
}); \
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1313,6 +1313,11 @@ static inline double SBig(double val) { return val; }
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template <typename SizeT>
|
||||||
|
constexpr void hash_combine_impl(SizeT& seed, SizeT value) {
|
||||||
|
seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace hecl
|
} // namespace hecl
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
|
|
@ -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)
|
|
|
@ -115,13 +115,13 @@ std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionC
|
||||||
|
|
||||||
if (reflectionCoords)
|
if (reflectionCoords)
|
||||||
retval +=
|
retval +=
|
||||||
"UBINDING3 uniform HECLReflectMtx\n"
|
"UBINDING3 uniform HECLReflectMtx\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" mat4 indMtx;\n"
|
" mat4 indMtx;\n"
|
||||||
" mat4 reflectMtx;\n"
|
" mat4 reflectMtx;\n"
|
||||||
" float reflectAlpha;\n"
|
" float reflectAlpha;\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
set(BLENDER_SOURCES
|
set(BLENDER_SOURCES
|
||||||
Connection.cpp
|
Connection.cpp
|
||||||
|
MeshOptimizer.hpp
|
||||||
|
MeshOptimizer.cpp
|
||||||
SDNARead.cpp
|
SDNARead.cpp
|
||||||
HMDL.cpp)
|
HMDL.cpp)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
#include "hecl/Blender/Connection.hpp"
|
#include "hecl/Blender/Connection.hpp"
|
||||||
#include "hecl/SteamFinder.hpp"
|
#include "hecl/SteamFinder.hpp"
|
||||||
|
#include "MeshOptimizer.hpp"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
@ -46,7 +47,7 @@ Token SharedBlenderToken;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
|
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
|
||||||
#else
|
#else
|
||||||
#define DEFAULT_BLENDER_BIN "blender"
|
#define DEFAULT_BLENDER_BIN "blender-2.8"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" uint8_t HECL_BLENDERSHELL[];
|
extern "C" uint8_t HECL_BLENDERSHELL[];
|
||||||
|
@ -500,16 +501,6 @@ Connection::Connection(int verbosityLevel) {
|
||||||
}
|
}
|
||||||
_writeStr("ACK");
|
_writeStr("ACK");
|
||||||
|
|
||||||
_readStr(lineBuf, 7);
|
|
||||||
if (!strcmp(lineBuf, "SLERP0"))
|
|
||||||
m_hasSlerp = false;
|
|
||||||
else if (!strcmp(lineBuf, "SLERP1"))
|
|
||||||
m_hasSlerp = true;
|
|
||||||
else {
|
|
||||||
_closePipe();
|
|
||||||
BlenderLog.report(logvisor::Fatal, "read '%s' from blender; expected 'SLERP(0|1)'", lineBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -524,6 +515,8 @@ void Vector3f::read(Connection& conn) { conn._readBuf(&val, 12); }
|
||||||
void Vector4f::read(Connection& conn) { conn._readBuf(&val, 16); }
|
void Vector4f::read(Connection& conn) { conn._readBuf(&val, 16); }
|
||||||
void Matrix4f::read(Connection& conn) { conn._readBuf(&val, 64); }
|
void Matrix4f::read(Connection& conn) { conn._readBuf(&val, 64); }
|
||||||
void Index::read(Connection& conn) { conn._readBuf(&val, 4); }
|
void Index::read(Connection& conn) { conn._readBuf(&val, 4); }
|
||||||
|
void Float::read(Connection& conn) { conn._readBuf(&val, 4); }
|
||||||
|
void Boolean::read(Connection& conn) { conn._readBuf(&val, 1); }
|
||||||
|
|
||||||
std::streambuf::int_type PyOutStream::StreamBuf::overflow(int_type ch) {
|
std::streambuf::int_type PyOutStream::StreamBuf::overflow(int_type ch) {
|
||||||
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
|
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
|
||||||
|
@ -755,10 +748,11 @@ void PyOutStream::AABBToBMesh(const atVec3f& min, const atVec3f& max) {
|
||||||
|
|
||||||
void PyOutStream::centerView() {
|
void PyOutStream::centerView() {
|
||||||
*this << "for obj in bpy.context.scene.objects:\n"
|
*this << "for obj in bpy.context.scene.objects:\n"
|
||||||
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n"
|
" if obj.type == 'CAMERA' or obj.type == 'LIGHT':\n"
|
||||||
" obj.hide = True\n"
|
" obj.hide_set(True)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"bpy.context.user_preferences.view.smooth_view = 0\n"
|
"old_smooth_view = bpy.context.preferences.view.smooth_view\n"
|
||||||
|
"bpy.context.preferences.view.smooth_view = 0\n"
|
||||||
"for window in bpy.context.window_manager.windows:\n"
|
"for window in bpy.context.window_manager.windows:\n"
|
||||||
" screen = window.screen\n"
|
" screen = window.screen\n"
|
||||||
" for area in screen.areas:\n"
|
" for area in screen.areas:\n"
|
||||||
|
@ -769,10 +763,11 @@ void PyOutStream::centerView() {
|
||||||
"area, 'region': region}\n"
|
"area, 'region': region}\n"
|
||||||
" bpy.ops.view3d.view_all(override)\n"
|
" bpy.ops.view3d.view_all(override)\n"
|
||||||
" break\n"
|
" break\n"
|
||||||
|
"bpy.context.preferences.view.smooth_view = old_smooth_view\n"
|
||||||
"\n"
|
"\n"
|
||||||
"for obj in bpy.context.scene.objects:\n"
|
"for obj in bpy.context.scene.objects:\n"
|
||||||
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n"
|
" if obj.type == 'CAMERA' or obj.type == 'LIGHT':\n"
|
||||||
" obj.hide = False\n";
|
" obj.hide_set(True)\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
ANIMOutStream::ANIMOutStream(Connection* parent) : m_parent(parent) {
|
ANIMOutStream::ANIMOutStream(Connection* parent) : m_parent(parent) {
|
||||||
|
@ -821,118 +816,65 @@ void ANIMOutStream::write(unsigned frame, float val) {
|
||||||
BlenderLog.report(logvisor::Fatal, "ANIMOutStream keyCount overflow");
|
BlenderLog.report(logvisor::Fatal, "ANIMOutStream keyCount overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::SkinBind::SkinBind(Connection& conn) { conn._readBuf(&boneIdx, 8); }
|
Mesh::SkinBind::SkinBind(Connection& conn) {
|
||||||
|
vg_idx = Index(conn).val;
|
||||||
|
weight = Float(conn).val;
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::normalizeSkinBinds() {
|
void Mesh::normalizeSkinBinds() {
|
||||||
for (std::vector<SkinBind>& skin : skins) {
|
for (auto& skin : skins) {
|
||||||
float accum = 0.f;
|
float accum = 0.f;
|
||||||
for (const SkinBind& bind : skin)
|
for (const SkinBind& bind : skin)
|
||||||
accum += bind.weight;
|
if (bind)
|
||||||
|
accum += bind.weight;
|
||||||
if (accum > FLT_EPSILON) {
|
if (accum > FLT_EPSILON) {
|
||||||
for (SkinBind& bind : skin)
|
for (SkinBind& bind : skin)
|
||||||
bind.weight /= accum;
|
if (bind)
|
||||||
|
bind.weight /= accum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::Mesh(Connection& conn, HMDLTopology topologyIn, int skinSlotCount, SurfProgFunc& surfProg)
|
Mesh::Mesh(Connection& conn, HMDLTopology topologyIn, int skinSlotCount, bool useLuvs)
|
||||||
: topology(topologyIn), sceneXf(conn), aabbMin(conn), aabbMax(conn) {
|
: topology(topologyIn), sceneXf(conn), aabbMin(conn), aabbMax(conn) {
|
||||||
uint32_t matSetCount;
|
Index matSetCount(conn);
|
||||||
conn._readBuf(&matSetCount, 4);
|
materialSets.reserve(matSetCount.val);
|
||||||
materialSets.reserve(matSetCount);
|
for (uint32_t i = 0; i < matSetCount.val; ++i) {
|
||||||
for (uint32_t i = 0; i < matSetCount; ++i) {
|
|
||||||
materialSets.emplace_back();
|
materialSets.emplace_back();
|
||||||
std::vector<Material>& materials = materialSets.back();
|
std::vector<Material>& materials = materialSets.back();
|
||||||
uint32_t matCount;
|
Index matCount(conn);
|
||||||
conn._readBuf(&matCount, 4);
|
materials.reserve(matCount.val);
|
||||||
materials.reserve(matCount);
|
for (uint32_t j = 0; j < matCount.val; ++j)
|
||||||
for (uint32_t i = 0; i < matCount; ++i)
|
|
||||||
materials.emplace_back(conn);
|
materials.emplace_back(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t count;
|
MeshOptimizer opt(conn, materialSets[0], useLuvs);
|
||||||
conn._readBuf(&count, 4);
|
opt.optimize(*this, skinSlotCount);
|
||||||
pos.reserve(count);
|
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
|
||||||
pos.emplace_back(conn);
|
|
||||||
|
|
||||||
conn._readBuf(&count, 4);
|
Index count(conn);
|
||||||
norm.reserve(count);
|
boneNames.reserve(count.val);
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
|
||||||
norm.emplace_back(conn);
|
|
||||||
|
|
||||||
conn._readBuf(&colorLayerCount, 4);
|
|
||||||
if (colorLayerCount > 4)
|
|
||||||
LogModule.report(logvisor::Fatal, "mesh has %u color-layers; max 4", colorLayerCount);
|
|
||||||
conn._readBuf(&count, 4);
|
|
||||||
color.reserve(count);
|
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
|
||||||
color.emplace_back(conn);
|
|
||||||
|
|
||||||
conn._readBuf(&uvLayerCount, 4);
|
|
||||||
if (uvLayerCount > 8)
|
|
||||||
LogModule.report(logvisor::Fatal, "mesh has %u UV-layers; max 8", uvLayerCount);
|
|
||||||
conn._readBuf(&count, 4);
|
|
||||||
uv.reserve(count);
|
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
|
||||||
uv.emplace_back(conn);
|
|
||||||
|
|
||||||
conn._readBuf(&luvLayerCount, 4);
|
|
||||||
if (luvLayerCount > 1)
|
|
||||||
LogModule.report(logvisor::Fatal, "mesh has %u LUV-layers; max 1", luvLayerCount);
|
|
||||||
conn._readBuf(&count, 4);
|
|
||||||
luv.reserve(count);
|
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
|
||||||
luv.emplace_back(conn);
|
|
||||||
|
|
||||||
conn._readBuf(&count, 4);
|
|
||||||
boneNames.reserve(count);
|
|
||||||
for (uint32_t i = 0; i < count; ++i) {
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
char name[128];
|
char name[128];
|
||||||
conn._readStr(name, 128);
|
conn._readStr(name, 128);
|
||||||
boneNames.emplace_back(name);
|
boneNames.emplace_back(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn._readBuf(&count, 4);
|
if (boneNames.size())
|
||||||
skins.reserve(count);
|
for (Surface& s : surfaces)
|
||||||
for (uint32_t i = 0; i < count; ++i) {
|
s.skinBankIdx = skinBanks.addSurface(*this, s, skinSlotCount);
|
||||||
skins.emplace_back();
|
|
||||||
std::vector<SkinBind>& binds = skins.back();
|
|
||||||
uint32_t bindCount;
|
|
||||||
conn._readBuf(&bindCount, 4);
|
|
||||||
binds.reserve(bindCount);
|
|
||||||
for (uint32_t j = 0; j < bindCount; ++j)
|
|
||||||
binds.emplace_back(conn);
|
|
||||||
}
|
|
||||||
normalizeSkinBinds();
|
|
||||||
|
|
||||||
/* Assume 16 islands per material for reserve */
|
|
||||||
if (materialSets.size())
|
|
||||||
surfaces.reserve(materialSets.front().size() * 16);
|
|
||||||
uint8_t isSurf;
|
|
||||||
conn._readBuf(&isSurf, 1);
|
|
||||||
int prog = 0;
|
|
||||||
while (isSurf) {
|
|
||||||
surfaces.emplace_back(conn, *this, skinSlotCount);
|
|
||||||
surfProg(++prog);
|
|
||||||
conn._readBuf(&isSurf, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom properties */
|
/* Custom properties */
|
||||||
uint32_t propCount;
|
Index propCount(conn);
|
||||||
conn._readBuf(&propCount, 4);
|
|
||||||
std::string keyBuf;
|
std::string keyBuf;
|
||||||
std::string valBuf;
|
std::string valBuf;
|
||||||
for (uint32_t i = 0; i < propCount; ++i) {
|
for (uint32_t i = 0; i < propCount.val; ++i) {
|
||||||
uint32_t kLen;
|
Index kLen(conn);
|
||||||
conn._readBuf(&kLen, 4);
|
keyBuf.assign(kLen.val, '\0');
|
||||||
keyBuf.assign(kLen, '\0');
|
conn._readBuf(&keyBuf[0], kLen.val);
|
||||||
conn._readBuf(&keyBuf[0], kLen);
|
|
||||||
|
|
||||||
uint32_t vLen;
|
Index vLen(conn);
|
||||||
conn._readBuf(&vLen, 4);
|
valBuf.assign(vLen.val, '\0');
|
||||||
valBuf.assign(vLen, '\0');
|
conn._readBuf(&valBuf[0], vLen.val);
|
||||||
conn._readBuf(&valBuf[0], vLen);
|
|
||||||
|
|
||||||
customProps[keyBuf] = valBuf;
|
customProps[keyBuf] = valBuf;
|
||||||
}
|
}
|
||||||
|
@ -991,28 +933,58 @@ Mesh Mesh::getContiguousSkinningVersion() const {
|
||||||
return newMesh;
|
return newMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T SwapFourCC(T fcc) {
|
||||||
|
return T(hecl::SBig(std::underlying_type_t<T>(fcc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Material::PASS::PASS(Connection& conn) {
|
||||||
|
conn._readBuf(&type, 4);
|
||||||
|
type = SwapFourCC(type);
|
||||||
|
|
||||||
|
uint32_t bufSz;
|
||||||
|
conn._readBuf(&bufSz, 4);
|
||||||
|
std::string readStr(bufSz, ' ');
|
||||||
|
conn._readBuf(&readStr[0], bufSz);
|
||||||
|
SystemStringConv absolute(readStr);
|
||||||
|
|
||||||
|
SystemString relative =
|
||||||
|
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
|
||||||
|
tex.assign(conn.getBlendPath().getProject().getProjectWorkingPath(), relative);
|
||||||
|
|
||||||
|
conn._readBuf(&source, 1);
|
||||||
|
conn._readBuf(&uvAnimType, 1);
|
||||||
|
uint32_t argCount;
|
||||||
|
conn._readBuf(&argCount, 4);
|
||||||
|
for (uint32_t i = 0; i < argCount; ++i)
|
||||||
|
conn._readBuf(&uvAnimParms[i], 4);
|
||||||
|
conn._readBuf(&alpha, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Material::CLR::CLR(Connection& conn) {
|
||||||
|
conn._readBuf(&type, 4);
|
||||||
|
type = SwapFourCC(type);
|
||||||
|
color.read(conn);
|
||||||
|
}
|
||||||
|
|
||||||
Material::Material(Connection& conn) {
|
Material::Material(Connection& conn) {
|
||||||
uint32_t bufSz;
|
uint32_t bufSz;
|
||||||
conn._readBuf(&bufSz, 4);
|
conn._readBuf(&bufSz, 4);
|
||||||
name.assign(bufSz, ' ');
|
name.assign(bufSz, ' ');
|
||||||
conn._readBuf(&name[0], bufSz);
|
conn._readBuf(&name[0], bufSz);
|
||||||
|
|
||||||
conn._readBuf(&bufSz, 4);
|
conn._readBuf(&passIndex, 4);
|
||||||
source.assign(bufSz, ' ');
|
conn._readBuf(&shaderType, 4);
|
||||||
conn._readBuf(&source[0], bufSz);
|
shaderType = SwapFourCC(shaderType);
|
||||||
|
|
||||||
uint32_t texCount;
|
uint32_t chunkCount;
|
||||||
conn._readBuf(&texCount, 4);
|
conn._readBuf(&chunkCount, 4);
|
||||||
texs.reserve(texCount);
|
chunks.reserve(chunkCount);
|
||||||
for (uint32_t i = 0; i < texCount; ++i) {
|
for (uint32_t i = 0; i < chunkCount; ++i) {
|
||||||
conn._readBuf(&bufSz, 4);
|
ChunkType type;
|
||||||
std::string readStr(bufSz, ' ');
|
conn._readBuf(&type, 4);
|
||||||
conn._readBuf(&readStr[0], bufSz);
|
type = SwapFourCC(type);
|
||||||
SystemStringConv absolute(readStr);
|
chunks.push_back(Chunk::Build(type, conn));
|
||||||
|
|
||||||
SystemString relative =
|
|
||||||
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
|
|
||||||
texs.emplace_back(conn.getBlendPath().getProject().getProjectWorkingPath(), relative);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t iPropCount;
|
uint32_t iPropCount;
|
||||||
|
@ -1028,36 +1000,7 @@ Material::Material(Connection& conn) {
|
||||||
iprops[readStr] = val;
|
iprops[readStr] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn._readBuf(&transparent, 1);
|
conn._readBuf(&blendMode, 4);
|
||||||
}
|
|
||||||
|
|
||||||
Mesh::Surface::Surface(Connection& conn, Mesh& parent, int skinSlotCount)
|
|
||||||
: centroid(conn), materialIdx(conn), aabbMin(conn), aabbMax(conn), reflectionNormal(conn) {
|
|
||||||
uint32_t countEstimate;
|
|
||||||
conn._readBuf(&countEstimate, 4);
|
|
||||||
verts.reserve(countEstimate);
|
|
||||||
|
|
||||||
uint8_t isVert;
|
|
||||||
conn._readBuf(&isVert, 1);
|
|
||||||
while (isVert) {
|
|
||||||
verts.emplace_back(conn, parent);
|
|
||||||
conn._readBuf(&isVert, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent.boneNames.size())
|
|
||||||
skinBankIdx = parent.skinBanks.addSurface(parent, *this, skinSlotCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh::Surface::Vert::Vert(Connection& conn, const Mesh& parent) {
|
|
||||||
conn._readBuf(&iPos, 4);
|
|
||||||
if (iPos == 0xffffffff)
|
|
||||||
return;
|
|
||||||
conn._readBuf(&iNorm, 4);
|
|
||||||
for (uint32_t i = 0; i < parent.colorLayerCount; ++i)
|
|
||||||
conn._readBuf(&iColor[i], 4);
|
|
||||||
for (uint32_t i = 0; i < parent.uvLayerCount; ++i)
|
|
||||||
conn._readBuf(&iUv[i], 4);
|
|
||||||
conn._readBuf(&iSkin, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::Surface::Vert::operator==(const Vert& other) const {
|
bool Mesh::Surface::Vert::operator==(const Vert& other) const {
|
||||||
|
@ -1087,26 +1030,21 @@ void Mesh::SkinBanks::Bank::addSkins(const Mesh& parent, const std::vector<uint3
|
||||||
for (uint32_t sidx : skinIdxs) {
|
for (uint32_t sidx : skinIdxs) {
|
||||||
m_skinIdxs.push_back(sidx);
|
m_skinIdxs.push_back(sidx);
|
||||||
for (const SkinBind& bind : parent.skins[sidx]) {
|
for (const SkinBind& bind : parent.skins[sidx]) {
|
||||||
|
if (!bind)
|
||||||
|
break;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (uint32_t bidx : m_boneIdxs) {
|
for (uint32_t bidx : m_boneIdxs) {
|
||||||
if (bidx == bind.boneIdx) {
|
if (bidx == bind.vg_idx) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
m_boneIdxs.push_back(bind.boneIdx);
|
m_boneIdxs.push_back(bind.vg_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Mesh::SkinBanks::Bank::lookupLocalBoneIdx(uint32_t boneIdx) const {
|
|
||||||
for (size_t i = 0; i < m_boneIdxs.size(); ++i)
|
|
||||||
if (m_boneIdxs[i] == boneIdx)
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Mesh::SkinBanks::Bank>::iterator Mesh::SkinBanks::addSkinBank(int skinSlotCount) {
|
std::vector<Mesh::SkinBanks::Bank>::iterator Mesh::SkinBanks::addSkinBank(int skinSlotCount) {
|
||||||
banks.emplace_back();
|
banks.emplace_back();
|
||||||
if (skinSlotCount > 0)
|
if (skinSlotCount > 0)
|
||||||
|
@ -1631,32 +1569,28 @@ const char* DataStream::MeshOutputModeString(HMDLTopology topology) {
|
||||||
return STRS[int(topology)];
|
return STRS[int(topology)];
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh DataStream::compileMesh(HMDLTopology topology, int skinSlotCount, Mesh::SurfProgFunc surfProg) {
|
Mesh DataStream::compileMesh(HMDLTopology topology, int skinSlotCount) {
|
||||||
if (m_parent->getBlendType() != BlendType::Mesh)
|
if (m_parent->getBlendType() != BlendType::Mesh)
|
||||||
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not a MESH blend"),
|
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not a MESH blend"),
|
||||||
m_parent->getBlendPath().getAbsolutePath().data());
|
m_parent->getBlendPath().getAbsolutePath().data());
|
||||||
|
|
||||||
char req[128];
|
m_parent->_writeStr("MESHCOMPILE");
|
||||||
snprintf(req, 128, "MESHCOMPILE %s %d", MeshOutputModeString(topology), skinSlotCount);
|
|
||||||
m_parent->_writeStr(req);
|
|
||||||
|
|
||||||
char readBuf[256];
|
char readBuf[256];
|
||||||
m_parent->_readStr(readBuf, 256);
|
m_parent->_readStr(readBuf, 256);
|
||||||
if (strcmp(readBuf, "OK"))
|
if (strcmp(readBuf, "OK"))
|
||||||
BlenderLog.report(logvisor::Fatal, "unable to cook mesh: %s", readBuf);
|
BlenderLog.report(logvisor::Fatal, "unable to cook mesh: %s", readBuf);
|
||||||
|
|
||||||
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
|
return Mesh(*m_parent, topology, skinSlotCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount, bool useLuv,
|
Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount, bool useLuv) {
|
||||||
Mesh::SurfProgFunc surfProg) {
|
|
||||||
if (m_parent->getBlendType() != BlendType::Area)
|
if (m_parent->getBlendType() != BlendType::Area)
|
||||||
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
|
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
|
||||||
m_parent->getBlendPath().getAbsolutePath().data());
|
m_parent->getBlendPath().getAbsolutePath().data());
|
||||||
|
|
||||||
char req[128];
|
char req[128];
|
||||||
snprintf(req, 128, "MESHCOMPILENAME %s %s %d %d", name.data(), MeshOutputModeString(topology), skinSlotCount,
|
snprintf(req, 128, "MESHCOMPILENAME %s %d", name.data(), int(useLuv));
|
||||||
int(useLuv));
|
|
||||||
m_parent->_writeStr(req);
|
m_parent->_writeStr(req);
|
||||||
|
|
||||||
char readBuf[256];
|
char readBuf[256];
|
||||||
|
@ -1664,7 +1598,7 @@ Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology, int s
|
||||||
if (strcmp(readBuf, "OK"))
|
if (strcmp(readBuf, "OK"))
|
||||||
BlenderLog.report(logvisor::Fatal, "unable to cook mesh '%s': %s", name.data(), readBuf);
|
BlenderLog.report(logvisor::Fatal, "unable to cook mesh '%s': %s", name.data(), readBuf);
|
||||||
|
|
||||||
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
|
return Mesh(*m_parent, topology, skinSlotCount, useLuv);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColMesh DataStream::compileColMesh(std::string_view name) {
|
ColMesh DataStream::compileColMesh(std::string_view name) {
|
||||||
|
@ -1710,24 +1644,6 @@ std::vector<ColMesh> DataStream::compileColMeshes() {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh DataStream::compileAllMeshes(HMDLTopology topology, int skinSlotCount, float maxOctantLength,
|
|
||||||
Mesh::SurfProgFunc surfProg) {
|
|
||||||
if (m_parent->getBlendType() != BlendType::Area)
|
|
||||||
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
|
|
||||||
m_parent->getBlendPath().getAbsolutePath().data());
|
|
||||||
|
|
||||||
char req[128];
|
|
||||||
snprintf(req, 128, "MESHCOMPILEALL %s %d %f", MeshOutputModeString(topology), skinSlotCount, maxOctantLength);
|
|
||||||
m_parent->_writeStr(req);
|
|
||||||
|
|
||||||
char readBuf[256];
|
|
||||||
m_parent->_readStr(readBuf, 256);
|
|
||||||
if (strcmp(readBuf, "OK"))
|
|
||||||
BlenderLog.report(logvisor::Fatal, "unable to cook all meshes: %s", readBuf);
|
|
||||||
|
|
||||||
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Light> DataStream::compileLights() {
|
std::vector<Light> DataStream::compileLights() {
|
||||||
if (m_parent->getBlendType() != BlendType::Area)
|
if (m_parent->getBlendType() != BlendType::Area)
|
||||||
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
|
BlenderLog.report(logvisor::Fatal, _SYS_STR("%s is not an AREA blend"),
|
||||||
|
|
|
@ -138,7 +138,7 @@ HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinInd
|
||||||
|
|
||||||
if (weightVecCount) {
|
if (weightVecCount) {
|
||||||
const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx];
|
const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx];
|
||||||
const std::vector<SkinBind>& binds = skins[v.iSkin];
|
const auto& binds = skins[v.iSkin];
|
||||||
|
|
||||||
auto it = bank.m_boneIdxs.cbegin();
|
auto it = bank.m_boneIdxs.cbegin();
|
||||||
for (size_t i = 0; i < weightVecCount; ++i) {
|
for (size_t i = 0; i < weightVecCount; ++i) {
|
||||||
|
@ -146,11 +146,14 @@ HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinInd
|
||||||
for (size_t j = 0; j < 4; ++j) {
|
for (size_t j = 0; j < 4; ++j) {
|
||||||
if (it == bank.m_boneIdxs.cend())
|
if (it == bank.m_boneIdxs.cend())
|
||||||
break;
|
break;
|
||||||
for (const SkinBind& bind : binds)
|
for (const SkinBind& bind : binds) {
|
||||||
if (bind.boneIdx == *it) {
|
if (!bind)
|
||||||
|
break;
|
||||||
|
if (bind.vg_idx == *it) {
|
||||||
vec.simd[j] = bind.weight;
|
vec.simd[j] = bind.weight;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
vboW.writeVec4fLittle(vec);
|
vboW.writeVec4fLittle(vec);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -9,8 +9,6 @@ endmacro(hecl_add_list)
|
||||||
include_directories(../extern/boo/glslang ../extern/boo)
|
include_directories(../extern/boo/glslang ../extern/boo)
|
||||||
|
|
||||||
add_subdirectory(Blender)
|
add_subdirectory(Blender)
|
||||||
add_subdirectory(Backend)
|
|
||||||
add_subdirectory(Frontend)
|
|
||||||
add_subdirectory(Runtime)
|
add_subdirectory(Runtime)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
@ -18,7 +16,6 @@ list(APPEND PLAT_SRCS winsupport.cpp ../include/hecl/winsupport.hpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp)
|
atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp)
|
||||||
atdna(atdna_Frontend.cpp ../include/hecl/Frontend.hpp)
|
|
||||||
atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp)
|
atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp)
|
||||||
atdna(atdna_SDNARead.cpp ../include/hecl/Blender/SDNARead.hpp)
|
atdna(atdna_SDNARead.cpp ../include/hecl/Blender/SDNARead.hpp)
|
||||||
|
|
||||||
|
@ -34,18 +31,13 @@ set(HECL_HEADERS
|
||||||
../include/hecl/hecl.hpp
|
../include/hecl/hecl.hpp
|
||||||
../include/hecl/MultiProgressPrinter.hpp
|
../include/hecl/MultiProgressPrinter.hpp
|
||||||
../include/hecl/FourCC.hpp
|
../include/hecl/FourCC.hpp
|
||||||
|
../include/hecl/TypedVariant.hpp
|
||||||
../include/hecl/HMDLMeta.hpp
|
../include/hecl/HMDLMeta.hpp
|
||||||
../include/hecl/Backend/Backend.hpp
|
../include/hecl/Backend.hpp
|
||||||
../include/hecl/Backend/GX.hpp
|
|
||||||
../include/hecl/Backend/ProgrammableCommon.hpp
|
|
||||||
../include/hecl/Backend/GLSL.hpp
|
|
||||||
../include/hecl/Backend/HLSL.hpp
|
|
||||||
../include/hecl/Backend/Metal.hpp
|
|
||||||
../include/hecl/Blender/Connection.hpp
|
../include/hecl/Blender/Connection.hpp
|
||||||
../include/hecl/Blender/SDNARead.hpp
|
../include/hecl/Blender/SDNARead.hpp
|
||||||
../include/hecl/Blender/Token.hpp
|
../include/hecl/Blender/Token.hpp
|
||||||
../include/hecl/SteamFinder.hpp
|
../include/hecl/SteamFinder.hpp
|
||||||
../include/hecl/Frontend.hpp
|
|
||||||
../include/hecl/Database.hpp
|
../include/hecl/Database.hpp
|
||||||
../include/hecl/Runtime.hpp
|
../include/hecl/Runtime.hpp
|
||||||
../include/hecl/ClientProcess.hpp
|
../include/hecl/ClientProcess.hpp
|
||||||
|
@ -72,7 +64,6 @@ set(COMMON_SOURCES
|
||||||
Compilers.cpp
|
Compilers.cpp
|
||||||
Pipeline.cpp
|
Pipeline.cpp
|
||||||
atdna_HMDLMeta.cpp
|
atdna_HMDLMeta.cpp
|
||||||
atdna_Frontend.cpp
|
|
||||||
atdna_CVar.cpp)
|
atdna_CVar.cpp)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
@ -80,7 +71,6 @@ if(UNIX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(hecl-full
|
add_library(hecl-full
|
||||||
${BACKEND_SOURCES}
|
|
||||||
${FRONTEND_SOURCES}
|
${FRONTEND_SOURCES}
|
||||||
${RUNTIME_SOURCES}
|
${RUNTIME_SOURCES}
|
||||||
${BLENDER_SOURCES}
|
${BLENDER_SOURCES}
|
||||||
|
|
|
@ -166,7 +166,7 @@ ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func)
|
||||||
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force,
|
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force,
|
||||||
bool fast) {
|
bool fast) {
|
||||||
if (spec->canCook(path, btok)) {
|
if (spec->canCook(path, btok)) {
|
||||||
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
|
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry());
|
||||||
if (specEnt) {
|
if (specEnt) {
|
||||||
hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
|
hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
|
||||||
if (fast)
|
if (fast)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
set(FRONTEND_SOURCES
|
|
||||||
Parser.cpp
|
|
||||||
Scanner.cpp
|
|
||||||
Diagnostics.cpp
|
|
||||||
Frontend.cpp)
|
|
||||||
|
|
||||||
hecl_add_list(Frontend FRONTEND_SOURCES)
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -331,15 +331,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
static void VisitFile(const ProjectPath& path, bool force, bool fast,
|
static void VisitFile(const ProjectPath& path, bool force, bool fast,
|
||||||
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, ClientProcess* cp,
|
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, ClientProcess* cp) {
|
||||||
int cookPass) {
|
|
||||||
for (auto& spec : specInsts) {
|
for (auto& spec : specInsts) {
|
||||||
if (spec->canCook(path, hecl::blender::SharedBlenderToken, cookPass)) {
|
if (spec->canCook(path, hecl::blender::SharedBlenderToken)) {
|
||||||
if (cp) {
|
if (cp) {
|
||||||
cp->addCookTransaction(path, force, fast, spec.get());
|
cp->addCookTransaction(path, force, fast, spec.get());
|
||||||
} else {
|
} else {
|
||||||
const DataSpecEntry* override =
|
const DataSpecEntry* override = spec->overrideDataSpec(path, spec->getDataSpecEntry());
|
||||||
spec->overrideDataSpec(path, spec->getDataSpecEntry(), hecl::blender::SharedBlenderToken);
|
|
||||||
if (!override)
|
if (!override)
|
||||||
continue;
|
continue;
|
||||||
ProjectPath cooked = path.getCookedPath(*override);
|
ProjectPath cooked = path.getCookedPath(*override);
|
||||||
|
@ -357,14 +355,14 @@ static void VisitFile(const ProjectPath& path, bool force, bool fast,
|
||||||
|
|
||||||
static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, bool fast,
|
static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, bool fast,
|
||||||
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress,
|
std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress,
|
||||||
ClientProcess* cp, int cookPass) {
|
ClientProcess* cp) {
|
||||||
if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.'))
|
if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() &&
|
if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() &&
|
||||||
hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) {
|
hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) {
|
||||||
/* Handle AudioGroup case */
|
/* Handle AudioGroup case */
|
||||||
VisitFile(dir, force, fast, specInsts, progress, cp, cookPass);
|
VisitFile(dir, force, fast, specInsts, progress, cp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +382,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
|
||||||
for (auto& child : children) {
|
for (auto& child : children) {
|
||||||
if (child.second.getPathType() == ProjectPath::Type::File) {
|
if (child.second.getPathType() == ProjectPath::Type::File) {
|
||||||
progress.changeFile(child.first.c_str(), progNum++ / progDenom);
|
progress.changeFile(child.first.c_str(), progNum++ / progDenom);
|
||||||
VisitFile(child.second, force, fast, specInsts, progress, cp, cookPass);
|
VisitFile(child.second, force, fast, specInsts, progress, cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progress.reportDirComplete();
|
progress.reportDirComplete();
|
||||||
|
@ -394,7 +392,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
|
||||||
for (auto& child : children) {
|
for (auto& child : children) {
|
||||||
switch (child.second.getPathType()) {
|
switch (child.second.getPathType()) {
|
||||||
case ProjectPath::Type::Directory: {
|
case ProjectPath::Type::Directory: {
|
||||||
VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp, cookPass);
|
VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -405,7 +403,7 @@ static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, b
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool recursive, bool force,
|
bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool recursive, bool force,
|
||||||
bool fast, const DataSpecEntry* spec, ClientProcess* cp, int cookPass) {
|
bool fast, const DataSpecEntry* spec, ClientProcess* cp) {
|
||||||
/* Construct DataSpec instances for cooking */
|
/* Construct DataSpec instances for cooking */
|
||||||
if (spec) {
|
if (spec) {
|
||||||
if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) {
|
if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) {
|
||||||
|
@ -426,11 +424,11 @@ bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter
|
||||||
case ProjectPath::Type::File:
|
case ProjectPath::Type::File:
|
||||||
case ProjectPath::Type::Glob: {
|
case ProjectPath::Type::Glob: {
|
||||||
cookProg.changeFile(path.getLastComponent().data(), 0.f);
|
cookProg.changeFile(path.getLastComponent().data(), 0.f);
|
||||||
VisitFile(path, force, fast, m_cookSpecs, cookProg, cp, cookPass);
|
VisitFile(path, force, fast, m_cookSpecs, cookProg, cp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProjectPath::Type::Directory: {
|
case ProjectPath::Type::Directory: {
|
||||||
VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp, cookPass);
|
VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue