Working normal maps!
This commit is contained in:
parent
6e75910bef
commit
8f40efc6a2
@ -22,21 +22,21 @@ layout(std140, binding = 1) uniform Lights {
|
|||||||
// Uniforms
|
// Uniforms
|
||||||
layout(location = 1) uniform mat4 model;
|
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 diffuse;
|
layout(location = 3, bindless_sampler) uniform sampler2D albedo_map;
|
||||||
|
layout(location = 4, bindless_sampler) uniform sampler2D normal_map;
|
||||||
|
|
||||||
// Input, output blocks
|
// Input, output blocks
|
||||||
|
|
||||||
VERTEX_EXPORT VertexData {
|
VERTEX_EXPORT VertexData {
|
||||||
vec3 position;
|
vec3 position;
|
||||||
vec3 normal;
|
|
||||||
vec2 uv;
|
vec2 uv;
|
||||||
vec3 tangent;
|
mat3 TBN;
|
||||||
} VertexOut;
|
} VertexOut;
|
||||||
|
|
||||||
#if VERTEX_SHADER
|
#if VERTEX_SHADER
|
||||||
|
|
||||||
layout(location = 0) in vec3 aPos;
|
layout(location = 0) in vec3 aPos;
|
||||||
layout(location = 1) in vec3 aNorm;
|
layout(location = 1) in vec3 aNormal;
|
||||||
layout(location = 2) in vec2 aUV;
|
layout(location = 2) in vec2 aUV;
|
||||||
layout(location = 3) in vec3 aTangent;
|
layout(location = 3) in vec3 aTangent;
|
||||||
|
|
||||||
@ -44,9 +44,12 @@ void main() {
|
|||||||
gl_Position = projection * view * model * vec4(aPos.xyz, 1.0);
|
gl_Position = projection * view * model * vec4(aPos.xyz, 1.0);
|
||||||
vec4 posWorld = model * vec4(aPos, 1.0);
|
vec4 posWorld = model * vec4(aPos, 1.0);
|
||||||
VertexOut.position = posWorld.xyz / posWorld.w;
|
VertexOut.position = posWorld.xyz / posWorld.w;
|
||||||
VertexOut.normal = aNorm;
|
|
||||||
VertexOut.uv = aUV;
|
VertexOut.uv = aUV;
|
||||||
VertexOut.tangent = aTangent;
|
vec3 aBitangent = cross(aTangent, aNormal);
|
||||||
|
vec3 T = normalize(vec3(model * vec4(aTangent, 0.0)));
|
||||||
|
vec3 B = normalize(vec3(model * vec4(aBitangent, 0.0)));
|
||||||
|
vec3 N = normalize(vec3(model * vec4(aNormal, 0.0)));
|
||||||
|
VertexOut.TBN = mat3(T, B, N);
|
||||||
}
|
}
|
||||||
#endif // VERTEX_SHADER
|
#endif // VERTEX_SHADER
|
||||||
|
|
||||||
@ -55,7 +58,14 @@ void main() {
|
|||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 diffuseColor = textureSize(diffuse, 0) == ivec2(0) ? color : texture(diffuse, VertexOut.uv).rgb;
|
vec3 albedoColor = textureSize(albedo_map, 0) == ivec2(0) ? color : texture(albedo_map, VertexOut.uv).rgb;
|
||||||
|
|
||||||
|
vec3 N = textureSize(normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(normal_map, VertexOut.uv).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);
|
||||||
|
N = normalize(VertexOut.TBN * N);
|
||||||
|
|
||||||
vec3 finalColor = vec3(0);
|
vec3 finalColor = vec3(0);
|
||||||
|
|
||||||
for (int i = 0; i < lights_count; i++) {
|
for (int i = 0; i < lights_count; i++) {
|
||||||
@ -70,9 +80,9 @@ void main() {
|
|||||||
// TODO: cutoff
|
// TODO: cutoff
|
||||||
att = max(att, 0);
|
att = max(att, 0);
|
||||||
|
|
||||||
float ndotl = max(dot(L, VertexOut.normal), 0);
|
float ndotl = max(dot(L, N), 0);
|
||||||
|
|
||||||
finalColor += ndotl * lights[i].color.w * lights[i].color.xyz * att * diffuseColor;
|
finalColor += ndotl * lights[i].color.w * lights[i].color.xyz * att * albedoColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
FragColor = vec4(finalColor, 1.0f);
|
FragColor = vec4(finalColor, 1.0f);
|
||||||
|
BIN
assets/tile.norm.jpg
(Stored with Git LFS)
Normal file
BIN
assets/tile.norm.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -375,10 +375,16 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
|||||||
}
|
}
|
||||||
errdefer gl.deleteTextures(1, &name);
|
errdefer gl.deleteTextures(1, &name);
|
||||||
|
|
||||||
|
const gl_format: gl.GLenum = switch (texture.header.format) {
|
||||||
|
.bc7 => gl.COMPRESSED_RGBA_BPTC_UNORM,
|
||||||
|
.bc5 => gl.COMPRESSED_RG_RGTC2,
|
||||||
|
.bc6 => gl.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT,
|
||||||
|
};
|
||||||
|
|
||||||
gl.textureStorage2D(
|
gl.textureStorage2D(
|
||||||
name,
|
name,
|
||||||
@intCast(texture.mipLevels()),
|
@intCast(texture.mipLevels()),
|
||||||
gl.COMPRESSED_RGBA_BPTC_UNORM,
|
gl_format,
|
||||||
@intCast(texture.header.width),
|
@intCast(texture.header.width),
|
||||||
@intCast(texture.header.height),
|
@intCast(texture.header.height),
|
||||||
);
|
);
|
||||||
@ -393,7 +399,7 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
|
|||||||
0,
|
0,
|
||||||
@intCast(desc.width),
|
@intCast(desc.width),
|
||||||
@intCast(desc.height),
|
@intCast(desc.height),
|
||||||
gl.COMPRESSED_RGBA_BPTC_UNORM,
|
gl_format,
|
||||||
@intCast(texture.data[mip_level].len),
|
@intCast(texture.data[mip_level].len),
|
||||||
@ptrCast(texture.data[mip_level].ptr),
|
@ptrCast(texture.data[mip_level].ptr),
|
||||||
);
|
);
|
||||||
|
@ -178,12 +178,16 @@ pub fn getPointLights(self: *Render) *PointLightArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *Render, cmd: DrawCommand) void {
|
pub fn draw(self: *Render, cmd: DrawCommand) void {
|
||||||
gl.uniformMatrix4fv(1, 1, gl.FALSE, @ptrCast(&cmd.transform.data));
|
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&cmd.transform.data));
|
||||||
gl.uniform3fv(2, 1, @ptrCast(&cmd.material.albedo.data));
|
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&cmd.material.albedo.data));
|
||||||
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
3,
|
Uniform.AlbedoMap.value(),
|
||||||
self.assetman.resolveTexture(cmd.material.albedo_map).handle,
|
self.assetman.resolveTexture(cmd.material.albedo_map).handle,
|
||||||
);
|
);
|
||||||
|
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(
|
||||||
|
Uniform.NormalMap.value(),
|
||||||
|
self.assetman.resolveTexture(cmd.material.normal_map).handle,
|
||||||
|
);
|
||||||
|
|
||||||
const mesh = self.assetman.resolveMesh(cmd.mesh);
|
const mesh = self.assetman.resolveMesh(cmd.mesh);
|
||||||
mesh.positions.bind(Render.Attrib.Position.value());
|
mesh.positions.bind(Render.Attrib.Position.value());
|
||||||
@ -256,6 +260,17 @@ pub const UBO = enum(gl.GLuint) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Uniform = enum(gl.GLint) {
|
||||||
|
ModelMatrix = 1,
|
||||||
|
Color = 2,
|
||||||
|
AlbedoMap = 3,
|
||||||
|
NormalMap = 4,
|
||||||
|
|
||||||
|
pub inline fn value(self: Uniform) gl.GLint {
|
||||||
|
return @intFromEnum(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: support ortho
|
// TODO: support ortho
|
||||||
pub const Camera = struct {
|
pub const Camera = struct {
|
||||||
fovy: f32 = 60,
|
fovy: f32 = 60,
|
||||||
@ -288,4 +303,5 @@ pub const PointLightArray = extern struct {
|
|||||||
pub const Material = struct {
|
pub const Material = struct {
|
||||||
albedo: Vec3 = Vec3.one(),
|
albedo: Vec3 = Vec3.one(),
|
||||||
albedo_map: AssetManager.Handle.Texture = .{},
|
albedo_map: AssetManager.Handle.Texture = .{},
|
||||||
|
normal_map: AssetManager.Handle.Texture = .{},
|
||||||
};
|
};
|
||||||
|
17
src/game.zig
17
src/game.zig
@ -162,8 +162,13 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
// Plane
|
// Plane
|
||||||
_ = globals.g_mem.world.addEntity(.{
|
_ = globals.g_mem.world.addEntity(.{
|
||||||
.flags = .{ .mesh = true },
|
.flags = .{ .mesh = true },
|
||||||
.transform = .{ .scale = Vec3.one().scale(100) },
|
.transform = .{ .scale = Vec3.one().scale(1) },
|
||||||
.mesh = .{ .handle = a.Meshes.plane },
|
.mesh = .{
|
||||||
|
.handle = a.Meshes.plane,
|
||||||
|
.material = .{
|
||||||
|
.normal_map = a.Textures.@"tile.norm",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 10 bunnies
|
// 10 bunnies
|
||||||
@ -173,7 +178,13 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
.transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3, 0, 0) },
|
.transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3, 0, 0) },
|
||||||
|
|
||||||
.flags = .{ .mesh = true },
|
.flags = .{ .mesh = true },
|
||||||
.mesh = .{ .handle = a.Meshes.bunny, .material = .{ .albedo_map = a.Textures.bunny_tex1 } },
|
.mesh = .{
|
||||||
|
.handle = a.Meshes.bunny,
|
||||||
|
.material = .{
|
||||||
|
.albedo_map = a.Textures.bunny_tex1,
|
||||||
|
// .normal_map = a.Textures.@"tile.norm",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,22 +209,29 @@ const MipLevel = struct {
|
|||||||
|
|
||||||
fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []const u8, hdr: bool) !void {
|
fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []const u8, hdr: bool) !void {
|
||||||
_ = hdr; // autofix
|
_ = hdr; // autofix
|
||||||
|
|
||||||
|
// For tex.norm.png - this will be ".norm"
|
||||||
|
const sub_ext = std.fs.path.extension(std.fs.path.stem(std.mem.span(input)));
|
||||||
|
const format = if (std.mem.eql(u8, sub_ext, ".norm")) formats.Texture.Format.bc5 else formats.Texture.Format.bc7;
|
||||||
|
|
||||||
var width_int: c_int = undefined;
|
var width_int: c_int = undefined;
|
||||||
var height_int: c_int = undefined;
|
var height_int: c_int = undefined;
|
||||||
var comps: c_int = undefined;
|
var comps: c_int = undefined;
|
||||||
|
|
||||||
c.stbi_set_flip_vertically_on_load(1);
|
c.stbi_set_flip_vertically_on_load(1);
|
||||||
const FORCED_COMPONENTS = 4; // force rgb
|
const rgba_data_c = c.stbi_load(input, &width_int, &height_int, &comps, 4);
|
||||||
const data_c = c.stbi_load(input, &width_int, &height_int, &comps, FORCED_COMPONENTS);
|
if (rgba_data_c == null) {
|
||||||
if (data_c == null) {
|
|
||||||
return error.ImageLoadError;
|
return error.ImageLoadError;
|
||||||
}
|
}
|
||||||
defer c.stbi_image_free(data_c);
|
defer c.stbi_image_free(rgba_data_c);
|
||||||
|
|
||||||
const width: usize = @intCast(width_int);
|
const width: usize = @intCast(width_int);
|
||||||
const height: usize = @intCast(height_int);
|
const height: usize = @intCast(height_int);
|
||||||
|
|
||||||
const data = data_c[0 .. width * height * FORCED_COMPONENTS];
|
const rgba_data = rgba_data_c[0 .. width * height * 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;
|
||||||
|
|
||||||
// TODO: support textures not divisible by 4
|
// TODO: support textures not divisible by 4
|
||||||
if (width % 4 != 0 or height % 4 != 0) {
|
if (width % 4 != 0 or height % 4 != 0) {
|
||||||
@ -232,19 +239,11 @@ fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []
|
|||||||
return error.ImageSizeShouldBeDivisibleBy4;
|
return error.ImageSizeShouldBeDivisibleBy4;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings: c.bc7_enc_settings = undefined;
|
if (comps == 4) {
|
||||||
|
|
||||||
if (comps == 3) {
|
|
||||||
c.GetProfile_ultrafast(&settings);
|
|
||||||
} else if (comps == 4) {
|
|
||||||
premultiplyAlpha(data);
|
premultiplyAlpha(data);
|
||||||
c.GetProfile_alpha_ultrafast(&settings);
|
|
||||||
} else {
|
|
||||||
std.log.debug("Channel count: {}\n", .{comps});
|
|
||||||
return error.UnsupportedChannelCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mip_levels_to_gen = 1 + @as(
|
const mip_levels_to_gen = if (data_channels == 2) 1 else 1 + @as(
|
||||||
u32,
|
u32,
|
||||||
@intFromFloat(@log2(@as(f32, @floatFromInt(@max(width, height))))),
|
@intFromFloat(@log2(@as(f32, @floatFromInt(@max(width, height))))),
|
||||||
);
|
);
|
||||||
@ -270,7 +269,7 @@ fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []
|
|||||||
MipLevel{
|
MipLevel{
|
||||||
.width = mip_width,
|
.width = mip_width,
|
||||||
.height = mip_height,
|
.height = mip_height,
|
||||||
.data = try allocator.alloc(u8, mip_width * mip_height * FORCED_COMPONENTS),
|
.data = try allocator.alloc(u8, mip_width * mip_height * data_channels),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
actual_mip_count += 1;
|
actual_mip_count += 1;
|
||||||
@ -280,24 +279,14 @@ fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []
|
|||||||
for (0..actual_mip_count) |mip_level| {
|
for (0..actual_mip_count) |mip_level| {
|
||||||
const mip_data = &mip_pyramid.items[mip_level];
|
const mip_data = &mip_pyramid.items[mip_level];
|
||||||
if (mip_level > 0) {
|
if (mip_level > 0) {
|
||||||
downsampleImage2X(&mip_pyramid.items[mip_level - 1], mip_data);
|
switch (data_channels) {
|
||||||
|
2 => downsampleRGImage2X(&mip_pyramid.items[mip_level - 1], mip_data),
|
||||||
|
4 => downsampleRGBAImage2X(&mip_pyramid.items[mip_level - 1], mip_data),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocks_x: usize = mip_data.width / 4;
|
mip_data.out_data = try compressBlocksAlloc(allocator, mip_data.data, data_channels, format, @intCast(comps), mip_data.width, mip_data.height);
|
||||||
const blocks_y: usize = mip_data.height / 4;
|
|
||||||
|
|
||||||
const out_data = try allocator.alloc(u8, blocks_x * blocks_y * 16);
|
|
||||||
|
|
||||||
const rgba_surf = c.rgba_surface{
|
|
||||||
.width = @intCast(mip_data.width),
|
|
||||||
.height = @intCast(mip_data.height),
|
|
||||||
.stride = @intCast(mip_data.width * FORCED_COMPONENTS),
|
|
||||||
.ptr = mip_data.data.ptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
c.CompressBlocksBC7(&rgba_surf, out_data.ptr, &settings);
|
|
||||||
|
|
||||||
mip_data.out_data = out_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const out_data = try allocator.alloc([]const u8, actual_mip_count);
|
const out_data = try allocator.alloc([]const u8, actual_mip_count);
|
||||||
@ -307,7 +296,7 @@ fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []
|
|||||||
|
|
||||||
const texture = formats.Texture{
|
const texture = formats.Texture{
|
||||||
.header = .{
|
.header = .{
|
||||||
.format = .bc7,
|
.format = format,
|
||||||
.width = @intCast(width),
|
.width = @intCast(width),
|
||||||
.height = @intCast(height),
|
.height = @intCast(height),
|
||||||
.mip_count = @intCast(actual_mip_count),
|
.mip_count = @intCast(actual_mip_count),
|
||||||
@ -322,6 +311,64 @@ fn processTexture(allocator: std.mem.Allocator, input: [*:0]const u8, output: []
|
|||||||
try buf_writer.flush();
|
try buf_writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compressBlocksAlloc(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pixels: []u8,
|
||||||
|
components: usize, // 2 for normal maps, 4 for everything else
|
||||||
|
format: formats.Texture.Format,
|
||||||
|
original_components: usize, // how many components in original image. Does not match actual components
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
) ![]u8 {
|
||||||
|
std.debug.assert(width % 4 == 0);
|
||||||
|
std.debug.assert(height % 4 == 0);
|
||||||
|
|
||||||
|
const blocks_x = width / 4;
|
||||||
|
const blocks_y = height / 4;
|
||||||
|
|
||||||
|
const rgba_surf = c.rgba_surface{
|
||||||
|
.width = @intCast(width),
|
||||||
|
.height = @intCast(height),
|
||||||
|
.stride = @intCast(width * components),
|
||||||
|
.ptr = pixels.ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
const output = try allocator.alloc(u8, blocks_x * blocks_y * 16);
|
||||||
|
switch (format) {
|
||||||
|
.bc7 => {
|
||||||
|
var settings: c.bc7_enc_settings = .{};
|
||||||
|
if (original_components == 3) {
|
||||||
|
c.GetProfile_ultrafast(&settings);
|
||||||
|
} else if (original_components == 4) {
|
||||||
|
c.GetProfile_alpha_ultrafast(&settings);
|
||||||
|
} else {
|
||||||
|
std.log.debug("Channel count: {}\n", .{original_components});
|
||||||
|
return error.UnsupportedChannelCount;
|
||||||
|
}
|
||||||
|
c.CompressBlocksBC7(&rgba_surf, output.ptr, &settings);
|
||||||
|
},
|
||||||
|
.bc5 => {
|
||||||
|
std.debug.assert(components == 2);
|
||||||
|
c.CompressBlocksBC5(&rgba_surf, output.ptr);
|
||||||
|
},
|
||||||
|
.bc6 => {
|
||||||
|
return error.NotImplementedYet;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dropChannels(rgba_data: []u8, channel_count: usize) []u8 {
|
||||||
|
for (0..rgba_data.len / 4) |i| {
|
||||||
|
for (0..channel_count) |j| {
|
||||||
|
rgba_data[i * 2 + j] = rgba_data[i * 4 + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgba_data[0 .. (rgba_data.len / 4) * channel_count];
|
||||||
|
}
|
||||||
|
|
||||||
const gamma = 2.2;
|
const gamma = 2.2;
|
||||||
const srgb_to_linear: [256]u8 = blk: {
|
const srgb_to_linear: [256]u8 = blk: {
|
||||||
@setEvalBranchQuota(10000);
|
@setEvalBranchQuota(10000);
|
||||||
@ -365,7 +412,31 @@ inline fn vecPow(x: @Vector(4, f32), y: f32) @Vector(4, f32) {
|
|||||||
return @exp(@log(x) * @as(@Vector(4, f32), @splat(y)));
|
return @exp(@log(x) * @as(@Vector(4, f32), @splat(y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downsampleImage2X(src: *const MipLevel, dst: *const MipLevel) void {
|
fn downsampleRGImage2X(src: *const MipLevel, dst: *const MipLevel) void {
|
||||||
|
const srcStride = src.width * 2;
|
||||||
|
const dstStride = dst.width * 2;
|
||||||
|
for (0..dst.height) |y| {
|
||||||
|
for (0..dst.width) |x| {
|
||||||
|
const x0 = x * 2;
|
||||||
|
const y0 = y * 2;
|
||||||
|
var result = @Vector(2, f32){ 0, 0 };
|
||||||
|
|
||||||
|
for (0..2) |y1| {
|
||||||
|
for (0..2) |x1| {
|
||||||
|
const srcX = x0 + x1;
|
||||||
|
const srcY = y0 + y1;
|
||||||
|
|
||||||
|
result += loadColorVec2(src.data[srcY * srcStride + srcX * 2 ..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= @splat(2);
|
||||||
|
storeColorVec2(dst.data[y * dstStride + x * 2 ..], result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downsampleRGBAImage2X(src: *const MipLevel, dst: *const MipLevel) void {
|
||||||
const srcStride = src.width * 4;
|
const srcStride = src.width * 4;
|
||||||
const dstStride = dst.width * 4;
|
const dstStride = dst.width * 4;
|
||||||
for (0..dst.height) |y| {
|
for (0..dst.height) |y| {
|
||||||
@ -379,17 +450,37 @@ fn downsampleImage2X(src: *const MipLevel, dst: *const MipLevel) void {
|
|||||||
const srcX = x0 + x1;
|
const srcX = x0 + x1;
|
||||||
const srcY = y0 + y1;
|
const srcY = y0 + y1;
|
||||||
|
|
||||||
result += loadColorVec(src.data[srcY * srcStride + srcX * 4 ..]);
|
result += loadColorVec4(src.data[srcY * srcStride + srcX * 4 ..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result /= @splat(4);
|
result /= @splat(4);
|
||||||
storeColorVec(dst.data[y * dstStride + x * 4 ..], result);
|
storeColorVec4(dst.data[y * dstStride + x * 4 ..], result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn loadColorVec(pixel: []const u8) @Vector(4, f32) {
|
inline fn loadColorVec2(pixel: []const u8) @Vector(2, f32) {
|
||||||
|
@setRuntimeSafety(false);
|
||||||
|
std.debug.assert(pixel.len >= 2);
|
||||||
|
|
||||||
|
return @Vector(2, f32){
|
||||||
|
@as(f32, @floatFromInt(pixel[0])),
|
||||||
|
@as(f32, @floatFromInt(pixel[1])),
|
||||||
|
} / @as(@Vector(2, f32), @splat(255.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn storeColorVec2(pixel: []u8, vec: @Vector(2, f32)) void {
|
||||||
|
@setRuntimeSafety(false);
|
||||||
|
std.debug.assert(pixel.len >= 2);
|
||||||
|
|
||||||
|
const out = vec * @as(@Vector(2, f32), @splat(255.0));
|
||||||
|
|
||||||
|
pixel[0] = @intFromFloat(out[0]);
|
||||||
|
pixel[1] = @intFromFloat(out[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn loadColorVec4(pixel: []const u8) @Vector(4, f32) {
|
||||||
@setRuntimeSafety(false);
|
@setRuntimeSafety(false);
|
||||||
std.debug.assert(pixel.len >= 4);
|
std.debug.assert(pixel.len >= 4);
|
||||||
|
|
||||||
@ -401,7 +492,7 @@ inline fn loadColorVec(pixel: []const u8) @Vector(4, f32) {
|
|||||||
} / @as(@Vector(4, f32), @splat(255.0));
|
} / @as(@Vector(4, f32), @splat(255.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn storeColorVec(pixel: []u8, vec: @Vector(4, f32)) void {
|
inline fn storeColorVec4(pixel: []u8, vec: @Vector(4, f32)) void {
|
||||||
@setRuntimeSafety(false);
|
@setRuntimeSafety(false);
|
||||||
std.debug.assert(pixel.len >= 4);
|
std.debug.assert(pixel.len >= 4);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user