bloom and shit

This commit is contained in:
Peter Li 2025-05-07 10:05:44 -07:00
parent 77a118ee8d
commit f76516df47
27 changed files with 628 additions and 243 deletions

View File

@ -81,6 +81,8 @@ pub const Impl = struct {
if (ig.begin("renderer debug", &self.rendererDebug, .{})) {
_ = ig.sliderFloat("directional light yaw", &rend.context().directionalLightYaw, 0, 360, null, .{});
_ = ig.sliderFloat("skyboxLevel", &rend.context().skyboxSystem.brightnessFactor, 0, 10, null, .{});
_ = ig.sliderFloat("lightLevel", &rend.context().lightPower, 0, 100, null, .{});
_ = ig.sliderFloat("ortho near", &rend.context().shadowOrthoNear, 0, 200, null, .{});
_ = ig.sliderFloat("ortho far", &rend.context().shadowOrthoFar, 0, 6000, null, .{});
if (self.dtAverage > 0.000001) {

View File

@ -1,4 +1,13 @@
float4 main(float3 Color : TEXCOORD0) : SV_Target0
struct PixelOutput {
float4 Color : SV_Target0; // First render target
float4 Emissive : SV_Target1; // Second render target
};
PixelOutput main(float3 Color : TEXCOORD0)
{
return float4(Color, 1.0);
PixelOutput o;
o.Color = float4(Color * 4, 1.0);
o.Emissive = float4(Color * 4, 1.0);
return o;
}

View File

@ -17,6 +17,11 @@
"type" : "vec4",
"name" : "out.var.SV_Target0",
"location" : 0
},
{
"type" : "vec4",
"name" : "out.var.SV_Target1",
"location" : 1
}
]
}

View File

@ -17,6 +17,7 @@ cbuffer Uniforms : register(b0, space3)
float4 directionalLight;
float4 directionalLightColor;
float2 screenSize;
float lightPower;
float time;
};
@ -173,17 +174,12 @@ float3 DirectionalLight(float3 normal, float3 fragPos, float4 DirectionalShadow
return diffuse;
}
float3 reinhard2(float3 x) {
const float L_white = 4.0;
struct PixelOutput {
float4 Color : SV_Target0; // First render target
float4 Emissive : SV_Target1; // Second render target
};
const float3 a = 1.0 + x / (L_white * L_white);
const float3 x2 = x * a;
const float3 x3 = 1.0 + x;
return x2 / x3;
}
float4 main(
PixelOutput main(
float4 ScreenPosition: SV_POSITION,
float2 UV : TEXCOORD0,
@ -191,9 +187,7 @@ float4 main(
float3 Normal: TEXCOORD2,
float4 DirectionalShadowFragPos: TEXCOORD3,
uint Instance: TEXCOORD4
) : SV_Target0
{
) {
float4 s;
@ -237,7 +231,7 @@ float4 main(
}
float3 lightPos = lightPosition.xyz; //+ float3(0, 20, 0);
float3 lightColor = float3(0.8, 0.8, 0.5);
float3 lightColor = float3(0.8, 0.8, 0.5) * lightPower;
float3 ambient = float3(0.1,0.1,0.1);
@ -249,11 +243,21 @@ float4 main(
if((texMode & 0x2) > 0) // full bright no lighting bit
{
c2 = col * 4;
c2 = col * 2.0;
}
// float4 rv = float4(pow(c2, 1.0 / 2.2), alpha);
float4 rv = float4(c2, alpha);
float4 rv = float4(pow(c2, 1.0 / 2.2), alpha);
PixelOutput o;
o.Color = float4(c2, alpha);
o.Emissive = float4(0.0, 0.0, 0.0, 1.0);
if(c2.x > 1.0) o.Emissive.x = c2.x;
if(c2.y > 1.0) o.Emissive.y = c2.y;
if(c2.z > 1.0) o.Emissive.z = c2.z;
return o;
// === debug bone zone
// float3 shadowProjection = DirectionalShadowFragPos.xyz / DirectionalShadowFragPos.w;
@ -273,6 +277,6 @@ float4 main(
// rv.x = rv.x + lightPos.x * 0.0001;
return rv;
// return rv;
}

View File

@ -6,7 +6,7 @@
}
],
"types" : {
"_19" : {
"_20" : {
"name" : "type.Uniforms",
"members" : [
{
@ -35,13 +35,18 @@
"offset" : 64
},
{
"name" : "time",
"name" : "lightPower",
"type" : "float",
"offset" : 72
},
{
"name" : "time",
"type" : "float",
"offset" : 76
}
]
},
"_22" : {
"_23" : {
"name" : "Scene",
"members" : [
{
@ -73,12 +78,12 @@
}
]
},
"_21" : {
"_22" : {
"name" : "type.StructuredBuffer.Scene",
"members" : [
{
"name" : "_m0",
"type" : "_22",
"type" : "_23",
"array" : [
0
],
@ -123,6 +128,11 @@
"type" : "vec4",
"name" : "out.var.SV_Target0",
"location" : 0
},
{
"type" : "vec4",
"name" : "out.var.SV_Target1",
"location" : 1
}
],
"separate_images" : [
@ -179,7 +189,7 @@
],
"ssbos" : [
{
"type" : "_21",
"type" : "_22",
"name" : "scene",
"readonly" : true,
"block_size" : 0,
@ -189,9 +199,9 @@
],
"ubos" : [
{
"type" : "_19",
"type" : "_20",
"name" : "type.Uniforms",
"block_size" : 76,
"block_size" : 80,
"set" : 3,
"binding" : 0
}

View File

@ -1,12 +1,101 @@
Texture2D<float4> Texture0 : register(t0, space2);
Texture2D<float4> ColorTexture : register(t0, space2);
SamplerState Sampler0 : register(s0, space2);
Texture2D<float4> EmissiveTexture : register(t1, space2);
SamplerState Sampler1 : register(s1, space2);
float3 reinhard2(float3 x) {
const float L_white = 10.0;
const float3 a = 1.0 + x / (L_white * L_white);
const float3 x2 = x * a;
const float3 x3 = 1.0 + x;
return x2 / x3;
}
float3 blur(float2 uv)
{
const float offset[5] = {0.0, 1.0, 2.0, 3.0, 4.0};
const float weight[5] = {0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162};
float3 c = EmissiveTexture.Sample(Sampler1, uv).xyz * weight[0];
for(int j = 0; j < 5; j++ )
{
for(int i = 0; i < 5; i++ )
{
{
float2 uv2 = uv + float2(j - 2.5, i) / 512;
float3 s = EmissiveTexture.Sample(Sampler1, uv2).xyz / 25;
c += s;
}
{
float2 uv2 = uv - float2(j - 2.5, i) / 512;
float3 s = EmissiveTexture.Sample(Sampler1, uv2).xyz / 25;
c += s;
}
#if 0
{
float2 uv2 = uv + float2(0.0, offset[i]) / 1024;
float3 s = EmissiveTexture.Sample(Sampler1, uv2).xyz * weight[i];
c += s;
}
{
float2 uv2 = uv - float2(0.0, offset[i]) / 1024;
float3 s = EmissiveTexture.Sample(Sampler1, uv2).xyz * weight[i];
c += s;
}
#endif
}
}
return c;
}
float4 main(float2 UV : TEXCOORD0) : SV_Target0
{
float2 uv = UV;
uv.y *= -1;
uv.y += 1.0;
float4 color = Texture0.Sample(Sampler0, uv);
float3 c;
return color;
#if 0
{
c = ColorTexture.Sample(Sampler0, uv).xyz;
c = reinhard2(c);
c = pow(c, 1.0/2.2);
}
#endif
c = ColorTexture.Sample(Sampler0, uv).xyz;
c = reinhard2(c);
float3 b = float3(0.0, 0.0, 0.0);
if(length(c) < 1.0)
{
b = blur(uv) * 0.3;
}
c = pow(c, 1.0/2.2);
float3 e = EmissiveTexture.Sample(Sampler1, uv).xyz;
c = c + blur(uv) * 0.05;
float exposure = 1.0;
c *= exposure;//= exposure(c, 1.0, 0.3);
// c = e;
// c = float3(uv.x, uv.y, 0.0);
c.x *= 1.0 + 0.000001 * EmissiveTexture.Sample(Sampler1, uv).x;
c.x *= 1.0 + 0.000001 * ColorTexture.Sample(Sampler0, uv).x;
return float4(c, 1.0);
}

View File

@ -22,9 +22,15 @@
"separate_images" : [
{
"type" : "texture2D",
"name" : "Texture0",
"name" : "ColorTexture",
"set" : 2,
"binding" : 0
},
{
"type" : "texture2D",
"name" : "EmissiveTexture",
"set" : 2,
"binding" : 1
}
],
"separate_samplers" : [
@ -33,6 +39,12 @@
"name" : "Sampler0",
"set" : 2,
"binding" : 0
},
{
"type" : "sampler",
"name" : "Sampler1",
"set" : 2,
"binding" : 1
}
]
}

View File

@ -1,8 +1,27 @@
TextureCube<float4> SkyboxTexture : register(t0, space2);
SamplerState SkyboxSampler : register(s0, space2);
float4 main(float3 TexCoord : TEXCOORD0) : SV_Target0
cbuffer Uniforms : register(b0, space3)
{
return pow(SkyboxTexture.Sample(SkyboxSampler, TexCoord), 1.0/2.2);
float brightnessFactor;
};
struct PixelOutput {
float4 Color : SV_Target0; // First render target
float4 Emissive : SV_Target1; // Second render target
};
PixelOutput main(float3 TexCoord : TEXCOORD0)
{
//return pow(SkyboxTexture.Sample(SkyboxSampler, TexCoord) * 4.0, 1.0/2.2);
PixelOutput o;
o.Color = SkyboxTexture.Sample(SkyboxSampler, TexCoord) * brightnessFactor;
if(length(o.Color) > 1.0)
{
o.Emissive = o.Color;
}
return o;
}

View File

@ -5,6 +5,18 @@
"mode" : "frag"
}
],
"types" : {
"_10" : {
"name" : "type.Uniforms",
"members" : [
{
"name" : "brightnessFactor",
"type" : "float",
"offset" : 0
}
]
}
},
"inputs" : [
{
"type" : "vec3",
@ -17,6 +29,11 @@
"type" : "vec4",
"name" : "out.var.SV_Target0",
"location" : 0
},
{
"type" : "vec4",
"name" : "out.var.SV_Target1",
"location" : 1
}
],
"separate_images" : [
@ -34,5 +51,14 @@
"set" : 2,
"binding" : 0
}
],
"ubos" : [
{
"type" : "_10",
"name" : "type.Uniforms",
"block_size" : 4,
"set" : 3,
"binding" : 0
}
]
}

View File

@ -194,12 +194,16 @@ pub fn createPipeline(self: *@This()) !*gpu.GPUGraphicsPipeline {
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = rend.context().depthFormat;
pci.target_info.num_color_targets = 1;
pci.target_info.num_color_targets = 2;
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
.{
.format = rend.context().hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = rend.context().hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
};
pci.rasterizer_state.fill_mode = .fillmodeLine;

View File

@ -12,6 +12,8 @@ skyboxColor: core.colors.Color = .{ .r = 71.0 / 256.0, .g = 200.0 / 256.0, .b =
skyboxMeshName: core.Name = core.DefineName("m_skybox"),
brightnessFactor: f32 = 4.0,
sampler: *gpu.GPUSampler = undefined,
pub fn create(allocator: std.mem.Allocator) !*@This() {
@ -58,12 +60,16 @@ fn createPipeline(self: *@This()) !void {
.vertex_attributes = @ptrCast(attributes.items.ptr),
};
pci.target_info.num_color_targets = 1;
pci.target_info.num_color_targets = 2;
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
.{
.format = ctx.hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = ctx.hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
};
pci.rasterizer_state.fill_mode = .fillmodeFill;
@ -84,12 +90,20 @@ pub fn destroy(self: *@This()) void {
}
pub fn render(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = rend.context().state.targetTexture,
.clear_color = self.skyboxColor,
.load_op = .loadopClear,
.store_op = .storeopStore,
});
const targetInfo: [2]gpu.GPUColorTargetInfo = .{
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = rend.context().state.targetTexture,
.clear_color = self.skyboxColor,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = rend.context().state.emissiveTarget,
.clear_color = .{},
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
};
if (self.skyboxMesh == null) {
self.skyboxMesh = rend.getMeshByName(&self.skyboxMeshName);
@ -101,7 +115,7 @@ pub fn render(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
}
}
const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, null);
const renderpass = cmd.beginGPURenderPass(&targetInfo, 2, null);
if (self.skyboxTexture) |skyboxTexture| {
if (self.skyboxMesh) |skyboxMesh| {
@ -122,16 +136,24 @@ pub fn render(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
}
pub fn uploadSkyboxUniforms(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
_ = self;
{
var data = std.mem.zeroes([@sizeOf(skybox_vert.Uniforms) / 8 + 1]usize);
const ptr: *skybox_vert.Uniforms = @ptrCast(@alignCast(&data));
var data = std.mem.zeroes([@sizeOf(skybox_vert.Uniforms) / 8 + 1]usize);
const ptr: *skybox_vert.Uniforms = @ptrCast(@alignCast(&data));
if (rend.context().activeCamera) |camera| {
ptr.ViewProjection = @bitCast(camera.noTranslate);
}
if (rend.context().activeCamera) |camera| {
ptr.ViewProjection = @bitCast(camera.noTranslate);
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(skybox_vert.Uniforms));
}
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(skybox_vert.Uniforms));
{
var data = std.mem.zeroes([@sizeOf(skybox_vert.Uniforms) / 8 + 1]usize);
const ptr: *skybox_frag.Uniforms = @ptrCast(@alignCast(&data));
ptr.brightnessFactor = self.brightnessFactor;
cmd.pushGPUFragmentUniformData(0, &data, @sizeOf(skybox_vert.Uniforms));
}
}
const skybox_mesh_obj align(8) = @embedFile("embedded/skybox.obj").*;

View File

@ -44,6 +44,7 @@ pub const Renderer = struct {
shadowMapProjection: core.Transform = core.zm.identity(),
blockySampler: *gpu.GPUSampler = undefined,
linearSampler: *gpu.GPUSampler = undefined,
cubeSampler: *gpu.GPUSampler = undefined,
defaultTexture: *rend.Texture = undefined,
@ -78,6 +79,8 @@ pub const Renderer = struct {
screenPlane: ?rend.IndexedMesh = null,
lightPower: f32 = 4.0,
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const MaxObjectCount = 50000;
@ -112,6 +115,7 @@ pub const Renderer = struct {
};
self.state.targetTexture = self.device.createGPUTexture(&gci);
self.state.emissiveTarget = self.device.createGPUTexture(&gci);
}
pub fn startRenderer(self: *@This()) !void {
@ -167,7 +171,6 @@ pub const Renderer = struct {
self.skyboxSystem = try self.createRendererEngineObject(SkyboxSystem);
_ = try core.fs().installFileBytesMount("embedded:plane.obj", @constCast(&plane_obj), true);
_ = try core.fs().installFileBytesMount("embedded:screenPlane.obj", @constCast(&screenPlane_obj), true);
try assets.load(
@ -237,6 +240,21 @@ pub const Renderer = struct {
.address_mode_v = .sampleraddressmodeRepeat,
.address_mode_w = .sampleraddressmodeRepeat,
}));
self.linearSampler = self.device.createGPUSampler(&std.mem.zeroInit(gpu.GPUSamplerCreateInfo, .{
.min_filter = .filterLinear,
.mag_filter = .filterLinear,
.mipmap_mode = .samplermipmapmodeNearest,
// .address_mode_u = .sampleraddressmodeMirroredRepeat,
// .address_mode_v = .sampleraddressmodeMirroredRepeat,
// .address_mode_w = .sampleraddressmodeMirroredRepeat,
// .address_mode_u = .sampleraddressmodeRepeat,
// .address_mode_v = .sampleraddressmodeRepeat,
// .address_mode_w = .sampleraddressmodeRepeat,
.address_mode_u = .sampleraddressmodeClampToEdge,
.address_mode_v = .sampleraddressmodeClampToEdge,
.address_mode_w = .sampleraddressmodeClampToEdge,
}));
}
// registers a pre-existing render object, does not add to destroys
@ -416,8 +434,12 @@ pub const Renderer = struct {
core.graphics_log("swapchain format: {any}", .{self.swapchainTargetFormat});
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = self.depthFormat;
pci.target_info.num_color_targets = 1;
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
pci.target_info.num_color_targets = 2;
pci.target_info.color_target_descriptions = &[2]gpu.GPUColorTargetDescription{
.{
.format = self.hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = self.hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
@ -553,6 +575,7 @@ pub const Renderer = struct {
ptr.lightPosition = @bitCast(self.lightPosition.toZm());
ptr.directionalLight = @bitCast(resolved.toZm());
ptr.directionalLightColor = @bitCast(self.directionalLightColor);
ptr.lightPower = self.lightPower;
cmd.pushGPUFragmentUniformData(0, &data, @sizeOf(lit_mesh_frag.Uniforms));
}
}
@ -597,8 +620,13 @@ pub const Renderer = struct {
}
pub fn tick(self: *@This(), dt: f64) void {
var z = tracy.ZoneN(@src(), "renderer tick");
defer z.End();
self.totalTime += dt;
var z2 = tracy.ZoneN(@src(), "Uploads and Updates");
self.state.cmd = self.device.acquireGPUCommandBuffer();
self.frameUploads();
@ -621,8 +649,11 @@ pub const Renderer = struct {
for (self.preDraws.items) |interface| {
interface.func(interface.ptr, self.state.cmd.?);
}
z2.End();
var z1 = tracy.ZoneN(@src(), "draw");
self.draw();
z1.End();
}
// orthographic view projection from the sun towards the center of the thing
@ -640,6 +671,9 @@ pub const Renderer = struct {
}
pub fn drawDirectionalShadowMap(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
var z = tracy.ZoneN(@src(), "drawDirectionalShadowMap");
defer z.End();
{
var data = std.mem.zeroes([@sizeOf(meshes_vert.Uniforms) / 8 + 1]usize);
const ptr: *meshes_vert.Uniforms = @ptrCast(@alignCast(&data));
@ -677,10 +711,117 @@ pub const Renderer = struct {
renderpass.endGPURenderPass();
}
pub fn drawMeshes(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
// can cache this
const targetInfo: [2]gpu.GPUColorTargetInfo = .{
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.targetTexture,
.load_op = .loadopLoad,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.emissiveTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
};
const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
.texture = self.depthTexture,
.load_op = .loadopClear,
.store_op = .storeopDontCare,
.stencil_load_op = .loadopDontCare,
.stencil_store_op = .storeopDontCare,
.clear_depth = 1.0,
.clear_stencil = 0,
});
self.state.pass = cmd.beginGPURenderPass(&targetInfo, 2, &depthTarget);
const renderpass = self.state.pass.?;
renderpass.setGPUScissor(&self.scissor);
//renderpass.bindGPUGraphicsPipeline(self.testPipeline);
renderpass.bindGPUFragmentSamplers(1, &.{ .sampler = self.blockySampler, .texture = self.defaultTexture.texture }, 1);
renderpass.bindGPUFragmentSamplers(2, &.{ .sampler = self.blockySampler, .texture = self.defaultTexture.texture }, 1);
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUFragmentStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
renderpass.bindGPUFragmentSamplers(self.shadowDepthTextureSlot, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1);
try self.uploadUniforms(cmd);
//rendering each mesh
{
// placeholder textures
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
for (0..uploadCount) |i| {
const component = &container.dense.items[i].value;
var t = component.texture;
if (t == null) {
t = self.defaultTexture;
}
if (component.mesh) |mesh| {
renderpass.bindGPUFragmentSamplers(0, &.{ .texture = t.?.texture, .sampler = self.blockySampler }, 1);
if (component.cmrf) |cmrf| {
cmrf(component.cmrf_ctx);
}
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
} else {}
}
}
for (self.postMesh.items) |interface| {
interface.func(interface.ptr, cmd, renderpass);
}
renderpass.endGPURenderPass();
}
pub fn drawPostProcess(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const postProcTargetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.swapchainTargetTexture.?,
.load_op = .loadopDontCare,
.store_op = .storeopStore,
});
const postProcPass = cmd.beginGPURenderPass(&postProcTargetInfo, 1, null);
postProcPass.bindGPUGraphicsPipeline(self.postProcessingPipeline);
postProcPass.bindGPUFragmentSamplers(0, &[2]gpu.GPUTextureSamplerBinding{
.{ .sampler = self.blockySampler, .texture = self.state.targetTexture },
.{ .sampler = self.linearSampler, .texture = self.state.emissiveTarget },
}, 2);
// postProcPass.bindGPUFragmentSamplers(1, &.{ .sampler = self.blockySampler, .texture = self.state.emissiveTarget }, 1);
// post processing + hdr etc...
if (self.screenPlane == null) {
self.screenPlane = getMesh("m_screenPlane");
}
if (self.screenPlane) |mesh| {
postProcPass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), 0);
}
postProcPass.endGPURenderPass();
}
pub fn draw(self: *@This()) void {
const cmd = self.state.cmd.?;
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, @ptrCast(&self.state.swapchainTargetTexture), null, null)) {
var z = tracy.ZoneN(@src(), "Acquiring Next frame");
const shouldDraw = cmd.waitAndAcquireGPUSwapchainTexture(self.window, @ptrCast(&self.state.swapchainTargetTexture), null, null);
z.End();
if (shouldDraw) {
if (self.state.swapchainTargetTexture == null) {
_ = cmd.submitGPUCommandBuffer();
return;
@ -688,103 +829,9 @@ pub const Renderer = struct {
// preMeshPasses
self.drawDirectionalShadowMap(cmd);
//
// draw skybox
{
self.skyboxSystem.render(cmd);
}
// can cache this
const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.targetTexture,
.load_op = .loadopLoad,
.store_op = .storeopStore,
});
const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
.texture = self.depthTexture,
.load_op = .loadopClear,
.store_op = .storeopDontCare,
.stencil_load_op = .loadopDontCare,
.stencil_store_op = .storeopDontCare,
.clear_depth = 1.0,
.clear_stencil = 0,
});
self.state.pass = cmd.beginGPURenderPass(&targetInfo, 1, &depthTarget);
const renderpass = self.state.pass.?;
renderpass.setGPUScissor(&self.scissor);
//renderpass.bindGPUGraphicsPipeline(self.testPipeline);
renderpass.bindGPUFragmentSamplers(1, &.{ .sampler = self.blockySampler, .texture = self.defaultTexture.texture }, 1);
renderpass.bindGPUFragmentSamplers(2, &.{ .sampler = self.blockySampler, .texture = self.defaultTexture.texture }, 1);
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUFragmentStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
// render the meshes
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
renderpass.bindGPUFragmentSamplers(self.shadowDepthTextureSlot, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1);
// todo move into materials system
try self.uploadUniforms(cmd);
//rendering each mesh
{
// placeholder textures
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
for (0..uploadCount) |i| {
const component = &container.dense.items[i].value;
var t = component.texture;
if (t == null) {
t = self.defaultTexture;
}
if (component.mesh) |mesh| {
renderpass.bindGPUFragmentSamplers(0, &.{ .texture = t.?.texture, .sampler = self.blockySampler }, 1);
if (component.cmrf) |cmrf| {
cmrf(component.cmrf_ctx);
}
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
} else {}
}
}
for (self.postMesh.items) |interface| {
interface.func(interface.ptr, cmd, renderpass);
}
renderpass.endGPURenderPass();
{
const postProcTargetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.swapchainTargetTexture.?,
.load_op = .loadopDontCare,
.store_op = .storeopStore,
});
const postProcPass = cmd.beginGPURenderPass(&postProcTargetInfo, 1, null);
postProcPass.bindGPUGraphicsPipeline(self.postProcessingPipeline);
postProcPass.bindGPUFragmentSamplers(0, &.{ .sampler = self.blockySampler, .texture = self.state.targetTexture }, 1);
// post processing + hdr etc...
if (self.screenPlane == null) {
self.screenPlane = getMesh("m_screenPlane");
}
if (self.screenPlane) |mesh| {
postProcPass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), 0);
}
postProcPass.endGPURenderPass();
}
self.skyboxSystem.render(cmd);
self.drawMeshes(cmd);
self.drawPostProcess(cmd);
for (self.postRenders.items) |interface| {
interface.func(interface.ptr, cmd);
@ -852,6 +899,7 @@ pub fn reloadShaders() !void {
return;
};
try gRenderer.createMeshPipeline();
try gRenderer.createPostProcessingPipeline();
}
// ====== renderer API =======
@ -900,6 +948,7 @@ pub const RendererState = struct {
cmd: ?*gpu.GPUCommandBuffer = null,
swapchainTargetTexture: ?*gpu.GPUTexture = null,
targetTexture: *gpu.GPUTexture = undefined,
emissiveTarget: *gpu.GPUTexture = undefined,
};
pub const CustomMeshRenderFunc = *const fn (?*anyopaque) void;
@ -907,3 +956,4 @@ pub const GPUTextureType = gpu.GPUTexture;
const plane_obj align(8) = @embedFile("embedded/plane.obj").*;
const screenPlane_obj align(8) = @embedFile("embedded/screenPlane.obj").*;
const tracy = core.tracy;

View File

@ -8,6 +8,11 @@ spawnRotation: core.Rotation = .{},
pub const SpawnEntry = struct {
typeName: []const u8,
spawnFunc: core.GameObjectList.SpawnFunc,
opts: Options,
};
pub const Options = struct {
absoluteSpawnposition: bool = false, // if set, won't move the object to where the spawn rotation and position is
};
pub fn create(allocator: std.mem.Allocator) !*@This() {
@ -29,7 +34,9 @@ pub fn tick(self: *@This(), dt: f64) void {
if (ig.smallButton(entry.typeName.ptr)) {
if (entry.spawnFunc(self.objectList)) |new| {
const entity = new.vtable.getEntity.?(new.ptr);
self.prepareEntity(entity);
if (!entry.opts.absoluteSpawnposition) {
self.prepareEntity(entity);
}
}
}
}
@ -39,7 +46,12 @@ pub fn tick(self: *@This(), dt: f64) void {
}
pub fn addSpawnFunction(self: *@This(), comptime name: []const u8, comptime T: type) !void {
try self.spawnFuncs.append(self.allocator, .{ .typeName = name, .spawnFunc = core.GameObjectList.spawnObjectFunction(T) });
var opts: Options = .{};
if (@hasDecl(T, "SpawnOptions")) {
opts = T.SpawnOptions;
}
try self.spawnFuncs.append(self.allocator, .{ .typeName = name, .spawnFunc = core.GameObjectList.spawnObjectFunction(T), .opts = opts });
}
pub fn prepareEntity(self: *@This(), entity: core.Entity) void {

View File

@ -6,6 +6,7 @@ using namespace metal;
struct main0_out
{
float4 out_var_SV_Target0 [[color(0)]];
float4 out_var_SV_Target1 [[color(1)]];
};
struct main0_in
@ -16,7 +17,9 @@ struct main0_in
fragment main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
out.out_var_SV_Target0 = float4(in.in_var_TEXCOORD0, 1.0);
float4 _20 = float4(in.in_var_TEXCOORD0 * 4.0, 1.0);
out.out_var_SV_Target0 = _20;
out.out_var_SV_Target1 = _20;
return out;
}

View File

@ -10,6 +10,7 @@ struct type_Uniforms
float4 directionalLight;
float4 directionalLightColor;
float2 screenSize;
float lightPower;
float time;
};
@ -27,12 +28,13 @@ struct type_StructuredBuffer_Scene
Scene _m0[1];
};
constant float4 _86 = {};
constant float3 _88 = {};
constant float4 _88 = {};
constant float3 _90 = {};
struct main0_out
{
float4 out_var_SV_Target0 [[color(0)]];
float4 out_var_SV_Target1 [[color(1)]];
};
struct main0_in
@ -47,110 +49,145 @@ struct main0_in
fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_Scene& scene [[buffer(1)]], texture2d<float> Texture0 [[texture(0)]], texture2d<float> Texture1 [[texture(1)]], texture2d<float> Texture2 [[texture(2)]], texture2d<float> DirectionalShadowDepthMap [[texture(3)]], sampler Sampler0 [[sampler(0)]], sampler Sampler1 [[sampler(1)]], sampler Sampler2 [[sampler(2)]], sampler DirectionalShadowSampler [[sampler(3)]])
{
main0_out out = {};
int _97 = int(scene._m0[in.in_var_TEXCOORD4].textureMode);
int _98 = _97 & 240;
float4 _132;
if (_98 == 16)
int _99 = int(scene._m0[in.in_var_TEXCOORD4].textureMode);
int _100 = _99 & 240;
float4 _134;
if (_100 == 16)
{
float3 _119 = float3(Texture0.sample(Sampler0, in.in_var_TEXCOORD0).x, Texture1.sample(Sampler1, in.in_var_TEXCOORD0).x, Texture2.sample(Sampler2, in.in_var_TEXCOORD0).x) + float3(-0.0625, -0.5, -0.5);
_132 = float4(dot(_119, float3(1.164000034332275390625, 0.0, 1.7929999828338623046875)), dot(_119, float3(1.164000034332275390625, -0.212999999523162841796875, -0.53299999237060546875)), dot(_119, float3(1.164000034332275390625, 2.111999988555908203125, 0.0)), 1.0);
float3 _121 = float3(Texture0.sample(Sampler0, in.in_var_TEXCOORD0).x, Texture1.sample(Sampler1, in.in_var_TEXCOORD0).x, Texture2.sample(Sampler2, in.in_var_TEXCOORD0).x) + float3(-0.0625, -0.5, -0.5);
_134 = float4(dot(_121, float3(1.164000034332275390625, 0.0, 1.7929999828338623046875)), dot(_121, float3(1.164000034332275390625, -0.212999999523162841796875, -0.53299999237060546875)), dot(_121, float3(1.164000034332275390625, 2.111999988555908203125, 0.0)), 1.0);
}
else
{
float4 _131;
if (_98 == 0)
float4 _133;
if (_100 == 0)
{
_131 = Texture0.sample(Sampler0, in.in_var_TEXCOORD0);
_133 = Texture0.sample(Sampler0, in.in_var_TEXCOORD0);
}
else
{
_131 = _86;
_133 = _88;
}
_132 = _131;
_134 = _133;
}
float4 _138;
if ((_97 & 1) == 1)
float4 _140;
if ((_99 & 1) == 1)
{
_138 = powr(_132, float4(2.2000000476837158203125));
_140 = powr(_134, float4(2.2000000476837158203125));
}
else
{
_138 = _132;
_140 = _134;
}
if (_138.w < 0.00999999977648258209228515625)
if (_140.w < 0.00999999977648258209228515625)
{
discard_fragment();
}
float3 _192;
float3 _150 = float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * Uniforms.lightPower;
float3 _197;
do
{
float3 _149 = Uniforms.lightPosition.xyz - in.in_var_TEXCOORD1;
float _150 = length(_149);
float _151 = _150 * _150;
float _157;
if (_150 > 2.0)
{
_157 = 0.5 / _151;
}
else
{
_157 = 1.0 / _151;
}
float3 _154 = Uniforms.lightPosition.xyz - in.in_var_TEXCOORD1;
float _155 = length(_154);
float _156 = _155 * _155;
float _162;
if (_150 > 4.0)
if (_155 > 2.0)
{
_162 = _157 * 0.5;
_162 = 0.5 / _156;
}
else
{
_162 = _157;
_162 = 1.0 / _156;
}
float _164 = (_150 > 15.0) ? 0.0 : _162;
float _166 = (_164 > 1.0) ? 1.0 : _164;
float _167;
if (_155 > 4.0)
{
_167 = _162 * 0.5;
}
else
{
_167 = _162;
}
float _169 = (_155 > 15.0) ? 0.0 : _167;
float _171 = (_169 > 1.0) ? 1.0 : _169;
if (length(in.in_var_TEXCOORD2) < 0.100000001490116119384765625)
{
_192 = (in.in_var_TEXCOORD1 * float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5)) * _166;
_197 = (in.in_var_TEXCOORD1 * _150) * _171;
break;
}
float3 _173 = fast::normalize(_149);
_192 = ((float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * precise::max(dot(_173, in.in_var_TEXCOORD2), 0.0)) * _166) + (((float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * powr(precise::max(dot(in.in_var_TEXCOORD2, fast::normalize(_173 + fast::normalize(in.in_var_TEXCOORD1 - Uniforms.viewPos.xyz))), 0.0), 30.0)) * 2.0) * _166);
float3 _178 = fast::normalize(_154);
_197 = ((_150 * precise::max(dot(_178, in.in_var_TEXCOORD2), 0.0)) * _171) + (((_150 * powr(precise::max(dot(in.in_var_TEXCOORD2, fast::normalize(_178 + fast::normalize(in.in_var_TEXCOORD1 - Uniforms.viewPos.xyz))), 0.0), 30.0)) * 2.0) * _171);
break;
} while(false);
float3 _209 = ((in.in_var_TEXCOORD3.xyz / float3(in.in_var_TEXCOORD3.w)) * 0.5) + float3(0.5);
float3 _211;
_211.x = _209.x;
float2 _212 = _211.xy;
_212.y = -_209.y;
float _219;
int _222;
int _224;
_219 = 0.0;
_222 = 0;
_224 = -4;
float _220;
int _223;
for (; _224 <= 4; _219 = _220, _222 = _223, _224++)
float3 _214 = ((in.in_var_TEXCOORD3.xyz / float3(in.in_var_TEXCOORD3.w)) * 0.5) + float3(0.5);
float3 _216;
_216.x = _214.x;
float2 _217 = _216.xy;
_217.y = -_214.y;
float _224;
int _227;
int _229;
_224 = 0.0;
_227 = 0;
_229 = -4;
float _225;
int _228;
for (; _229 <= 4; _224 = _225, _227 = _228, _229++)
{
_223 = _222;
_220 = _219;
for (int _233 = -4; _233 <= 4; )
_228 = _227;
_225 = _224;
for (int _238 = -4; _238 <= 4; )
{
_223++;
_220 += float((in.in_var_TEXCOORD3.z - 0.004999999888241291046142578125) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, (_212 + float2(float(_233) * 0.000244140625, float(_224) * 0.000244140625))).x);
_233++;
_228++;
_225 += float((in.in_var_TEXCOORD3.z - 0.004999999888241291046142578125) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, (_217 + float2(float(_238) * 0.000244140625, float(_229) * 0.000244140625))).x);
_238++;
continue;
}
}
float3 _262;
if ((_97 & 2) > 0)
float3 _267;
if ((_99 & 2) > 0)
{
_262 = _138.xyz * 4.0;
_267 = _140.xyz * 2.0;
}
else
{
_262 = _138.xyz * ((float3(0.100000001490116119384765625) + _192) + ((Uniforms.directionalLightColor.xyz * precise::max(dot(Uniforms.directionalLight.xyz, in.in_var_TEXCOORD2), 0.0)) * (1.0 - (_219 / float(_222)))));
_267 = _140.xyz * ((float3(0.100000001490116119384765625) + _197) + ((Uniforms.directionalLightColor.xyz * precise::max(dot(Uniforms.directionalLight.xyz, in.in_var_TEXCOORD2), 0.0)) * (1.0 - (_224 / float(_227)))));
}
out.out_var_SV_Target0 = float4(powr(_262, float3(0.4545454680919647216796875)), _138.w);
float4 _276;
if (_267.x > 1.0)
{
float4 _275 = float4(0.0, 0.0, 0.0, 1.0);
_275.x = _267.x;
_276 = _275;
}
else
{
_276 = float4(0.0, 0.0, 0.0, 1.0);
}
float4 _281;
if (_267.y > 1.0)
{
float4 _280 = _276;
_280.y = _267.y;
_281 = _280;
}
else
{
_281 = _276;
}
float4 _286;
if (_267.z > 1.0)
{
float4 _285 = _281;
_285.z = _267.z;
_286 = _285;
}
else
{
_286 = _281;
}
out.out_var_SV_Target0 = float4(_267, _140.w);
out.out_var_SV_Target1 = _286;
return out;
}

View File

@ -13,12 +13,31 @@ struct main0_in
float2 in_var_TEXCOORD0 [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]])
fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> ColorTexture [[texture(0)]], texture2d<float> EmissiveTexture [[texture(1)]], sampler Sampler0 [[sampler(0)]], sampler Sampler1 [[sampler(1)]])
{
main0_out out = {};
float2 _19 = in.in_var_TEXCOORD0;
_19.y = -_19.y;
out.out_var_SV_Target0 = Texture0.sample(Sampler0, _19);
float2 _43 = in.in_var_TEXCOORD0;
_43.y = (_43.y * (-1.0)) + 1.0;
float4 _51 = ColorTexture.sample(Sampler0, _43);
float3 _52 = _51.xyz;
float3 _66;
int _69;
_66 = EmissiveTexture.sample(Sampler1, _43).xyz * 0.2270270287990570068359375;
_69 = 0;
float3 _67;
for (; _69 < 5; _66 = _67, _69++)
{
_67 = _66;
for (int _77 = 0; _77 < 5; )
{
float2 _85 = float2(float(_69) - 2.5, float(_77)) * float2(0.001953125);
_67 = (_67 + (EmissiveTexture.sample(Sampler1, (_43 + _85)).xyz * float3(0.039999999105930328369140625))) + (EmissiveTexture.sample(Sampler1, (_43 - _85)).xyz * float3(0.039999999105930328369140625));
_77++;
continue;
}
}
float3 _99 = (powr((_52 * (float3(1.0) + (_52 * float3(0.00999999977648258209228515625)))) / (float3(1.0) + _52), float3(0.4545454680919647216796875)) + (_66 * 0.0500000007450580596923828125)) * 1.0;
out.out_var_SV_Target0 = float4((_99.x * (1.0 + (9.9999999747524270787835121154785e-07 * EmissiveTexture.sample(Sampler1, _43).x))) * (1.0 + (9.9999999747524270787835121154785e-07 * ColorTexture.sample(Sampler0, _43).x)), _99.yz, 1.0);
return out;
}

View File

@ -3,9 +3,17 @@
using namespace metal;
struct type_Uniforms
{
float brightnessFactor;
};
constant float4 _28 = {};
struct main0_out
{
float4 out_var_SV_Target0 [[color(0)]];
float4 out_var_SV_Target1 [[color(1)]];
};
struct main0_in
@ -13,10 +21,12 @@ struct main0_in
float3 in_var_TEXCOORD0 [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], texturecube<float> SkyboxTexture [[texture(0)]], sampler SkyboxSampler [[sampler(0)]])
fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texturecube<float> SkyboxTexture [[texture(0)]], sampler SkyboxSampler [[sampler(0)]])
{
main0_out out = {};
out.out_var_SV_Target0 = powr(SkyboxTexture.sample(SkyboxSampler, in.in_var_TEXCOORD0), float4(0.4545454680919647216796875));
float4 _38 = SkyboxTexture.sample(SkyboxSampler, in.in_var_TEXCOORD0) * Uniforms.brightnessFactor;
out.out_var_SV_Target0 = _38;
out.out_var_SV_Target1 = select(_28, _38, bool4(length(_38) > 1.0));
return out;
}

View File

@ -0,0 +1,57 @@
data: core.GameObjectData = undefined,
entity: core.Entity,
pub const SpawnOptions: extras.ObjectSpawner.Options = .{
.absoluteSpawnposition = true,
};
const assetReferences = [_]assets.AssetImportReference{
assets.MakeImportRefOptions(
"Mesh",
"m_empire",
.{ .path = "meshes/lost_empire.obj" },
),
assets.MakeImportRefOptions(
"Texture",
"t_empire",
.{ .path = "textures/lost_empire-RGBA.png" },
),
};
pub var assetsLoaded: bool = false;
pub fn create(alloc: std.mem.Allocator) !*@This() {
const self = try alloc.create(@This());
if (!assetsLoaded) {
try assets.loadList(assetReferences);
assetsLoaded = true;
}
const lostEmpire = try core.createEntity();
const scene = lostEmpire.addComponent(core.Scene).?;
scene.setPosition(.{ .y = -20 });
const empireMesh = lostEmpire.addComponent(rend.MeshComponent).?;
empireMesh.setMesh("m_empire");
empireMesh.setTexture("t_empire");
self.entity = lostEmpire;
return self;
}
pub fn getEntity(self: *@This()) core.Entity {
return self.entity;
}
pub fn destroy(self: *@This()) void {
self.entity.destroy();
self.data.release(self);
}
const backlog = @import("Backlog");
const std = @import("std");
const rend = backlog.rend;
const core = backlog.core;
const assets = backlog.assets;
const extras = @import("gameExtras");

View File

@ -36,11 +36,6 @@ pub fn init(allocator: std.mem.Allocator) !*@This() {
}
const assetReferences = [_]assets.AssetImportReference{
assets.MakeImportRefOptions(
"Mesh",
"m_empire",
.{ .path = "meshes/lost_empire.obj" },
),
assets.MakeImportRefOptions(
"Mesh",
"m_skybox",
@ -56,11 +51,6 @@ const assetReferences = [_]assets.AssetImportReference{
"t_fox",
.{ .path = "gltf-samples/Fox/glTF/Texture.png" },
),
assets.MakeImportRefOptions(
"Texture",
"t_empire",
.{ .path = "textures/lost_empire-RGBA.png" },
),
assets.MakeImportRefOptions(
"Texture",
"t_skybox",
@ -118,6 +108,7 @@ pub fn prepare(self: *@This()) !void {
self.objectSpawner = try extras.ObjectSpawner.create(self.allocator);
try self.objectSpawner.addSpawnFunction("fox", FoxObject);
try self.objectSpawner.addSpawnFunction("doomplayer", DoomPlayer.DoomCanvas);
try self.objectSpawner.addSpawnFunction("empire", @import("empire.zig"));
try assets.loadList(assetReferences);
self.videoplayer = try VideoPlayer.create(self.allocator);
@ -143,17 +134,6 @@ pub fn prepare(self: *@This()) !void {
self.fpcamera.setPosition(.{ .z = 15 });
self.fpcamera.activateCamera();
{
core.engine_log("creating empire", .{});
const lostEmpire = try core.createEntity();
const scene = lostEmpire.addComponent(core.Scene).?;
scene.setPosition(.{ .y = -20 });
const empireMesh = lostEmpire.addComponent(rend.MeshComponent).?;
empireMesh.setMesh("m_empire");
empireMesh.setTexture("t_empire");
}
const moveInput = try core.Axis2dBinding.create(core.MakeName("movement"));
moveInput.addKey(.w, 1.0, .y);
@ -304,10 +284,24 @@ pub fn getMouseMovement(self: *@This()) core.Vector2f {
pub fn tick(self: *@This(), dt: f64) void {
const fdt: f32 = @floatCast(dt);
const z = tracy.ZoneN(@src(), "tick sampleGameMain");
defer z.End();
const z1 = tracy.ZoneN(@src(), "inputDebugger");
extras.inputDebugger.tick();
z1.End();
const z2 = tracy.ZoneN(@src(), "inputDebugger");
self.objectSpawner.tick(dt);
z2.End();
const z3 = tracy.ZoneN(@src(), "VideoPlayer");
self.videoplayer.tick(dt);
z3.End();
const z4 = tracy.ZoneN(@src(), "fpCamera");
self.fpcamera.tick(dt);
z4.End();
var movement = self.fpcamera.getForwardXZ().fmul(self.moveVector.z * fdt * self.cameraSpeed);
movement = movement.add(self.fpcamera.getRightXZ().fmul(self.moveVector.x * fdt * self.cameraSpeed));
@ -385,3 +379,4 @@ const core = backlog.core;
const ig = backlog.imgui.api;
const rend = backlog.rend;
const script = core.script;
const tracy = core.tracy;