Update Healer Man server
This commit is contained in:
1 parent
6114a2295f
commit
69809f7228
593 files changed
+903643
-40774
No files matched your search
@@ -16,6 +16,8 @@ from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from native_material_alpha import native_alpha_action, native_material_identity
|
||||
|
||||
|
||||
COLLISION_CHUNK_TRIANGLE_LIMIT = 90_000
|
||||
@@ -135,12 +137,14 @@ def image_alpha_role(image: bpy.types.Image | None) -> str:
|
||||
return "cutout"
|
||||
|
||||
|
||||
def preserve_material_alpha(material: bpy.types.Material) -> str:
|
||||
image = material_image(material)
|
||||
role = image_alpha_role(image)
|
||||
if role == "opaque" or material.node_tree is None:
|
||||
def apply_material_alpha_role(
|
||||
material: bpy.types.Material,
|
||||
image: bpy.types.Image | None,
|
||||
role: str,
|
||||
threshold: float = 0.5,
|
||||
) -> str:
|
||||
if role == "opaque" or image is None or material.node_tree is None:
|
||||
return role
|
||||
|
||||
image_node = next(
|
||||
node
|
||||
for node in material.node_tree.nodes
|
||||
@@ -167,32 +171,117 @@ def preserve_material_alpha(material: bpy.types.Material) -> str:
|
||||
alpha_clip = material.node_tree.nodes.new("ShaderNodeMath")
|
||||
alpha_clip.name = "RuneWaker Alpha Clip"
|
||||
alpha_clip.operation = "GREATER_THAN"
|
||||
alpha_clip.inputs[1].default_value = 0.5
|
||||
alpha_clip.inputs[1].default_value = threshold
|
||||
material.node_tree.links.new(alpha_output, alpha_clip.inputs[0])
|
||||
material.node_tree.links.new(alpha_clip.outputs[0], alpha_input)
|
||||
material.blend_method = "CLIP"
|
||||
material.alpha_threshold = 0.5
|
||||
material.alpha_threshold = threshold
|
||||
return role
|
||||
|
||||
|
||||
def deduplicate_materials(objects: list[bpy.types.Object]) -> dict[str, int]:
|
||||
def preserve_material_alpha(
|
||||
material: bpy.types.Material,
|
||||
source_report: dict[str, object] | None,
|
||||
) -> tuple[str, dict[str, object] | None, dict[str, object]]:
|
||||
image = material_image(material)
|
||||
role = image_alpha_role(image)
|
||||
image_path = bpy.path.abspath(image.filepath) if image is not None else ""
|
||||
identity = native_material_identity(material.name, image_path, source_report)
|
||||
decision = native_alpha_action(identity, role)
|
||||
action = str(decision.get("action", "unsupported"))
|
||||
if action == "pixel-alpha-heuristic":
|
||||
applied_role = apply_material_alpha_role(material, image, role)
|
||||
decision = {
|
||||
"action": applied_role,
|
||||
"reason": decision.get("reason", "no-verified-native-material-match"),
|
||||
}
|
||||
elif action in {"blend", "mask", "additive"}:
|
||||
applied_role = apply_material_alpha_role(
|
||||
material,
|
||||
image,
|
||||
"cutout" if action == "mask" else "blend",
|
||||
float(decision.get("threshold", 0.5)),
|
||||
)
|
||||
decision = {**decision, "applied": applied_role}
|
||||
if identity is not None:
|
||||
material["nativeSourceModel"] = identity["sourceModel"]
|
||||
material["nativeSourceResourceSha256"] = identity["sourceResourceSha256"]
|
||||
material["nativeMaterialIndex"] = identity["materialIndex"]
|
||||
material["nativeSourceTexture"] = identity["sourceTexture"]
|
||||
material["nativeAlphaPolicy"] = str(decision.get("reason", action))
|
||||
lighting = identity.get("lightingType")
|
||||
lighting_name = lighting.get("name") if isinstance(lighting, dict) else None
|
||||
if isinstance(lighting_name, str) and lighting_name.strip():
|
||||
material["nativeLightingType"] = lighting_name.strip().lower()
|
||||
elif "nativeLightingType" in material:
|
||||
del material["nativeLightingType"]
|
||||
if action == "additive":
|
||||
material["nativeBlendMode"] = "additive"
|
||||
elif "nativeBlendMode" in material:
|
||||
del material["nativeBlendMode"]
|
||||
return role, identity, decision
|
||||
|
||||
|
||||
def deduplicate_materials(
|
||||
objects: list[bpy.types.Object],
|
||||
source_report: dict[str, object] | None,
|
||||
) -> dict[str, object]:
|
||||
canonical: dict[str, bpy.types.Material] = {}
|
||||
replacements = 0
|
||||
native_alpha_rows: dict[int, dict[str, object]] = {}
|
||||
for obj in objects:
|
||||
for slot in obj.material_slots:
|
||||
material = slot.material
|
||||
if material is None:
|
||||
continue
|
||||
key = material_image_key(material)
|
||||
alpha_role, identity, decision = preserve_material_alpha(material, source_report)
|
||||
semantics_key = (
|
||||
repr((identity["sourceModel"], identity["sourceResourceSha256"],
|
||||
identity["materialIndex"], identity["sourceTexture"],
|
||||
sorted(identity["alphaBlendParameters"].items())))
|
||||
if identity is not None
|
||||
else "pixel-alpha-heuristic:" + alpha_role
|
||||
)
|
||||
key = material_image_key(material) + "|" + semantics_key
|
||||
if identity is not None:
|
||||
native_alpha_rows[identity["materialIndex"]] = {
|
||||
"material": identity["materialName"],
|
||||
"materialIndex": identity["materialIndex"],
|
||||
"sourceModel": identity["sourceModel"],
|
||||
"sourceResourceSha256": identity["sourceResourceSha256"],
|
||||
"sourceTexture": identity["sourceTexture"],
|
||||
"stagedTexture": identity["stagedTexture"],
|
||||
"materialType": identity["materialType"],
|
||||
"lightingType": identity["lightingType"],
|
||||
"alphaBlendParameters": identity["alphaBlendParameters"],
|
||||
"sourceImageAlphaRole": alpha_role,
|
||||
"conversionPolicy": decision,
|
||||
}
|
||||
if key in canonical:
|
||||
if slot.material != canonical[key]:
|
||||
slot.material = canonical[key]
|
||||
replacements += 1
|
||||
else:
|
||||
canonical[key] = material
|
||||
material["semanticRole"] = preserve_material_alpha(material)
|
||||
material["semanticRole"] = decision.get("action", alpha_role)
|
||||
material["sourceFormat"] = "RuneWaker DDS"
|
||||
return {"unique": len(canonical), "replacements": replacements}
|
||||
source_model = source_report.get("sourceModel") if isinstance(source_report, dict) else None
|
||||
source_hash = source_report.get("sourceResourceSha256") if isinstance(source_report, dict) else None
|
||||
native_alpha_materials = [native_alpha_rows[index] for index in sorted(native_alpha_rows)]
|
||||
return {
|
||||
"unique": len(canonical),
|
||||
"replacements": replacements,
|
||||
"nativeAlpha": {
|
||||
"sourceModel": source_model,
|
||||
"sourceResourceSha256": source_hash,
|
||||
"matchedMaterialCount": len(native_alpha_materials),
|
||||
"materials": native_alpha_materials,
|
||||
"unsupportedPolicies": [
|
||||
row for row in native_alpha_materials
|
||||
if row["conversionPolicy"].get("action") == "unsupported"
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def export_glb(
|
||||
@@ -302,6 +391,12 @@ def render_topdown_preview(
|
||||
def main() -> None:
|
||||
source_obj, output_dir, slug, unit_scale = arguments()
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_report_file = source_obj.parent / "export-report.json"
|
||||
source_report = (
|
||||
json.loads(source_report_file.read_text(encoding="utf-8"))
|
||||
if source_report_file.is_file()
|
||||
else None
|
||||
)
|
||||
for stale in output_dir.glob(f"{slug}-collision*.glb"):
|
||||
stale.unlink()
|
||||
|
||||
@@ -330,7 +425,7 @@ def main() -> None:
|
||||
obj["sourceFormat"] = "RuneWaker ROS via OBJ bridge"
|
||||
obj["sourceUnitScaleMeters"] = unit_scale
|
||||
|
||||
material_stats = deduplicate_materials(visual)
|
||||
material_stats = deduplicate_materials(visual, source_report)
|
||||
bounds_min, bounds_max = three_bounds(visual)
|
||||
source_triangles = sum(triangles(obj) for obj in visual)
|
||||
|
||||
@@ -364,12 +459,6 @@ def main() -> None:
|
||||
|
||||
blend_file = output_dir / f"{slug}.source.blend"
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(blend_file))
|
||||
source_report_file = source_obj.parent / "export-report.json"
|
||||
source_report = (
|
||||
json.loads(source_report_file.read_text(encoding="utf-8"))
|
||||
if source_report_file.is_file()
|
||||
else None
|
||||
)
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"dungeonId": slug,
|
||||
@@ -379,6 +468,7 @@ def main() -> None:
|
||||
"obj": source_obj.name,
|
||||
"report": source_report_file.name if source_report is not None else None,
|
||||
"model": source_report.get("sourceModel") if source_report else None,
|
||||
"resourceSha256": source_report.get("sourceResourceSha256") if source_report else None,
|
||||
},
|
||||
"coordinateTransform": {
|
||||
"nativeBridge": "three(x,y,z)=(runewaker.x,runewaker.y,-runewaker.z)",
|
||||
|
||||
Reference in new issue
Block a user