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;
|
||||
};
|
||||
|
||||
// TODO: rename
|
||||
layout(std140, binding = 1) uniform Lights {
|
||||
Light lights[MAX_POINT_LIGHTS];
|
||||
uint lights_count;
|
||||
@ -25,17 +26,22 @@ layout(location = 1) uniform mat4 model;
|
||||
|
||||
layout(location = 2) uniform vec3 color;
|
||||
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 = 6, bindless_sampler) uniform sampler2D metallic_map;
|
||||
layout(location = 7) uniform float metallic;
|
||||
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 = 8, bindless_sampler) uniform sampler2D roughness_map;
|
||||
layout(location = 10) uniform float roughness;
|
||||
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 = 10, bindless_sampler) uniform sampler2D emission_map;
|
||||
layout(location = 13) uniform vec3 emission;
|
||||
layout(location = 14, bindless_sampler) uniform sampler2D emission_map;
|
||||
layout(location = 15) uniform vec2 emission_map_uv_scale = vec2(1);
|
||||
|
||||
|
||||
// Input, output blocks
|
||||
@ -80,11 +86,11 @@ struct Material {
|
||||
|
||||
Material evalMaterial() {
|
||||
Material result;
|
||||
result.albedo = textureSize(albedo_map, 0) == ivec2(0) ? pow(color, vec3(2.2)) : texture(albedo_map, VertexOut.uv).rgb;
|
||||
float fMetallic = textureSize(metallic_map, 0) == ivec2(0) ? metallic : texture(metallic_map, VertexOut.uv).b;
|
||||
result.metallic = fMetallic > 0.5;
|
||||
result.roughness = max(0.01, textureSize(roughness_map, 0) == ivec2(0) ? roughness : texture(roughness_map, VertexOut.uv).g);
|
||||
result.emission = textureSize(emission_map, 0) == ivec2(0) ? emission : texture(emission_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 * metallic_map_uv_scale).b;
|
||||
result.metallic = fMetallic > 0.1;
|
||||
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 * emission_map_uv_scale).rgb;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -110,8 +116,8 @@ float ggxDistribution(Material mat, float NDotH) {
|
||||
return alpha2 / (PI * d * d);
|
||||
}
|
||||
|
||||
float lightAttenuation(float dist, float radius) {
|
||||
float d = max(dist - radius, 0);
|
||||
float lightAttenuation(float point, float dist, float radius) {
|
||||
float d = max(dist - radius, 0) * point;
|
||||
|
||||
float denom = d/radius + 1;
|
||||
float att = 1 / (denom * denom);
|
||||
@ -128,13 +134,22 @@ vec3 microfacetModel(Material mat, Light light, vec3 P, vec3 N) {
|
||||
diffuseBrdf = mat.albedo;
|
||||
}
|
||||
|
||||
vec3 lightI = light.color.rgb * light.color.a;
|
||||
float lightRadius = light.vPos.w;
|
||||
vec3 L = light.vPos.xyz - P;
|
||||
// 0 - means directional, 1 - means point light
|
||||
float point = light.vPos.w;
|
||||
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);
|
||||
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;
|
||||
|
||||
vec3 V = normalize(-P);
|
||||
@ -153,7 +168,7 @@ vec3 microfacetModel(Material mat, Light light, vec3 P, vec3 N) {
|
||||
void main() {
|
||||
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.z = sqrt(clamp(1 - N.x * N.x - N.y * N.y, 0, 1));
|
||||
N = normalize(N);
|
||||
|
@ -22,6 +22,7 @@ const asset_manifest = @import("asset_manifest");
|
||||
const assets = @import("assets");
|
||||
const checkGLError = @import("Render.zig").checkGLError;
|
||||
// const basisu = @import("mach-basisu");
|
||||
const Vec2 = @import("zalgebra").Vec2;
|
||||
const Vec3 = @import("zalgebra").Vec3;
|
||||
|
||||
pub const AssetId = assets.AssetId;
|
||||
@ -67,22 +68,22 @@ pub fn deinit(self: *AssetManager) void {
|
||||
self.loaded_assets.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn resolveShader(self: *AssetManager, handle: Handle.Shader) *const LoadedShader {
|
||||
if (handle.id == 0) return &NullShader;
|
||||
pub fn resolveShader(self: *AssetManager, handle: Handle.Shader) LoadedShader {
|
||||
if (handle.id == 0) return NullShader;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
return &asset.shader;
|
||||
return asset.shader;
|
||||
}
|
||||
|
||||
return self.loadShader(handle.id);
|
||||
}
|
||||
|
||||
pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) *const LoadedShaderProgram {
|
||||
if (handle.id == 0) return &NullShaderProgram;
|
||||
pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram {
|
||||
if (handle.id == 0) return NullShaderProgram;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
switch (asset.*) {
|
||||
.shaderProgram => |*shader| {
|
||||
.shaderProgram => |shader| {
|
||||
return shader;
|
||||
},
|
||||
else => unreachable,
|
||||
@ -92,12 +93,12 @@ pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) *
|
||||
return self.loadShaderProgram(handle);
|
||||
}
|
||||
|
||||
pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) *const LoadedMesh {
|
||||
if (handle.id == 0) return &NullMesh;
|
||||
pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh {
|
||||
if (handle.id == 0) return NullMesh;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
switch (asset.*) {
|
||||
.mesh => |*mesh| {
|
||||
.mesh => |mesh| {
|
||||
return mesh;
|
||||
},
|
||||
else => unreachable,
|
||||
@ -107,12 +108,12 @@ pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) *const LoadedMesh {
|
||||
return self.loadMesh(handle.id);
|
||||
}
|
||||
|
||||
pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) *const LoadedTexture {
|
||||
if (handle.id == 0) return &NullTexture;
|
||||
pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) LoadedTexture {
|
||||
if (handle.id == 0) return NullTexture;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
switch (asset.*) {
|
||||
.texture => |*texture| {
|
||||
.texture => |texture| {
|
||||
return texture;
|
||||
},
|
||||
else => unreachable,
|
||||
@ -122,27 +123,27 @@ pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) *const Loaded
|
||||
return self.loadTexture(handle.id);
|
||||
}
|
||||
|
||||
pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) *const formats.Scene {
|
||||
if (handle.id == 0) return &NullScene.scene;
|
||||
pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) formats.Scene {
|
||||
if (handle.id == 0) return NullScene.scene;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
switch (asset.*) {
|
||||
.scene => |*scene| {
|
||||
return &scene.scene;
|
||||
.scene => |scene| {
|
||||
return scene.scene;
|
||||
},
|
||||
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 {
|
||||
if (handle.id == 0) return &NullMaterial;
|
||||
pub fn resolveMaterial(self: *AssetManager, handle: Handle.Material) formats.Material {
|
||||
if (handle.id == 0) return NullMaterial;
|
||||
|
||||
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||
switch (asset.*) {
|
||||
.material => |*material| {
|
||||
.material => |material| {
|
||||
return material;
|
||||
},
|
||||
else => unreachable,
|
||||
@ -181,15 +182,15 @@ pub const ShaderProgramDefinition = struct {
|
||||
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| {
|
||||
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 program = formats.ShaderProgram.fromBuffer(data.bytes);
|
||||
|
||||
@ -237,12 +238,15 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !*LoadedShaderProgram
|
||||
return error.ProgramLinkFailed;
|
||||
}
|
||||
|
||||
const loaded_shader_program = LoadedShaderProgram{
|
||||
.program = prog,
|
||||
};
|
||||
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);
|
||||
|
||||
return &self.loaded_assets.getPtr(id).?.shaderProgram;
|
||||
return loaded_shader_program;
|
||||
}
|
||||
|
||||
const NullShader = LoadedShader{
|
||||
@ -296,16 +300,17 @@ const NullScene = LoadedScene{
|
||||
|
||||
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| {
|
||||
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 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);
|
||||
|
||||
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.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| {
|
||||
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 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);
|
||||
|
||||
@ -424,8 +430,8 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
||||
name,
|
||||
@intCast(texture.mipLevels()),
|
||||
gl_format,
|
||||
@intCast(texture.header.width),
|
||||
@intCast(texture.header.height),
|
||||
@intCast(texture.header.padded_width),
|
||||
@intCast(texture.header.padded_height),
|
||||
);
|
||||
checkGLError();
|
||||
|
||||
@ -445,63 +451,69 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
||||
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);
|
||||
gl.GL_ARB_bindless_texture.makeTextureHandleResidentARB(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(
|
||||
self.allocator,
|
||||
id,
|
||||
.{
|
||||
.texture = LoadedTexture{
|
||||
.name = name,
|
||||
.handle = handle,
|
||||
},
|
||||
},
|
||||
.{ .texture = loaded_texture },
|
||||
);
|
||||
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| {
|
||||
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 data = try self.loadFile(self.allocator, path, TEXTURE_MAX_BYTES);
|
||||
|
||||
const scene = try formats.Scene.fromBuffer(data.bytes);
|
||||
const loaded_scene = LoadedScene{
|
||||
.buf = data.bytes,
|
||||
.scene = scene,
|
||||
};
|
||||
|
||||
try self.loaded_assets.put(
|
||||
self.allocator,
|
||||
id,
|
||||
.{
|
||||
.scene = LoadedScene{
|
||||
.buf = data.bytes,
|
||||
.scene = scene,
|
||||
},
|
||||
.scene = loaded_scene,
|
||||
},
|
||||
);
|
||||
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| {
|
||||
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 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);
|
||||
|
||||
return &self.loaded_assets.getPtr(id).?.material;
|
||||
return material;
|
||||
}
|
||||
|
||||
const LoadedAsset = union(enum) {
|
||||
@ -549,6 +561,7 @@ const LoadedMesh = struct {
|
||||
const LoadedTexture = struct {
|
||||
name: gl.GLuint,
|
||||
handle: gl.GLuint64,
|
||||
uv_scale: Vec2 = Vec2.one(),
|
||||
};
|
||||
|
||||
const LoadedScene = struct {
|
||||
@ -614,22 +627,23 @@ fn loadFile(self: *AssetManager, allocator: std.mem.Allocator, path: []const u8,
|
||||
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| {
|
||||
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 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);
|
||||
|
||||
return &self.loaded_assets.getPtr(id).?.shader;
|
||||
return loaded_shader;
|
||||
}
|
||||
|
||||
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 {
|
||||
std.log.debug("unload asset id {}: {s}\n", .{ id, asset_manifest.getPath(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;
|
||||
|
||||
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&cmd.transform.data));
|
||||
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&material.albedo.data));
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.AlbedoMap.value(),
|
||||
self.assetman.resolveTexture(material.albedo_map).handle,
|
||||
);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.NormalMap.value(),
|
||||
self.assetman.resolveTexture(material.normal_map).handle,
|
||||
);
|
||||
gl.uniform1fv(Uniform.Metallic.value(), 1, &material.metallic);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.MetallicMap.value(),
|
||||
self.assetman.resolveTexture(material.metallic_map).handle,
|
||||
);
|
||||
gl.uniform1fv(Uniform.Roughness.value(), 1, &material.roughness);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.RoughnessMap.value(),
|
||||
self.assetman.resolveTexture(material.roughness_map).handle,
|
||||
);
|
||||
gl.uniform1fv(Uniform.Emission.value(), 1, &material.emission);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.EmissionMap.value(),
|
||||
self.assetman.resolveTexture(material.emission_map).handle,
|
||||
);
|
||||
{
|
||||
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(
|
||||
Uniform.AlbedoMap.value(),
|
||||
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(
|
||||
Uniform.NormalMap.value(),
|
||||
normal_map.handle,
|
||||
);
|
||||
gl.uniform2fv(Uniform.NormalMapUVScale.value(), 1, @ptrCast(&normal_map.uv_scale.data));
|
||||
}
|
||||
{
|
||||
gl.uniform1fv(Uniform.Metallic.value(), 1, &material.metallic);
|
||||
|
||||
const metallic_map = self.assetman.resolveTexture(material.metallic_map);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.MetallicMap.value(),
|
||||
metallic_map.handle,
|
||||
);
|
||||
gl.uniform2fv(Uniform.MetallicMapUVScale.value(), 1, @ptrCast(&metallic_map.uv_scale.data));
|
||||
}
|
||||
{
|
||||
gl.uniform1fv(Uniform.Roughness.value(), 1, &material.roughness);
|
||||
|
||||
const roughness_map = self.assetman.resolveTexture(material.roughness_map);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.RoughnessMap.value(),
|
||||
roughness_map.handle,
|
||||
);
|
||||
gl.uniform2fv(Uniform.RoughnessMapUVScale.value(), 1, @ptrCast(&roughness_map.uv_scale.data));
|
||||
}
|
||||
{
|
||||
gl.uniform1fv(Uniform.Emission.value(), 1, &material.emission);
|
||||
|
||||
const emission_map = self.assetman.resolveTexture(material.emission_map);
|
||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||
Uniform.EmissionMap.value(),
|
||||
emission_map.handle,
|
||||
);
|
||||
gl.uniform2fv(Uniform.EmissionMapUVScale.value(), 1, @ptrCast(&emission_map.uv_scale.data));
|
||||
}
|
||||
|
||||
mesh.positions.bind(Render.Attrib.Position.value());
|
||||
mesh.normals.bind(Render.Attrib.Normal.value());
|
||||
@ -306,13 +330,18 @@ pub const Uniform = enum(gl.GLint) {
|
||||
ModelMatrix = 1,
|
||||
Color = 2,
|
||||
AlbedoMap = 3,
|
||||
NormalMap = 4,
|
||||
Metallic = 5,
|
||||
MetallicMap = 6,
|
||||
Roughness = 7,
|
||||
RoughnessMap = 8,
|
||||
Emission = 9,
|
||||
EmissionMap = 10,
|
||||
AlbedoMapUVScale = 4,
|
||||
NormalMap = 5,
|
||||
NormalMapUVScale = 6,
|
||||
Metallic = 7,
|
||||
MetallicMap = 8,
|
||||
MetallicMapUVScale = 9,
|
||||
Roughness = 10,
|
||||
RoughnessMap = 11,
|
||||
RoughnessMapUVScale = 12,
|
||||
Emission = 13,
|
||||
EmissionMap = 14,
|
||||
EmissionMapUVScale = 15,
|
||||
|
||||
pub inline fn value(self: Uniform) gl.GLint {
|
||||
return @intFromEnum(self);
|
||||
@ -339,10 +368,11 @@ const CameraMatrices = extern struct {
|
||||
view: Mat4,
|
||||
};
|
||||
pub const PointLight = extern struct {
|
||||
pos_radius: Vec4, // x, y, z - vPos, w - radius
|
||||
color_intensity: Vec4, // x, y, z - color, w - intensity
|
||||
pos: Vec4, // x, y, z, w - vPos
|
||||
color_radius: Vec4, // x, y, z - color, w - radius
|
||||
};
|
||||
|
||||
// TODO: rename
|
||||
pub const PointLightArray = extern struct {
|
||||
lights: [MAX_POINT_LIGHTS]PointLight,
|
||||
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 za = @import("zalgebra");
|
||||
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();
|
||||
|
||||
@ -197,6 +197,8 @@ pub const Texture = struct {
|
||||
format: Format,
|
||||
width: u32,
|
||||
height: u32,
|
||||
padded_width: u32,
|
||||
padded_height: u32,
|
||||
mip_count: u32,
|
||||
};
|
||||
|
||||
@ -210,8 +212,8 @@ pub const Texture = struct {
|
||||
pub fn getMipDesc(self: *const Texture, mip_level: usize) MipDesc {
|
||||
const divisor = std.math.powi(u32, 2, @intCast(mip_level)) catch unreachable;
|
||||
return MipDesc{
|
||||
.width = self.header.width / divisor,
|
||||
.height = self.header.height / divisor,
|
||||
.width = self.header.padded_width / 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");
|
||||
};
|
||||
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 {
|
||||
@ -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);
|
||||
|
||||
_ = 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(.{
|
||||
.flags = .{ .rotate = true },
|
||||
.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(.{
|
||||
.transform = .{ .pos = Vec3.new(1.8, 1, 0) },
|
||||
.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 },
|
||||
});
|
||||
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(.{
|
||||
.transform = .{ .pos = Vec3.new(-2, 0, 0) },
|
||||
.flags = .{ .point_light = true, .rotate = true },
|
||||
.point_light = .{
|
||||
.color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0),
|
||||
.radius = 0.1,
|
||||
},
|
||||
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0) },
|
||||
.point_light = .{ .radius = 0.1 },
|
||||
});
|
||||
light2.ptr.setParent(light1.handle);
|
||||
|
||||
_ = globals.g_mem.world.addEntity(.{
|
||||
.transform = .{ .pos = Vec3.new(1, 0.5, 4) },
|
||||
.flags = .{ .point_light = true },
|
||||
.point_light = .{
|
||||
.color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0),
|
||||
.radius = 1,
|
||||
},
|
||||
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0) },
|
||||
.point_light = .{ .radius = 1 },
|
||||
});
|
||||
|
||||
// Plane
|
||||
@ -441,9 +444,23 @@ export fn game_update() bool {
|
||||
var pos4 = Vec4.new(pos.x(), pos.y(), pos.z(), 1.0);
|
||||
pos4 = gmem.render.camera.view_mat.mulByVec4(pos4);
|
||||
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
point_lights.lights[point_lights.count] = .{
|
||||
.pos_radius = Vec4.new(pos4.x(), pos4.y(), pos4.z(), ent.data.point_light.radius),
|
||||
.color_intensity = ent.data.point_light.color_intensity,
|
||||
.pos = Vec4.new(pos4.x(), pos4.y(), pos4.z(), 1),
|
||||
.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;
|
||||
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
||||
@ -469,7 +486,7 @@ export fn game_update() bool {
|
||||
} else if (ent.data.flags.point_light) {
|
||||
gmem.render.draw(.{
|
||||
.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).*,
|
||||
});
|
||||
}
|
||||
|
207
src/globals.zig
207
src/globals.zig
@ -1,10 +1,8 @@
|
||||
const std = @import("std");
|
||||
const c = @import("sdl.zig");
|
||||
const AssetManager = @import("AssetManager.zig");
|
||||
const Render = @import("Render.zig");
|
||||
const formats = @import("formats.zig");
|
||||
const Material = formats.Material;
|
||||
const Scene = formats.Scene;
|
||||
const AssetManager = @import("AssetManager.zig");
|
||||
const World = @import("entity.zig").World;
|
||||
|
||||
const za = @import("zalgebra");
|
||||
const Vec2 = za.Vec2;
|
||||
@ -21,8 +19,6 @@ pub var g_assetman: *AssetManager = undefined;
|
||||
pub const DEFAULT_WIDTH = 800;
|
||||
pub const DEFAULT_HEIGHT = 600;
|
||||
|
||||
pub const MAX_ENTITIES = 1024;
|
||||
|
||||
pub const InitMemory = struct {
|
||||
global_allocator: std.mem.Allocator,
|
||||
window: *c.SDL_Window,
|
||||
@ -33,205 +29,6 @@ pub const InitMemory = struct {
|
||||
vsync: bool = false,
|
||||
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 {
|
||||
global_allocator: std.mem.Allocator,
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -166,7 +165,6 @@ fn writeAssetManifest(arena: std.mem.Allocator, writer: anytype, assets: []Asset
|
||||
);
|
||||
for (assets) |asset_list_entry| {
|
||||
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 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];
|
||||
|
||||
if (comps == 4) {
|
||||
premultiplyAlpha(rgba_data);
|
||||
var padded_width: usize = width;
|
||||
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 = if (data_channels < 4) dropChannels(rgba_data, data_channels) else rgba_data;
|
||||
|
||||
// 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 data = if (data_channels < 4) dropChannels(rgba_data_padded, data_channels) else rgba_data_padded;
|
||||
|
||||
const mip_levels_to_gen = 1 + @as(
|
||||
u32,
|
||||
@ -600,14 +618,14 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
||||
var mip_pyramid = std.ArrayList(MipLevel).init(allocator);
|
||||
try mip_pyramid.append(MipLevel{
|
||||
.data = data,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.width = padded_width,
|
||||
.height = padded_height,
|
||||
});
|
||||
|
||||
for (1..mip_levels_to_gen) |mip_level| {
|
||||
const divisor = std.math.powi(usize, 2, mip_level) catch unreachable;
|
||||
const mip_width = width / divisor;
|
||||
const mip_height = height / divisor;
|
||||
const mip_width = padded_width / divisor;
|
||||
const mip_height = padded_height / divisor;
|
||||
|
||||
if (mip_width % 4 != 0 or mip_height % 4 != 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -647,6 +666,8 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
||||
.format = format,
|
||||
.width = @intCast(width),
|
||||
.height = @intCast(height),
|
||||
.padded_width = @intCast(padded_width),
|
||||
.padded_height = @intCast(padded_height),
|
||||
.mip_count = @intCast(actual_mip_count),
|
||||
},
|
||||
.data = out_data,
|
||||
|
Loading…
x
Reference in New Issue
Block a user