Bring back translucent rendering, try to optimize shaders, try figuring out which materials are masked

This commit is contained in:
sergeypdev 2024-08-24 17:25:07 +04:00
parent 637ad85979
commit 8031b77c8a
7 changed files with 143 additions and 81 deletions

View File

@ -55,8 +55,8 @@ layout(location = 2) in vec2 aUV;
layout(location = 3) in vec3 aTangent; layout(location = 3) in vec3 aTangent;
void main() { void main() {
DrawID = gl_DrawID; DrawID = gl_BaseInstance + gl_InstanceID;
mat4 model = draw_data[gl_DrawID].transform; mat4 model = draw_data[DrawID].transform;
mat4 viewModel = view * model; mat4 viewModel = view * model;
vec4 vPos = viewModel * vec4(aPos.xyz, 1.0); vec4 vPos = viewModel * vec4(aPos.xyz, 1.0);
gl_Position = projection * vPos; gl_Position = projection * vPos;
@ -126,13 +126,6 @@ layout(std430, binding = 2) readonly buffer Materials {
out vec4 FragColor; out vec4 FragColor;
struct EvalMaterial {
vec4 albedo;
float metallic;
float roughness;
vec3 emission;
};
int getShadowMapIndex(int lightIdx) { int getShadowMapIndex(int lightIdx) {
return int(lights[lightIdx].params.z); return int(lights[lightIdx].params.z);
} }
@ -153,39 +146,44 @@ int getCSMSplit(int lightIdx, float depth) {
return CSM_SPLITS - 1; return CSM_SPLITS - 1;
} }
EvalMaterial evalMaterial() { vec4 getAlbedo(int materialIdx) {
EvalMaterial result; return textureSize(materials[materialIdx].albedo_map, 0) == ivec2(0) ? vec4(pow(materials[materialIdx].albedo.rgb, vec3(2.2)), materials[materialIdx].albedo.a) : texture(materials[materialIdx].albedo_map, VertexOut.uv * materials[materialIdx].albedo_map_uv_scale);
int materialIdx = draw_data[DrawID].materialIdx;
result.albedo = textureSize(materials[materialIdx].albedo_map, 0) == ivec2(0) ? vec4(pow(materials[materialIdx].albedo.rgb, vec3(2.2)), materials[materialIdx].albedo.a) : texture(materials[materialIdx].albedo_map, VertexOut.uv * materials[materialIdx].albedo_map_uv_scale);
result.metallic = textureSize(materials[materialIdx].metallic_map, 0) == ivec2(0) ? materials[materialIdx].metallic : texture(materials[materialIdx].metallic_map, VertexOut.uv * materials[materialIdx].metallic_map_uv_scale).b;
result.roughness = max(0.01, textureSize(materials[materialIdx].roughness_map, 0) == ivec2(0) ? materials[materialIdx].roughness : texture(materials[materialIdx].roughness_map, VertexOut.uv * materials[materialIdx].roughness_map_uv_scale).g);
result.emission = textureSize(materials[materialIdx].emission_map, 0) == ivec2(0) ? materials[materialIdx].emission : texture(materials[materialIdx].emission_map, VertexOut.uv * materials[materialIdx].emission_map_uv_scale).rgb;
return result;
} }
vec3 schlickFresnel(EvalMaterial mat, float NDotV) { float getRoughness(int materialIdx) {
vec3 f0 = mix(vec3(0.04), mat.albedo.rgb, mat.metallic); return max(0.01, textureSize(materials[materialIdx].roughness_map, 0) == ivec2(0) ? materials[materialIdx].roughness : texture(materials[materialIdx].roughness_map, VertexOut.uv * materials[materialIdx].roughness_map_uv_scale).g);
}
float getMetallic(int materialIdx) {
return textureSize(materials[materialIdx].metallic_map, 0) == ivec2(0) ? materials[materialIdx].metallic : texture(materials[materialIdx].metallic_map, VertexOut.uv * materials[materialIdx].metallic_map_uv_scale).b;
}
vec3 getEmission(int materialIdx) {
return textureSize(materials[materialIdx].emission_map, 0) == ivec2(0) ? materials[materialIdx].emission : texture(materials[materialIdx].emission_map, VertexOut.uv * materials[materialIdx].emission_map_uv_scale).rgb;
}
vec3 schlickFresnel(int matIdx, float NDotV) {
vec3 f0 = mix(vec3(0.04), getAlbedo(matIdx).rgb, getMetallic(matIdx));
return f0 + (1.0 - f0) * pow(1.0 - NDotV, 5.0); return f0 + (1.0 - f0) * pow(1.0 - NDotV, 5.0);
} }
vec3 schlickFresnelRoughness(EvalMaterial mat, float NDotV) { vec3 schlickFresnelRoughness(int matIdx, float NDotV) {
vec3 f0 = mix(vec3(0.04), mat.albedo.rgb, mat.metallic); vec3 f0 = mix(vec3(0.04), getAlbedo(matIdx).rgb, getMetallic(matIdx));
return f0 + (max(vec3(1.0 - mat.roughness), f0) - f0) * pow(1.0 - NDotV, 5.0); return f0 + (max(vec3(1.0 - getRoughness(matIdx)), f0) - f0) * pow(1.0 - NDotV, 5.0);
} }
const float eps = 0.0001; const float eps = 0.0001;
float geomSmith(EvalMaterial mat, float DotVal) { float geomSmith(int matIdx, float DotVal) {
float k = ((mat.roughness + 1.0) * (mat.roughness + 1.0)) / 8.0; float k = ((getRoughness(matIdx) + 1.0) * (getRoughness(matIdx) + 1.0)) / 8.0;
float denom = DotVal * (1 - k) + k; float denom = DotVal * (1 - k) + k;
return DotVal / denom; return DotVal / denom;
} }
float ggxDistribution(EvalMaterial mat, float NDotH) { float ggxDistribution(int matIdx, float NDotH) {
float a = mat.roughness * mat.roughness; float a = getRoughness(matIdx) * getRoughness(matIdx);
float alpha2 = a * a; float alpha2 = a * a;
float NDotH2 = NDotH * NDotH; float NDotH2 = NDotH * NDotH;
float nom = alpha2; float nom = alpha2;
@ -223,12 +221,12 @@ float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1); return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
} }
vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) { vec3 microfacetModel(int matIdx, int light_idx, vec3 P, vec3 N) {
// Light light = lights[light_idx]; // Light light = lights[light_idx];
vec3 diffuseBrdf = vec3(0); // metallic vec3 diffuseBrdf = vec3(0); // metallic
if (mat.metallic < 1.0) { if (getMetallic(matIdx) < 1.0) {
diffuseBrdf = mat.albedo.rgb / PI; diffuseBrdf = getAlbedo(matIdx).rgb / PI;
} }
// 0 - means directional, 1 - means point light // 0 - means directional, 1 - means point light
@ -311,20 +309,20 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
} }
shadow_mult = clamp(shadow_mult, 0.0, 1.0); shadow_mult = clamp(shadow_mult, 0.0, 1.0);
vec3 F = schlickFresnelRoughness(mat, NDotV); vec3 F = schlickFresnelRoughness(matIdx, NDotV);
vec3 specBrdf = F * ggxDistribution(mat, NDotH) * geomSmith(mat, NDotV) * geomSmith(mat, NDotL); vec3 specBrdf = F * (ggxDistribution(matIdx, NDotH) * geomSmith(matIdx, NDotV) * geomSmith(matIdx, NDotL));
specBrdf /= 4.0 * NDotL * NDotV + 0.0001; specBrdf /= 4.0 * NDotL * NDotV + 0.0001;
vec3 kS = F; vec3 kS = F;
vec3 kD = vec3(1.0) - kS; vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - mat.metallic; kD *= 1.0 - getMetallic(matIdx);
return (kD * diffuseBrdf + specBrdf) * lightI * NDotL * shadow_mult; return (kD * diffuseBrdf + specBrdf) * lightI * (NDotL * shadow_mult);
} }
vec3 ibl(EvalMaterial mat, vec3 N, vec3 V) { vec3 ibl(int matIdx, vec3 N, vec3 V) {
float NDotV = max(dot(N, V), 0.0); float NDotV = max(dot(N, V), 0.0);
vec3 F = schlickFresnelRoughness(mat, NDotV); vec3 F = schlickFresnelRoughness(matIdx, NDotV);
vec3 kS = F; vec3 kS = F;
vec3 kD = 1.0 - kS; vec3 kD = 1.0 - kS;
@ -333,19 +331,18 @@ vec3 ibl(EvalMaterial mat, vec3 N, vec3 V) {
float ambient_spec = dot(R, VertexOut.vUp) * 0.5 + 0.5; float ambient_spec = dot(R, VertexOut.vUp) * 0.5 + 0.5;
vec3 irradiance = vec3(1.0, 0.9764705882352941, 0.9921568627450981) * 79 * ambient_diff; vec3 irradiance = vec3(1.0, 0.9764705882352941, 0.9921568627450981) * 79 * ambient_diff;
vec3 diffuse = irradiance * mat.albedo.rgb; vec3 diffuse = irradiance * getAlbedo(matIdx).rgb;
vec3 reflectedColor = vec3(0.9, 0.9064705882352941, 0.9921568627450981) * 79 * ambient_spec; vec3 reflectedColor = vec3(0.9, 0.9064705882352941, 0.9921568627450981) * 79 * ambient_spec;
vec2 envBRDF = textureLod(brdfLut, vec2(NDotV, mat.roughness), 0).rg; vec2 envBRDF = textureLod(brdfLut, vec2(NDotV, getRoughness(matIdx)), 0).rg;
vec3 specular = reflectedColor * (F * envBRDF.x + envBRDF.y); vec3 specular = reflectedColor * (F * envBRDF.x + envBRDF.y);
return kD * diffuse + specular; return kD * diffuse + specular;
} }
void main() { void main() {
int materialIdx = draw_data[DrawID].materialIdx; int matIdx = draw_data[DrawID].materialIdx;
sampler2D normal_map = materials[materialIdx].normal_map; sampler2D normal_map = materials[matIdx].normal_map;
vec2 normal_map_uv_scale = materials[materialIdx].normal_map_uv_scale; vec2 normal_map_uv_scale = materials[matIdx].normal_map_uv_scale;
EvalMaterial material = evalMaterial();
vec3 N = textureSize(normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(normal_map, VertexOut.uv * normal_map_uv_scale).xy, 0); vec3 N = textureSize(normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(normal_map, VertexOut.uv * normal_map_uv_scale).xy, 0);
N = N * 2.0 - 1.0; N = N * 2.0 - 1.0;
@ -356,18 +353,17 @@ void main() {
vec3 finalColor = vec3(0); vec3 finalColor = vec3(0);
// int n_lights = clamp(int(lights_count), 0, MAX_POINT_LIGHTS); int n_lights = clamp(int(lights_count), 0, MAX_POINT_LIGHTS);
for (int i = 0; i < MAX_POINT_LIGHTS; i++) { for (int i = 0; i < n_lights; i++) {
if (i >= lights_count) break; finalColor += microfacetModel(matIdx, i, VertexOut.vPos, N);
finalColor += microfacetModel(material, i, VertexOut.vPos, N);
} }
vec3 V = normalize(-VertexOut.vPos); vec3 V = normalize(-VertexOut.vPos);
// ambient // ambient
finalColor += ibl(material, N, V); finalColor += ibl(matIdx, N, V);
finalColor += material.emission; finalColor += getEmission(matIdx);
FragColor = vec4(finalColor, material.albedo.a); FragColor = vec4(finalColor, getAlbedo(matIdx).a);
} }

View File

@ -69,9 +69,9 @@ vec3 linearToSRGB(vec3 color) {
void main() { void main() {
vec3 hdr_color = texture(screen_sampler, VertexOut.uv).rgb; vec3 hdr_color = texture(screen_sampler, VertexOut.uv).rgb;
hdr_color = ACESFitted(hdr_color * 0.008); hdr_color = ACESFitted(hdr_color * 0.02);
FragColor.rgb = linearToSRGB(hdr_color); FragColor.rgb = hdr_color;
FragColor.a = 1; FragColor.a = 1;
} }

View File

@ -20,7 +20,7 @@ layout(std430, binding = 3) readonly buffer DrawCmdDatas {
layout(location = 0) in vec3 aPos; layout(location = 0) in vec3 aPos;
void main() { void main() {
mat4 model = draw_data[gl_DrawID].transform; mat4 model = draw_data[gl_BaseInstance + gl_InstanceID].transform;
mat4 viewModel = view * model; mat4 viewModel = view * model;
vec4 vPos = viewModel * vec4(aPos.xyz, 1.0); vec4 vPos = viewModel * vec4(aPos.xyz, 1.0);
gl_Position = projection * vPos; gl_Position = projection * vPos;

View File

@ -429,9 +429,9 @@ pub const LightCommand = union(LightKind) {
}; };
const DrawCommandKey = packed struct { const DrawCommandKey = packed struct {
transparent: u1 = 0,
distance: u15 = 0,
mesh: u16 = 0, mesh: u16 = 0,
distance: u15 = 0,
transparent: u1 = 0,
}; };
pub fn drawLight(self: *Render, cmd: LightCommand) void { pub fn drawLight(self: *Render, cmd: LightCommand) void {
@ -449,7 +449,7 @@ pub fn draw(self: *Render, cmd: DrawCommand) void {
const dist: u15 = @intFromFloat(std.math.clamp(view_origin.distance(cmd.transform.extractTranslation()) / max_value, 0.0, max_value)); const dist: u15 = @intFromFloat(std.math.clamp(view_origin.distance(cmd.transform.extractTranslation()) / max_value, 0.0, max_value));
const key = DrawCommandKey{ const key = DrawCommandKey{
.transparent = if (material.blend_mode == .AlphaBlend) 1 else 0, .transparent = if (material.blend_mode == .AlphaBlend) 1 else 0,
.distance = if (material.blend_mode == .AlphaBlend) ~dist else dist, // TODO: calculate distance. Opaque should be front to back, transparent back to front .distance = if (material.blend_mode == .AlphaBlend) dist else dist, // TODO: calculate distance. Opaque should be front to back, transparent back to front
.mesh = @intCast(cmd.mesh.id % std.math.maxInt(u16)), .mesh = @intCast(cmd.mesh.id % std.math.maxInt(u16)),
}; };
self.command_buffer[self.command_count].key = key; self.command_buffer[self.command_count].key = key;
@ -547,6 +547,20 @@ pub fn finish(self: *Render) void {
defer zoneSort.deinit(); defer zoneSort.deinit();
self.sortCommands(self.command_buffer[0..self.command_count]); self.sortCommands(self.command_buffer[0..self.command_count]);
// Sorting validation
if (false) {
var alpha = false;
for (self.command_buffer[0..self.command_count]) |cmd| {
if (!alpha and cmd.key.transparent == 1) {
alpha = true;
}
if (alpha and cmd.key.transparent == 0) {
std.log.err("WRONG SORTING!\n", .{});
}
}
}
} }
if (self.update_view_frustum) { if (self.update_view_frustum) {
@ -801,8 +815,7 @@ pub fn finish(self: *Render) void {
gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const switched_to_alpha_blend = false; var switched_to_alpha_blend = false;
gl.disable(gl.BLEND);
var draw_indirect_cmds = self.frame_arena.alloc(DrawIndirectCmd, MAX_DRAW_COMMANDS) catch @panic("OOM"); var draw_indirect_cmds = self.frame_arena.alloc(DrawIndirectCmd, MAX_DRAW_COMMANDS) catch @panic("OOM");
var draw_cmd_data = self.frame_arena.alloc(DrawCommandData, MAX_DRAW_COMMANDS) catch @panic("OOM"); var draw_cmd_data = self.frame_arena.alloc(DrawCommandData, MAX_DRAW_COMMANDS) catch @panic("OOM");
@ -818,6 +831,7 @@ pub fn finish(self: *Render) void {
defer gl.deleteBuffers(1, &draw_cmd_data_buf); defer gl.deleteBuffers(1, &draw_cmd_data_buf);
var rendered_count: usize = 0; var rendered_count: usize = 0;
var rendered_opaque_count: usize = 0;
// Prepare indirect draw commands // Prepare indirect draw commands
{ {
@ -830,7 +844,7 @@ pub fn finish(self: *Render) void {
var material_map = std.StringHashMap(i32).init(self.frame_arena); var material_map = std.StringHashMap(i32).init(self.frame_arena);
var materials_count: usize = 0; var materials_count: usize = 0;
cmds: for (self.command_buffer[0..self.command_count]) |*cmd| { for (self.command_buffer[0..self.command_count]) |*cmd| {
const mesh = self.assetman.resolveMesh(cmd.mesh); const mesh = self.assetman.resolveMesh(cmd.mesh);
// const aabb = math.AABB.fromMinMax(mesh.aabb.min, mesh.aabb.max); // const aabb = math.AABB.fromMinMax(mesh.aabb.min, mesh.aabb.max);
@ -842,10 +856,9 @@ pub fn finish(self: *Render) void {
// Opaque objects are drawn, start rendering alpha blended objects // Opaque objects are drawn, start rendering alpha blended objects
if (material.blend_mode == .AlphaBlend and !switched_to_alpha_blend) { if (material.blend_mode == .AlphaBlend and !switched_to_alpha_blend) {
break :cmds; rendered_opaque_count = rendered_count;
// switched_to_alpha_blend = true; std.log.debug("opaque: {}\n", .{rendered_opaque_count});
// gl.enable(gl.BLEND); switched_to_alpha_blend = true;
// gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
} }
const material_bytes = std.mem.asBytes(&material); const material_bytes = std.mem.asBytes(&material);
@ -868,13 +881,17 @@ pub fn finish(self: *Render) void {
.instance_count = 1, .instance_count = 1,
.first_index = mesh.indices.offset / 4, .first_index = mesh.indices.offset / 4,
.base_vertex = mesh.indices.base_vertex, .base_vertex = mesh.indices.base_vertex,
.base_instance = 0, .base_instance = @intCast(rendered_count),
}; };
rendered_count += 1; rendered_count += 1;
} }
} }
if (rendered_opaque_count == 0) {
rendered_opaque_count = rendered_count;
}
{ {
const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[self.tripple_buffer_index * self.uboAlignedSizeOf(CameraMatrices) ..].ptr)); const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[self.tripple_buffer_index * self.uboAlignedSizeOf(CameraMatrices) ..].ptr));
camera_matrix.* = .{ camera_matrix.* = .{
@ -903,13 +920,15 @@ pub fn finish(self: *Render) void {
{ {
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.z_prepass).program); gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.z_prepass).program);
gl.bindVertexArray(self.shadow_vao); gl.bindVertexArray(self.shadow_vao);
gl.depthFunc(gl.LESS);
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value()); self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value());
checkGLError(); checkGLError();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
checkGLError(); checkGLError();
gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_count), @sizeOf(DrawIndirectCmd)); gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_opaque_count), @sizeOf(DrawIndirectCmd));
checkGLError();
} }
// Main pass // Main pass
@ -933,11 +952,25 @@ pub fn finish(self: *Render) void {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
checkGLError(); checkGLError();
gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_count), 0); gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_opaque_count), @sizeOf(DrawIndirectCmd));
checkGLError();
} }
gl.disable(gl.BLEND); // Alpha Pass
gl.depthFunc(gl.LESS); const blended_draws_count = rendered_count - rendered_opaque_count;
if (blended_draws_count > 0) {
std.log.debug("blended: {}\n", .{blended_draws_count});
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.depthFunc(gl.LEQUAL);
gl.depthMask(gl.FALSE);
gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, @ptrFromInt(@sizeOf(DrawIndirectCmd) * rendered_opaque_count), @intCast(blended_draws_count), @sizeOf(DrawIndirectCmd));
gl.disable(gl.BLEND);
gl.depthFunc(gl.LEQUAL);
gl.depthMask(gl.TRUE);
}
// Debug stuff // Debug stuff
{ {

View File

@ -370,6 +370,7 @@ pub const Material = extern struct {
pub const BlendMode = enum(u8) { pub const BlendMode = enum(u8) {
Opaque = 0, Opaque = 0,
AlphaBlend = 1, AlphaBlend = 1,
AlphaMask = 2,
}; };
albedo: Vec4 = Vec4.one(), albedo: Vec4 = Vec4.one(),

View File

@ -505,7 +505,7 @@ export fn game_update() bool {
gmem.render.drawLight(.{ gmem.render.drawLight(.{
.directional = .{ .directional = .{
.dir = dir4.toVec3(), .dir = dir4.toVec3(),
.color = color, .color = color.add(Vec3.new(100, 0, 0)),
}, },
}); });
} }

View File

@ -193,6 +193,18 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
} }
} }
for (texture_outputs) |*tex_out| {
defer tex_out.asset.file.close();
try processTexture(
allocator,
tex_out.tex_type,
@as([*]u8, @ptrCast(tex_out.texture.pcData))[0..@intCast(tex_out.texture.mWidth)],
tex_out.asset.file,
&tex_out.has_alpha,
);
}
// Materials // Materials
var material_outputs = try allocator.alloc(formats.Material, @intCast(scene.mNumMaterials)); var material_outputs = try allocator.alloc(formats.Material, @intCast(scene.mNumMaterials));
if (scene.mMaterials != null) { if (scene.mMaterials != null) {
@ -205,31 +217,41 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
if (c.aiGetMaterialColor(material, AI_MATKEY_BASE_COLOR, 0, 0, &base_color) == c.aiReturn_SUCCESS) { if (c.aiGetMaterialColor(material, AI_MATKEY_BASE_COLOR, 0, 0, &base_color) == c.aiReturn_SUCCESS) {
// TODO: rgba // TODO: rgba
mat_output.albedo = Vec4.new(base_color.r, base_color.g, base_color.b, base_color.a); mat_output.albedo = Vec4.new(base_color.r, base_color.g, base_color.b, base_color.a);
if (std.math.approxEqAbs(f32, base_color.a, 0.0, std.math.floatEps(f32))) {
mat_output.blend_mode = .AlphaMask;
} else if (base_color.a < 1.0) {
mat_output.blend_mode = .AlphaBlend;
}
} }
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_BASE_COLOR) > 0) { if (c.aiGetMaterialTextureCount(material, c.aiTextureType_BASE_COLOR) > 0) {
const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_BASE_COLOR, 0); const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_BASE_COLOR, 0);
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs); var has_alpha = false;
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs, &has_alpha);
mat_output.albedo_map.id = entry.getAssetId(); mat_output.albedo_map.id = entry.getAssetId();
if (has_alpha) {
mat_output.blend_mode = .AlphaBlend;
}
} }
_ = c.aiGetMaterialFloat(material, AI_MATKEY_METALLIC_FACTOR, 0, 0, &mat_output.metallic); _ = c.aiGetMaterialFloat(material, AI_MATKEY_METALLIC_FACTOR, 0, 0, &mat_output.metallic);
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_METALNESS) > 0) { if (c.aiGetMaterialTextureCount(material, c.aiTextureType_METALNESS) > 0) {
const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_METALNESS, 0); const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_METALNESS, 0);
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs); const entry = mat_texture.path.resolveAssetListEntry(texture_outputs, null);
mat_output.metallic_map.id = entry.getAssetId(); mat_output.metallic_map.id = entry.getAssetId();
} }
_ = c.aiGetMaterialFloat(material, AI_MATKEY_ROUGHNESS_FACTOR, 0, 0, &mat_output.roughness); _ = c.aiGetMaterialFloat(material, AI_MATKEY_ROUGHNESS_FACTOR, 0, 0, &mat_output.roughness);
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_DIFFUSE_ROUGHNESS) > 0) { if (c.aiGetMaterialTextureCount(material, c.aiTextureType_DIFFUSE_ROUGHNESS) > 0) {
const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_DIFFUSE_ROUGHNESS, 0); const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_DIFFUSE_ROUGHNESS, 0);
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs); const entry = mat_texture.path.resolveAssetListEntry(texture_outputs, null);
mat_output.roughness_map.id = entry.getAssetId(); mat_output.roughness_map.id = entry.getAssetId();
} }
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_NORMALS) > 0) { if (c.aiGetMaterialTextureCount(material, c.aiTextureType_NORMALS) > 0) {
const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_NORMALS, 0); const mat_texture = try getMaterialTexture(allocator, input_dir, material, c.aiTextureType_NORMALS, 0);
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs); const entry = mat_texture.path.resolveAssetListEntry(texture_outputs, null);
switch (mat_texture.path) { switch (mat_texture.path) {
.embedded => |idx| { .embedded => |idx| {
texture_outputs[idx].tex_type = .Normal; texture_outputs[idx].tex_type = .Normal;
@ -242,12 +264,6 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
} }
} }
for (texture_outputs) |tex_out| {
defer tex_out.asset.file.close();
try processTexture(allocator, tex_out.tex_type, @as([*]u8, @ptrCast(tex_out.texture.pcData))[0..@intCast(tex_out.texture.mWidth)], tex_out.asset.file);
}
const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]); const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]);
var mesh_outputs = try allocator.alloc(AssetListEntry, meshes.len); var mesh_outputs = try allocator.alloc(AssetListEntry, meshes.len);
for (meshes, 0..) |mesh, i| { for (meshes, 0..) |mesh, i| {
@ -351,6 +367,7 @@ const TextureOutput = struct {
texture: *c.aiTexture, texture: *c.aiTexture,
asset: AssetOutput, asset: AssetOutput,
tex_type: TextureType = .Color, tex_type: TextureType = .Color,
has_alpha: bool = false,
}; };
const AssimpTextureRef = union(enum) { const AssimpTextureRef = union(enum) {
@ -373,13 +390,17 @@ const AssimpTextureRef = union(enum) {
return .{ .external = cwd_relative_path }; return .{ .external = cwd_relative_path };
} }
pub fn resolveAssetListEntry(self: AssimpTextureRef, embedded: []const TextureOutput) AssetListEntry { pub fn resolveAssetListEntry(self: AssimpTextureRef, embedded: []const TextureOutput, out_has_alpha: ?*bool) AssetListEntry {
switch (self) { switch (self) {
.embedded => |idx| { .embedded => |idx| {
if (out_has_alpha) |has_alpha| {
has_alpha.* = embedded[idx].has_alpha;
}
return embedded[idx].asset.list_entry; return embedded[idx].asset.list_entry;
}, },
.external => |path| { .external => |path| {
// TODO: resolve relative to current input file // TODO: resolve relative to current input file
// TODO: has_alpha
return AssetListEntry{ .src_path = AssetPath.fromString(path), .type = .Texture }; return AssetListEntry{ .src_path = AssetPath.fromString(path), .type = .Texture };
}, },
} }
@ -547,7 +568,8 @@ fn processTextureFromFile(allocator: std.mem.Allocator, input: []const u8, outpu
const contents = try std.fs.cwd().readFileAlloc(allocator, input, ASSET_MAX_BYTES); const contents = try std.fs.cwd().readFileAlloc(allocator, input, ASSET_MAX_BYTES);
const texture_type = guessTextureTypeFromName(input); const texture_type = guessTextureTypeFromName(input);
try processTexture(allocator, texture_type, contents, output.file); var has_alpha = false;
try processTexture(allocator, texture_type, contents, output.file, &has_alpha);
} }
/// Using naming conventions /// Using naming conventions
@ -576,7 +598,7 @@ const TextureType = enum {
HDR, HDR,
}; };
fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, contents: []const u8, out_file: std.fs.File) !void { fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, contents: []const u8, out_file: std.fs.File, out_has_alpha: *bool) !void {
var width_int: c_int = 0; var width_int: c_int = 0;
var height_int: c_int = 0; var height_int: c_int = 0;
var comps: c_int = 0; var comps: c_int = 0;
@ -601,6 +623,16 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
const rgba_data = rgba_data_c[0 .. width * height * 4]; const rgba_data = rgba_data_c[0 .. width * height * 4];
if (comps == 4) {
var i: usize = 3;
while (i < rgba_data.len) : (i += 4) {
if (rgba_data[i] < 255) {
out_has_alpha.* = true;
break;
}
}
}
var padded_width: usize = width; var padded_width: usize = width;
var padded_height: usize = height; var padded_height: usize = height;
var rgba_data_padded = rgba_data; var rgba_data_padded = rgba_data;