Support odd sized textures, fix segfault in AssetManager
This commit is contained in:
parent
35ff95e694
commit
7b15f55173
@ -15,6 +15,7 @@ layout(std140, binding = 0) uniform Matrices {
|
|||||||
mat4 view;
|
mat4 view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: rename
|
||||||
layout(std140, binding = 1) uniform Lights {
|
layout(std140, binding = 1) uniform Lights {
|
||||||
Light lights[MAX_POINT_LIGHTS];
|
Light lights[MAX_POINT_LIGHTS];
|
||||||
uint lights_count;
|
uint lights_count;
|
||||||
@ -25,17 +26,22 @@ layout(location = 1) uniform mat4 model;
|
|||||||
|
|
||||||
layout(location = 2) uniform vec3 color;
|
layout(location = 2) uniform vec3 color;
|
||||||
layout(location = 3, bindless_sampler) uniform sampler2D albedo_map;
|
layout(location = 3, bindless_sampler) uniform sampler2D albedo_map;
|
||||||
|
layout(location = 4) uniform vec2 albedo_map_uv_scale = vec2(1);
|
||||||
|
|
||||||
layout(location = 4, bindless_sampler) uniform sampler2D normal_map;
|
layout(location = 5, bindless_sampler) uniform sampler2D normal_map;
|
||||||
|
layout(location = 6) uniform vec2 normal_map_uv_scale = vec2(1);
|
||||||
|
|
||||||
layout(location = 5) uniform float metallic;
|
layout(location = 7) uniform float metallic;
|
||||||
layout(location = 6, bindless_sampler) uniform sampler2D metallic_map;
|
layout(location = 8, bindless_sampler) uniform sampler2D metallic_map;
|
||||||
|
layout(location = 9) uniform vec2 metallic_map_uv_scale = vec2(1);
|
||||||
|
|
||||||
layout(location = 7) uniform float roughness;
|
layout(location = 10) uniform float roughness;
|
||||||
layout(location = 8, bindless_sampler) uniform sampler2D roughness_map;
|
layout(location = 11, bindless_sampler) uniform sampler2D roughness_map;
|
||||||
|
layout(location = 12) uniform vec2 roughness_map_uv_scale = vec2(1);
|
||||||
|
|
||||||
layout(location = 9) uniform vec3 emission;
|
layout(location = 13) uniform vec3 emission;
|
||||||
layout(location = 10, bindless_sampler) uniform sampler2D emission_map;
|
layout(location = 14, bindless_sampler) uniform sampler2D emission_map;
|
||||||
|
layout(location = 15) uniform vec2 emission_map_uv_scale = vec2(1);
|
||||||
|
|
||||||
|
|
||||||
// Input, output blocks
|
// Input, output blocks
|
||||||
@ -80,11 +86,11 @@ struct Material {
|
|||||||
|
|
||||||
Material evalMaterial() {
|
Material evalMaterial() {
|
||||||
Material result;
|
Material result;
|
||||||
result.albedo = textureSize(albedo_map, 0) == ivec2(0) ? pow(color, vec3(2.2)) : texture(albedo_map, VertexOut.uv).rgb;
|
result.albedo = textureSize(albedo_map, 0) == ivec2(0) ? pow(color, vec3(2.2)) : texture(albedo_map, VertexOut.uv * albedo_map_uv_scale).rgb;
|
||||||
float fMetallic = textureSize(metallic_map, 0) == ivec2(0) ? metallic : texture(metallic_map, VertexOut.uv).b;
|
float fMetallic = textureSize(metallic_map, 0) == ivec2(0) ? metallic : texture(metallic_map, VertexOut.uv * metallic_map_uv_scale).b;
|
||||||
result.metallic = fMetallic > 0.5;
|
result.metallic = fMetallic > 0.1;
|
||||||
result.roughness = max(0.01, textureSize(roughness_map, 0) == ivec2(0) ? roughness : texture(roughness_map, VertexOut.uv).g);
|
result.roughness = max(0.01, textureSize(roughness_map, 0) == ivec2(0) ? roughness : texture(roughness_map, VertexOut.uv * roughness_map_uv_scale).g);
|
||||||
result.emission = textureSize(emission_map, 0) == ivec2(0) ? emission : texture(emission_map, VertexOut.uv).rgb;
|
result.emission = textureSize(emission_map, 0) == ivec2(0) ? emission : texture(emission_map, VertexOut.uv * emission_map_uv_scale).rgb;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -110,8 +116,8 @@ float ggxDistribution(Material mat, float NDotH) {
|
|||||||
return alpha2 / (PI * d * d);
|
return alpha2 / (PI * d * d);
|
||||||
}
|
}
|
||||||
|
|
||||||
float lightAttenuation(float dist, float radius) {
|
float lightAttenuation(float point, float dist, float radius) {
|
||||||
float d = max(dist - radius, 0);
|
float d = max(dist - radius, 0) * point;
|
||||||
|
|
||||||
float denom = d/radius + 1;
|
float denom = d/radius + 1;
|
||||||
float att = 1 / (denom * denom);
|
float att = 1 / (denom * denom);
|
||||||
@ -128,13 +134,22 @@ vec3 microfacetModel(Material mat, Light light, vec3 P, vec3 N) {
|
|||||||
diffuseBrdf = mat.albedo;
|
diffuseBrdf = mat.albedo;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 lightI = light.color.rgb * light.color.a;
|
// 0 - means directional, 1 - means point light
|
||||||
float lightRadius = light.vPos.w;
|
float point = light.vPos.w;
|
||||||
vec3 L = light.vPos.xyz - P;
|
vec3 lightI = light.color.rgb;
|
||||||
|
float lightRadius = light.color.a;
|
||||||
|
vec3 L = mix(-light.vPos.xyz, light.vPos.xyz - P, light.vPos.w);
|
||||||
float dist = length(L);
|
float dist = length(L);
|
||||||
L /= dist;
|
L /= dist;
|
||||||
|
|
||||||
float att = lightAttenuation(dist, lightRadius);
|
// TODO: I think this is uniform control flow
|
||||||
|
// so makes sense to use `if` there for directional/point
|
||||||
|
// and don't calculate attenuation for directional at all
|
||||||
|
float att = lightAttenuation(
|
||||||
|
point,
|
||||||
|
dist,
|
||||||
|
lightRadius
|
||||||
|
);
|
||||||
lightI *= att;
|
lightI *= att;
|
||||||
|
|
||||||
vec3 V = normalize(-P);
|
vec3 V = normalize(-P);
|
||||||
@ -153,7 +168,7 @@ vec3 microfacetModel(Material mat, Light light, vec3 P, vec3 N) {
|
|||||||
void main() {
|
void main() {
|
||||||
Material material = evalMaterial();
|
Material material = evalMaterial();
|
||||||
|
|
||||||
vec3 N = textureSize(normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(normal_map, VertexOut.uv).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;
|
||||||
N.z = sqrt(clamp(1 - N.x * N.x - N.y * N.y, 0, 1));
|
N.z = sqrt(clamp(1 - N.x * N.x - N.y * N.y, 0, 1));
|
||||||
N = normalize(N);
|
N = normalize(N);
|
||||||
|
@ -22,6 +22,7 @@ const asset_manifest = @import("asset_manifest");
|
|||||||
const assets = @import("assets");
|
const assets = @import("assets");
|
||||||
const checkGLError = @import("Render.zig").checkGLError;
|
const checkGLError = @import("Render.zig").checkGLError;
|
||||||
// const basisu = @import("mach-basisu");
|
// const basisu = @import("mach-basisu");
|
||||||
|
const Vec2 = @import("zalgebra").Vec2;
|
||||||
const Vec3 = @import("zalgebra").Vec3;
|
const Vec3 = @import("zalgebra").Vec3;
|
||||||
|
|
||||||
pub const AssetId = assets.AssetId;
|
pub const AssetId = assets.AssetId;
|
||||||
@ -67,22 +68,22 @@ pub fn deinit(self: *AssetManager) void {
|
|||||||
self.loaded_assets.deinit(self.allocator);
|
self.loaded_assets.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveShader(self: *AssetManager, handle: Handle.Shader) *const LoadedShader {
|
pub fn resolveShader(self: *AssetManager, handle: Handle.Shader) LoadedShader {
|
||||||
if (handle.id == 0) return &NullShader;
|
if (handle.id == 0) return NullShader;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
return &asset.shader;
|
return asset.shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.loadShader(handle.id);
|
return self.loadShader(handle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) *const LoadedShaderProgram {
|
pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram {
|
||||||
if (handle.id == 0) return &NullShaderProgram;
|
if (handle.id == 0) return NullShaderProgram;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
switch (asset.*) {
|
switch (asset.*) {
|
||||||
.shaderProgram => |*shader| {
|
.shaderProgram => |shader| {
|
||||||
return shader;
|
return shader;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
@ -92,12 +93,12 @@ pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) *
|
|||||||
return self.loadShaderProgram(handle);
|
return self.loadShaderProgram(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) *const LoadedMesh {
|
pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh {
|
||||||
if (handle.id == 0) return &NullMesh;
|
if (handle.id == 0) return NullMesh;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
switch (asset.*) {
|
switch (asset.*) {
|
||||||
.mesh => |*mesh| {
|
.mesh => |mesh| {
|
||||||
return mesh;
|
return mesh;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
@ -107,12 +108,12 @@ pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) *const LoadedMesh {
|
|||||||
return self.loadMesh(handle.id);
|
return self.loadMesh(handle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) *const LoadedTexture {
|
pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) LoadedTexture {
|
||||||
if (handle.id == 0) return &NullTexture;
|
if (handle.id == 0) return NullTexture;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
switch (asset.*) {
|
switch (asset.*) {
|
||||||
.texture => |*texture| {
|
.texture => |texture| {
|
||||||
return texture;
|
return texture;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
@ -122,27 +123,27 @@ pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) *const Loaded
|
|||||||
return self.loadTexture(handle.id);
|
return self.loadTexture(handle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) *const formats.Scene {
|
pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) formats.Scene {
|
||||||
if (handle.id == 0) return &NullScene.scene;
|
if (handle.id == 0) return NullScene.scene;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
switch (asset.*) {
|
switch (asset.*) {
|
||||||
.scene => |*scene| {
|
.scene => |scene| {
|
||||||
return &scene.scene;
|
return scene.scene;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &self.loadScene(handle.id).scene;
|
return self.loadScene(handle.id).scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveMaterial(self: *AssetManager, handle: Handle.Material) *const formats.Material {
|
pub fn resolveMaterial(self: *AssetManager, handle: Handle.Material) formats.Material {
|
||||||
if (handle.id == 0) return &NullMaterial;
|
if (handle.id == 0) return NullMaterial;
|
||||||
|
|
||||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
switch (asset.*) {
|
switch (asset.*) {
|
||||||
.material => |*material| {
|
.material => |material| {
|
||||||
return material;
|
return material;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
@ -181,15 +182,15 @@ pub const ShaderProgramDefinition = struct {
|
|||||||
fragment: []const u8,
|
fragment: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) *const LoadedShaderProgram {
|
pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram {
|
||||||
return self.loadShaderProgramErr(handle.id) catch |err| {
|
return self.loadShaderProgramErr(handle.id) catch |err| {
|
||||||
std.log.err("Failed to load shader program {}\n", .{err});
|
std.log.err("Failed to load shader program {}\n", .{err});
|
||||||
|
|
||||||
return &NullShaderProgram;
|
return NullShaderProgram;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !*LoadedShaderProgram {
|
fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !LoadedShaderProgram {
|
||||||
const data = try self.loadFile(self.frame_arena, asset_manifest.getPath(id), SHADER_MAX_BYTES);
|
const data = try self.loadFile(self.frame_arena, asset_manifest.getPath(id), SHADER_MAX_BYTES);
|
||||||
const program = formats.ShaderProgram.fromBuffer(data.bytes);
|
const program = formats.ShaderProgram.fromBuffer(data.bytes);
|
||||||
|
|
||||||
@ -237,12 +238,15 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !*LoadedShaderProgram
|
|||||||
return error.ProgramLinkFailed;
|
return error.ProgramLinkFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loaded_shader_program = LoadedShaderProgram{
|
||||||
|
.program = prog,
|
||||||
|
};
|
||||||
try self.loaded_assets.put(self.allocator, id, .{
|
try self.loaded_assets.put(self.allocator, id, .{
|
||||||
.shaderProgram = .{ .program = prog },
|
.shaderProgram = loaded_shader_program,
|
||||||
});
|
});
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
return &self.loaded_assets.getPtr(id).?.shaderProgram;
|
return loaded_shader_program;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NullShader = LoadedShader{
|
const NullShader = LoadedShader{
|
||||||
@ -296,16 +300,17 @@ const NullScene = LoadedScene{
|
|||||||
|
|
||||||
const NullMaterial = formats.Material{};
|
const NullMaterial = formats.Material{};
|
||||||
|
|
||||||
pub fn loadMesh(self: *AssetManager, id: AssetId) *const LoadedMesh {
|
pub fn loadMesh(self: *AssetManager, id: AssetId) LoadedMesh {
|
||||||
return self.loadMeshErr(id) catch |err| {
|
return self.loadMeshErr(id) catch |err| {
|
||||||
std.log.err("Error: {} loading mesh at path: {s}", .{ err, asset_manifest.getPath(id) });
|
std.log.err("Error: {} loading mesh at path: {s}", .{ err, asset_manifest.getPath(id) });
|
||||||
return &NullMesh;
|
return NullMesh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadMeshErr(self: *AssetManager, id: AssetId) !*const LoadedMesh {
|
fn loadMeshErr(self: *AssetManager, id: AssetId) !LoadedMesh {
|
||||||
const path = asset_manifest.getPath(id);
|
const path = asset_manifest.getPath(id);
|
||||||
const data = try self.loadFile(self.frame_arena, path, MESH_MAX_BYTES);
|
const data = try self.loadFile(self.frame_arena, path, MESH_MAX_BYTES);
|
||||||
|
defer self.frame_arena.free(data.bytes);
|
||||||
const mesh = formats.Mesh.fromBuffer(data.bytes);
|
const mesh = formats.Mesh.fromBuffer(data.bytes);
|
||||||
|
|
||||||
var bufs = [_]gl.GLuint{ 0, 0, 0, 0, 0 };
|
var bufs = [_]gl.GLuint{ 0, 0, 0, 0, 0 };
|
||||||
@ -390,20 +395,21 @@ fn loadMeshErr(self: *AssetManager, id: AssetId) !*const LoadedMesh {
|
|||||||
|
|
||||||
try self.loaded_assets.put(self.allocator, id, .{ .mesh = loaded_mesh });
|
try self.loaded_assets.put(self.allocator, id, .{ .mesh = loaded_mesh });
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
return &self.loaded_assets.getPtr(id).?.mesh;
|
return loaded_mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadTexture(self: *AssetManager, id: AssetId) *const LoadedTexture {
|
fn loadTexture(self: *AssetManager, id: AssetId) LoadedTexture {
|
||||||
return self.loadTextureErr(id) catch |err| {
|
return self.loadTextureErr(id) catch |err| {
|
||||||
std.log.err("Error: {} loading texture at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
std.log.err("Error: {} loading texture at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
||||||
|
|
||||||
return &NullTexture;
|
return NullTexture;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture {
|
||||||
const path = asset_manifest.getPath(id);
|
const path = asset_manifest.getPath(id);
|
||||||
const data = try self.loadFile(self.frame_arena, path, TEXTURE_MAX_BYTES);
|
const data = try self.loadFile(self.frame_arena, path, TEXTURE_MAX_BYTES);
|
||||||
|
defer self.frame_arena.free(data.bytes);
|
||||||
|
|
||||||
const texture = try formats.Texture.fromBuffer(self.frame_arena, data.bytes);
|
const texture = try formats.Texture.fromBuffer(self.frame_arena, data.bytes);
|
||||||
|
|
||||||
@ -424,8 +430,8 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
|||||||
name,
|
name,
|
||||||
@intCast(texture.mipLevels()),
|
@intCast(texture.mipLevels()),
|
||||||
gl_format,
|
gl_format,
|
||||||
@intCast(texture.header.width),
|
@intCast(texture.header.padded_width),
|
||||||
@intCast(texture.header.height),
|
@intCast(texture.header.padded_height),
|
||||||
);
|
);
|
||||||
checkGLError();
|
checkGLError();
|
||||||
|
|
||||||
@ -445,63 +451,69 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
|||||||
checkGLError();
|
checkGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uv_scale = Vec2.new(
|
||||||
|
@as(f32, @floatFromInt(texture.header.width)) / @as(f32, @floatFromInt(texture.header.padded_width)),
|
||||||
|
@as(f32, @floatFromInt(texture.header.height)) / @as(f32, @floatFromInt(texture.header.padded_height)),
|
||||||
|
);
|
||||||
|
|
||||||
const handle = gl.GL_ARB_bindless_texture.getTextureHandleARB(name);
|
const handle = gl.GL_ARB_bindless_texture.getTextureHandleARB(name);
|
||||||
gl.GL_ARB_bindless_texture.makeTextureHandleResidentARB(handle);
|
gl.GL_ARB_bindless_texture.makeTextureHandleResidentARB(handle);
|
||||||
errdefer gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(handle);
|
errdefer gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(handle);
|
||||||
|
|
||||||
|
const loaded_texture = LoadedTexture{
|
||||||
|
.name = name,
|
||||||
|
.handle = handle,
|
||||||
|
.uv_scale = uv_scale,
|
||||||
|
};
|
||||||
try self.loaded_assets.put(
|
try self.loaded_assets.put(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
id,
|
id,
|
||||||
.{
|
.{ .texture = loaded_texture },
|
||||||
.texture = LoadedTexture{
|
|
||||||
.name = name,
|
|
||||||
.handle = handle,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
return &self.loaded_assets.getPtr(id).?.texture;
|
return loaded_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadScene(self: *AssetManager, id: AssetId) *const LoadedScene {
|
fn loadScene(self: *AssetManager, id: AssetId) LoadedScene {
|
||||||
return self.loadSceneErr(id) catch |err| {
|
return self.loadSceneErr(id) catch |err| {
|
||||||
std.log.err("Error: {} loading scene at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
std.log.err("Error: {} loading scene at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
||||||
|
|
||||||
return &NullScene;
|
return NullScene;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadSceneErr(self: *AssetManager, id: AssetId) !*const LoadedScene {
|
fn loadSceneErr(self: *AssetManager, id: AssetId) !LoadedScene {
|
||||||
const path = asset_manifest.getPath(id);
|
const path = asset_manifest.getPath(id);
|
||||||
const data = try self.loadFile(self.allocator, path, TEXTURE_MAX_BYTES);
|
const data = try self.loadFile(self.allocator, path, TEXTURE_MAX_BYTES);
|
||||||
|
|
||||||
const scene = try formats.Scene.fromBuffer(data.bytes);
|
const scene = try formats.Scene.fromBuffer(data.bytes);
|
||||||
|
const loaded_scene = LoadedScene{
|
||||||
|
.buf = data.bytes,
|
||||||
|
.scene = scene,
|
||||||
|
};
|
||||||
|
|
||||||
try self.loaded_assets.put(
|
try self.loaded_assets.put(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
id,
|
id,
|
||||||
.{
|
.{
|
||||||
.scene = LoadedScene{
|
.scene = loaded_scene,
|
||||||
.buf = data.bytes,
|
|
||||||
.scene = scene,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
return &self.loaded_assets.getPtr(id).?.scene;
|
return loaded_scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadMaterial(self: *AssetManager, id: AssetId) *const formats.Material {
|
fn loadMaterial(self: *AssetManager, id: AssetId) formats.Material {
|
||||||
return self.loadMaterialErr(id) catch |err| {
|
return self.loadMaterialErr(id) catch |err| {
|
||||||
std.log.err("Error: {} loading material at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
std.log.err("Error: {} loading material at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
||||||
|
|
||||||
return &NullMaterial;
|
return NullMaterial;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadMaterialErr(self: *AssetManager, id: AssetId) !*const formats.Material {
|
fn loadMaterialErr(self: *AssetManager, id: AssetId) !formats.Material {
|
||||||
const path = asset_manifest.getPath(id);
|
const path = asset_manifest.getPath(id);
|
||||||
const data = try self.loadFile(self.frame_arena, path, TEXTURE_MAX_BYTES);
|
const data = try self.loadFile(self.frame_arena, path, TEXTURE_MAX_BYTES);
|
||||||
|
|
||||||
@ -516,7 +528,7 @@ fn loadMaterialErr(self: *AssetManager, id: AssetId) !*const formats.Material {
|
|||||||
);
|
);
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
return &self.loaded_assets.getPtr(id).?.material;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoadedAsset = union(enum) {
|
const LoadedAsset = union(enum) {
|
||||||
@ -549,6 +561,7 @@ const LoadedMesh = struct {
|
|||||||
const LoadedTexture = struct {
|
const LoadedTexture = struct {
|
||||||
name: gl.GLuint,
|
name: gl.GLuint,
|
||||||
handle: gl.GLuint64,
|
handle: gl.GLuint64,
|
||||||
|
uv_scale: Vec2 = Vec2.one(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoadedScene = struct {
|
const LoadedScene = struct {
|
||||||
@ -614,22 +627,23 @@ fn loadFile(self: *AssetManager, allocator: std.mem.Allocator, path: []const u8,
|
|||||||
return .{ .bytes = bytes, .modified = meta.modified() };
|
return .{ .bytes = bytes, .modified = meta.modified() };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadShader(self: *AssetManager, id: AssetId) *const LoadedShader {
|
fn loadShader(self: *AssetManager, id: AssetId) LoadedShader {
|
||||||
return self.loadShaderErr(id) catch |err| {
|
return self.loadShaderErr(id) catch |err| {
|
||||||
std.log.err("Error: {} when loading shader id {} {s}", .{ err, id, asset_manifest.getPath(id) });
|
std.log.err("Error: {} when loading shader id {} {s}", .{ err, id, asset_manifest.getPath(id) });
|
||||||
return &NullShader;
|
return NullShader;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadShaderErr(self: *AssetManager, id: AssetId) !*LoadedShader {
|
fn loadShaderErr(self: *AssetManager, id: AssetId) !LoadedShader {
|
||||||
const path = asset_manifest.getPath(id);
|
const path = asset_manifest.getPath(id);
|
||||||
|
|
||||||
const data = try self.loadFile(self.allocator, path, SHADER_MAX_BYTES);
|
const data = try self.loadFile(self.allocator, path, SHADER_MAX_BYTES);
|
||||||
|
|
||||||
try self.loaded_assets.put(self.allocator, id, .{ .shader = LoadedShader{ .source = data.bytes } });
|
const loaded_shader = LoadedShader{ .source = data.bytes };
|
||||||
|
try self.loaded_assets.put(self.allocator, id, .{ .shader = loaded_shader });
|
||||||
try self.modified_times.put(self.allocator, id, data.modified);
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
return &self.loaded_assets.getPtr(id).?.shader;
|
return loaded_shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compileShader(self: *AssetManager, source: []const u8, shader_type: ShaderType) !gl.GLuint {
|
fn compileShader(self: *AssetManager, source: []const u8, shader_type: ShaderType) !gl.GLuint {
|
||||||
@ -691,6 +705,7 @@ fn deleteDependees(self: *AssetManager, id: AssetId) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unloadAssetWithDependees(self: *AssetManager, id: AssetId) void {
|
fn unloadAssetWithDependees(self: *AssetManager, id: AssetId) void {
|
||||||
|
std.log.debug("unload asset id {}: {s}\n", .{ id, asset_manifest.getPath(id) });
|
||||||
self.deleteDependees(id);
|
self.deleteDependees(id);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -206,30 +206,54 @@ pub fn draw(self: *Render, cmd: DrawCommand) void {
|
|||||||
const material: Material = if (cmd.material_override) |mat| mat else mesh.material;
|
const material: Material = if (cmd.material_override) |mat| mat else mesh.material;
|
||||||
|
|
||||||
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&cmd.transform.data));
|
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&cmd.transform.data));
|
||||||
|
{
|
||||||
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&material.albedo.data));
|
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&material.albedo.data));
|
||||||
|
|
||||||
|
const albedo_map = self.assetman.resolveTexture(material.albedo_map);
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
Uniform.AlbedoMap.value(),
|
Uniform.AlbedoMap.value(),
|
||||||
self.assetman.resolveTexture(material.albedo_map).handle,
|
albedo_map.handle,
|
||||||
);
|
);
|
||||||
|
gl.uniform2fv(Uniform.AlbedoMapUVScale.value(), 1, @ptrCast(&albedo_map.uv_scale.data));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const normal_map = self.assetman.resolveTexture(material.normal_map);
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
Uniform.NormalMap.value(),
|
Uniform.NormalMap.value(),
|
||||||
self.assetman.resolveTexture(material.normal_map).handle,
|
normal_map.handle,
|
||||||
);
|
);
|
||||||
|
gl.uniform2fv(Uniform.NormalMapUVScale.value(), 1, @ptrCast(&normal_map.uv_scale.data));
|
||||||
|
}
|
||||||
|
{
|
||||||
gl.uniform1fv(Uniform.Metallic.value(), 1, &material.metallic);
|
gl.uniform1fv(Uniform.Metallic.value(), 1, &material.metallic);
|
||||||
|
|
||||||
|
const metallic_map = self.assetman.resolveTexture(material.metallic_map);
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
Uniform.MetallicMap.value(),
|
Uniform.MetallicMap.value(),
|
||||||
self.assetman.resolveTexture(material.metallic_map).handle,
|
metallic_map.handle,
|
||||||
);
|
);
|
||||||
|
gl.uniform2fv(Uniform.MetallicMapUVScale.value(), 1, @ptrCast(&metallic_map.uv_scale.data));
|
||||||
|
}
|
||||||
|
{
|
||||||
gl.uniform1fv(Uniform.Roughness.value(), 1, &material.roughness);
|
gl.uniform1fv(Uniform.Roughness.value(), 1, &material.roughness);
|
||||||
|
|
||||||
|
const roughness_map = self.assetman.resolveTexture(material.roughness_map);
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
Uniform.RoughnessMap.value(),
|
Uniform.RoughnessMap.value(),
|
||||||
self.assetman.resolveTexture(material.roughness_map).handle,
|
roughness_map.handle,
|
||||||
);
|
);
|
||||||
|
gl.uniform2fv(Uniform.RoughnessMapUVScale.value(), 1, @ptrCast(&roughness_map.uv_scale.data));
|
||||||
|
}
|
||||||
|
{
|
||||||
gl.uniform1fv(Uniform.Emission.value(), 1, &material.emission);
|
gl.uniform1fv(Uniform.Emission.value(), 1, &material.emission);
|
||||||
|
|
||||||
|
const emission_map = self.assetman.resolveTexture(material.emission_map);
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
Uniform.EmissionMap.value(),
|
Uniform.EmissionMap.value(),
|
||||||
self.assetman.resolveTexture(material.emission_map).handle,
|
emission_map.handle,
|
||||||
);
|
);
|
||||||
|
gl.uniform2fv(Uniform.EmissionMapUVScale.value(), 1, @ptrCast(&emission_map.uv_scale.data));
|
||||||
|
}
|
||||||
|
|
||||||
mesh.positions.bind(Render.Attrib.Position.value());
|
mesh.positions.bind(Render.Attrib.Position.value());
|
||||||
mesh.normals.bind(Render.Attrib.Normal.value());
|
mesh.normals.bind(Render.Attrib.Normal.value());
|
||||||
@ -306,13 +330,18 @@ pub const Uniform = enum(gl.GLint) {
|
|||||||
ModelMatrix = 1,
|
ModelMatrix = 1,
|
||||||
Color = 2,
|
Color = 2,
|
||||||
AlbedoMap = 3,
|
AlbedoMap = 3,
|
||||||
NormalMap = 4,
|
AlbedoMapUVScale = 4,
|
||||||
Metallic = 5,
|
NormalMap = 5,
|
||||||
MetallicMap = 6,
|
NormalMapUVScale = 6,
|
||||||
Roughness = 7,
|
Metallic = 7,
|
||||||
RoughnessMap = 8,
|
MetallicMap = 8,
|
||||||
Emission = 9,
|
MetallicMapUVScale = 9,
|
||||||
EmissionMap = 10,
|
Roughness = 10,
|
||||||
|
RoughnessMap = 11,
|
||||||
|
RoughnessMapUVScale = 12,
|
||||||
|
Emission = 13,
|
||||||
|
EmissionMap = 14,
|
||||||
|
EmissionMapUVScale = 15,
|
||||||
|
|
||||||
pub inline fn value(self: Uniform) gl.GLint {
|
pub inline fn value(self: Uniform) gl.GLint {
|
||||||
return @intFromEnum(self);
|
return @intFromEnum(self);
|
||||||
@ -339,10 +368,11 @@ const CameraMatrices = extern struct {
|
|||||||
view: Mat4,
|
view: Mat4,
|
||||||
};
|
};
|
||||||
pub const PointLight = extern struct {
|
pub const PointLight = extern struct {
|
||||||
pos_radius: Vec4, // x, y, z - vPos, w - radius
|
pos: Vec4, // x, y, z, w - vPos
|
||||||
color_intensity: Vec4, // x, y, z - color, w - intensity
|
color_radius: Vec4, // x, y, z - color, w - radius
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: rename
|
||||||
pub const PointLightArray = extern struct {
|
pub const PointLightArray = extern struct {
|
||||||
lights: [MAX_POINT_LIGHTS]PointLight,
|
lights: [MAX_POINT_LIGHTS]PointLight,
|
||||||
count: c_uint,
|
count: c_uint,
|
||||||
|
217
src/entity.zig
Normal file
217
src/entity.zig
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Handle = @import("assets").Handle;
|
||||||
|
const formats = @import("formats.zig");
|
||||||
|
const Material = formats.Material;
|
||||||
|
const Scene = formats.Scene;
|
||||||
|
|
||||||
|
const za = @import("zalgebra");
|
||||||
|
const Vec2 = za.Vec2;
|
||||||
|
const Vec3 = za.Vec3;
|
||||||
|
const Vec4 = za.Vec4;
|
||||||
|
const Mat4 = za.Mat4;
|
||||||
|
const Quat = za.Quat;
|
||||||
|
|
||||||
|
pub const MAX_ENTITIES = 1024 * 10;
|
||||||
|
|
||||||
|
pub const Entity = struct {
|
||||||
|
pub const Flags = packed struct {
|
||||||
|
active: bool = false,
|
||||||
|
mesh: bool = false,
|
||||||
|
point_light: bool = false,
|
||||||
|
dir_light: bool = false,
|
||||||
|
rotate: bool = false,
|
||||||
|
_pad: u3 = 0, // make it abi sized
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Transform = extern struct {
|
||||||
|
pos: Vec3 = Vec3.zero(),
|
||||||
|
rot: Quat = Quat.identity(),
|
||||||
|
scale: Vec3 = Vec3.one(),
|
||||||
|
|
||||||
|
_local_dirty: bool = true,
|
||||||
|
_local: Mat4 = Mat4.identity(),
|
||||||
|
|
||||||
|
_global_dirty: bool = true,
|
||||||
|
_global: Mat4 = Mat4.identity(),
|
||||||
|
|
||||||
|
pub fn dirty(self: *Transform) void {
|
||||||
|
self._local_dirty = true;
|
||||||
|
self._global_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setPos(self: *Transform, new_pos: Vec3) void {
|
||||||
|
self.pos = new_pos;
|
||||||
|
self.dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(self: *Transform, by: Vec3) void {
|
||||||
|
self.pos = self.pos.add(by);
|
||||||
|
self.dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(self: *Transform, axis: Vec3, angle: f32) void {
|
||||||
|
self.rot = self.rot.mul(Quat.fromAxis(angle, axis));
|
||||||
|
self.dirty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Mesh = extern struct {
|
||||||
|
handle: Handle.Mesh = .{},
|
||||||
|
material: Material = .{}, // used if override_material == true
|
||||||
|
override_material: bool = false,
|
||||||
|
};
|
||||||
|
pub const Light = extern struct {
|
||||||
|
color_intensity: Vec4 = Vec4.one(), // x, y, z - color, w - intensity
|
||||||
|
|
||||||
|
/// Returns Vec3 color multiplied by intensity (w)
|
||||||
|
pub fn premultipliedColor(self: *const Light) Vec3 {
|
||||||
|
const col = self.color_intensity;
|
||||||
|
return Vec3.new(col.x(), col.y(), col.z()).scale(col.w());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const PointLight = extern struct {
|
||||||
|
radius: f32 = std.math.floatEps(f32), // should never be 0 or bad things happen
|
||||||
|
};
|
||||||
|
pub const Rotate = extern struct {
|
||||||
|
axis: Vec3 = Vec3.up(),
|
||||||
|
rate: f32 = 0, // deg/s
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Serializable entity data
|
||||||
|
pub const Data = extern struct {
|
||||||
|
flags: Flags = .{},
|
||||||
|
transform: Transform = .{},
|
||||||
|
mesh: Mesh = .{},
|
||||||
|
light: Light = .{},
|
||||||
|
point_light: PointLight = .{},
|
||||||
|
rotate: Rotate = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Entity list and handle management
|
||||||
|
idx: u32 = 0,
|
||||||
|
gen: u32 = 0,
|
||||||
|
// Free list
|
||||||
|
next: ?*Entity = null,
|
||||||
|
|
||||||
|
parent: ?EntityHandle = null,
|
||||||
|
|
||||||
|
data: Data = .{},
|
||||||
|
|
||||||
|
pub fn setParent(self: *Entity, parent: ?EntityHandle) void {
|
||||||
|
self.parent = parent;
|
||||||
|
self.data.transform.dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn localMatrix(self: *Entity) *const Mat4 {
|
||||||
|
if (self.data.transform._local_dirty) {
|
||||||
|
self.data.transform._local = Mat4.recompose(self.data.transform.pos, self.data.transform.rot, self.data.transform.scale);
|
||||||
|
self.data.transform._local_dirty = false;
|
||||||
|
}
|
||||||
|
return &self.data.transform._local;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn globalMatrix(self: *Entity, world: *World) *const Mat4 {
|
||||||
|
// TODO: think how to reduce pointer chasing
|
||||||
|
if (self.parent) |parent_ent| {
|
||||||
|
if (world.getEntity(parent_ent)) |parent| {
|
||||||
|
if (parent.data.transform._global_dirty or self.data.transform._global_dirty) {
|
||||||
|
self.data.transform._global = parent.globalMatrix(world).mul(self.localMatrix().*);
|
||||||
|
self.data.transform._global_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &self.data.transform._global;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.localMatrix();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EntityHandle = packed struct {
|
||||||
|
idx: u32 = 0,
|
||||||
|
gen: u32 = 0,
|
||||||
|
|
||||||
|
/// Returns handle with 0 idx and gen.
|
||||||
|
/// 0 gen is invalid, valid generations start from 0
|
||||||
|
pub fn invalid() EntityHandle {
|
||||||
|
return EntityHandle{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EntityCreateResult = struct {
|
||||||
|
handle: EntityHandle,
|
||||||
|
ptr: *Entity,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const World = struct {
|
||||||
|
frame_arena: std.mem.Allocator,
|
||||||
|
entities: [MAX_ENTITIES]Entity = [_]Entity{.{}} ** MAX_ENTITIES,
|
||||||
|
entity_count: usize = 0,
|
||||||
|
free_entity: ?*Entity = null,
|
||||||
|
|
||||||
|
pub fn addEntity(self: *World, data: Entity.Data) EntityCreateResult {
|
||||||
|
const ent = result: {
|
||||||
|
if (self.free_entity) |ent| {
|
||||||
|
break :result ent;
|
||||||
|
} else {
|
||||||
|
const new_entity = &self.entities[self.entity_count];
|
||||||
|
new_entity.idx = @intCast(self.entity_count);
|
||||||
|
self.entity_count += 1;
|
||||||
|
break :result new_entity;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const next = ent.next;
|
||||||
|
const gen = ent.gen;
|
||||||
|
const idx = ent.idx;
|
||||||
|
ent.data = data;
|
||||||
|
// TODO: handle wrapping
|
||||||
|
ent.gen = gen + 1;
|
||||||
|
ent.idx = idx;
|
||||||
|
ent.data.flags.active = true;
|
||||||
|
self.free_entity = next;
|
||||||
|
|
||||||
|
return EntityCreateResult{
|
||||||
|
.handle = EntityHandle{ .idx = idx, .gen = ent.gen },
|
||||||
|
.ptr = ent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns a scene and returns a hand to the root entity
|
||||||
|
pub fn createScene(self: *World, scene: Scene) EntityHandle {
|
||||||
|
if (scene.entities.len == 0) {
|
||||||
|
return EntityHandle.invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handles = self.frame_arena.alloc(EntityHandle, scene.entities.len) catch @panic("OOM"); // not handling error, this is unrecoverable
|
||||||
|
|
||||||
|
for (0.., handles, scene.entities, scene.parents) |i, *out_handle, *ent, parent| {
|
||||||
|
const res = self.addEntity(ent.*);
|
||||||
|
out_handle.* = res.handle;
|
||||||
|
|
||||||
|
if (parent >= 0) {
|
||||||
|
std.debug.assert(parent < i);
|
||||||
|
res.ptr.parent = handles[@intCast(parent)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handles[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getEntity(self: *World, handle: EntityHandle) ?*Entity {
|
||||||
|
// Gen 0 is always invalid
|
||||||
|
if (handle.gen == 0) return null;
|
||||||
|
|
||||||
|
const ent = &self.entities[handle.idx];
|
||||||
|
if (ent.gen != handle.gen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeEntity(self: *World, handle: EntityHandle) void {
|
||||||
|
const ent = self.getEntity(handle) orelse return;
|
||||||
|
ent.flags.active = false;
|
||||||
|
ent.next = self.free_entity;
|
||||||
|
self.free_entity = ent;
|
||||||
|
}
|
||||||
|
};
|
@ -3,7 +3,7 @@ const builtin = @import("builtin");
|
|||||||
const Handle = @import("assets").Handle;
|
const Handle = @import("assets").Handle;
|
||||||
const za = @import("zalgebra");
|
const za = @import("zalgebra");
|
||||||
const Vec3 = za.Vec3;
|
const Vec3 = za.Vec3;
|
||||||
pub const Entity = @import("globals.zig").Entity;
|
pub const Entity = @import("entity.zig").Entity;
|
||||||
|
|
||||||
pub const native_endian = builtin.cpu.arch.endian();
|
pub const native_endian = builtin.cpu.arch.endian();
|
||||||
|
|
||||||
@ -197,6 +197,8 @@ pub const Texture = struct {
|
|||||||
format: Format,
|
format: Format,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
padded_width: u32,
|
||||||
|
padded_height: u32,
|
||||||
mip_count: u32,
|
mip_count: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,8 +212,8 @@ pub const Texture = struct {
|
|||||||
pub fn getMipDesc(self: *const Texture, mip_level: usize) MipDesc {
|
pub fn getMipDesc(self: *const Texture, mip_level: usize) MipDesc {
|
||||||
const divisor = std.math.powi(u32, 2, @intCast(mip_level)) catch unreachable;
|
const divisor = std.math.powi(u32, 2, @intCast(mip_level)) catch unreachable;
|
||||||
return MipDesc{
|
return MipDesc{
|
||||||
.width = self.header.width / divisor,
|
.width = self.header.padded_width / divisor,
|
||||||
.height = self.header.height / divisor,
|
.height = self.header.padded_height / divisor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
src/game.zig
43
src/game.zig
@ -113,7 +113,7 @@ fn loadGL() void {
|
|||||||
@panic("gl.load");
|
@panic("gl.load");
|
||||||
};
|
};
|
||||||
gl.debugMessageCallback(glDebugCallback, null);
|
gl.debugMessageCallback(glDebugCallback, null);
|
||||||
gl.enable(gl.DEBUG_OUTPUT);
|
//gl.enable(gl.DEBUG_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glDebugCallback(source: gl.GLenum, _type: gl.GLenum, id: gl.GLuint, severity: gl.GLenum, length: gl.GLsizei, message: [*:0]const u8, userParam: ?*anyopaque) callconv(.C) void {
|
fn glDebugCallback(source: gl.GLenum, _type: gl.GLenum, id: gl.GLuint, severity: gl.GLenum, length: gl.GLsizei, message: [*:0]const u8, userParam: ?*anyopaque) callconv(.C) void {
|
||||||
@ -185,6 +185,12 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
|
|
||||||
gl.viewport(0, 0, globals.g_init.width, globals.g_init.height);
|
gl.viewport(0, 0, globals.g_init.width, globals.g_init.height);
|
||||||
|
|
||||||
|
_ = globals.g_mem.world.addEntity(.{
|
||||||
|
.flags = .{ .dir_light = true },
|
||||||
|
.transform = .{ .rot = Quat.fromEulerAngles(Vec3.new(60, 15, 0)) },
|
||||||
|
.light = .{ .color_intensity = Vec4.new(1, 1, 0.83, 1) },
|
||||||
|
});
|
||||||
|
|
||||||
const light_root = globals.g_mem.world.addEntity(.{
|
const light_root = globals.g_mem.world.addEntity(.{
|
||||||
.flags = .{ .rotate = true },
|
.flags = .{ .rotate = true },
|
||||||
.transform = .{ .pos = Vec3.new(0, 0.1, 0) },
|
.transform = .{ .pos = Vec3.new(0, 0.1, 0) },
|
||||||
@ -194,7 +200,8 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
const light1 = globals.g_mem.world.addEntity(.{
|
const light1 = globals.g_mem.world.addEntity(.{
|
||||||
.transform = .{ .pos = Vec3.new(1.8, 1, 0) },
|
.transform = .{ .pos = Vec3.new(1.8, 1, 0) },
|
||||||
.flags = .{ .point_light = true, .rotate = true },
|
.flags = .{ .point_light = true, .rotate = true },
|
||||||
.point_light = .{ .color_intensity = Vec4.new(1.0, 0.3, 0.1, 100.0), .radius = 0.1 },
|
.light = .{ .color_intensity = Vec4.new(1.0, 0.3, 0.1, 100.0) },
|
||||||
|
.point_light = .{ .radius = 0.1 },
|
||||||
.rotate = .{ .axis = Vec3.up(), .rate = -40 },
|
.rotate = .{ .axis = Vec3.up(), .rate = -40 },
|
||||||
});
|
});
|
||||||
light1.ptr.setParent(light_root.handle);
|
light1.ptr.setParent(light_root.handle);
|
||||||
@ -202,20 +209,16 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
const light2 = globals.g_mem.world.addEntity(.{
|
const light2 = globals.g_mem.world.addEntity(.{
|
||||||
.transform = .{ .pos = Vec3.new(-2, 0, 0) },
|
.transform = .{ .pos = Vec3.new(-2, 0, 0) },
|
||||||
.flags = .{ .point_light = true, .rotate = true },
|
.flags = .{ .point_light = true, .rotate = true },
|
||||||
.point_light = .{
|
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0) },
|
||||||
.color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0),
|
.point_light = .{ .radius = 0.1 },
|
||||||
.radius = 0.1,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
light2.ptr.setParent(light1.handle);
|
light2.ptr.setParent(light1.handle);
|
||||||
|
|
||||||
_ = globals.g_mem.world.addEntity(.{
|
_ = globals.g_mem.world.addEntity(.{
|
||||||
.transform = .{ .pos = Vec3.new(1, 0.5, 4) },
|
.transform = .{ .pos = Vec3.new(1, 0.5, 4) },
|
||||||
.flags = .{ .point_light = true },
|
.flags = .{ .point_light = true },
|
||||||
.point_light = .{
|
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0) },
|
||||||
.color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0),
|
.point_light = .{ .radius = 1 },
|
||||||
.radius = 1,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Plane
|
// Plane
|
||||||
@ -441,9 +444,23 @@ export fn game_update() bool {
|
|||||||
var pos4 = Vec4.new(pos.x(), pos.y(), pos.z(), 1.0);
|
var pos4 = Vec4.new(pos.x(), pos.y(), pos.z(), 1.0);
|
||||||
pos4 = gmem.render.camera.view_mat.mulByVec4(pos4);
|
pos4 = gmem.render.camera.view_mat.mulByVec4(pos4);
|
||||||
|
|
||||||
|
const color = ent.data.light.premultipliedColor();
|
||||||
point_lights.lights[point_lights.count] = .{
|
point_lights.lights[point_lights.count] = .{
|
||||||
.pos_radius = Vec4.new(pos4.x(), pos4.y(), pos4.z(), ent.data.point_light.radius),
|
.pos = Vec4.new(pos4.x(), pos4.y(), pos4.z(), 1),
|
||||||
.color_intensity = ent.data.point_light.color_intensity,
|
.color_radius = Vec4.new(color.x(), color.y(), color.z(), ent.data.point_light.radius),
|
||||||
|
};
|
||||||
|
point_lights.count += 1;
|
||||||
|
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ent.data.flags.dir_light) {
|
||||||
|
var dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
|
||||||
|
dir4 = gmem.render.camera.view_mat.mulByVec4(dir4);
|
||||||
|
const color = ent.data.light.premultipliedColor();
|
||||||
|
point_lights.lights[point_lights.count] = .{
|
||||||
|
.pos = dir4,
|
||||||
|
.color_radius = Vec4.new(color.x(), color.y(), color.z(), 1),
|
||||||
};
|
};
|
||||||
point_lights.count += 1;
|
point_lights.count += 1;
|
||||||
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
||||||
@ -469,7 +486,7 @@ export fn game_update() bool {
|
|||||||
} else if (ent.data.flags.point_light) {
|
} else if (ent.data.flags.point_light) {
|
||||||
gmem.render.draw(.{
|
gmem.render.draw(.{
|
||||||
.mesh = a.Meshes.sphere.Icosphere,
|
.mesh = a.Meshes.sphere.Icosphere,
|
||||||
.material_override = .{ .albedo = ent.data.point_light.color() },
|
.material_override = .{ .albedo = ent.data.light.premultipliedColor() },
|
||||||
.transform = ent.globalMatrix(&gmem.world).*,
|
.transform = ent.globalMatrix(&gmem.world).*,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
207
src/globals.zig
207
src/globals.zig
@ -1,10 +1,8 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("sdl.zig");
|
const c = @import("sdl.zig");
|
||||||
const AssetManager = @import("AssetManager.zig");
|
|
||||||
const Render = @import("Render.zig");
|
const Render = @import("Render.zig");
|
||||||
const formats = @import("formats.zig");
|
const AssetManager = @import("AssetManager.zig");
|
||||||
const Material = formats.Material;
|
const World = @import("entity.zig").World;
|
||||||
const Scene = formats.Scene;
|
|
||||||
|
|
||||||
const za = @import("zalgebra");
|
const za = @import("zalgebra");
|
||||||
const Vec2 = za.Vec2;
|
const Vec2 = za.Vec2;
|
||||||
@ -21,8 +19,6 @@ pub var g_assetman: *AssetManager = undefined;
|
|||||||
pub const DEFAULT_WIDTH = 800;
|
pub const DEFAULT_WIDTH = 800;
|
||||||
pub const DEFAULT_HEIGHT = 600;
|
pub const DEFAULT_HEIGHT = 600;
|
||||||
|
|
||||||
pub const MAX_ENTITIES = 1024;
|
|
||||||
|
|
||||||
pub const InitMemory = struct {
|
pub const InitMemory = struct {
|
||||||
global_allocator: std.mem.Allocator,
|
global_allocator: std.mem.Allocator,
|
||||||
window: *c.SDL_Window,
|
window: *c.SDL_Window,
|
||||||
@ -33,205 +29,6 @@ pub const InitMemory = struct {
|
|||||||
vsync: bool = false,
|
vsync: bool = false,
|
||||||
syswm_info: c.SDL_SysWMinfo = .{},
|
syswm_info: c.SDL_SysWMinfo = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Entity = struct {
|
|
||||||
pub const Flags = packed struct {
|
|
||||||
active: bool = false,
|
|
||||||
mesh: bool = false,
|
|
||||||
point_light: bool = false,
|
|
||||||
rotate: bool = false,
|
|
||||||
_pad: u4 = 0, // make it abi sized
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Transform = extern struct {
|
|
||||||
pos: Vec3 = Vec3.zero(),
|
|
||||||
rot: Quat = Quat.identity(),
|
|
||||||
scale: Vec3 = Vec3.one(),
|
|
||||||
|
|
||||||
_local_dirty: bool = true,
|
|
||||||
_local: Mat4 = Mat4.identity(),
|
|
||||||
|
|
||||||
_global_dirty: bool = true,
|
|
||||||
_global: Mat4 = Mat4.identity(),
|
|
||||||
|
|
||||||
pub fn dirty(self: *Transform) void {
|
|
||||||
self._local_dirty = true;
|
|
||||||
self._global_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setPos(self: *Transform, new_pos: Vec3) void {
|
|
||||||
self.pos = new_pos;
|
|
||||||
self.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn translate(self: *Transform, by: Vec3) void {
|
|
||||||
self.pos = self.pos.add(by);
|
|
||||||
self.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rotate(self: *Transform, axis: Vec3, angle: f32) void {
|
|
||||||
self.rot = self.rot.mul(Quat.fromAxis(angle, axis));
|
|
||||||
self.dirty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Mesh = extern struct {
|
|
||||||
handle: AssetManager.Handle.Mesh = .{},
|
|
||||||
material: Material = .{}, // used if override_material == true
|
|
||||||
override_material: bool = false,
|
|
||||||
};
|
|
||||||
pub const PointLight = extern struct {
|
|
||||||
radius: f32 = std.math.floatEps(f32), // should never be 0 or bad things happen
|
|
||||||
color_intensity: Vec4 = Vec4.one(), // x, y, z - color, w - intensity
|
|
||||||
|
|
||||||
pub fn color(self: *PointLight) Vec3 {
|
|
||||||
const col = self.color_intensity;
|
|
||||||
return Vec3.new(col.x(), col.y(), col.z());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pub const Rotate = extern struct {
|
|
||||||
axis: Vec3 = Vec3.up(),
|
|
||||||
rate: f32 = 0, // deg/s
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Serializable entity data
|
|
||||||
pub const Data = extern struct {
|
|
||||||
flags: Flags = .{},
|
|
||||||
transform: Transform = .{},
|
|
||||||
mesh: Mesh = .{},
|
|
||||||
point_light: PointLight = .{},
|
|
||||||
rotate: Rotate = .{},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Entity list and handle management
|
|
||||||
idx: u32 = 0,
|
|
||||||
gen: u32 = 0,
|
|
||||||
// Free list
|
|
||||||
next: ?*Entity = null,
|
|
||||||
|
|
||||||
parent: ?EntityHandle = null,
|
|
||||||
|
|
||||||
data: Data = .{},
|
|
||||||
|
|
||||||
pub fn setParent(self: *Entity, parent: ?EntityHandle) void {
|
|
||||||
self.parent = parent;
|
|
||||||
self.data.transform.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn localMatrix(self: *Entity) *const Mat4 {
|
|
||||||
if (self.data.transform._local_dirty) {
|
|
||||||
self.data.transform._local = Mat4.recompose(self.data.transform.pos, self.data.transform.rot, self.data.transform.scale);
|
|
||||||
self.data.transform._local_dirty = false;
|
|
||||||
}
|
|
||||||
return &self.data.transform._local;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn globalMatrix(self: *Entity, world: *World) *const Mat4 {
|
|
||||||
// TODO: think how to reduce pointer chasing
|
|
||||||
if (self.parent) |parent_ent| {
|
|
||||||
if (world.getEntity(parent_ent)) |parent| {
|
|
||||||
if (parent.data.transform._global_dirty or self.data.transform._global_dirty) {
|
|
||||||
self.data.transform._global = parent.globalMatrix(world).mul(self.localMatrix().*);
|
|
||||||
self.data.transform._global_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &self.data.transform._global;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.localMatrix();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const EntityHandle = packed struct {
|
|
||||||
idx: u32 = 0,
|
|
||||||
gen: u32 = 0,
|
|
||||||
|
|
||||||
/// Returns handle with 0 idx and gen.
|
|
||||||
/// 0 gen is invalid, valid generations start from 0
|
|
||||||
pub fn invalid() EntityHandle {
|
|
||||||
return EntityHandle{};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const EntityCreateResult = struct {
|
|
||||||
handle: EntityHandle,
|
|
||||||
ptr: *Entity,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const World = struct {
|
|
||||||
frame_arena: std.mem.Allocator,
|
|
||||||
entities: [MAX_ENTITIES]Entity = [_]Entity{.{}} ** MAX_ENTITIES,
|
|
||||||
entity_count: usize = 0,
|
|
||||||
free_entity: ?*Entity = null,
|
|
||||||
|
|
||||||
pub fn addEntity(self: *World, data: Entity.Data) EntityCreateResult {
|
|
||||||
const ent = result: {
|
|
||||||
if (self.free_entity) |ent| {
|
|
||||||
break :result ent;
|
|
||||||
} else {
|
|
||||||
const new_entity = &self.entities[self.entity_count];
|
|
||||||
new_entity.idx = @intCast(self.entity_count);
|
|
||||||
self.entity_count += 1;
|
|
||||||
break :result new_entity;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const next = ent.next;
|
|
||||||
const gen = ent.gen;
|
|
||||||
const idx = ent.idx;
|
|
||||||
ent.data = data;
|
|
||||||
// TODO: handle wrapping
|
|
||||||
ent.gen = gen + 1;
|
|
||||||
ent.idx = idx;
|
|
||||||
ent.data.flags.active = true;
|
|
||||||
self.free_entity = next;
|
|
||||||
|
|
||||||
return EntityCreateResult{
|
|
||||||
.handle = EntityHandle{ .idx = idx, .gen = ent.gen },
|
|
||||||
.ptr = ent,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawns a scene and returns a hand to the root entity
|
|
||||||
pub fn createScene(self: *World, scene: *const Scene) EntityHandle {
|
|
||||||
if (scene.entities.len == 0) {
|
|
||||||
return EntityHandle.invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handles = self.frame_arena.alloc(EntityHandle, scene.entities.len) catch @panic("OOM"); // not handling error, this is unrecoverable
|
|
||||||
|
|
||||||
for (0.., handles, scene.entities, scene.parents) |i, *out_handle, *ent, parent| {
|
|
||||||
const res = self.addEntity(ent.*);
|
|
||||||
out_handle.* = res.handle;
|
|
||||||
|
|
||||||
if (parent >= 0) {
|
|
||||||
std.debug.assert(parent < i);
|
|
||||||
res.ptr.parent = handles[@intCast(parent)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return handles[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getEntity(self: *World, handle: EntityHandle) ?*Entity {
|
|
||||||
// Gen 0 is always invalid
|
|
||||||
if (handle.gen == 0) return null;
|
|
||||||
|
|
||||||
const ent = &self.entities[handle.idx];
|
|
||||||
if (ent.gen != handle.gen) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn removeEntity(self: *World, handle: EntityHandle) void {
|
|
||||||
const ent = self.getEntity(handle) orelse return;
|
|
||||||
ent.flags.active = false;
|
|
||||||
ent.next = self.free_entity;
|
|
||||||
self.free_entity = ent;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GameMemory = struct {
|
pub const GameMemory = struct {
|
||||||
global_allocator: std.mem.Allocator,
|
global_allocator: std.mem.Allocator,
|
||||||
frame_fba: std.heap.FixedBufferAllocator,
|
frame_fba: std.heap.FixedBufferAllocator,
|
||||||
|
@ -103,7 +103,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
|
|
||||||
var line_stream = std.io.fixedBufferStream(line);
|
var line_stream = std.io.fixedBufferStream(line);
|
||||||
const asset_list_entry = try asset_list.readAssetListEntryText(alloc, line_stream.reader());
|
const asset_list_entry = try asset_list.readAssetListEntryText(alloc, line_stream.reader());
|
||||||
std.log.debug("list entry {} {s}", .{ asset_list_entry.type, asset_list_entry.src_path.getPath() });
|
|
||||||
try assets.append(asset_list_entry);
|
try assets.append(asset_list_entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +165,6 @@ fn writeAssetManifest(arena: std.mem.Allocator, writer: anytype, assets: []Asset
|
|||||||
);
|
);
|
||||||
for (assets) |asset_list_entry| {
|
for (assets) |asset_list_entry| {
|
||||||
const path = try asset_list_entry.getOutputPath(&buf);
|
const path = try asset_list_entry.getOutputPath(&buf);
|
||||||
std.log.debug("asset output path: {s}\n", .{path});
|
|
||||||
try std.fmt.format(writer, " asset_paths.put(fba.allocator(), {}, \"{}\") catch @panic(\"OOM\");\n", .{ asset_list_entry.getAssetId(), std.zig.fmtEscapes(path) });
|
try std.fmt.format(writer, " asset_paths.put(fba.allocator(), {}, \"{}\") catch @panic(\"OOM\");\n", .{ asset_list_entry.getAssetId(), std.zig.fmtEscapes(path) });
|
||||||
}
|
}
|
||||||
try writer.writeAll("}\n\n");
|
try writer.writeAll("}\n\n");
|
||||||
|
@ -578,18 +578,36 @@ 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 padded_width: usize = width;
|
||||||
premultiplyAlpha(rgba_data);
|
var padded_height: usize = height;
|
||||||
|
var rgba_data_padded = rgba_data;
|
||||||
|
if (width % 4 != 0 or height % 4 != 0 or width < 4 or height < 4) {
|
||||||
|
padded_width = @max(width + width % 4, 4);
|
||||||
|
padded_height = @max(height + height % 4, 4);
|
||||||
|
|
||||||
|
rgba_data_padded = try allocator.alloc(u8, padded_width * padded_height * 4);
|
||||||
|
|
||||||
|
var dst_surf = c.rgba_surface{
|
||||||
|
.width = @intCast(padded_width),
|
||||||
|
.height = @intCast(padded_height),
|
||||||
|
.stride = @intCast(padded_width * 4),
|
||||||
|
.ptr = rgba_data_padded.ptr,
|
||||||
|
};
|
||||||
|
var src_surf = c.rgba_surface{
|
||||||
|
.width = @intCast(width),
|
||||||
|
.height = @intCast(height),
|
||||||
|
.stride = @intCast(width * 4),
|
||||||
|
.ptr = rgba_data.ptr,
|
||||||
|
};
|
||||||
|
c.ReplicateBorders(&dst_surf, &src_surf, 0, 0, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (comps == 4) {
|
||||||
|
// premultiplyAlpha(rgba_data);
|
||||||
|
// }
|
||||||
|
|
||||||
const data_channels: usize = if (format == .bc5) 2 else 4;
|
const data_channels: usize = if (format == .bc5) 2 else 4;
|
||||||
const data = if (data_channels < 4) dropChannels(rgba_data, data_channels) else rgba_data;
|
const data = if (data_channels < 4) dropChannels(rgba_data_padded, data_channels) else rgba_data_padded;
|
||||||
|
|
||||||
// TODO: support textures not divisible by 4
|
|
||||||
if (width % 4 != 0 or height % 4 != 0) {
|
|
||||||
std.log.debug("Image size: {}X{}\n", .{ width, height });
|
|
||||||
return error.ImageSizeShouldBeDivisibleBy4;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mip_levels_to_gen = 1 + @as(
|
const mip_levels_to_gen = 1 + @as(
|
||||||
u32,
|
u32,
|
||||||
@ -600,14 +618,14 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
|||||||
var mip_pyramid = std.ArrayList(MipLevel).init(allocator);
|
var mip_pyramid = std.ArrayList(MipLevel).init(allocator);
|
||||||
try mip_pyramid.append(MipLevel{
|
try mip_pyramid.append(MipLevel{
|
||||||
.data = data,
|
.data = data,
|
||||||
.width = width,
|
.width = padded_width,
|
||||||
.height = height,
|
.height = padded_height,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (1..mip_levels_to_gen) |mip_level| {
|
for (1..mip_levels_to_gen) |mip_level| {
|
||||||
const divisor = std.math.powi(usize, 2, mip_level) catch unreachable;
|
const divisor = std.math.powi(usize, 2, mip_level) catch unreachable;
|
||||||
const mip_width = width / divisor;
|
const mip_width = padded_width / divisor;
|
||||||
const mip_height = height / divisor;
|
const mip_height = padded_height / divisor;
|
||||||
|
|
||||||
if (mip_width % 4 != 0 or mip_height % 4 != 0) {
|
if (mip_width % 4 != 0 or mip_height % 4 != 0) {
|
||||||
break;
|
break;
|
||||||
@ -634,6 +652,7 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std.log.debug("mip size {}x{}", .{ mip_data.width, mip_data.height });
|
||||||
mip_data.out_data = try compressBlocksAlloc(allocator, mip_data.data, data_channels, format, @intCast(comps), mip_data.width, mip_data.height);
|
mip_data.out_data = try compressBlocksAlloc(allocator, mip_data.data, data_channels, format, @intCast(comps), mip_data.width, mip_data.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,6 +666,8 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
|||||||
.format = format,
|
.format = format,
|
||||||
.width = @intCast(width),
|
.width = @intCast(width),
|
||||||
.height = @intCast(height),
|
.height = @intCast(height),
|
||||||
|
.padded_width = @intCast(padded_width),
|
||||||
|
.padded_height = @intCast(padded_height),
|
||||||
.mip_count = @intCast(actual_mip_count),
|
.mip_count = @intCast(actual_mip_count),
|
||||||
},
|
},
|
||||||
.data = out_data,
|
.data = out_data,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user