Skip to content

Commit

Permalink
Merge pull request #16 from ryanjsims/feature/skin-shader
Browse files Browse the repository at this point in the history
Add skin material support for viper commando sets
  • Loading branch information
xypwn authored Jan 13, 2025
2 parents 05091ff + 250f65e commit 65cf0bf
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 41 deletions.
23 changes: 22 additions & 1 deletion extractor/material/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ const (
CoveringNormal TextureUsage = 0x4c6fc000
WeatheringDataMask TextureUsage = 0xb4dcc2c1
CapeLUT TextureUsage = 0x0e494183
NormalSpecularAO TextureUsage = 0xe64c5236
ColorRoughness TextureUsage = 0x8a013406
ColorSpecularB TextureUsage = 0x828a53ad
DetailNormals TextureUsage = 0xbe22de88
)

func (usage *TextureUsage) String() string {
Expand Down Expand Up @@ -312,6 +316,14 @@ func (usage *TextureUsage) String() string {
return "weathering_data_mask"
case CapeLUT:
return "cape_lut"
case NormalSpecularAO:
return "normal_specular_ao"
case ColorRoughness:
return "color_roughness"
case ColorSpecularB:
return "color_specular_b"
case DetailNormals:
return "detail_normals"
default:
return "unknown texture usage!"
}
Expand Down Expand Up @@ -381,6 +393,7 @@ func AddMaterial(ctx extractor.Context, mat *material.Material, doc *gltf.Docume
var normalTexture *gltf.NormalTexture
var postProcess func(image.Image) error
var albedoPostProcess func(image.Image) error = postProcessToOpaque
var normalPostProcess func(image.Image) error = postProcessReconstructNormalZ
var emissiveFactor [3]float32
origImgOpts := imgOpts
lutImgOpts := &ImageOptions{
Expand All @@ -391,6 +404,8 @@ func AddMaterial(ctx extractor.Context, mat *material.Material, doc *gltf.Docume
}
for texUsage := range mat.Textures {
switch TextureUsage(texUsage.Value) {
case ColorRoughness:
fallthrough
case AlbedoIridescence:
albedoPostProcess = nil
fallthrough
Expand All @@ -409,6 +424,11 @@ func AddMaterial(ctx extractor.Context, mat *material.Material, doc *gltf.Docume
}
usedTextures[TextureUsage(texUsage.Value)] = index
albedoPostProcess = postProcessToOpaque
case NormalSpecularAO:
// GLTF normals will look wonky, but our own material will be able to use the specular+ao in this map
// in blender
normalPostProcess = nil
fallthrough
case Normal:
fallthrough
case NormalMap:
Expand All @@ -424,7 +444,7 @@ func AddMaterial(ctx extractor.Context, mat *material.Material, doc *gltf.Docume
if unitData != nil && TextureUsage(texUsage.Value) == BaseData && unitData.BaseData.Value != 0 {
hash = unitData.BaseData
}
index, err := writeTexture(ctx, doc, hash, postProcessReconstructNormalZ, imgOpts)
index, err := writeTexture(ctx, doc, hash, normalPostProcess, imgOpts)
if err != nil {
continue
return 0, err
Expand All @@ -433,6 +453,7 @@ func AddMaterial(ctx extractor.Context, mat *material.Material, doc *gltf.Docume
Index: gltf.Index(index),
}
usedTextures[TextureUsage(texUsage.Value)] = index
normalPostProcess = postProcessReconstructNormalZ
case MRA:
index, err := writeTexture(ctx, doc, mat.Textures[texUsage], postProcess, imgOpts)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions patterns/material.hexpat
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ enum ImageUsage : u32 {
MetalSurfaceData = 0xe32e3fa5,
ConcreteSurfaceData = 0x8d69d2ee,
WeatheringDataMask = 0xb4dcc2c1,
NormalSpecularAO = 0xe64c5236,
ColorRoughness = 0x8a013406,
ColorSpecularB = 0x828a53ad,
DetailNormals = 0xbe22de88,
/*
normal_map,
dirt_map,
Expand All @@ -89,6 +93,10 @@ enum ImageUsage : u32 {
covering_albedo,
covering_normal,
weathering_data_mask,
normal_specular_ao,
color_roughness,
color_specular_b,
detail_normals,
*/
};

Expand Down
110 changes: 70 additions & 40 deletions scripts/hd2_accurate_blender_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import struct
import numpy as np
from argparse import ArgumentParser
from bpy.types import BlendData, Image, Object, ShaderNodeGroup, ShaderNodeTexImage
from bpy.types import BlendData, Image, Object, ShaderNodeGroup, ShaderNodeTexImage, Material
from pathlib import Path
from io import BytesIO
from typing import Optional, Dict, List, Tuple
from random import randint

from dds_float16 import DDS
from openexr_builder import make_exr
Expand Down Expand Up @@ -128,6 +129,64 @@ def add_texture(gltf, textureIdx, usage: Optional[str] = None) -> Image:
blImage.use_fake_user = True
return blImage

def add_accurate_material(shader_mat: Material, material: dict, shader_module, unused_secondary_lut: Image, textures: Dict[str, Image]):
object_mat = shader_mat.copy()
object_mat.name = "HD2 Mat " + material["name"]
template_group: ShaderNodeGroup = object_mat.node_tree.nodes["HD2 Shader Template"]
template_group.node_tree = template_group.node_tree.copy()
template_group.node_tree.name = object_mat.name + shader_module.ScriptVersion
HD2_Shader = template_group.node_tree

print(" Applying textures")
config_nodes: Dict[str, ShaderNodeTexImage] = object_mat.node_tree.nodes
config_nodes["Secondary Material LUT Texture"].image = unused_secondary_lut
for usage, image in textures.items():
match usage:
case "id_masks_array":
config_nodes["ID Mask Array Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "pattern_masks_array":
config_nodes["Pattern Mask Array"].image = image
image.colorspace_settings.name = "Non-Color"
case "decal_sheet":
config_nodes["Decal Texture"].image = image
image.colorspace_settings.name = "sRGB"
image.alpha_mode = "CHANNEL_PACKED"
case "material_lut":
config_nodes["Primary Material LUT Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "pattern_lut":
config_nodes["Pattern LUT Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "base_data":
config_nodes["Normal Map"].image = image
image.colorspace_settings.name = "Non-Color"

print(" Finalizing material")
shader_module.update_images(HD2_Shader, object_mat)
shader_module.update_slot_defaults(HD2_Shader, object_mat)
shader_module.connect_input_links(HD2_Shader)
shader_module.update_array_uvs(object_mat)
return object_mat

def add_skin_material(skin_mat: Material, material: dict, textures: Dict[str, Image]):
object_mat = skin_mat.copy()
object_mat.name = "HD2 Mat " + material["name"]

print(" Applying textures")
config_nodes: Dict[str, ShaderNodeTexImage] = object_mat.node_tree.nodes
for usage, image in textures.items():
image.colorspace_settings.name = "Non-Color"
match usage:
case "color_roughness":
config_nodes["Image Texture"].image = image
case "normal_specular_ao":
config_nodes["Image Texture.001"].image = image
print(" Finalizing material")
# Set ethnicity to a random value
config_nodes["Value"].outputs[0].default_value = float(randint(0, 4))
return object_mat

def main():
parser = ArgumentParser("hd2_accurate_blender_importer")
parser.add_argument("input_model", type=Path, help="Path to filediver-exported .glb to import into a .blend file")
Expand Down Expand Up @@ -171,6 +230,8 @@ def main():
our_blend.materials = shader_blend.materials
shader_mat = bpy.data.materials["HD2 Shader"]
shader_mat.use_fake_user = True
skin_mat = bpy.data.materials["HD2 Skin"]
skin_mat.use_fake_user = True

optional_usages = ["decal_sheet", "pattern_masks_array"]
unused_texture = bpy.data.images.new("unused", 1, 1, alpha=True, float_buffer=True)
Expand Down Expand Up @@ -203,11 +264,13 @@ def main():
if "material" not in primitive:
continue
material = gltf["materials"][primitive["material"]]
is_pbr = "albedo" in material["extras"] or "albedo_iridescence" in material["extras"] or "normal" in material["extras"]
is_skin = "color_roughness" in material["extras"] and "normal_specular_ao" in material["extras"] and len(material["extras"]) == 2
is_lut = "material_lut" in material["extras"]
if material["name"] in materialTextures:
textures = materialTextures[material["name"]]
else:
is_pbr = "albedo" in material["extras"] or "albedo_iridescence" in material["extras"] or "normal" in material["extras"]
if len(material["extras"]) == 0 or ("material_lut" not in material["extras"] and not is_pbr):
if len(material["extras"]) == 0 or not any((is_pbr, is_skin, is_lut)):
continue
if not args.packall and is_pbr:
continue
Expand All @@ -234,43 +297,10 @@ def main():
obj: Object = bpy.data.objects[node["name"]]
if not ("HD2 Mat " + material["name"]) in bpy.data.materials:
print(" Copying template material")
object_mat = shader_mat.copy()
object_mat.name = "HD2 Mat " + material["name"]
template_group: ShaderNodeGroup = object_mat.node_tree.nodes["HD2 Shader Template"]
template_group.node_tree = template_group.node_tree.copy()
template_group.node_tree.name = object_mat.name + shader_module.ScriptVersion
HD2_Shader = template_group.node_tree

print(" Applying textures")
config_nodes: Dict[str, ShaderNodeTexImage] = object_mat.node_tree.nodes
config_nodes["Secondary Material LUT Texture"].image = unused_secondary_lut
for usage, image in textures.items():
match usage:
case "id_masks_array":
config_nodes["ID Mask Array Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "pattern_masks_array":
config_nodes["Pattern Mask Array"].image = image
image.colorspace_settings.name = "Non-Color"
case "decal_sheet":
config_nodes["Decal Texture"].image = image
image.colorspace_settings.name = "sRGB"
image.alpha_mode = "CHANNEL_PACKED"
case "material_lut":
config_nodes["Primary Material LUT Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "pattern_lut":
config_nodes["Pattern LUT Texture"].image = image
image.colorspace_settings.name = "Non-Color"
case "base_data":
config_nodes["Normal Map"].image = image
image.colorspace_settings.name = "Non-Color"

print(" Finalizing material")
shader_module.update_images(HD2_Shader, object_mat)
shader_module.update_slot_defaults(HD2_Shader, object_mat)
shader_module.connect_input_links(HD2_Shader)
shader_module.update_array_uvs(object_mat)
if is_lut:
object_mat = add_accurate_material(shader_mat, material, shader_module, unused_secondary_lut, textures)
elif is_skin:
object_mat = add_skin_material(skin_mat, material, textures)
else:
print(f" Found existing material 'HD2 Mat {material['name']}'")
object_mat = bpy.data.materials["HD2 Mat " + material["name"]]
Expand Down
Binary file modified scripts/resources/Helldivers2 Shader v1.0.5.blend
Binary file not shown.

0 comments on commit 65cf0bf

Please sign in to comment.