This commit is contained in:
Peter Li 2025-04-12 17:21:53 -07:00
parent fc6b0a154a
commit 653f61e8d9
211 changed files with 35980 additions and 36276 deletions

View File

@ -13,12 +13,12 @@ const engineDepList = [_][]const u8{
"assets",
"audio",
"core",
"graphics",
"papyrus",
"platform",
"physics",
"ui",
"vkImgui",
// "graphics",
// "ui",
// "vkImgui",
};
const BuildSystem = @This();
@ -110,20 +110,64 @@ pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Step.C
mod.addImport("Backlog", self.nw_mod);
exe.root_module.addOptions("BacklogOptions", self.options);
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/triangle_mesh.vert"), "triangle_mesh_vert");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/default_lit.frag"), "default_lit");
// I want to generate definitions from the spirv-reflect-tool during pre-build.
//
// shaders will now be part of content, not code. However. .zig code definitions will be generated
// from content via the build.zig script.
//
// heres some thoughts:
//
// -Dupdate_shaders=true
//
// -Dupdate_shaders basically adds a run step to the build process 'python', 'tools/scripts/cook-shaders.py'
//
// 1. ensure spirv-reflect is compiled.
// 2. shadercross cooker generates all shader outputs - specifically spv files. they are output to content/_shaders/
// - search paths for shaders:
// - content/shaders/**
// - engine/<module names>/shaders/**
// - shaders must have a unique name across the entire project, naming scheme should be <module>.shadername.<stage>.hlsl
// 3. spirv-cross --reflect is called on each shader, updating the content/_shaders/defs/shader-name.json folder with up to date .json files
//
// final tree
//
// engine/
// - ui/
// - shaders/
// - papyrus.rect.vert.hlsl
// content/
// - _shaders/
// - def/
// papyrus.rect.vert.json
// - msl/
// papyrus.rect.vert.msl
// - spv/
// papyrus.rect.vert.spv
// - dxil/
// papyrus.rect.vert.dxil
//
//
// During build:
// 1. spirv-reflect is called on each json in each folder and .zig shader interfaces added to the executable as a global import.
// 2. if -Dupdate_shaders is not
//
// At any time:
// asset-cooker will cook shaders and place them under content/_shaders/<msl|dxil|dxil>/<shader-name>.<msl|spv|dxil>
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.vert"), "debug_vert");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.frag"), "debug_frag");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/triangle_mesh.vert"), "triangle_mesh_vert");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/default_lit.frag"), "default_lit");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.vert"), "skybox_vert");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.frag"), "skybox_frag");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.vert"), "debug_vert");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.frag"), "debug_frag");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.vert"), "papyrus_vk_vert");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.frag"), "papyrus_vk_frag");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.vert"), "skybox_vert");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.frag"), "skybox_frag");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.vert"), "FontSDF_vert");
self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.frag"), "FontSDF_frag");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.vert"), "papyrus_vk_vert");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.frag"), "papyrus_vk_frag");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.vert"), "FontSDF_vert");
// self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.frag"), "FontSDF_frag");
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());

View File

@ -1,16 +1,17 @@
.{
.name = "Backlog",
.name = .Backlog,
.version = "0.0.0",
.dependencies = .{
.assets = .{ .path = "engine/assets" },
.audio = .{ .path = "engine/audio" },
.core = .{ .path = "engine/core" },
.graphics = .{ .path = "engine/graphics" },
.papyrus = .{ .path = "engine/papyrus" },
.physics = .{ .path = "engine/physics" },
.platform = .{ .path = "engine/platform" },
.ui = .{ .path = "engine/ui" },
.vkImgui = .{ .path = "engine/vkImgui" },
// .graphics = .{ .path = "engine/graphics" },
// .ui = .{ .path = "engine/ui" },
// .vkImgui = .{ .path = "engine/vkImgui" },
.SpirvReflect = .{ .path = "lib/spirv-reflect-zig" },
.ozz = .{ .path = "lib/ozz" },
@ -18,4 +19,5 @@
.paths = .{
"",
},
.fingerprint = 0xcf9bab998abe37e3
}

View File

@ -1,21 +1,21 @@
.{
.name = "graphics",
.version = "0.0.0",
.dependencies = .{
//
.vulkan = .{ .path = "../../lib/vulkan" },
.vma = .{ .path = "../../lib/vma" },
.glfw3 = .{ .path = "../../lib/glfw3" },
.cgltf = .{ .path = "../../lib/cgltf" },
.objLoader = .{ .path = "../../lib/objLoader" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.ozz = .{ .path = "../../lib/ozz" },
// core
.core = .{ .path = "../core" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
},
.paths = .{
"",
},
}
.{
.name = "graphics",
.version = "0.0.0",
.dependencies = .{
//
.vulkan = .{ .path = "../../lib/vulkan" },
.vma = .{ .path = "../../lib/vma" },
.glfw3 = .{ .path = "../../lib/glfw3" },
.cgltf = .{ .path = "../../lib/cgltf" },
.objLoader = .{ .path = "../../lib/objLoader" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.ozz = .{ .path = "../../lib/ozz" },
// core
.core = .{ .path = "../core" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
},
.paths = .{
"",
},
}

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,32 +1,32 @@
//glsl version 4.5
#version 450
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
void main()
{
vec3 color = in_color.rgb;
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = (1.0) - (cameraDist / 300);
outFragColor = vec4(color, opacity * 1.0);
}
//glsl version 4.5
#version 450
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
void main()
{
vec3 color = in_color.rgb;
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = (1.0) - (cameraDist / 300);
outFragColor = vec4(color, opacity * 1.0);
}

View File

@ -1,40 +1,40 @@
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
// size: 16 x 4 + 3 x 4 = 76 => 128 bytes per object per alignment
struct ObjectData {
mat4 model;
vec4 color;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
void main()
{
ObjectData object = objectBuffer.objects[gl_BaseInstance];
mat4 modelMatrix = object.model;
mat4 final = (cameraData.viewproj * modelMatrix);
vec4 position = final * vec4(vPosition, 1.0f);
gl_Position = position;
outColor = object.color.xyz;
texCoord = vTexCoord;
vec4 modelPos = modelMatrix * vec4(vPosition, 1.0f);
worldPosition = modelPos.xyz;
}
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
// size: 16 x 4 + 3 x 4 = 76 => 128 bytes per object per alignment
struct ObjectData {
mat4 model;
vec4 color;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
void main()
{
ObjectData object = objectBuffer.objects[gl_BaseInstance];
mat4 modelMatrix = object.model;
mat4 final = (cameraData.viewproj * modelMatrix);
vec4 position = final * vec4(vPosition, 1.0f);
gl_Position = position;
outColor = object.color.xyz;
texCoord = vTexCoord;
vec4 modelPos = modelMatrix * vec4(vPosition, 1.0f);
worldPosition = modelPos.xyz;
}

View File

@ -1,47 +1,47 @@
//glsl version 4.5
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 3) flat in uint textureId;
layout (location = 4) flat in uint baseInstance;
layout (location = 0) out vec4 outFragColor;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
void main()
{
// outFragColor = vec4(in_color + 0.25 * sceneData.ambientColor.xyz,1.0f);
// outFragColor = vec4(texCoord.x, texCoord.y, 0.5f, 1.0f);
// vec4 color = texture(tex1, texCoord).xyzw;
vec4 color = texture(gTex[textureId], texCoord).xyzw;
if(color.w < 0.05f)
{
discard;
}
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = clamp((1.f) - (clamp(cameraDist - 300, 0, 300) / 300.f), 0.f, 1.f);
//float opacity = 1.0;
if(opacity < 0.05f)
{
discard;
}
// outFragColor = vec4(mix(sceneData.fogColor.xyz, color.xyz, opacity), color.w);
outFragColor = vec4(color.xyz, opacity);
//vec3 mixed = mix(normalize(vec3(0.5, 0.3, 0.2)) * 3, vec3(0.2, 0.2, 1) * 3, texCoord.y * 2);
//outFragColor = vec4(color.xyz, 1.0f);
//outFragColor = vec4(0.0, 1.0, 0.0, 1.0f);
}
//glsl version 4.5
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 3) flat in uint textureId;
layout (location = 4) flat in uint baseInstance;
layout (location = 0) out vec4 outFragColor;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
void main()
{
// outFragColor = vec4(in_color + 0.25 * sceneData.ambientColor.xyz,1.0f);
// outFragColor = vec4(texCoord.x, texCoord.y, 0.5f, 1.0f);
// vec4 color = texture(tex1, texCoord).xyzw;
vec4 color = texture(gTex[textureId], texCoord).xyzw;
if(color.w < 0.05f)
{
discard;
}
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = clamp((1.f) - (clamp(cameraDist - 300, 0, 300) / 300.f), 0.f, 1.f);
//float opacity = 1.0;
if(opacity < 0.05f)
{
discard;
}
// outFragColor = vec4(mix(sceneData.fogColor.xyz, color.xyz, opacity), color.w);
outFragColor = vec4(color.xyz, opacity);
//vec3 mixed = mix(normalize(vec3(0.5, 0.3, 0.2)) * 3, vec3(0.2, 0.2, 1) * 3, texCoord.y * 2);
//outFragColor = vec4(color.xyz, 1.0f);
//outFragColor = vec4(0.0, 1.0, 0.0, 1.0f);
}

View File

@ -1,18 +1,18 @@
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
mat4 viewprojAlt;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
layout(set = 0, binding = 2) uniform sampler2D[] gTex;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
mat4 viewprojAlt;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
layout(set = 0, binding = 2) uniform sampler2D[] gTex;

View File

@ -1,25 +1,25 @@
struct ObjectData {
mat4 model;
uint textureId;
int animation; // this is the index offset of the first matrix in the animation finals buffer.
uint flags0;
// packed flags0 flags;
// [0,0]: alwaysInFront
// [1,1]: useAltCamera
uint pad1;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
uint flag0_AlwaysInFront(uint flags)
{
return flags & 0x1;
}
uint flag0_useAltFov(uint flags)
{
return (flags >> 1) & 0x1;
}
struct ObjectData {
mat4 model;
uint textureId;
int animation; // this is the index offset of the first matrix in the animation finals buffer.
uint flags0;
// packed flags0 flags;
// [0,0]: alwaysInFront
// [1,1]: useAltCamera
uint pad1;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
uint flag0_AlwaysInFront(uint flags)
{
return flags & 0x1;
}
uint flag0_useAltFov(uint flags)
{
return (flags >> 1) & 0x1;
}

View File

@ -1,3 +1,3 @@
layout(std140, set = 1, binding = 1) readonly buffer BoneBuffer{
mat4 finals[];
} animationBuffer;
layout(std140, set = 1, binding = 1) readonly buffer BoneBuffer{
mat4 finals[];
} animationBuffer;

View File

@ -1,15 +1,15 @@
#version 450
#include "../globalSet.glsl"
layout(set = 1, binding = 0) uniform samplerCube cubemap;
layout (location = 0) in vec3 inUVW;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = texture(cubemap, inUVW);
}
#version 450
#include "../globalSet.glsl"
layout(set = 1, binding = 0) uniform samplerCube cubemap;
layout (location = 0) in vec3 inUVW;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = texture(cubemap, inUVW);
}

View File

@ -1,17 +1,17 @@
#version 450
#include "../vertexInput.glsl"
#include "../globalSet.glsl"
layout (location = 0) out vec3 outUVW;
void main()
{
outUVW = vPosition;
// Convert cubemap coordinates into Vulkan coordinate space
// Remove translation from view matrix
mat4 viewMat = mat4(mat3(cameraData.view));
gl_Position = cameraData.proj * viewMat * vec4(vPosition.xyz, 1.0);
}
#version 450
#include "../vertexInput.glsl"
#include "../globalSet.glsl"
layout (location = 0) out vec3 outUVW;
void main()
{
outUVW = vPosition;
// Convert cubemap coordinates into Vulkan coordinate space
// Remove translation from view matrix
mat4 viewMat = mat4(mat3(cameraData.view));
gl_Position = cameraData.proj * viewMat * vec4(vPosition.xyz, 1.0);
}

View File

@ -1,66 +1,66 @@
#version 460
#include "vertexInput.glsl"
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (location = 3) flat out uint textureId;
layout (location = 4) flat out uint baseInstance;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
#include "skeletalBuffers.glsl"
void main()
{
vec3 vertexPos = vec3(0.0);
int animation = objectBuffer.objects[gl_BaseInstance].animation;
if(animation == -1)
{
vertexPos = vPosition;
}
else
{
uint animation = objectBuffer.objects[gl_BaseInstance].animation;
for(int i = 0; i < 4; i += 1)
{
uint boneIndex = bones[i];
float weight = float(weights[i]) / 255;
// this will depend on ozz's finals format
mat4 boneTransform = animationBuffer.finals[animation + boneIndex];
vertexPos += weight * ( boneTransform * vec4(vPosition, 1.0) ).xyz;
}
}
mat4 modelMatrix = objectBuffer.objects[gl_BaseInstance].model;
mat4 final;
if(flag0_useAltFov(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
final = (cameraData.viewprojAlt * modelMatrix);
}
else
{
final = (cameraData.viewproj * modelMatrix);
}
vec4 position = final * vec4(vertexPos, 1.0f);
if( flag0_AlwaysInFront(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
position.z *= 0.0001;
}
baseInstance = gl_BaseInstance;
gl_Position = position;
textureId = objectBuffer.objects[gl_BaseInstance].textureId;
outColor = vec3(vColor.x, vColor.y, vColor.z);
texCoord = vTexCoord;
worldPosition = (modelMatrix * vec4(0,0,0,1)).xyz;
}
#version 460
#include "vertexInput.glsl"
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (location = 3) flat out uint textureId;
layout (location = 4) flat out uint baseInstance;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
#include "skeletalBuffers.glsl"
void main()
{
vec3 vertexPos = vec3(0.0);
int animation = objectBuffer.objects[gl_BaseInstance].animation;
if(animation == -1)
{
vertexPos = vPosition;
}
else
{
uint animation = objectBuffer.objects[gl_BaseInstance].animation;
for(int i = 0; i < 4; i += 1)
{
uint boneIndex = bones[i];
float weight = float(weights[i]) / 255;
// this will depend on ozz's finals format
mat4 boneTransform = animationBuffer.finals[animation + boneIndex];
vertexPos += weight * ( boneTransform * vec4(vPosition, 1.0) ).xyz;
}
}
mat4 modelMatrix = objectBuffer.objects[gl_BaseInstance].model;
mat4 final;
if(flag0_useAltFov(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
final = (cameraData.viewprojAlt * modelMatrix);
}
else
{
final = (cameraData.viewproj * modelMatrix);
}
vec4 position = final * vec4(vertexPos, 1.0f);
if( flag0_AlwaysInFront(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
position.z *= 0.0001;
}
baseInstance = gl_BaseInstance;
gl_Position = position;
textureId = objectBuffer.objects[gl_BaseInstance].textureId;
outColor = vec3(vColor.x, vColor.y, vColor.z);
texCoord = vTexCoord;
worldPosition = (modelMatrix * vec4(0,0,0,1)).xyz;
}

View File

@ -1,7 +1,7 @@
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : enable
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 4) in u8vec4 bones;
layout (location = 5) in u8vec4 weights;
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : enable
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 4) in u8vec4 bones;
layout (location = 5) in u8vec4 weights;

View File

@ -1,32 +1,32 @@
pixels: []u8,
extent: core.Vector2i,
const std = @import("std");
const core = @import("core");
const colors = core.colors;
pub fn init(allocator: std.mem.Allocator, extent: core.Vector2i) !@This() {
return .{
.pixels = try allocator.alignedAlloc(u8, 8, @intCast(extent.x * extent.y * 4)),
.extent = extent,
};
}
pub fn clear(self: *@This(), clearColor: colors.ColorRGBA8) void {
var as32: []u32 = undefined;
as32.len = self.pixels.len / 4;
as32.ptr = @alignCast(@ptrCast(self.pixels.ptr));
@memset(as32, @as(u32, @bitCast(clearColor)));
}
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
allocator.free(self.pixels);
}
pub inline fn getPixel(self: *@This(), position: core.Vector2i) *colors.ColorRGBA8 {
const offset = position.x * position.y * 4;
const r: *u8 = &self.pixels[@intCast(offset)];
return @as(*colors.ColorRGBA8, @alignCast(@ptrCast(r)));
}
pixels: []u8,
extent: core.Vector2i,
const std = @import("std");
const core = @import("core");
const colors = core.colors;
pub fn init(allocator: std.mem.Allocator, extent: core.Vector2i) !@This() {
return .{
.pixels = try allocator.alignedAlloc(u8, 8, @intCast(extent.x * extent.y * 4)),
.extent = extent,
};
}
pub fn clear(self: *@This(), clearColor: colors.ColorRGBA8) void {
var as32: []u32 = undefined;
as32.len = self.pixels.len / 4;
as32.ptr = @alignCast(@ptrCast(self.pixels.ptr));
@memset(as32, @as(u32, @bitCast(clearColor)));
}
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
allocator.free(self.pixels);
}
pub inline fn getPixel(self: *@This(), position: core.Vector2i) *colors.ColorRGBA8 {
const offset = position.x * position.y * 4;
const r: *u8 = &self.pixels[@intCast(offset)];
return @as(*colors.ColorRGBA8, @alignCast(@ptrCast(r)));
}

View File

@ -1,232 +1,232 @@
pub const AnimResolverRef = core.Reference(AnimResolverInterface);
pub const AnimResolverInterface = core.MakeInterface("AnimResolverVTable", struct {
// this tick function should evaluate the current state of the resolver
// and then update the animator's finals[] matrix list.
resolve: *const fn (*anyopaque, f64, *Animator) void,
onSkeletonSet: ?*const fn (*anyopaque, *Animator) void = null,
create: *const fn (std.mem.Allocator) core.EngineDataEventError!*anyopaque,
destroy: *const fn (*anyopaque) void,
pub fn Implement(comptime TargetType: type) @This() {
const Wrap = struct {
pub fn create(allocator: std.mem.Allocator) core.EngineDataEventError!*anyopaque {
const new = TargetType.create(allocator) catch return core.EngineDataEventError.BadInit;
return @ptrCast(new);
}
pub fn destroy(p: *anyopaque) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.destroy();
}
pub fn onSkeletonSet(p: *anyopaque, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.onSkeletonSet(a) catch unreachable;
}
pub fn resolve(p: *anyopaque, dt: f64, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.resolve(dt, a) catch unreachable;
}
};
return .{
.destroy = Wrap.destroy,
.create = Wrap.create,
.resolve = Wrap.resolve,
};
}
});
pub const AnimSampler = struct {
name: ?core.Name = null,
track: ?*AnimationTrack = null,
playbackRate: f32 = 1.0,
time: f32 = 0.0,
outputLocals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
// other features
// paused: bool = false,
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.outputLocals.deinit(allocator);
}
pub fn getOutput(self: *@This()) []ozz.SoaTransform {
return self.outputLocals.items;
}
pub fn sampleAndAdvance(self: *@This(), allocator: std.mem.Allocator, dt: f64, animator: *Animator) void {
self.sample(allocator, animator);
self.advance(dt);
}
pub fn advance(self: *@This(), dt: f64) void {
if (self.track == null) {
return;
}
FloatHelpers.updateTrackTime(&self.time, dt, self.playbackRate, self.track.?.endTime);
}
pub fn setName(self: *@This(), name: core.Name) void {
self.name = name;
self.track = null;
}
pub fn sample(self: *@This(), allocator: std.mem.Allocator, animator: *Animator) void {
if (self.name == null) {
return;
}
if (self.track == null) {
self.track = animation_system.gAnimationSys.animTracks.get(self.name.?.handle());
}
self.outputLocals.resize(allocator, animator.jointLength) catch return;
if (self.track) |track| {
if (track.endTime < 0.01) {
return;
}
animator.sampleAnimation(self.time, track, self.outputLocals.items);
}
}
};
pub const BlenderList = struct {
backing: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
jobLayers: std.ArrayListUnmanaged(ozz.Layer) = .{},
jobLayersAdditive: std.ArrayListUnmanaged(ozz.Layer) = .{},
useAdditive: bool = false,
threshold: f32 = 0.01,
jointLength: usize = 0,
blendingJob: ozz.BlendingJob = .{},
pub fn create(backingAllocator: std.mem.Allocator) !*@This() {
const self = try backingAllocator.create(@This());
self.* = .{
.backing = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
};
return self;
}
pub fn destroy(self: *@This()) void {
self.arena.deinit();
self.backing.destroy(self);
}
pub fn updateRestPose(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
self.jointLength = skeleton.sk.numJoints();
self.blendingJob.rest_pose = skeleton.sk.getRestPoseModel();
}
}
pub fn clearLayers(self: *@This()) void {
self.jobLayersAdditive.clearRetainingCapacity();
self.jobLayers.clearRetainingCapacity();
}
pub fn addLayer(self: *@This(), transform: []ozz.SoaTransform, weight: f32, settings: anytype) void {
const layer = self.jobLayers.addOne(self.arena.allocator()) catch unreachable;
layer.* = .{
.weight = weight,
.transform = ozz.makeSpan(transform),
};
_ = settings;
}
pub fn updateAndRun(self: *@This(), output: []ozz.SoaTransform) void {
self.updateBlendingJob();
self.runBlendingJob(output) catch return;
}
pub fn updateBlendingJob(self: *@This()) void {
self.blendingJob.threshold = self.threshold;
self.blendingJob.layers = ozz.makeSpan(self.jobLayers.items);
//self.blendingJob.additive_layers = if (self.useAdditive) ozz.makeSpan(self.jobLayersAdditive.items) else .{};
self.blendingJob.additive_layers = .{};
}
pub fn runBlendingJob(self: *@This(), output: []ozz.SoaTransform) !void {
self.blendingJob.output = ozz.makeSpan(output);
if (!self.blendingJob.run()) {
core.engine_logs("blending job failed");
return;
}
}
};
// resolver helpers
pub const FloatHelpers = struct {
pub inline fn updateTrackTime(target: *f32, dt: f64, rate: f32, endTime: f32) void {
target.* += @as(f32, @floatCast(dt)) * rate;
while (target.* > endTime) {
target.* -= endTime;
}
}
};
// samples a single animation, same as the default behaviour.
// used as a test for the resolver system
pub const SingleAnimationResolver = struct {
allocator: std.mem.Allocator,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
pub const AnimResolverVTable = AnimResolverInterface.Implement(@This());
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn onSkeletonSet(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
try self.locals.resize(self.allocator, skeleton.sk.numSoaJoints());
}
}
pub fn resolve(self: *@This(), dt: f64, animator: *Animator) !void {
self.track = animator.track;
if (self.track == null) {
return;
}
const track = self.track.?;
if (track.endTime < 0.01) {
return;
}
FloatHelpers.updateTrackTime(&self.playback, dt, self.playbackRate, track.endTime);
animator.sampleAnimation(self.playback, track, self.locals.items);
animator.commitLocalToModel(self.locals.items);
animator.modelToFinal();
}
pub fn destroy(self: *@This()) void {
self.locals.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const animation_system = @import("animationSystem.zig");
const Animator = animation_system.Animator;
const AnimationTrack = animation_system.AnimationTrack;
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");
pub const AnimResolverRef = core.Reference(AnimResolverInterface);
pub const AnimResolverInterface = core.MakeInterface("AnimResolverVTable", struct {
// this tick function should evaluate the current state of the resolver
// and then update the animator's finals[] matrix list.
resolve: *const fn (*anyopaque, f64, *Animator) void,
onSkeletonSet: ?*const fn (*anyopaque, *Animator) void = null,
create: *const fn (std.mem.Allocator) core.EngineDataEventError!*anyopaque,
destroy: *const fn (*anyopaque) void,
pub fn Implement(comptime TargetType: type) @This() {
const Wrap = struct {
pub fn create(allocator: std.mem.Allocator) core.EngineDataEventError!*anyopaque {
const new = TargetType.create(allocator) catch return core.EngineDataEventError.BadInit;
return @ptrCast(new);
}
pub fn destroy(p: *anyopaque) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.destroy();
}
pub fn onSkeletonSet(p: *anyopaque, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.onSkeletonSet(a) catch unreachable;
}
pub fn resolve(p: *anyopaque, dt: f64, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.resolve(dt, a) catch unreachable;
}
};
return .{
.destroy = Wrap.destroy,
.create = Wrap.create,
.resolve = Wrap.resolve,
};
}
});
pub const AnimSampler = struct {
name: ?core.Name = null,
track: ?*AnimationTrack = null,
playbackRate: f32 = 1.0,
time: f32 = 0.0,
outputLocals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
// other features
// paused: bool = false,
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.outputLocals.deinit(allocator);
}
pub fn getOutput(self: *@This()) []ozz.SoaTransform {
return self.outputLocals.items;
}
pub fn sampleAndAdvance(self: *@This(), allocator: std.mem.Allocator, dt: f64, animator: *Animator) void {
self.sample(allocator, animator);
self.advance(dt);
}
pub fn advance(self: *@This(), dt: f64) void {
if (self.track == null) {
return;
}
FloatHelpers.updateTrackTime(&self.time, dt, self.playbackRate, self.track.?.endTime);
}
pub fn setName(self: *@This(), name: core.Name) void {
self.name = name;
self.track = null;
}
pub fn sample(self: *@This(), allocator: std.mem.Allocator, animator: *Animator) void {
if (self.name == null) {
return;
}
if (self.track == null) {
self.track = animation_system.gAnimationSys.animTracks.get(self.name.?.handle());
}
self.outputLocals.resize(allocator, animator.jointLength) catch return;
if (self.track) |track| {
if (track.endTime < 0.01) {
return;
}
animator.sampleAnimation(self.time, track, self.outputLocals.items);
}
}
};
pub const BlenderList = struct {
backing: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
jobLayers: std.ArrayListUnmanaged(ozz.Layer) = .{},
jobLayersAdditive: std.ArrayListUnmanaged(ozz.Layer) = .{},
useAdditive: bool = false,
threshold: f32 = 0.01,
jointLength: usize = 0,
blendingJob: ozz.BlendingJob = .{},
pub fn create(backingAllocator: std.mem.Allocator) !*@This() {
const self = try backingAllocator.create(@This());
self.* = .{
.backing = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
};
return self;
}
pub fn destroy(self: *@This()) void {
self.arena.deinit();
self.backing.destroy(self);
}
pub fn updateRestPose(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
self.jointLength = skeleton.sk.numJoints();
self.blendingJob.rest_pose = skeleton.sk.getRestPoseModel();
}
}
pub fn clearLayers(self: *@This()) void {
self.jobLayersAdditive.clearRetainingCapacity();
self.jobLayers.clearRetainingCapacity();
}
pub fn addLayer(self: *@This(), transform: []ozz.SoaTransform, weight: f32, settings: anytype) void {
const layer = self.jobLayers.addOne(self.arena.allocator()) catch unreachable;
layer.* = .{
.weight = weight,
.transform = ozz.makeSpan(transform),
};
_ = settings;
}
pub fn updateAndRun(self: *@This(), output: []ozz.SoaTransform) void {
self.updateBlendingJob();
self.runBlendingJob(output) catch return;
}
pub fn updateBlendingJob(self: *@This()) void {
self.blendingJob.threshold = self.threshold;
self.blendingJob.layers = ozz.makeSpan(self.jobLayers.items);
//self.blendingJob.additive_layers = if (self.useAdditive) ozz.makeSpan(self.jobLayersAdditive.items) else .{};
self.blendingJob.additive_layers = .{};
}
pub fn runBlendingJob(self: *@This(), output: []ozz.SoaTransform) !void {
self.blendingJob.output = ozz.makeSpan(output);
if (!self.blendingJob.run()) {
core.engine_logs("blending job failed");
return;
}
}
};
// resolver helpers
pub const FloatHelpers = struct {
pub inline fn updateTrackTime(target: *f32, dt: f64, rate: f32, endTime: f32) void {
target.* += @as(f32, @floatCast(dt)) * rate;
while (target.* > endTime) {
target.* -= endTime;
}
}
};
// samples a single animation, same as the default behaviour.
// used as a test for the resolver system
pub const SingleAnimationResolver = struct {
allocator: std.mem.Allocator,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
pub const AnimResolverVTable = AnimResolverInterface.Implement(@This());
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn onSkeletonSet(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
try self.locals.resize(self.allocator, skeleton.sk.numSoaJoints());
}
}
pub fn resolve(self: *@This(), dt: f64, animator: *Animator) !void {
self.track = animator.track;
if (self.track == null) {
return;
}
const track = self.track.?;
if (track.endTime < 0.01) {
return;
}
FloatHelpers.updateTrackTime(&self.playback, dt, self.playbackRate, track.endTime);
animator.sampleAnimation(self.playback, track, self.locals.items);
animator.commitLocalToModel(self.locals.items);
animator.modelToFinal();
}
pub fn destroy(self: *@This()) void {
self.locals.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const animation_system = @import("animationSystem.zig");
const Animator = animation_system.Animator;
const AnimationTrack = animation_system.AnimationTrack;
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");

View File

@ -1,480 +1,480 @@
// big main sy.itemsstem for animation
const ozz = @import("ozz");
const core = @import("core");
const std = @import("std");
pub const BoneHandle = enum(u8) { _ };
pub const Skeleton = struct {
sk: *ozz.Skeleton,
inverseBinds: std.ArrayListUnmanaged(core.Mat) = .{},
jointMapping: std.StringHashMapUnmanaged(u8) = .{},
pub fn buildJointMap(self: *@This(), allocator: std.mem.Allocator) !void {
for (self.sk.getJointsList(), 0..) |jointName, i| {
// std.debug.print("jointName {d} {s}\n", .{ i, jointName });
const str = std.mem.span(jointName);
try self.jointMapping.put(allocator, str, @intCast(i));
}
}
pub fn getBoneHandleByName(self: @This(), string: []const u8) ?BoneHandle {
if (self.jointMapping.get(string)) |x| {
return @enumFromInt(x);
} else {
return null;
}
}
pub fn deinit(self: *@This()) void {
self.sk.destroy();
}
};
pub const AnimationTrack = struct {
animation: *ozz.Animation,
endTime: f32 = 1.0,
pub fn deinit(self: *@This()) void {
self.animation.destroy();
}
};
pub const PlaybackTrack = struct {
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
};
pub const Animator = struct {
jointRemap: ?[]u8 = null,
animationName: ?core.Name = null,
skeleton: ?*Skeleton = null,
skeletonName: ?core.Name = null,
sjc: *ozz.SamplingJobContext = undefined,
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
// todo.. implement blending
// animations: [4]*ozz.Animation = undefined,
// timelines: [4]f32 = .{ 0, 0, 0, 0 },
animationCount: u32 = 0,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
models: std.ArrayListUnmanaged(ozz.Float4x4) = .{},
finals: std.ArrayListUnmanaged(core.Mat) = .{},
finalsSpan: core.Span = undefined,
entity: core.Entity = undefined,
jointLength: usize = 0,
resolverRef: ?AnimResolverRef = null,
pub var allocator: std.mem.Allocator = undefined;
// oh god if I want to support multiple animation blending...
// maybe the kernel should contain a fixed amount of animations?
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
// get the mesh component
self.entity = core.Entity{ .handle = handle };
if (self.entity.get(graphics.StaticMesh)) |mesh| {
mesh.animated = true; //todo
mesh.animator = self;
self.sjc = ozz.SamplingJobContext.createMaxTracks(256);
} else {
@panic("animator added to an entity that does not have a mesh component");
}
}
pub fn getBoneTransform(self: *@This(), handle: BoneHandle) core.Mat {
return @bitCast(self.models.items[@intFromEnum(handle)]);
}
pub fn setSkeletonByName(self: *@This(), skName: core.Name) !void {
if (self.skeleton != null) {
// return the previous span and allocate a new one.
gAnimationSys.slots.removeSpan(self.finalsSpan);
}
self.skeletonName = skName;
self.skeleton = gAnimationSys.skeletons.get(self.skeletonName.?.handle()).?;
const numJoints = self.skeleton.?.sk.numJoints();
self.jointLength = numJoints;
self.sjc.resize(@intCast(numJoints));
try self.locals.resize(allocator, self.skeleton.?.sk.numSoaJoints());
try self.models.resize(allocator, numJoints);
try self.finals.resize(allocator, numJoints);
self.finalsSpan = try gAnimationSys.slots.allocate(@intCast(numJoints));
if (self.resolverRef) |ref| {
if (ref.vtable.onSkeletonSet) |f| {
f(ref.ptr, self);
}
}
self.jointRemap = null;
}
pub fn setSkeleton(self: *@This(), skeleton: []const u8) void {
self.setSkeletonByName(core.MakeName(skeleton)) catch unreachable;
}
pub fn addResolver(self: *@This(), comptime Resolver: type) !*Resolver {
const resolver = try Resolver.create(allocator);
try resolver.onSkeletonSet(self);
self.resolverRef = core.refFromPtr(AnimResolverInterface, resolver);
return resolver;
}
pub fn removeResolver(self: *@This()) void {
if (self.resolverRef) |ref| {
ref.vtable.destroy(ref.ptr);
self.resolverRef = null;
}
}
pub fn update(self: *@This(), dt: f64) void {
if (self.skeleton == null) {
return;
}
// if a resolver is present, use that to update my the finals instead of the default function below
if (self.resolverRef) |ref| {
ref.vtable.resolve(ref.ptr, dt, self);
return;
}
if (!self.defaultSample(dt)) {
return;
}
self.modelToFinal();
}
fn defaultSample(self: *@This(), dt: f64) bool {
if (self.track == null)
return false;
const track = self.track.?;
if (track.endTime < 0.01)
return false;
const skeleton = self.skeleton.?;
self.playback += @as(f32, @floatCast(dt)) * self.playbackRate;
while (self.playback > track.endTime) {
self.playback -= track.endTime;
}
var samplingJob: ozz.SamplingJob = .{
.ratio = self.playback / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(self.locals.items),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return false;
}
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = skeleton.sk,
.input = ozz.makeSpan(self.locals.items),
.output = ozz.makeSpan(self.models.items),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return false;
}
return true;
}
pub fn commitLocalToModel(self: *@This(), input: []ozz.SoaTransform) void {
self.localToModel(input, self.models.items);
}
pub fn localToModel(self: *@This(), input: []ozz.SoaTransform, output: []ozz.Float4x4) void {
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = self.skeleton.?.sk,
.input = ozz.makeSpan(input),
.output = ozz.makeSpan(output),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return;
}
}
pub fn sampleAnimation(self: *@This(), time: f32, track: *AnimationTrack, output: []ozz.SoaTransform) void {
var samplingJob: ozz.SamplingJob = .{
.ratio = time / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(output),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return;
}
}
pub fn modelToFinal(self: *@This()) void {
if (self.jointRemap == null) {
if (self.entity.get(graphics.StaticMesh)) |meshComponent| {
if (meshComponent.mesh) |mesh| {
self.jointRemap = mesh.jointRemap;
}
}
}
const skeleton = self.skeleton.?;
for (self.models.items, 0..) |model, i| {
const transform: core.Mat = @bitCast(model);
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, transform)), 0.03, .{
// .color = if (i == 15) .{ .x = 1 } else .{ .y = 1 },
// });
const final = core.zm.mul(skeleton.inverseBinds.items[i], transform);
// joint remap ozz -> gltf
if (self.jointRemap) |jr| {
// core.engine_log("{d} xx {d}", .{ i, jr[i] });
self.finals.items[@intCast(jr[i])] = final;
} else {
self.finals.items[i] = final;
}
// core.engine_log(
// "[{d}] {d} {d} {d} {d}, {d} {d} {d} {d}",
// .{ i, transform[0][0], transform[0][1], transform[0][2], transform[0][3], transform[1][0], transform[1][1], transform[1][2], transform[1][3] },
// );
}
}
pub fn setAnimationByName(self: *@This(), _name: core.Name) !void {
var name = _name;
self.track = gAnimationSys.animTracks.get(name.handle());
}
pub fn setAnimation(self: *@This(), path: []const u8) void {
const name = core.MakeName(path);
self.setAnimationByName(name) catch unreachable;
}
pub fn deinit(self: *@This()) void {
self.sjc.destroy();
self.removeResolver();
self.finals.deinit(allocator);
self.locals.deinit(allocator);
self.models.deinit(allocator);
}
pub var BaseContainer: *core.SparseMap(@This()) = undefined;
pub const ComponentName = "Animator";
pub const ScriptExports: []const []const u8 = &.{};
};
pub const AnimationSystem = struct {
backingAllocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
slots: MergedSpans,
// Only AnimationTrack and Skeletons are made using the ArenaAllocator
animTracks: std.AutoHashMapUnmanaged(u32, *AnimationTrack) = .{},
skeletons: std.AutoHashMapUnmanaged(u32, *Skeleton) = .{},
sharedArena: [2]std.heap.ArenaAllocator, // could be a good usecase for a fat bump arena
shared: [2]std.ArrayListUnmanaged(MatrixUploads) = .{ .{}, .{} },
sharedLocks: [2]std.Thread.Mutex = .{ .{}, .{} }, // could be a good usecase for a fat bump arena
pub const MatrixUploads = struct {
offset: u32,
matrices: std.ArrayListUnmanaged(core.Mat) = .{},
};
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub fn preTick(self: *@This(), dt: f64) !void {
_ = self;
var z1 = core.tracy.ZoneN(@src(), "animation system tick");
defer z1.End();
for (Animator.BaseContainer.list.items) |animator| {
animator.update(dt);
}
}
pub fn newAnimTrack(self: *@This(), _name: core.Name, anim: *ozz.Animation) !void {
var name = _name;
const new = try self.arenaAllocator().create(AnimationTrack);
new.* = .{
.animation = anim,
.endTime = anim.getDuration(),
};
try self.animTracks.put(self.backingAllocator, name.handle(), new);
}
pub fn newSkeleton(self: *@This(), _name: core.Name, sk: *ozz.Skeleton) !void {
const new = try self.arenaAllocator().create(Skeleton);
new.* = .{
.sk = sk,
};
var name = _name;
try new.buildJointMap(self.arenaAllocator());
try new.inverseBinds.resize(self.arenaAllocator(), new.sk.numJoints());
if (new.inverseBinds.items.len > 256) {
@panic("too many bones in skeleton, not supported");
}
var bindModels = std.ArrayList(ozz.Float4x4).init(self.backingAllocator);
defer bindModels.deinit();
try bindModels.resize(new.sk.numJoints());
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = new.sk,
.input = new.sk.getRestPoseModel(),
.output = ozz.makeSpan(bindModels.items),
};
core.engine_log("creating bind pose {d} joints", .{new.inverseBinds.items.len});
if (!ltmJob.run()) {
core.engine_logs("unable to get bind pose");
return error.UnableToLoad;
}
for (bindModels.items, 0..) |bind, i| {
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, @as(core.Mat, @bitCast(bind)))), 0.1, .{ .duration = 100 });
new.inverseBinds.items[i] = core.zm.inverse(@as(core.Mat, @bitCast(bind)));
}
try self.skeletons.put(self.backingAllocator, name.handle(), new);
}
pub fn arenaAllocator(self: *@This()) std.mem.Allocator {
return self.arena.allocator();
}
pub fn getShared(self: @This(), fi: u32) []const MatrixUploads {
return self.shared[fi].items;
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
const fi: usize = @intCast(frameIndex);
self.sharedLocks[fi].lock();
defer self.sharedLocks[fi].unlock();
_ = self.sharedArena[fi].reset(.retain_capacity);
const allocator = self.sharedArena[fi].allocator();
const shared = &self.shared[fi];
shared.* = .{};
for (Animator.BaseContainer.list.items) |animator| {
var upload: MatrixUploads = .{ .offset = animator.finalsSpan.start };
// core.engine_log(
// "finalsSpan size offset{d} {d} animator finals {d}\n",
// .{
// animator.finalsSpan.start,
// animator.finalsSpan.size,
// animator.finals.items.len
// });
upload.matrices.resize(allocator, animator.finalsSpan.size) catch unreachable;
for (animator.finals.items, 0..) |final, i| {
upload.matrices.items[i] = final;
}
shared.append(allocator, upload) catch unreachable;
}
}
pub fn init(alloc: std.mem.Allocator) !*@This() {
const self = try alloc.create(@This());
self.* = .{
.backingAllocator = alloc,
.arena = std.heap.ArenaAllocator.init(alloc),
.sharedArena = .{
std.heap.ArenaAllocator.init(alloc),
std.heap.ArenaAllocator.init(alloc),
},
.slots = try MergedSpans.init(alloc, vk_constants.MAX_SKIN_SLOTS),
};
gAnimationSys = self;
Animator.allocator = alloc;
try core.defineComponent(Animator, alloc);
return self;
}
pub fn deinit(self: *@This()) void {
core.engine_logs("deinitializing animation system");
{
core.engine_log("skeleton count {d}", .{self.skeletons.count()});
var iter = self.skeletons.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
{
core.engine_log("animTracks count {d}", .{self.animTracks.count()});
var iter = self.animTracks.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
for (self.sharedArena) |arena| {
arena.deinit();
}
for (Animator.BaseContainer.list.items) |animator| {
animator.deinit();
}
self.slots.deinit();
core.undefineComponent(Animator);
self.arena.deinit();
self.skeletons.deinit(self.backingAllocator);
self.animTracks.deinit(self.backingAllocator);
self.backingAllocator.destroy(self);
}
};
pub var gAnimationSys: *AnimationSystem = undefined;
pub fn getSkeletonByName(_name: core.Name) ?*Skeleton {
var name = _name;
return gAnimationSys.skeletons.get(name.handle());
}
const graphics = @import("../graphics.zig");
const MergedSpans = core.MergedSpans;
const vk_constants = @import("../vk_constants.zig");
const anim_resolver = @import("animResolver.zig");
const AnimResolverRef = anim_resolver.AnimResolverRef;
const AnimResolverInterface = anim_resolver.AnimResolverInterface;
// big main sy.itemsstem for animation
const ozz = @import("ozz");
const core = @import("core");
const std = @import("std");
pub const BoneHandle = enum(u8) { _ };
pub const Skeleton = struct {
sk: *ozz.Skeleton,
inverseBinds: std.ArrayListUnmanaged(core.Mat) = .{},
jointMapping: std.StringHashMapUnmanaged(u8) = .{},
pub fn buildJointMap(self: *@This(), allocator: std.mem.Allocator) !void {
for (self.sk.getJointsList(), 0..) |jointName, i| {
// std.debug.print("jointName {d} {s}\n", .{ i, jointName });
const str = std.mem.span(jointName);
try self.jointMapping.put(allocator, str, @intCast(i));
}
}
pub fn getBoneHandleByName(self: @This(), string: []const u8) ?BoneHandle {
if (self.jointMapping.get(string)) |x| {
return @enumFromInt(x);
} else {
return null;
}
}
pub fn deinit(self: *@This()) void {
self.sk.destroy();
}
};
pub const AnimationTrack = struct {
animation: *ozz.Animation,
endTime: f32 = 1.0,
pub fn deinit(self: *@This()) void {
self.animation.destroy();
}
};
pub const PlaybackTrack = struct {
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
};
pub const Animator = struct {
jointRemap: ?[]u8 = null,
animationName: ?core.Name = null,
skeleton: ?*Skeleton = null,
skeletonName: ?core.Name = null,
sjc: *ozz.SamplingJobContext = undefined,
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
// todo.. implement blending
// animations: [4]*ozz.Animation = undefined,
// timelines: [4]f32 = .{ 0, 0, 0, 0 },
animationCount: u32 = 0,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
models: std.ArrayListUnmanaged(ozz.Float4x4) = .{},
finals: std.ArrayListUnmanaged(core.Mat) = .{},
finalsSpan: core.Span = undefined,
entity: core.Entity = undefined,
jointLength: usize = 0,
resolverRef: ?AnimResolverRef = null,
pub var allocator: std.mem.Allocator = undefined;
// oh god if I want to support multiple animation blending...
// maybe the kernel should contain a fixed amount of animations?
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
// get the mesh component
self.entity = core.Entity{ .handle = handle };
if (self.entity.get(graphics.StaticMesh)) |mesh| {
mesh.animated = true; //todo
mesh.animator = self;
self.sjc = ozz.SamplingJobContext.createMaxTracks(256);
} else {
@panic("animator added to an entity that does not have a mesh component");
}
}
pub fn getBoneTransform(self: *@This(), handle: BoneHandle) core.Mat {
return @bitCast(self.models.items[@intFromEnum(handle)]);
}
pub fn setSkeletonByName(self: *@This(), skName: core.Name) !void {
if (self.skeleton != null) {
// return the previous span and allocate a new one.
gAnimationSys.slots.removeSpan(self.finalsSpan);
}
self.skeletonName = skName;
self.skeleton = gAnimationSys.skeletons.get(self.skeletonName.?.handle()).?;
const numJoints = self.skeleton.?.sk.numJoints();
self.jointLength = numJoints;
self.sjc.resize(@intCast(numJoints));
try self.locals.resize(allocator, self.skeleton.?.sk.numSoaJoints());
try self.models.resize(allocator, numJoints);
try self.finals.resize(allocator, numJoints);
self.finalsSpan = try gAnimationSys.slots.allocate(@intCast(numJoints));
if (self.resolverRef) |ref| {
if (ref.vtable.onSkeletonSet) |f| {
f(ref.ptr, self);
}
}
self.jointRemap = null;
}
pub fn setSkeleton(self: *@This(), skeleton: []const u8) void {
self.setSkeletonByName(core.MakeName(skeleton)) catch unreachable;
}
pub fn addResolver(self: *@This(), comptime Resolver: type) !*Resolver {
const resolver = try Resolver.create(allocator);
try resolver.onSkeletonSet(self);
self.resolverRef = core.refFromPtr(AnimResolverInterface, resolver);
return resolver;
}
pub fn removeResolver(self: *@This()) void {
if (self.resolverRef) |ref| {
ref.vtable.destroy(ref.ptr);
self.resolverRef = null;
}
}
pub fn update(self: *@This(), dt: f64) void {
if (self.skeleton == null) {
return;
}
// if a resolver is present, use that to update my the finals instead of the default function below
if (self.resolverRef) |ref| {
ref.vtable.resolve(ref.ptr, dt, self);
return;
}
if (!self.defaultSample(dt)) {
return;
}
self.modelToFinal();
}
fn defaultSample(self: *@This(), dt: f64) bool {
if (self.track == null)
return false;
const track = self.track.?;
if (track.endTime < 0.01)
return false;
const skeleton = self.skeleton.?;
self.playback += @as(f32, @floatCast(dt)) * self.playbackRate;
while (self.playback > track.endTime) {
self.playback -= track.endTime;
}
var samplingJob: ozz.SamplingJob = .{
.ratio = self.playback / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(self.locals.items),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return false;
}
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = skeleton.sk,
.input = ozz.makeSpan(self.locals.items),
.output = ozz.makeSpan(self.models.items),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return false;
}
return true;
}
pub fn commitLocalToModel(self: *@This(), input: []ozz.SoaTransform) void {
self.localToModel(input, self.models.items);
}
pub fn localToModel(self: *@This(), input: []ozz.SoaTransform, output: []ozz.Float4x4) void {
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = self.skeleton.?.sk,
.input = ozz.makeSpan(input),
.output = ozz.makeSpan(output),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return;
}
}
pub fn sampleAnimation(self: *@This(), time: f32, track: *AnimationTrack, output: []ozz.SoaTransform) void {
var samplingJob: ozz.SamplingJob = .{
.ratio = time / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(output),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return;
}
}
pub fn modelToFinal(self: *@This()) void {
if (self.jointRemap == null) {
if (self.entity.get(graphics.StaticMesh)) |meshComponent| {
if (meshComponent.mesh) |mesh| {
self.jointRemap = mesh.jointRemap;
}
}
}
const skeleton = self.skeleton.?;
for (self.models.items, 0..) |model, i| {
const transform: core.Mat = @bitCast(model);
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, transform)), 0.03, .{
// .color = if (i == 15) .{ .x = 1 } else .{ .y = 1 },
// });
const final = core.zm.mul(skeleton.inverseBinds.items[i], transform);
// joint remap ozz -> gltf
if (self.jointRemap) |jr| {
// core.engine_log("{d} xx {d}", .{ i, jr[i] });
self.finals.items[@intCast(jr[i])] = final;
} else {
self.finals.items[i] = final;
}
// core.engine_log(
// "[{d}] {d} {d} {d} {d}, {d} {d} {d} {d}",
// .{ i, transform[0][0], transform[0][1], transform[0][2], transform[0][3], transform[1][0], transform[1][1], transform[1][2], transform[1][3] },
// );
}
}
pub fn setAnimationByName(self: *@This(), _name: core.Name) !void {
var name = _name;
self.track = gAnimationSys.animTracks.get(name.handle());
}
pub fn setAnimation(self: *@This(), path: []const u8) void {
const name = core.MakeName(path);
self.setAnimationByName(name) catch unreachable;
}
pub fn deinit(self: *@This()) void {
self.sjc.destroy();
self.removeResolver();
self.finals.deinit(allocator);
self.locals.deinit(allocator);
self.models.deinit(allocator);
}
pub var BaseContainer: *core.SparseMap(@This()) = undefined;
pub const ComponentName = "Animator";
pub const ScriptExports: []const []const u8 = &.{};
};
pub const AnimationSystem = struct {
backingAllocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
slots: MergedSpans,
// Only AnimationTrack and Skeletons are made using the ArenaAllocator
animTracks: std.AutoHashMapUnmanaged(u32, *AnimationTrack) = .{},
skeletons: std.AutoHashMapUnmanaged(u32, *Skeleton) = .{},
sharedArena: [2]std.heap.ArenaAllocator, // could be a good usecase for a fat bump arena
shared: [2]std.ArrayListUnmanaged(MatrixUploads) = .{ .{}, .{} },
sharedLocks: [2]std.Thread.Mutex = .{ .{}, .{} }, // could be a good usecase for a fat bump arena
pub const MatrixUploads = struct {
offset: u32,
matrices: std.ArrayListUnmanaged(core.Mat) = .{},
};
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub fn preTick(self: *@This(), dt: f64) !void {
_ = self;
var z1 = core.tracy.ZoneN(@src(), "animation system tick");
defer z1.End();
for (Animator.BaseContainer.list.items) |animator| {
animator.update(dt);
}
}
pub fn newAnimTrack(self: *@This(), _name: core.Name, anim: *ozz.Animation) !void {
var name = _name;
const new = try self.arenaAllocator().create(AnimationTrack);
new.* = .{
.animation = anim,
.endTime = anim.getDuration(),
};
try self.animTracks.put(self.backingAllocator, name.handle(), new);
}
pub fn newSkeleton(self: *@This(), _name: core.Name, sk: *ozz.Skeleton) !void {
const new = try self.arenaAllocator().create(Skeleton);
new.* = .{
.sk = sk,
};
var name = _name;
try new.buildJointMap(self.arenaAllocator());
try new.inverseBinds.resize(self.arenaAllocator(), new.sk.numJoints());
if (new.inverseBinds.items.len > 256) {
@panic("too many bones in skeleton, not supported");
}
var bindModels = std.ArrayList(ozz.Float4x4).init(self.backingAllocator);
defer bindModels.deinit();
try bindModels.resize(new.sk.numJoints());
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = new.sk,
.input = new.sk.getRestPoseModel(),
.output = ozz.makeSpan(bindModels.items),
};
core.engine_log("creating bind pose {d} joints", .{new.inverseBinds.items.len});
if (!ltmJob.run()) {
core.engine_logs("unable to get bind pose");
return error.UnableToLoad;
}
for (bindModels.items, 0..) |bind, i| {
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, @as(core.Mat, @bitCast(bind)))), 0.1, .{ .duration = 100 });
new.inverseBinds.items[i] = core.zm.inverse(@as(core.Mat, @bitCast(bind)));
}
try self.skeletons.put(self.backingAllocator, name.handle(), new);
}
pub fn arenaAllocator(self: *@This()) std.mem.Allocator {
return self.arena.allocator();
}
pub fn getShared(self: @This(), fi: u32) []const MatrixUploads {
return self.shared[fi].items;
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
const fi: usize = @intCast(frameIndex);
self.sharedLocks[fi].lock();
defer self.sharedLocks[fi].unlock();
_ = self.sharedArena[fi].reset(.retain_capacity);
const allocator = self.sharedArena[fi].allocator();
const shared = &self.shared[fi];
shared.* = .{};
for (Animator.BaseContainer.list.items) |animator| {
var upload: MatrixUploads = .{ .offset = animator.finalsSpan.start };
// core.engine_log(
// "finalsSpan size offset{d} {d} animator finals {d}\n",
// .{
// animator.finalsSpan.start,
// animator.finalsSpan.size,
// animator.finals.items.len
// });
upload.matrices.resize(allocator, animator.finalsSpan.size) catch unreachable;
for (animator.finals.items, 0..) |final, i| {
upload.matrices.items[i] = final;
}
shared.append(allocator, upload) catch unreachable;
}
}
pub fn init(alloc: std.mem.Allocator) !*@This() {
const self = try alloc.create(@This());
self.* = .{
.backingAllocator = alloc,
.arena = std.heap.ArenaAllocator.init(alloc),
.sharedArena = .{
std.heap.ArenaAllocator.init(alloc),
std.heap.ArenaAllocator.init(alloc),
},
.slots = try MergedSpans.init(alloc, vk_constants.MAX_SKIN_SLOTS),
};
gAnimationSys = self;
Animator.allocator = alloc;
try core.defineComponent(Animator, alloc);
return self;
}
pub fn deinit(self: *@This()) void {
core.engine_logs("deinitializing animation system");
{
core.engine_log("skeleton count {d}", .{self.skeletons.count()});
var iter = self.skeletons.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
{
core.engine_log("animTracks count {d}", .{self.animTracks.count()});
var iter = self.animTracks.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
for (self.sharedArena) |arena| {
arena.deinit();
}
for (Animator.BaseContainer.list.items) |animator| {
animator.deinit();
}
self.slots.deinit();
core.undefineComponent(Animator);
self.arena.deinit();
self.skeletons.deinit(self.backingAllocator);
self.animTracks.deinit(self.backingAllocator);
self.backingAllocator.destroy(self);
}
};
pub var gAnimationSys: *AnimationSystem = undefined;
pub fn getSkeletonByName(_name: core.Name) ?*Skeleton {
var name = _name;
return gAnimationSys.skeletons.get(name.handle());
}
const graphics = @import("../graphics.zig");
const MergedSpans = core.MergedSpans;
const vk_constants = @import("../vk_constants.zig");
const anim_resolver = @import("animResolver.zig");
const AnimResolverRef = anim_resolver.AnimResolverRef;
const AnimResolverInterface = anim_resolver.AnimResolverInterface;

View File

@ -1,86 +1,86 @@
pub const AnimationLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Animation", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const animation = ozz.Animation.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
animation.loadFromBytes(mapping.bytes);
self.sys.newAnimTrack(assetRef.name, animation) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub const SkeletonLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Skeleton", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const sk = ozz.Skeleton.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
sk.loadFromBytes(mapping.bytes);
self.sys.newSkeleton(assetRef.name, sk) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gSkeletonLoader: *SkeletonLoader = undefined;
pub var gAnimationLoader: *AnimationLoader = undefined;
pub fn initLoaders() !void {
gSkeletonLoader = try core.createObject(SkeletonLoader, .{});
gAnimationLoader = try core.createObject(AnimationLoader, .{});
try assets.gAssetSys.registerLoader(gSkeletonLoader);
try assets.gAssetSys.registerLoader(gAnimationLoader);
}
const animation_system = @import("animationSystem.zig");
const assets = @import("assets");
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");
pub const AnimationLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Animation", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const animation = ozz.Animation.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
animation.loadFromBytes(mapping.bytes);
self.sys.newAnimTrack(assetRef.name, animation) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub const SkeletonLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Skeleton", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const sk = ozz.Skeleton.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
sk.loadFromBytes(mapping.bytes);
self.sys.newSkeleton(assetRef.name, sk) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gSkeletonLoader: *SkeletonLoader = undefined;
pub var gAnimationLoader: *AnimationLoader = undefined;
pub fn initLoaders() !void {
gSkeletonLoader = try core.createObject(SkeletonLoader, .{});
gAnimationLoader = try core.createObject(AnimationLoader, .{});
try assets.gAssetSys.registerLoader(gSkeletonLoader);
try assets.gAssetSys.registerLoader(gAnimationLoader);
}
const animation_system = @import("animationSystem.zig");
const assets = @import("assets");
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");

View File

@ -1,239 +1,239 @@
const MeshConfig = struct {
info: CookInfo = .{ .assetType = "Mesh" }, // there must always be a CookInfo field
sourceType: []const u8 = "obj",
animated: bool = false,
};
const extList = [_][]const u8{ "gltf", "obj", "glb" };
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
out.clearRetainingCapacity();
const ext = core.getFileExtension(path)[1..];
var config: MeshConfig = .{};
for (extList) |e| {
if (std.mem.eql(u8, e, ext)) {
config.sourceType = e;
if (std.mem.eql(u8, e, "glb")) {
config.sourceType = "gltf";
}
}
}
std.json.stringify(
config,
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
fn cookObj(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) cook.CookResult {
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
var out = std.ArrayList(u8).init(allocator);
var vertices = std.ArrayList(Vertex).init(allocator);
defer vertices.deinit();
var objs = obj.loadObjBytes(rawFileBytes, allocator) catch unreachable;
defer objs.deinit();
if (objs.meshes.items.len > 0) {
mesh.loadObjMeshVertices(&vertices, objs.meshes.items[0]) catch unreachable;
for (vertices.items) |vert| {
out.appendSlice(&@as([@sizeOf(Vertex)]u8, @bitCast(vert))) catch unreachable;
}
return .{
.bytes = out,
.result = .Success,
};
} else {
return .{
.bytes = out,
.result = .Failure,
};
}
}
fn ensureGltf2ozz(allocator: std.mem.Allocator) !void {
const suffix = if (builtin.os.tag == .windows) ".exe" else "";
std.fs.cwd().access("zig-out/tools/gltf2ozz" ++ suffix, .{}) catch {
const argv: []const []const u8 = &.{ "zig", "build", "tools" };
core.engine_log("gltf2ozz missing, building it...", .{});
var child = std.process.Child.init(argv, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
child.cwd = ".";
switch (try child.spawnAndWait()) {
.Exited => |value| {
if (value == 0) {
core.engine_log("gltf2ozz built", .{});
} else {
core.engine_logs("unable to build gltf2ozz");
}
},
.Signal => {
core.engine_logs("unable to build gltf2ozz");
},
.Stopped => {},
.Unknown => {
unreachable;
},
}
return;
};
core.engine_log("gltf2ozz found", .{});
}
fn cookAnimations(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) !void {
_ = config;
// 1. check if it has an associated .ozzconfig file.
const gltf2OzzAbs = try std.fs.cwd().realpathAlloc(allocator, "zig-out/tools/gltf2ozz.exe");
defer allocator.free(gltf2OzzAbs);
const ozzconfig = try std.fmt.allocPrint(allocator, "{s}.ozzconfig", .{path});
defer allocator.free(ozzconfig);
const fileArg = try std.fmt.allocPrint(allocator, "--file={s}", .{core.getBasePath(path)});
defer allocator.free(fileArg);
const configArg = try std.fmt.allocPrint(allocator, "--config_file={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(configArg);
// todo, fix this later, idrc right now.
const newConfigArg = try std.fmt.allocPrint(allocator, "--config_dump_reference={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(newConfigArg);
const absFile = try dir.realpathAlloc(allocator, path);
defer allocator.free(absFile);
var argv: []const []const u8 = &.{
gltf2OzzAbs,
fileArg,
configArg,
};
dir.access(ozzconfig, .{}) catch {
argv = &.{
gltf2OzzAbs,
fileArg,
newConfigArg,
};
core.engine_log("creating ozz config for file, marked as animated but no animation data", .{});
};
// std.debug.print("{s} {s} {s} cwd = {s}\n", .{ argv[0], argv[1], argv[2], core.getFolder(absFile) });
const result = try std.process.Child.run(.{
.argv = argv,
.allocator = allocator,
.cwd = core.getFolder(absFile),
.max_output_bytes = 150 * 1024 * 1024,
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
var success: bool = true;
switch (result.term) {
.Exited => |value| {
if (value != 0) {
success = false;
}
},
.Signal => {
success = false;
},
.Stopped => {
success = false;
// no-op should be ok?
},
.Unknown => {
unreachable;
},
}
if (success) {
core.engine_log("generated animations for {s}", .{path});
} else {
core.engine_log("error generating animations {s} stdout:\n{s}\n stderr:{s}\n", .{ path, result.stdout, result.stderr });
}
// 2. if so, run it through gltf2ozz with that file.
}
fn cookGltf(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) cook.CookResult {
core.engine_logs("gltf cooking not implemeted");
const out = std.ArrayList(u8).init(allocator);
// check if it's animated. if it's animated, then invoke gltf2ozz and create a .ozzconfig file and
// make a subfolder called
if (config.animated) {
// if gltf2ozz isn't there then we have to call zig build tools
ensureGltf2ozz(allocator) catch unreachable;
cookAnimations(allocator, dir, path, config) catch unreachable;
}
return .{ .bytes = out, .result = .Failure };
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
core.engine_log("{s}", .{params.cookFileName});
const fc = cook.loadFileAlloc(allocator, dir, params.cookFileName) catch unreachable;
defer allocator.free(fc);
const config = std.json.parseFromSlice(MeshConfig, allocator, fc[0 .. fc.len - 1], .{}) catch unreachable;
defer config.deinit();
if (std.mem.eql(u8, config.value.sourceType, "obj")) {
return cookObj(allocator, dir, path);
} else {
return cookGltf(allocator, dir, path, config.value);
}
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Mesh", generateFunction, cookFunction, &.{
".obj",
".gltf",
".glb",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const obj = @import("objLoader");
const builtin = @import("builtin");
const mesh = @import("../mesh.zig");
const Mesh = mesh.Mesh;
const Vertex = mesh.MeshVertex;
const MeshConfig = struct {
info: CookInfo = .{ .assetType = "Mesh" }, // there must always be a CookInfo field
sourceType: []const u8 = "obj",
animated: bool = false,
};
const extList = [_][]const u8{ "gltf", "obj", "glb" };
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
out.clearRetainingCapacity();
const ext = core.getFileExtension(path)[1..];
var config: MeshConfig = .{};
for (extList) |e| {
if (std.mem.eql(u8, e, ext)) {
config.sourceType = e;
if (std.mem.eql(u8, e, "glb")) {
config.sourceType = "gltf";
}
}
}
std.json.stringify(
config,
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
fn cookObj(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) cook.CookResult {
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
var out = std.ArrayList(u8).init(allocator);
var vertices = std.ArrayList(Vertex).init(allocator);
defer vertices.deinit();
var objs = obj.loadObjBytes(rawFileBytes, allocator) catch unreachable;
defer objs.deinit();
if (objs.meshes.items.len > 0) {
mesh.loadObjMeshVertices(&vertices, objs.meshes.items[0]) catch unreachable;
for (vertices.items) |vert| {
out.appendSlice(&@as([@sizeOf(Vertex)]u8, @bitCast(vert))) catch unreachable;
}
return .{
.bytes = out,
.result = .Success,
};
} else {
return .{
.bytes = out,
.result = .Failure,
};
}
}
fn ensureGltf2ozz(allocator: std.mem.Allocator) !void {
const suffix = if (builtin.os.tag == .windows) ".exe" else "";
std.fs.cwd().access("zig-out/tools/gltf2ozz" ++ suffix, .{}) catch {
const argv: []const []const u8 = &.{ "zig", "build", "tools" };
core.engine_log("gltf2ozz missing, building it...", .{});
var child = std.process.Child.init(argv, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
child.cwd = ".";
switch (try child.spawnAndWait()) {
.Exited => |value| {
if (value == 0) {
core.engine_log("gltf2ozz built", .{});
} else {
core.engine_logs("unable to build gltf2ozz");
}
},
.Signal => {
core.engine_logs("unable to build gltf2ozz");
},
.Stopped => {},
.Unknown => {
unreachable;
},
}
return;
};
core.engine_log("gltf2ozz found", .{});
}
fn cookAnimations(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) !void {
_ = config;
// 1. check if it has an associated .ozzconfig file.
const gltf2OzzAbs = try std.fs.cwd().realpathAlloc(allocator, "zig-out/tools/gltf2ozz.exe");
defer allocator.free(gltf2OzzAbs);
const ozzconfig = try std.fmt.allocPrint(allocator, "{s}.ozzconfig", .{path});
defer allocator.free(ozzconfig);
const fileArg = try std.fmt.allocPrint(allocator, "--file={s}", .{core.getBasePath(path)});
defer allocator.free(fileArg);
const configArg = try std.fmt.allocPrint(allocator, "--config_file={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(configArg);
// todo, fix this later, idrc right now.
const newConfigArg = try std.fmt.allocPrint(allocator, "--config_dump_reference={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(newConfigArg);
const absFile = try dir.realpathAlloc(allocator, path);
defer allocator.free(absFile);
var argv: []const []const u8 = &.{
gltf2OzzAbs,
fileArg,
configArg,
};
dir.access(ozzconfig, .{}) catch {
argv = &.{
gltf2OzzAbs,
fileArg,
newConfigArg,
};
core.engine_log("creating ozz config for file, marked as animated but no animation data", .{});
};
// std.debug.print("{s} {s} {s} cwd = {s}\n", .{ argv[0], argv[1], argv[2], core.getFolder(absFile) });
const result = try std.process.Child.run(.{
.argv = argv,
.allocator = allocator,
.cwd = core.getFolder(absFile),
.max_output_bytes = 150 * 1024 * 1024,
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
var success: bool = true;
switch (result.term) {
.Exited => |value| {
if (value != 0) {
success = false;
}
},
.Signal => {
success = false;
},
.Stopped => {
success = false;
// no-op should be ok?
},
.Unknown => {
unreachable;
},
}
if (success) {
core.engine_log("generated animations for {s}", .{path});
} else {
core.engine_log("error generating animations {s} stdout:\n{s}\n stderr:{s}\n", .{ path, result.stdout, result.stderr });
}
// 2. if so, run it through gltf2ozz with that file.
}
fn cookGltf(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) cook.CookResult {
core.engine_logs("gltf cooking not implemeted");
const out = std.ArrayList(u8).init(allocator);
// check if it's animated. if it's animated, then invoke gltf2ozz and create a .ozzconfig file and
// make a subfolder called
if (config.animated) {
// if gltf2ozz isn't there then we have to call zig build tools
ensureGltf2ozz(allocator) catch unreachable;
cookAnimations(allocator, dir, path, config) catch unreachable;
}
return .{ .bytes = out, .result = .Failure };
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
core.engine_log("{s}", .{params.cookFileName});
const fc = cook.loadFileAlloc(allocator, dir, params.cookFileName) catch unreachable;
defer allocator.free(fc);
const config = std.json.parseFromSlice(MeshConfig, allocator, fc[0 .. fc.len - 1], .{}) catch unreachable;
defer config.deinit();
if (std.mem.eql(u8, config.value.sourceType, "obj")) {
return cookObj(allocator, dir, path);
} else {
return cookGltf(allocator, dir, path, config.value);
}
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Mesh", generateFunction, cookFunction, &.{
".obj",
".gltf",
".glb",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const obj = @import("objLoader");
const builtin = @import("builtin");
const mesh = @import("../mesh.zig");
const Mesh = mesh.Mesh;
const Vertex = mesh.MeshVertex;

View File

@ -1,60 +1,60 @@
const TextureConfig = struct {
info: CookInfo = .{ .assetType = "Texture" }, // there must always be a CookInfo field
sourceType: []const u8 = "png",
};
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
_ = path;
out.clearRetainingCapacity();
std.json.stringify(
TextureConfig{},
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
_ = params;
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
// 1. load the file, and create a bytes buffer
var contents = png.PngContents.initFromBytes(allocator, path, rawFileBytes) catch unreachable;
defer contents.deinit();
// png.PngContents.initFromBytes(allocator: std.mem.Allocator, pathName: []const u8, pngFileContents: []const u8)
// 2. use the PngContents function to cook it.
return .{
.bytes = contents.toBuffer() catch unreachable,
.result = .Success,
};
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Texture", generateFunction, cookFunction, &.{
".png",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const png = core.png;
const TextureConfig = struct {
info: CookInfo = .{ .assetType = "Texture" }, // there must always be a CookInfo field
sourceType: []const u8 = "png",
};
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
_ = path;
out.clearRetainingCapacity();
std.json.stringify(
TextureConfig{},
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
_ = params;
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
// 1. load the file, and create a bytes buffer
var contents = png.PngContents.initFromBytes(allocator, path, rawFileBytes) catch unreachable;
defer contents.deinit();
// png.PngContents.initFromBytes(allocator: std.mem.Allocator, pathName: []const u8, pngFileContents: []const u8)
// 2. use the PngContents function to cook it.
return .{
.bytes = contents.toBuffer() catch unreachable,
.result = .Success,
};
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Texture", generateFunction, cookFunction, &.{
".png",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const png = core.png;

View File

@ -1,52 +1,52 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const VkConstants = @import("vk_constants.zig");
const meshes = @import("mesh.zig");
const NeonVkContext = @import("vk_renderer.zig").NeonVkContext;
const vk_pipeline = @import("vk_pipeline.zig");
const NeonVkPipelineBuilder = vk_pipeline.NeonVkPipelineBuilder;
const EulerAngles = core.EulerAngles;
const Mat = core.Mat;
const Vectorf = core.Vectorf;
const Quat = core.Quat;
const zm = core.zm;
const mul = zm.mul;
pub const Material = struct {
materialName: core.Name,
textureSet: vk.DescriptorSet = .null_handle,
pipeline: vk.Pipeline,
layout: vk.PipelineLayout,
pub fn deinit(self: *Material, ctx: *NeonVkContext) void {
ctx.vkd.destroyPipeline(ctx.dev, self.pipeline, null);
ctx.vkAllocator.destroyPipelineLayout(ctx.dev, self.layout);
}
};
pub const MaterialBuilder = struct {
const Self = @This();
allocator: std.mem.Allocator,
ctx: *NeonVkContext,
pipelineBuilder: NeonVkPipelineBuilder,
pub fn init(ctx: *NeonVkContext) MaterialBuilder {
const self = MaterialBuilder{
.allocator = ctx.allocator,
.ctx = ctx,
};
return self;
}
pub fn build(self: *Self) !void {
_ = self;
// try self.ctx.add_material();
}
pub fn deinit(self: *Self) void {
_ = self;
}
};
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const VkConstants = @import("vk_constants.zig");
const meshes = @import("mesh.zig");
const NeonVkContext = @import("vk_renderer.zig").NeonVkContext;
const vk_pipeline = @import("vk_pipeline.zig");
const NeonVkPipelineBuilder = vk_pipeline.NeonVkPipelineBuilder;
const EulerAngles = core.EulerAngles;
const Mat = core.Mat;
const Vectorf = core.Vectorf;
const Quat = core.Quat;
const zm = core.zm;
const mul = zm.mul;
pub const Material = struct {
materialName: core.Name,
textureSet: vk.DescriptorSet = .null_handle,
pipeline: vk.Pipeline,
layout: vk.PipelineLayout,
pub fn deinit(self: *Material, ctx: *NeonVkContext) void {
ctx.vkd.destroyPipeline(ctx.dev, self.pipeline, null);
ctx.vkAllocator.destroyPipelineLayout(ctx.dev, self.layout);
}
};
pub const MaterialBuilder = struct {
const Self = @This();
allocator: std.mem.Allocator,
ctx: *NeonVkContext,
pipelineBuilder: NeonVkPipelineBuilder,
pub fn init(ctx: *NeonVkContext) MaterialBuilder {
const self = MaterialBuilder{
.allocator = ctx.allocator,
.ctx = ctx,
};
return self;
}
pub fn build(self: *Self) !void {
_ = self;
// try self.ctx.add_material();
}
pub fn deinit(self: *Self) void {
_ = self;
}
};

View File

@ -1,96 +1,96 @@
allocator: std.mem.Allocator,
cubeMapShared: [graphics.NumFrames]?vk.DescriptorSet = .{ null, null },
cubeMapTextureSet: ?vk.DescriptorSet = null,
cubeMapName: ?core.Name = null,
material: *graphics.Material = undefined,
mesh: ?graphics.IndexedMesh = null,
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub var gSkybox: *@This() = undefined;
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
gSkybox = self;
try self.initPipeline();
try graphics.registerRendererPlugin(self);
return self;
}
pub fn sendShared(self: *@This(), fi: u32) void {
if (self.cubeMapName == null) {
self.cubeMapShared[fi] = self.cubeMapTextureSet;
return;
} else {
if (self.mesh == null)
self.mesh = graphics.getIndexedMeshByName(core.MakeName("m_skybox"));
if (self.cubeMapTextureSet == null) {
const handle = self.cubeMapName.?.handle();
self.cubeMapTextureSet = graphics.getContext().textureSets.get(handle);
}
}
self.cubeMapShared[fi] = self.cubeMapTextureSet;
}
pub fn initPipeline(self: *@This()) !void {
const gc = graphics.getContext();
var pipelineBuilder = try graphics.NeonVkPipelineBuilder.init(
gc.dev,
gc.vkd,
self.allocator,
gc.vkAllocator,
skybox_vert.spv(),
skybox_frag.spv(),
);
defer pipelineBuilder.deinit();
try pipelineBuilder.add_mesh_description();
try pipelineBuilder.add_layout(gc.globalDescriptorLayout);
try pipelineBuilder.add_layout(gc.singleTextureSetLayout);
try pipelineBuilder.add_depth_stencil(); // todo.. we might not want this for a skybox.
try pipelineBuilder.init_triangle_pipeline(gc.actual_extent);
pipelineBuilder.pdsci.?.depth_write_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_test_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_compare_op = .never;
const materialName = core.MakeName("Mat_skybox");
self.material = try self.allocator.create(graphics.Material);
self.material.* = graphics.Material{
.materialName = materialName,
.pipeline = (try pipelineBuilder.build(gc.renderPass)).?,
.layout = pipelineBuilder.pipelineLayout,
};
try gc.add_material(self.material);
}
pub fn setSkybox(textureName: []const u8) !void {
const name = core.MakeName(textureName);
gSkybox.cubeMapName = name;
}
pub fn destroy(self: *@This()) void {
self.allocator.destroy(self);
}
const vk_renderer_interface = @import("vk_renderer/vk_renderer_interface.zig");
const RendererInterface = vk_renderer_interface.RendererInterface;
const std = @import("std");
const graphics = @import("graphics.zig");
const core = @import("core");
const vk = @import("vulkan");
const skybox_vert = @import("skybox_vert");
const skybox_frag = @import("skybox_frag");
const vkinit = @import("vk_init.zig");
allocator: std.mem.Allocator,
cubeMapShared: [graphics.NumFrames]?vk.DescriptorSet = .{ null, null },
cubeMapTextureSet: ?vk.DescriptorSet = null,
cubeMapName: ?core.Name = null,
material: *graphics.Material = undefined,
mesh: ?graphics.IndexedMesh = null,
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub var gSkybox: *@This() = undefined;
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
gSkybox = self;
try self.initPipeline();
try graphics.registerRendererPlugin(self);
return self;
}
pub fn sendShared(self: *@This(), fi: u32) void {
if (self.cubeMapName == null) {
self.cubeMapShared[fi] = self.cubeMapTextureSet;
return;
} else {
if (self.mesh == null)
self.mesh = graphics.getIndexedMeshByName(core.MakeName("m_skybox"));
if (self.cubeMapTextureSet == null) {
const handle = self.cubeMapName.?.handle();
self.cubeMapTextureSet = graphics.getContext().textureSets.get(handle);
}
}
self.cubeMapShared[fi] = self.cubeMapTextureSet;
}
pub fn initPipeline(self: *@This()) !void {
const gc = graphics.getContext();
var pipelineBuilder = try graphics.NeonVkPipelineBuilder.init(
gc.dev,
gc.vkd,
self.allocator,
gc.vkAllocator,
skybox_vert.spv(),
skybox_frag.spv(),
);
defer pipelineBuilder.deinit();
try pipelineBuilder.add_mesh_description();
try pipelineBuilder.add_layout(gc.globalDescriptorLayout);
try pipelineBuilder.add_layout(gc.singleTextureSetLayout);
try pipelineBuilder.add_depth_stencil(); // todo.. we might not want this for a skybox.
try pipelineBuilder.init_triangle_pipeline(gc.actual_extent);
pipelineBuilder.pdsci.?.depth_write_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_test_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_compare_op = .never;
const materialName = core.MakeName("Mat_skybox");
self.material = try self.allocator.create(graphics.Material);
self.material.* = graphics.Material{
.materialName = materialName,
.pipeline = (try pipelineBuilder.build(gc.renderPass)).?,
.layout = pipelineBuilder.pipelineLayout,
};
try gc.add_material(self.material);
}
pub fn setSkybox(textureName: []const u8) !void {
const name = core.MakeName(textureName);
gSkybox.cubeMapName = name;
}
pub fn destroy(self: *@This()) void {
self.allocator.destroy(self);
}
const vk_renderer_interface = @import("vk_renderer/vk_renderer_interface.zig");
const RendererInterface = vk_renderer_interface.RendererInterface;
const std = @import("std");
const graphics = @import("graphics.zig");
const core = @import("core");
const vk = @import("vulkan");
const skybox_vert = @import("skybox_vert");
const skybox_frag = @import("skybox_frag");
const vkinit = @import("vk_init.zig");

View File

@ -1,39 +1,39 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const vkinit = @import("vk_init.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
pub const PixelPos = struct {
x: u32,
y: u32,
/// returns y/x of the pixel position
pub fn ratio(self: @This()) f32 {
return @as(f32, @floatFromInt(self.y)) / @as(f32, @floatFromInt(self.x));
}
};
// This is a simple display texture
pub const Texture = struct {
image: NeonVkImage,
imageView: vk.ImageView,
isCube: bool = false,
pub fn deinit(self: *@This(), ctx: *NeonVkContext) void {
ctx.vkd.destroyImageView(ctx.dev, self.imageView, null);
self.image.deinit(ctx.vkAllocator);
}
pub fn getDimensions(self: @This()) PixelPos {
return .{
.x = self.image.pixelWidth,
.y = self.image.pixelHeight,
};
}
};
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const vkinit = @import("vk_init.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
pub const PixelPos = struct {
x: u32,
y: u32,
/// returns y/x of the pixel position
pub fn ratio(self: @This()) f32 {
return @as(f32, @floatFromInt(self.y)) / @as(f32, @floatFromInt(self.x));
}
};
// This is a simple display texture
pub const Texture = struct {
image: NeonVkImage,
imageView: vk.ImageView,
isCube: bool = false,
pub fn deinit(self: *@This(), ctx: *NeonVkContext) void {
ctx.vkd.destroyImageView(ctx.dev, self.imageView, null);
self.image.deinit(ctx.vkAllocator);
}
pub fn getDimensions(self: @This()) PixelPos {
return .{
.x = self.image.pixelWidth,
.y = self.image.pixelHeight,
};
}
};

View File

@ -1,16 +1,16 @@
// global api
//
// i hate lugging these variables around.
// device and cmd buffers are fine,
// but the dispatch variables are going to be kept here and easily accessible.
pub var _vkb: constants.BaseDispatch = undefined;
pub var _vki: constants.InstanceDispatch = undefined;
pub var _vkd: constants.DeviceDispatch = undefined;
pub const vkb = &_vkb;
pub const vki = &_vki;
pub const vkd = &_vkd;
const vk = @import("vulkan");
const constants = @import("vk_constants.zig");
// global api
//
// i hate lugging these variables around.
// device and cmd buffers are fine,
// but the dispatch variables are going to be kept here and easily accessible.
pub var _vkb: constants.BaseDispatch = undefined;
pub var _vki: constants.InstanceDispatch = undefined;
pub var _vkd: constants.DeviceDispatch = undefined;
pub const vkb = &_vkb;
pub const vki = &_vki;
pub const vkd = &_vkd;
const vk = @import("vulkan");
const constants = @import("vk_constants.zig");

View File

@ -1,373 +1,373 @@
const std = @import("std");
const vk = @import("vulkan");
const graphics = @import("graphics.zig");
const core = @import("core");
const assets = @import("assets");
const vk_utils = @import("vk_utils.zig");
const vkinit = @import("vk_init.zig");
const vk_cubemap = @import("vk_renderer/vk_cubemap.zig");
const tracy = core.tracy;
const materials = @import("materials.zig");
const vk_renderer = @import("vk_renderer.zig");
const mesh = @import("mesh.zig");
const texture = @import("texture.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const Material = materials.Material;
const Mesh = mesh.Mesh;
const Texture = texture.Texture;
pub const TextureLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Texture", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
const StagedTextureDescription = struct {
name: core.Name,
stagingResults: vk_utils.LoadAndStageImage,
textureListResults: ?[]vk_utils.LoadAndStageImage = null,
assetRef: assets.AssetRef,
properties: assets.AssetPropertiesBag,
pub fn deinit(self: *@This(), gc: *NeonVkContext) void {
self.stagingResults.deinit(gc.vkAllocator);
if (self.textureListResults) |results| {
for (results) |*result| {
result.deinit(gc.vkAllocator);
}
}
}
};
const RTAssetsReady = struct {
name: core.Name,
texture: *Texture,
textureSet: vk.DescriptorSet,
textureId: u32,
};
gc: *NeonVkContext,
assetsReady: core.RingQueue(StagedTextureDescription),
rtAssetsReady: core.RingQueue(RTAssetsReady),
discarding: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, props: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
if (self.discarding.load(.seq_cst)) {
return;
}
var z = tracy.ZoneN(@src(), "TextureLoader loadAsset");
const Lambda = struct {
loader: *TextureLoader,
assetRef: assets.AssetRef,
gc: *NeonVkContext,
properties: assets.AssetPropertiesBag,
pub fn eFunc(ctx: @This()) !void {
var z1 = tracy.ZoneN(@src(), "Loading file from TextureLoader");
const gc = ctx.gc;
defer {
_ = ctx.gc.outstandingJobsCount.fetchSub(1, .seq_cst);
}
var loadAndStageResults: vk_utils.LoadAndStageImage = undefined;
if (!ctx.properties.textureCube) {
loadAndStageResults = try vk_utils.load_and_stage_image_from_file(gc, ctx.properties.path);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
} else {
loadAndStageResults = try vk_cubemap.stageCubeTexture(ctx.properties.textureList.?);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
}
var assetRefName = ctx.assetRef.name;
tracy.Message(assetRefName.utf8());
tracy.Message(ctx.properties.path);
core.engine_log("loaded: {s} from: {s}", .{ assetRefName.utf8(), ctx.properties.path });
var loadedDescription = StagedTextureDescription{
.name = ctx.assetRef.name,
.stagingResults = loadAndStageResults,
.assetRef = ctx.assetRef,
.properties = ctx.properties,
};
if (ctx.properties.textureList) |textureList| {
const tlResults = try ctx.gc.allocator.alloc(vk_utils.LoadAndStageImage, textureList.len);
errdefer ctx.gc.allocator.free(tlResults);
for (textureList, 0..) |tPath, i| {
const rv = vk_utils.load_and_stage_image_from_file(gc, tPath) catch {
core.engine_log("unable to load file {s}", .{tPath});
return error.FailedToLoad;
};
errdefer rv.deinit();
tlResults[i] = rv;
}
loadedDescription.textureListResults = tlResults;
}
z1.End();
ctx.loader.assetsReady.pushLocked(loadedDescription) catch unreachable;
}
pub fn func(ctx: @This(), _: *core.JobContext) void {
ctx.eFunc() catch unreachable;
}
};
_ = self.gc.outstandingJobsCount.fetchAdd(1, .seq_cst);
core.dispatchJob(Lambda{
.loader = self,
.gc = self.gc,
.assetRef = assetRef,
.properties = props.?,
}) catch return error.UnableToLoad;
z.End();
}
pub fn processRenderThreadEvents(ptr: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(ptr));
self.processEventInner() catch {};
}
pub fn createImageFromStagingResult(self: *@This(), name: core.Name, stagingResults: *vk_utils.LoadAndStageImage, properties: assets.AssetPropertiesBag) core.EngineDataEventError!void {
const gc = self.gc;
var stagingBuffer = stagingResults.stagingBuffer;
const image = stagingResults.image;
if (stagingResults.cubeOffsets != null) {
vk_cubemap.submitTextureCube(&gc.uploader, stagingResults) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var ivc = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
ivc.view_type = .cube;
ivc.subresource_range.layer_count = 6;
const imageView = gc.vkd.createImageView(gc.dev, &ivc, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
gc.cubeSampler,
false,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
} else {
vk_utils.submit_copy_from_staging(gc, stagingBuffer, image, stagingResults.mipLevel) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var imageViewCreate = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
const imageView = gc.vkd.createImageView(gc.dev, &imageViewCreate, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const sampler = if (properties.textureUseBlockySampler) gc.blockySampler else gc.linearSampler;
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
sampler,
true,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
}
}
fn processEventInner(self: *@This()) core.EngineDataEventError!void {
if (self.assetsReady.count() > 0) {
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |ar| {
var assetReady = ar;
var z1 = tracy.ZoneN(@src(), "Uploading asset loaded by TextureLoader");
tracy.Message("TextureLoader");
tracy.Message(assetReady.assetRef.name.utf8());
tracy.Message(assetReady.properties.path);
core.engine_log("async texture load complete registry: {s}", .{assetReady.name.utf8()});
try self.createImageFromStagingResult(assetReady.name, &assetReady.stagingResults, assetReady.properties);
if (assetReady.textureListResults) |results| {
var buf: [256]u8 = undefined;
for (results, 0..) |res, i| {
var r = res;
var arName = assetReady.name;
const newName = std.fmt.bufPrint(&buf, "{s}[{d}]", .{ arName.utf8(), i }) catch return core.EngineDataEventError.OutOfMemory;
try self.createImageFromStagingResult(core.MakeName(newName), &r, assetReady.properties);
}
self.gc.allocator.free(results);
}
z1.End();
}
}
}
// processing events, some should really be processing events rather than
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
if (self.rtAssetsReady.count() > 0) {
self.rtAssetsReady.lock();
defer self.rtAssetsReady.unlock();
while (self.rtAssetsReady.popFromUnlocked()) |a| {
self.gc.install_texture_into_registry(a.name, a.texture, a.textureSet, a.textureId) catch return error.UnknownStatePanic;
}
}
}
pub fn discardAll(self: *@This()) void {
self.discarding.store(true, .seq_cst);
core.graphics_log("discarding {d} outstanding jobs", .{self.assetsReady.count()});
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |assetReady| {
var copy = assetReady;
StagedTextureDescription.deinit(&copy, self.gc);
}
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
//todo: the EngineObjectVTable init function should have a handleable error
.assetsReady = core.RingQueue(StagedTextureDescription).init(allocator, 1024) catch unreachable,
.rtAssetsReady = core.RingQueue(RTAssetsReady).init(allocator, 1024) catch unreachable,
};
try self.gc.renderthread.installListener(self, processRenderThreadEvents);
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
self.assetsReady.deinit();
self.rtAssetsReady.deinit();
allocator.destroy(self);
}
};
pub const MeshLoader = struct {
pub var LoaderInterfaceVTable = assets.AssetLoaderInterface.from("Mesh", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
gc: *NeonVkContext,
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
};
return self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
_ = self;
const sourceType = getSourceType(propertiesBag);
core.engine_log("loading mesh asset {s} [{s}]", .{ propertiesBag.?.path, if (sourceType) |s| @tagName(s) else "default" });
graphics.loadIndexedMeshForPooling(assetRef.name, .{
.path = propertiesBag.?.path,
.sourceType = getSourceType(propertiesBag),
.skeletonName = if (propertiesBag.?.skeletonName) |skName| core.MakeName(skName) else null,
}) catch return error.UnableToLoad;
}
fn getSourceType(propertiesBag: ?assets.AssetPropertiesBag) ?graphics.MeshSourceType {
if (propertiesBag) |bag| {
if (bag.meshType) |meshType| {
if (std.mem.eql(u8, meshType, "obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, meshType, "gltf")) {
return graphics.MeshSourceType.gltf;
}
}
// try to deduce it by file name, if nothing is set.
const ext = core.getFileExtension(bag.path);
if (std.mem.eql(u8, ext, ".obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, ext, ".gltf")) {
return graphics.MeshSourceType.gltf;
}
if (std.mem.eql(u8, ext, ".glb")) {
return graphics.MeshSourceType.gltf;
}
}
return null;
}
pub fn discardAll(self: *@This()) void {
// totally synchronous, nothing to do for a discard
_ = self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gTextureLoader: *TextureLoader = undefined;
pub var gMeshLoader: *MeshLoader = undefined;
pub fn init_loaders(allocator: std.mem.Allocator) !void {
gTextureLoader = try core.createObject(TextureLoader, .{
.responds_to_events = true,
});
gMeshLoader = try allocator.create(MeshLoader);
gMeshLoader.* = .{ .gc = vk_renderer.gContext };
try assets.gAssetSys.registerLoader(gTextureLoader);
try assets.gAssetSys.registerLoader(gMeshLoader);
}
// submit an abort message to TextureLoader and MeshLoader
pub fn discardAll() void {
gTextureLoader.discardAll();
gMeshLoader.discardAll();
}
const std = @import("std");
const vk = @import("vulkan");
const graphics = @import("graphics.zig");
const core = @import("core");
const assets = @import("assets");
const vk_utils = @import("vk_utils.zig");
const vkinit = @import("vk_init.zig");
const vk_cubemap = @import("vk_renderer/vk_cubemap.zig");
const tracy = core.tracy;
const materials = @import("materials.zig");
const vk_renderer = @import("vk_renderer.zig");
const mesh = @import("mesh.zig");
const texture = @import("texture.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const Material = materials.Material;
const Mesh = mesh.Mesh;
const Texture = texture.Texture;
pub const TextureLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Texture", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
const StagedTextureDescription = struct {
name: core.Name,
stagingResults: vk_utils.LoadAndStageImage,
textureListResults: ?[]vk_utils.LoadAndStageImage = null,
assetRef: assets.AssetRef,
properties: assets.AssetPropertiesBag,
pub fn deinit(self: *@This(), gc: *NeonVkContext) void {
self.stagingResults.deinit(gc.vkAllocator);
if (self.textureListResults) |results| {
for (results) |*result| {
result.deinit(gc.vkAllocator);
}
}
}
};
const RTAssetsReady = struct {
name: core.Name,
texture: *Texture,
textureSet: vk.DescriptorSet,
textureId: u32,
};
gc: *NeonVkContext,
assetsReady: core.RingQueue(StagedTextureDescription),
rtAssetsReady: core.RingQueue(RTAssetsReady),
discarding: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, props: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
if (self.discarding.load(.seq_cst)) {
return;
}
var z = tracy.ZoneN(@src(), "TextureLoader loadAsset");
const Lambda = struct {
loader: *TextureLoader,
assetRef: assets.AssetRef,
gc: *NeonVkContext,
properties: assets.AssetPropertiesBag,
pub fn eFunc(ctx: @This()) !void {
var z1 = tracy.ZoneN(@src(), "Loading file from TextureLoader");
const gc = ctx.gc;
defer {
_ = ctx.gc.outstandingJobsCount.fetchSub(1, .seq_cst);
}
var loadAndStageResults: vk_utils.LoadAndStageImage = undefined;
if (!ctx.properties.textureCube) {
loadAndStageResults = try vk_utils.load_and_stage_image_from_file(gc, ctx.properties.path);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
} else {
loadAndStageResults = try vk_cubemap.stageCubeTexture(ctx.properties.textureList.?);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
}
var assetRefName = ctx.assetRef.name;
tracy.Message(assetRefName.utf8());
tracy.Message(ctx.properties.path);
core.engine_log("loaded: {s} from: {s}", .{ assetRefName.utf8(), ctx.properties.path });
var loadedDescription = StagedTextureDescription{
.name = ctx.assetRef.name,
.stagingResults = loadAndStageResults,
.assetRef = ctx.assetRef,
.properties = ctx.properties,
};
if (ctx.properties.textureList) |textureList| {
const tlResults = try ctx.gc.allocator.alloc(vk_utils.LoadAndStageImage, textureList.len);
errdefer ctx.gc.allocator.free(tlResults);
for (textureList, 0..) |tPath, i| {
const rv = vk_utils.load_and_stage_image_from_file(gc, tPath) catch {
core.engine_log("unable to load file {s}", .{tPath});
return error.FailedToLoad;
};
errdefer rv.deinit();
tlResults[i] = rv;
}
loadedDescription.textureListResults = tlResults;
}
z1.End();
ctx.loader.assetsReady.pushLocked(loadedDescription) catch unreachable;
}
pub fn func(ctx: @This(), _: *core.JobContext) void {
ctx.eFunc() catch unreachable;
}
};
_ = self.gc.outstandingJobsCount.fetchAdd(1, .seq_cst);
core.dispatchJob(Lambda{
.loader = self,
.gc = self.gc,
.assetRef = assetRef,
.properties = props.?,
}) catch return error.UnableToLoad;
z.End();
}
pub fn processRenderThreadEvents(ptr: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(ptr));
self.processEventInner() catch {};
}
pub fn createImageFromStagingResult(self: *@This(), name: core.Name, stagingResults: *vk_utils.LoadAndStageImage, properties: assets.AssetPropertiesBag) core.EngineDataEventError!void {
const gc = self.gc;
var stagingBuffer = stagingResults.stagingBuffer;
const image = stagingResults.image;
if (stagingResults.cubeOffsets != null) {
vk_cubemap.submitTextureCube(&gc.uploader, stagingResults) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var ivc = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
ivc.view_type = .cube;
ivc.subresource_range.layer_count = 6;
const imageView = gc.vkd.createImageView(gc.dev, &ivc, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
gc.cubeSampler,
false,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
} else {
vk_utils.submit_copy_from_staging(gc, stagingBuffer, image, stagingResults.mipLevel) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var imageViewCreate = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
const imageView = gc.vkd.createImageView(gc.dev, &imageViewCreate, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const sampler = if (properties.textureUseBlockySampler) gc.blockySampler else gc.linearSampler;
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
sampler,
true,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
}
}
fn processEventInner(self: *@This()) core.EngineDataEventError!void {
if (self.assetsReady.count() > 0) {
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |ar| {
var assetReady = ar;
var z1 = tracy.ZoneN(@src(), "Uploading asset loaded by TextureLoader");
tracy.Message("TextureLoader");
tracy.Message(assetReady.assetRef.name.utf8());
tracy.Message(assetReady.properties.path);
core.engine_log("async texture load complete registry: {s}", .{assetReady.name.utf8()});
try self.createImageFromStagingResult(assetReady.name, &assetReady.stagingResults, assetReady.properties);
if (assetReady.textureListResults) |results| {
var buf: [256]u8 = undefined;
for (results, 0..) |res, i| {
var r = res;
var arName = assetReady.name;
const newName = std.fmt.bufPrint(&buf, "{s}[{d}]", .{ arName.utf8(), i }) catch return core.EngineDataEventError.OutOfMemory;
try self.createImageFromStagingResult(core.MakeName(newName), &r, assetReady.properties);
}
self.gc.allocator.free(results);
}
z1.End();
}
}
}
// processing events, some should really be processing events rather than
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
if (self.rtAssetsReady.count() > 0) {
self.rtAssetsReady.lock();
defer self.rtAssetsReady.unlock();
while (self.rtAssetsReady.popFromUnlocked()) |a| {
self.gc.install_texture_into_registry(a.name, a.texture, a.textureSet, a.textureId) catch return error.UnknownStatePanic;
}
}
}
pub fn discardAll(self: *@This()) void {
self.discarding.store(true, .seq_cst);
core.graphics_log("discarding {d} outstanding jobs", .{self.assetsReady.count()});
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |assetReady| {
var copy = assetReady;
StagedTextureDescription.deinit(&copy, self.gc);
}
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
//todo: the EngineObjectVTable init function should have a handleable error
.assetsReady = core.RingQueue(StagedTextureDescription).init(allocator, 1024) catch unreachable,
.rtAssetsReady = core.RingQueue(RTAssetsReady).init(allocator, 1024) catch unreachable,
};
try self.gc.renderthread.installListener(self, processRenderThreadEvents);
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
self.assetsReady.deinit();
self.rtAssetsReady.deinit();
allocator.destroy(self);
}
};
pub const MeshLoader = struct {
pub var LoaderInterfaceVTable = assets.AssetLoaderInterface.from("Mesh", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
gc: *NeonVkContext,
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
};
return self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
_ = self;
const sourceType = getSourceType(propertiesBag);
core.engine_log("loading mesh asset {s} [{s}]", .{ propertiesBag.?.path, if (sourceType) |s| @tagName(s) else "default" });
graphics.loadIndexedMeshForPooling(assetRef.name, .{
.path = propertiesBag.?.path,
.sourceType = getSourceType(propertiesBag),
.skeletonName = if (propertiesBag.?.skeletonName) |skName| core.MakeName(skName) else null,
}) catch return error.UnableToLoad;
}
fn getSourceType(propertiesBag: ?assets.AssetPropertiesBag) ?graphics.MeshSourceType {
if (propertiesBag) |bag| {
if (bag.meshType) |meshType| {
if (std.mem.eql(u8, meshType, "obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, meshType, "gltf")) {
return graphics.MeshSourceType.gltf;
}
}
// try to deduce it by file name, if nothing is set.
const ext = core.getFileExtension(bag.path);
if (std.mem.eql(u8, ext, ".obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, ext, ".gltf")) {
return graphics.MeshSourceType.gltf;
}
if (std.mem.eql(u8, ext, ".glb")) {
return graphics.MeshSourceType.gltf;
}
}
return null;
}
pub fn discardAll(self: *@This()) void {
// totally synchronous, nothing to do for a discard
_ = self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gTextureLoader: *TextureLoader = undefined;
pub var gMeshLoader: *MeshLoader = undefined;
pub fn init_loaders(allocator: std.mem.Allocator) !void {
gTextureLoader = try core.createObject(TextureLoader, .{
.responds_to_events = true,
});
gMeshLoader = try allocator.create(MeshLoader);
gMeshLoader.* = .{ .gc = vk_renderer.gContext };
try assets.gAssetSys.registerLoader(gTextureLoader);
try assets.gAssetSys.registerLoader(gMeshLoader);
}
// submit an abort message to TextureLoader and MeshLoader
pub fn discardAll() void {
gTextureLoader.discardAll();
gMeshLoader.discardAll();
}

View File

@ -1,25 +1,25 @@
// higher level descriptor and SSBO wrangling libraries.
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const vkinit = @import("vk_init.zig");
const vk_constants = @import("vk_constants.zig");
const tracy = core.tracy;
const spng = core.spng;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
const NumFrames = vk_constants.NUM_FRAMES;
pub const DescriptorSetLayoutInfo = struct {
bindingCount: u32,
};
// higher level descriptor and SSBO wrangling libraries.
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const vkinit = @import("vk_init.zig");
const vk_constants = @import("vk_constants.zig");
const tracy = core.tracy;
const spng = core.spng;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
const NumFrames = vk_constants.NUM_FRAMES;
pub const DescriptorSetLayoutInfo = struct {
bindingCount: u32,
};

View File

@ -1,435 +1,435 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const constants = @import("vk_constants.zig");
const vk_utils = @import("vk_utils.zig");
const NeonVkUploader = vk_utils.NeonVkUploader;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const Vector2f = core.Vector2f;
const LinearColor = core.colors.Color;
const NeonVkContext = vk_renderer.NeonVkContext;
const mesh = @import("mesh.zig");
const MeshVertex = mesh.MeshVertex;
const debug_struct = core.debug_struct;
pub const DynamicMeshManager = struct {
gc: *NeonVkContext,
allocator: std.mem.Allocator,
dynMeshes: std.ArrayListUnmanaged(*DynamicMesh) = .{},
uploader: NeonVkUploader,
first: bool = true,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = @This(){
.gc = gc,
.allocator = gc.allocator,
.uploader = try NeonVkUploader.init(gc, "dynamic mesh manager uploader"),
};
// core.graphics_log("creating the mesh manager", .{});
return self;
}
pub fn deinit(self: *@This()) void {
self.uploader.deinit();
self.dynMeshes.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn addDynamicMesh(self: *@This(), dynamicMesh: *DynamicMesh) !void {
try self.dynMeshes.append(self.allocator, dynamicMesh);
}
pub fn updateMeshes(self: *@This(), cmd: vk.CommandBuffer) !void {
for (self.dynMeshes.items) |dynMesh| {
try dynMesh.maybeUpdateVertices(cmd);
}
}
pub fn finishUpload(self: *@This()) !void {
if (self.uploader.isActive) {
var t3 = core.tracy.ZoneN(@src(), "finishing dynamic mesh upload context");
defer t3.End();
try self.uploader.waitForFences();
for (self.dynMeshes.items) |dynMesh| {
if (dynMesh.isDirty) {
dynMesh.bumpSwapId();
}
}
}
}
};
pub const DynamicMesh = struct {
pub const GeometryMode = enum { quads, triangles };
allocator: std.mem.Allocator,
gc: *NeonVkContext,
vertices: []MeshVertex = undefined,
geometryMode: GeometryMode = .quads, // geometry elaboration mode
indicesMaxCount: u32 = 0,
indexBuffers: [2]NeonVkBuffer = undefined,
indexBufferLen: [2]u32 = .{ 0, 0 },
vertexBuffers: [2]NeonVkBuffer = undefined,
vertexBufferLen: [2]u32 = .{ 0, 0 },
swapId: usize = 0, // the index of the previously uploaded vertex buffer
vertexCount: u32 = 0, //
stagingVertexBuffer: NeonVkBuffer = undefined,
stagingIndexBuffer: NeonVkBuffer = undefined,
isDirty: bool = true,
maxVertexCount: u32,
pub fn init(gc: *NeonVkContext, allocator: std.mem.Allocator, opts: struct {
maxVertexCount: u32 = 4096,
maxIndexCount: u32 = 4096 * 6 / 4,
mode: GeometryMode = .quads,
}) !*@This() {
var self = try allocator.create(@This());
self.* = .{
.maxVertexCount = opts.maxVertexCount,
.allocator = allocator,
.vertices = try allocator.alloc(MeshVertex, opts.maxVertexCount),
.gc = gc,
.geometryMode = opts.mode,
};
try gc.dynamicMeshManager.addDynamicMesh(self);
self.allocator = allocator;
self.gc = gc;
{
self.stagingIndexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxIndexCount * @sizeOf(u32), "DynamicMesh.init - index staging");
self.stagingVertexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), "DynamicMesh.init - vertex staging");
inline for (0..2) |i| {
self.indexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxIndexCount * @sizeOf(u32), .{
.index_buffer_bit = true,
}, "DynamicMesh.init - gpu indexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
self.vertexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), .{
.vertex_buffer_bit = true,
}, "DynamicMesh.init - gpu vertexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
}
}
return self;
}
pub fn getIndexBuffer(self: *@This()) NeonVkBuffer {
return self.indexBuffers[self.swapId];
}
pub fn getVertexBuffer(self: *@This()) NeonVkBuffer {
return self.vertexBuffers[self.swapId];
}
pub fn getIndexBufferLen(self: *@This()) u32 {
return self.indexBufferLen[self.swapId];
}
pub fn getVertexBufferLen(self: *@This()) u32 {
return self.vertexBufferLen[self.swapId];
}
pub fn maybeUpdateVertices(self: *@This(), cmd: vk.CommandBuffer) !void {
if (!self.isDirty) {
return;
}
var t1 = core.tracy.ZoneN(@src(), "Dynamic Mesh upload with barriers");
defer t1.End();
self.isDirty = false;
try self.stageDirtyVertices();
if (self.indexBufferLen[self.swapId] == 0 or self.vertexCount == 0) {
return;
}
var vkd = self.gc.vkd;
var copy = vk.BufferCopy{
.dst_offset = 0,
.src_offset = 0,
.size = self.indexBufferLen[self.swapId] * @as(u32, @intCast(@sizeOf(u32))),
};
// submit index Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingIndexBuffer.buffer,
self.indexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
var indexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.indexBuffers[self.swapId].buffer,
.src_access_mask = .{ .transfer_read_bit = true },
.dst_access_mask = .{ .index_read_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
// Insert Barrier for indexBuffer
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&indexMemoryBarrier),
0,
undefined,
);
copy.size = self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex)));
// submit vertex Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingVertexBuffer.buffer,
self.vertexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
// Insert Barrier for vertexBuffer
var vertexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.vertexBuffers[self.swapId].buffer,
.src_access_mask = .{
.transfer_read_bit = true,
},
.dst_access_mask = .{
// .transfer_write_bit = true,
.vertex_attribute_read_bit = true,
},
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&vertexMemoryBarrier),
0,
undefined,
);
}
pub fn stageDirtyVertices(self: *@This()) !void {
const newSwapId = (self.swapId + 1) % 2;
// map buffers
var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// copy over vertices to mapped buffer
for (0..self.vertexCount) |i| {
slice[i] = self.vertices[i];
}
self.vertexBufferLen[newSwapId] = self.vertexCount;
// interpret vertices as quads.
if (self.geometryMode == .quads) {
var index: u32 = 0;
var vertex: u32 = 0;
while (vertex < self.vertexCount) {
indexSlice[index + 0] = vertex + 0;
indexSlice[index + 1] = vertex + 1;
indexSlice[index + 2] = vertex + 2;
indexSlice[index + 3] = vertex + 2;
indexSlice[index + 4] = vertex + 3;
indexSlice[index + 5] = vertex + 0;
vertex += 4;
index += 6;
}
self.indexBufferLen[newSwapId] = index;
}
self.swapId = newSwapId;
}
// a stream-like interface for creating vertices
// pub fn uploadVertices(self: *@This(), uploader: *NeonVkUploader) !void {
// if (!self.isDirty) {
// return;
// }
// self.dirty = false;
// const newSwapId = (self.swapId + 1) % 2;
// // map buffers
// var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
// var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
// defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
// defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// // copy over vertices to mapped buffer
// // so... this right here would need to lock.. actually this would be a try-lock
// // if we fail to lock it... that's ok. we can just try again at the end of the frame.
// // what happens if we always fail to lock it?
// for (0..self.vertexCount) |i| {
// slice[i] = self.vertices[i];
// }
// self.vertexBufferLen[newSwapId] = self.vertexCount;
// // interpret vertices as quads.
// if (self.geometryMode == .quads) {
// var index: u32 = 0;
// var vertex: u32 = 0;
// while (vertex < self.vertexCount) {
// indexSlice[index + 0] = vertex + 0;
// indexSlice[index + 1] = vertex + 1;
// indexSlice[index + 2] = vertex + 2;
// indexSlice[index + 3] = vertex + 2;
// indexSlice[index + 4] = vertex + 3;
// indexSlice[index + 5] = vertex + 0;
// vertex += 4;
// index += 6;
// }
// self.indexBufferLen[newSwapId] = index;
// }
// // upload index and vertex buffers
// try uploader.addBufferUpload(
// self.stagingIndexBuffer,
// self.indexBuffers[newSwapId],
// self.indexBufferLen[newSwapId] * @as(u32, @intCast(@sizeOf(u32))),
// );
// try uploader.addBufferUpload(
// self.stagingVertexBuffer,
// self.vertexBuffers[newSwapId],
// self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex))),
// );
// }
pub fn bumpSwapId(self: *@This()) void {
self.swapId = (self.swapId + 1) % 2;
self.isDirty = false;
}
pub fn clearVertices(self: *@This()) void {
self.vertexCount = 0;
self.isDirty = true;
}
pub fn addVertexList(self: *@This(), list: []const MeshVertex) void {
if (list.len > 0) {
self.isDirty = true;
}
for (list) |v| {
self.vertices[self.vertexCount] = v;
self.vertexCount += 1;
}
}
// adds a quad only in the X and y Space,
pub fn addQuad2D(
self: *@This(),
_topLeft: core.Vectorf, // only x and y is considered
_size: core.Vectorf, // only x and y is considered
topLeftUV: core.Vector2f,
uvSize: core.Vector2f,
color: LinearColor,
) void {
var topLeft = _topLeft;
var size = _size;
topLeft.z = 0;
size.z = 0;
const normal = Vectorf{ .x = 0, .y = 0, .z = -1 };
var vertices: [4]MeshVertex = undefined;
vertices[0] = .{
.position = topLeft,
.normal = normal,
.uv = topLeftUV,
.color = color,
};
vertices[1] = .{
.position = topLeft.add(.{ .x = size.x }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .x = uvSize.x }),
.color = color,
};
vertices[2] = .{
.position = topLeft.add(size),
.normal = normal,
.uv = topLeftUV.add(uvSize),
.color = color,
};
vertices[3] = .{
.position = topLeft.add(.{ .y = size.y }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .y = uvSize.y }),
.color = color,
};
self.addVertexList(&vertices);
}
pub fn deinit(self: *@This()) void {
self.allocator.free(self.vertices);
const vkAllocator = self.gc.vkAllocator;
self.stagingVertexBuffer.deinit(vkAllocator);
self.stagingIndexBuffer.deinit(vkAllocator);
for (0..self.indexBuffers.len) |i| {
self.indexBuffers[i].deinit(vkAllocator);
self.vertexBuffers[i].deinit(vkAllocator);
}
self.allocator.destroy(self);
// should remove ourselves from the manager
}
};
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const constants = @import("vk_constants.zig");
const vk_utils = @import("vk_utils.zig");
const NeonVkUploader = vk_utils.NeonVkUploader;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const Vector2f = core.Vector2f;
const LinearColor = core.colors.Color;
const NeonVkContext = vk_renderer.NeonVkContext;
const mesh = @import("mesh.zig");
const MeshVertex = mesh.MeshVertex;
const debug_struct = core.debug_struct;
pub const DynamicMeshManager = struct {
gc: *NeonVkContext,
allocator: std.mem.Allocator,
dynMeshes: std.ArrayListUnmanaged(*DynamicMesh) = .{},
uploader: NeonVkUploader,
first: bool = true,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = @This(){
.gc = gc,
.allocator = gc.allocator,
.uploader = try NeonVkUploader.init(gc, "dynamic mesh manager uploader"),
};
// core.graphics_log("creating the mesh manager", .{});
return self;
}
pub fn deinit(self: *@This()) void {
self.uploader.deinit();
self.dynMeshes.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn addDynamicMesh(self: *@This(), dynamicMesh: *DynamicMesh) !void {
try self.dynMeshes.append(self.allocator, dynamicMesh);
}
pub fn updateMeshes(self: *@This(), cmd: vk.CommandBuffer) !void {
for (self.dynMeshes.items) |dynMesh| {
try dynMesh.maybeUpdateVertices(cmd);
}
}
pub fn finishUpload(self: *@This()) !void {
if (self.uploader.isActive) {
var t3 = core.tracy.ZoneN(@src(), "finishing dynamic mesh upload context");
defer t3.End();
try self.uploader.waitForFences();
for (self.dynMeshes.items) |dynMesh| {
if (dynMesh.isDirty) {
dynMesh.bumpSwapId();
}
}
}
}
};
pub const DynamicMesh = struct {
pub const GeometryMode = enum { quads, triangles };
allocator: std.mem.Allocator,
gc: *NeonVkContext,
vertices: []MeshVertex = undefined,
geometryMode: GeometryMode = .quads, // geometry elaboration mode
indicesMaxCount: u32 = 0,
indexBuffers: [2]NeonVkBuffer = undefined,
indexBufferLen: [2]u32 = .{ 0, 0 },
vertexBuffers: [2]NeonVkBuffer = undefined,
vertexBufferLen: [2]u32 = .{ 0, 0 },
swapId: usize = 0, // the index of the previously uploaded vertex buffer
vertexCount: u32 = 0, //
stagingVertexBuffer: NeonVkBuffer = undefined,
stagingIndexBuffer: NeonVkBuffer = undefined,
isDirty: bool = true,
maxVertexCount: u32,
pub fn init(gc: *NeonVkContext, allocator: std.mem.Allocator, opts: struct {
maxVertexCount: u32 = 4096,
maxIndexCount: u32 = 4096 * 6 / 4,
mode: GeometryMode = .quads,
}) !*@This() {
var self = try allocator.create(@This());
self.* = .{
.maxVertexCount = opts.maxVertexCount,
.allocator = allocator,
.vertices = try allocator.alloc(MeshVertex, opts.maxVertexCount),
.gc = gc,
.geometryMode = opts.mode,
};
try gc.dynamicMeshManager.addDynamicMesh(self);
self.allocator = allocator;
self.gc = gc;
{
self.stagingIndexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxIndexCount * @sizeOf(u32), "DynamicMesh.init - index staging");
self.stagingVertexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), "DynamicMesh.init - vertex staging");
inline for (0..2) |i| {
self.indexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxIndexCount * @sizeOf(u32), .{
.index_buffer_bit = true,
}, "DynamicMesh.init - gpu indexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
self.vertexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), .{
.vertex_buffer_bit = true,
}, "DynamicMesh.init - gpu vertexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
}
}
return self;
}
pub fn getIndexBuffer(self: *@This()) NeonVkBuffer {
return self.indexBuffers[self.swapId];
}
pub fn getVertexBuffer(self: *@This()) NeonVkBuffer {
return self.vertexBuffers[self.swapId];
}
pub fn getIndexBufferLen(self: *@This()) u32 {
return self.indexBufferLen[self.swapId];
}
pub fn getVertexBufferLen(self: *@This()) u32 {
return self.vertexBufferLen[self.swapId];
}
pub fn maybeUpdateVertices(self: *@This(), cmd: vk.CommandBuffer) !void {
if (!self.isDirty) {
return;
}
var t1 = core.tracy.ZoneN(@src(), "Dynamic Mesh upload with barriers");
defer t1.End();
self.isDirty = false;
try self.stageDirtyVertices();
if (self.indexBufferLen[self.swapId] == 0 or self.vertexCount == 0) {
return;
}
var vkd = self.gc.vkd;
var copy = vk.BufferCopy{
.dst_offset = 0,
.src_offset = 0,
.size = self.indexBufferLen[self.swapId] * @as(u32, @intCast(@sizeOf(u32))),
};
// submit index Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingIndexBuffer.buffer,
self.indexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
var indexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.indexBuffers[self.swapId].buffer,
.src_access_mask = .{ .transfer_read_bit = true },
.dst_access_mask = .{ .index_read_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
// Insert Barrier for indexBuffer
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&indexMemoryBarrier),
0,
undefined,
);
copy.size = self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex)));
// submit vertex Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingVertexBuffer.buffer,
self.vertexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
// Insert Barrier for vertexBuffer
var vertexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.vertexBuffers[self.swapId].buffer,
.src_access_mask = .{
.transfer_read_bit = true,
},
.dst_access_mask = .{
// .transfer_write_bit = true,
.vertex_attribute_read_bit = true,
},
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&vertexMemoryBarrier),
0,
undefined,
);
}
pub fn stageDirtyVertices(self: *@This()) !void {
const newSwapId = (self.swapId + 1) % 2;
// map buffers
var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// copy over vertices to mapped buffer
for (0..self.vertexCount) |i| {
slice[i] = self.vertices[i];
}
self.vertexBufferLen[newSwapId] = self.vertexCount;
// interpret vertices as quads.
if (self.geometryMode == .quads) {
var index: u32 = 0;
var vertex: u32 = 0;
while (vertex < self.vertexCount) {
indexSlice[index + 0] = vertex + 0;
indexSlice[index + 1] = vertex + 1;
indexSlice[index + 2] = vertex + 2;
indexSlice[index + 3] = vertex + 2;
indexSlice[index + 4] = vertex + 3;
indexSlice[index + 5] = vertex + 0;
vertex += 4;
index += 6;
}
self.indexBufferLen[newSwapId] = index;
}
self.swapId = newSwapId;
}
// a stream-like interface for creating vertices
// pub fn uploadVertices(self: *@This(), uploader: *NeonVkUploader) !void {
// if (!self.isDirty) {
// return;
// }
// self.dirty = false;
// const newSwapId = (self.swapId + 1) % 2;
// // map buffers
// var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
// var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
// defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
// defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// // copy over vertices to mapped buffer
// // so... this right here would need to lock.. actually this would be a try-lock
// // if we fail to lock it... that's ok. we can just try again at the end of the frame.
// // what happens if we always fail to lock it?
// for (0..self.vertexCount) |i| {
// slice[i] = self.vertices[i];
// }
// self.vertexBufferLen[newSwapId] = self.vertexCount;
// // interpret vertices as quads.
// if (self.geometryMode == .quads) {
// var index: u32 = 0;
// var vertex: u32 = 0;
// while (vertex < self.vertexCount) {
// indexSlice[index + 0] = vertex + 0;
// indexSlice[index + 1] = vertex + 1;
// indexSlice[index + 2] = vertex + 2;
// indexSlice[index + 3] = vertex + 2;
// indexSlice[index + 4] = vertex + 3;
// indexSlice[index + 5] = vertex + 0;
// vertex += 4;
// index += 6;
// }
// self.indexBufferLen[newSwapId] = index;
// }
// // upload index and vertex buffers
// try uploader.addBufferUpload(
// self.stagingIndexBuffer,
// self.indexBuffers[newSwapId],
// self.indexBufferLen[newSwapId] * @as(u32, @intCast(@sizeOf(u32))),
// );
// try uploader.addBufferUpload(
// self.stagingVertexBuffer,
// self.vertexBuffers[newSwapId],
// self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex))),
// );
// }
pub fn bumpSwapId(self: *@This()) void {
self.swapId = (self.swapId + 1) % 2;
self.isDirty = false;
}
pub fn clearVertices(self: *@This()) void {
self.vertexCount = 0;
self.isDirty = true;
}
pub fn addVertexList(self: *@This(), list: []const MeshVertex) void {
if (list.len > 0) {
self.isDirty = true;
}
for (list) |v| {
self.vertices[self.vertexCount] = v;
self.vertexCount += 1;
}
}
// adds a quad only in the X and y Space,
pub fn addQuad2D(
self: *@This(),
_topLeft: core.Vectorf, // only x and y is considered
_size: core.Vectorf, // only x and y is considered
topLeftUV: core.Vector2f,
uvSize: core.Vector2f,
color: LinearColor,
) void {
var topLeft = _topLeft;
var size = _size;
topLeft.z = 0;
size.z = 0;
const normal = Vectorf{ .x = 0, .y = 0, .z = -1 };
var vertices: [4]MeshVertex = undefined;
vertices[0] = .{
.position = topLeft,
.normal = normal,
.uv = topLeftUV,
.color = color,
};
vertices[1] = .{
.position = topLeft.add(.{ .x = size.x }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .x = uvSize.x }),
.color = color,
};
vertices[2] = .{
.position = topLeft.add(size),
.normal = normal,
.uv = topLeftUV.add(uvSize),
.color = color,
};
vertices[3] = .{
.position = topLeft.add(.{ .y = size.y }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .y = uvSize.y }),
.color = color,
};
self.addVertexList(&vertices);
}
pub fn deinit(self: *@This()) void {
self.allocator.free(self.vertices);
const vkAllocator = self.gc.vkAllocator;
self.stagingVertexBuffer.deinit(vkAllocator);
self.stagingIndexBuffer.deinit(vkAllocator);
for (0..self.indexBuffers.len) |i| {
self.indexBuffers[i].deinit(vkAllocator);
self.vertexBuffers[i].deinit(vkAllocator);
}
self.allocator.destroy(self);
// should remove ourselves from the manager
}
};

View File

@ -1,174 +1,174 @@
pub fn MakeCubeMapList(
comptime left: []const u8,
comptime up: []const u8,
comptime down: []const u8,
comptime front: []const u8,
comptime back: []const u8,
) []const []const u8 {
return &.{
left,
up,
down,
front,
back,
};
}
pub const CubeMapDirs = enum(u8) {
right,
left,
up,
down,
front,
back,
};
const vk_utils = @import("../vk_utils.zig");
const LoadAndStageImage = vk_utils.LoadAndStageImage;
pub fn stageCubeTexture(list: []const []const u8) !LoadAndStageImage {
try core.assert(list.len == 6);
const gc = graphics.getContext();
const allocator = gc.allocator;
var pngs: [6]core.png.PngContents = undefined;
for (0..6) |i| {
pngs[i] = try core.png.PngContents.initFromPathSpec(list[i], allocator);
}
defer {
for (&pngs) |*png| {
png.deinit();
}
}
const width = pngs[0].size.x;
const height = pngs[0].size.y;
try core.assert(width == height);
core.engine_log("cubemap dimensionss {d}x{d}", .{ width, height });
var totalLen: u32 = 0;
for (pngs) |png| {
try core.assertf(width == png.size.x, "inconsistent cubemap dimensions", .{});
try core.assertf(height == png.size.y, "inconsistent cubemap dimensions", .{});
totalLen += @intCast(png.pixels.len);
}
const stagingBuffer = try gc.vkAllocator.createStagingBuffer(totalLen, "cubemap creation staging texture map");
const pixelBuffer = try gc.vkAllocator.mapBuffer(u8, stagingBuffer);
var offset: u32 = 0;
var bufferOffsets: [6]u32 = undefined;
for (pngs, 0..) |png, i| {
const dest = pixelBuffer[offset .. offset + png.pixels.len];
bufferOffsets[i] = offset;
offset += @intCast(png.pixels.len);
@memcpy(dest, png.pixels);
}
const imageExtent = vk.Extent3D{
.width = @as(u32, @intCast(width)),
.height = @as(u32, @intCast(height)),
.depth = 1,
};
//const mipLevel = std.math.log2(@max(imageExtent.width, imageExtent.height)) + 1;
const mipLevel = 1;
var imgCreateInfo = vkinit.imageCreateInfo(.r8g8b8a8_srgb, .{
.sampled_bit = true,
.transfer_dst_bit = true,
}, imageExtent, mipLevel);
imgCreateInfo.array_layers = 6;
imgCreateInfo.flags.cube_compatible_bit = true;
if (mipLevel > 1) {
imgCreateInfo.usage.transfer_src_bit = true;
}
const imgAllocInfo = vma.AllocationCreateInfo{
.requiredFlags = .{},
.usage = .gpuOnly,
};
const newImage = try gc.vkAllocator.createImage(imgCreateInfo, imgAllocInfo, "cubemap creation image");
gc.vkAllocator.unmapMemory(stagingBuffer);
return .{
.stagingBuffer = stagingBuffer,
.image = newImage,
.mipLevel = mipLevel,
.cubeOffsets = bufferOffsets,
};
}
pub fn submitTextureCube(uploader: *vk_utils.NeonVkUploader, state: *const LoadAndStageImage) !void {
try core.assert(state.cubeOffsets != null);
if (state.cubeOffsets) |cubeOffsets| {
try uploader.startUploadContext();
{
const newImage = state.image;
const mipLevel = state.mipLevel;
const cmd = uploader.commandBuffer;
transitions.into_transferDst(cmd, newImage.image, mipLevel, 0, 6);
for (cubeOffsets, 0..) |offset, face| {
var copyRegion = vk.BufferImageCopy{
.buffer_offset = offset,
.buffer_row_length = 0,
.buffer_image_height = 0,
.image_offset = std.mem.zeroes(vk.Offset3D),
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = @intCast(face),
.layer_count = 1,
},
.image_extent = .{
.width = newImage.pixelWidth,
.height = newImage.pixelHeight,
.depth = 1,
},
};
vkd.cmdCopyBufferToImage(
cmd,
state.stagingBuffer.buffer,
newImage.image,
.transfer_dst_optimal,
1,
@ptrCast(&copyRegion),
);
try vk_utils.generateMipMaps(cmd, newImage, mipLevel, 0);
}
transitions.transferDst_into_shaderReadOnly(cmd, newImage.image, mipLevel, 0, 6);
}
try uploader.finishUploadContext();
}
}
pub fn createDescriptorSet(
dev: vk.Device,
) struct {
layout: vk.DescriptorSetLayout,
descriptorSet: vk.DescriptorSet,
} {
_ = dev;
}
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const vma = @import("vma");
const graphics = @import("../graphics.zig");
const vk = @import("vulkan");
const vkinit = @import("../vk_init.zig");
const vk_constants = @import("../vk_constants.zig");
const std = @import("std");
const vkd = vk_api.vkd;
const vk_api = @import("../vk_api.zig");
const transitions = @import("../vk_transitions.zig");
pub fn MakeCubeMapList(
comptime left: []const u8,
comptime up: []const u8,
comptime down: []const u8,
comptime front: []const u8,
comptime back: []const u8,
) []const []const u8 {
return &.{
left,
up,
down,
front,
back,
};
}
pub const CubeMapDirs = enum(u8) {
right,
left,
up,
down,
front,
back,
};
const vk_utils = @import("../vk_utils.zig");
const LoadAndStageImage = vk_utils.LoadAndStageImage;
pub fn stageCubeTexture(list: []const []const u8) !LoadAndStageImage {
try core.assert(list.len == 6);
const gc = graphics.getContext();
const allocator = gc.allocator;
var pngs: [6]core.png.PngContents = undefined;
for (0..6) |i| {
pngs[i] = try core.png.PngContents.initFromPathSpec(list[i], allocator);
}
defer {
for (&pngs) |*png| {
png.deinit();
}
}
const width = pngs[0].size.x;
const height = pngs[0].size.y;
try core.assert(width == height);
core.engine_log("cubemap dimensionss {d}x{d}", .{ width, height });
var totalLen: u32 = 0;
for (pngs) |png| {
try core.assertf(width == png.size.x, "inconsistent cubemap dimensions", .{});
try core.assertf(height == png.size.y, "inconsistent cubemap dimensions", .{});
totalLen += @intCast(png.pixels.len);
}
const stagingBuffer = try gc.vkAllocator.createStagingBuffer(totalLen, "cubemap creation staging texture map");
const pixelBuffer = try gc.vkAllocator.mapBuffer(u8, stagingBuffer);
var offset: u32 = 0;
var bufferOffsets: [6]u32 = undefined;
for (pngs, 0..) |png, i| {
const dest = pixelBuffer[offset .. offset + png.pixels.len];
bufferOffsets[i] = offset;
offset += @intCast(png.pixels.len);
@memcpy(dest, png.pixels);
}
const imageExtent = vk.Extent3D{
.width = @as(u32, @intCast(width)),
.height = @as(u32, @intCast(height)),
.depth = 1,
};
//const mipLevel = std.math.log2(@max(imageExtent.width, imageExtent.height)) + 1;
const mipLevel = 1;
var imgCreateInfo = vkinit.imageCreateInfo(.r8g8b8a8_srgb, .{
.sampled_bit = true,
.transfer_dst_bit = true,
}, imageExtent, mipLevel);
imgCreateInfo.array_layers = 6;
imgCreateInfo.flags.cube_compatible_bit = true;
if (mipLevel > 1) {
imgCreateInfo.usage.transfer_src_bit = true;
}
const imgAllocInfo = vma.AllocationCreateInfo{
.requiredFlags = .{},
.usage = .gpuOnly,
};
const newImage = try gc.vkAllocator.createImage(imgCreateInfo, imgAllocInfo, "cubemap creation image");
gc.vkAllocator.unmapMemory(stagingBuffer);
return .{
.stagingBuffer = stagingBuffer,
.image = newImage,
.mipLevel = mipLevel,
.cubeOffsets = bufferOffsets,
};
}
pub fn submitTextureCube(uploader: *vk_utils.NeonVkUploader, state: *const LoadAndStageImage) !void {
try core.assert(state.cubeOffsets != null);
if (state.cubeOffsets) |cubeOffsets| {
try uploader.startUploadContext();
{
const newImage = state.image;
const mipLevel = state.mipLevel;
const cmd = uploader.commandBuffer;
transitions.into_transferDst(cmd, newImage.image, mipLevel, 0, 6);
for (cubeOffsets, 0..) |offset, face| {
var copyRegion = vk.BufferImageCopy{
.buffer_offset = offset,
.buffer_row_length = 0,
.buffer_image_height = 0,
.image_offset = std.mem.zeroes(vk.Offset3D),
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = @intCast(face),
.layer_count = 1,
},
.image_extent = .{
.width = newImage.pixelWidth,
.height = newImage.pixelHeight,
.depth = 1,
},
};
vkd.cmdCopyBufferToImage(
cmd,
state.stagingBuffer.buffer,
newImage.image,
.transfer_dst_optimal,
1,
@ptrCast(&copyRegion),
);
try vk_utils.generateMipMaps(cmd, newImage, mipLevel, 0);
}
transitions.transferDst_into_shaderReadOnly(cmd, newImage.image, mipLevel, 0, 6);
}
try uploader.finishUploadContext();
}
}
pub fn createDescriptorSet(
dev: vk.Device,
) struct {
layout: vk.DescriptorSetLayout,
descriptorSet: vk.DescriptorSet,
} {
_ = dev;
}
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const vma = @import("vma");
const graphics = @import("../graphics.zig");
const vk = @import("vulkan");
const vkinit = @import("../vk_init.zig");
const vk_constants = @import("../vk_constants.zig");
const std = @import("std");
const vkd = vk_api.vkd;
const vk_api = @import("../vk_api.zig");
const transitions = @import("../vk_transitions.zig");

View File

@ -1,72 +1,72 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const vk_allocator = @import("../vk_allocator.zig");
const vk_constants = @import("../vk_constants.zig");
pub const NeonVkQueue = struct {
handle: vk.Queue,
family: u32,
pub fn init(vkd: vk_constants.DeviceDispatch, dev: vk.Device, family: u32, index: u32) @This() {
return .{
.handle = vkd.getDeviceQueue(dev, family, index),
.family = family,
};
}
};
pub const NeonVkFrameData = struct {
// descriptors
globalDescriptorSet: vk.DescriptorSet,
objectDescriptorSet: vk.DescriptorSet,
spriteDescriptorSet: vk.DescriptorSet,
// buffers
spriteBuffer: vk_allocator.NeonVkBuffer,
objectBuffer: vk_allocator.NeonVkBuffer,
animationsBuffer: vk_allocator.NeonVkBuffer,
cameraBuffer: vk_allocator.NeonVkBuffer,
};
pub const triangle_mesh_vert = @import("triangle_mesh_vert");
pub const NeonVkObjectDataGpu = triangle_mesh_vert.ObjectData;
pub const VertexBoneData = triangle_mesh_vert.VertexBoneData;
pub const NeonVkSceneDataGpu = struct {
fogColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
fogDistances: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
ambientColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightDirection: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
};
pub const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
.{ .type = .uniform_buffer, .descriptor_count = 1000 },
.{ .type = .uniform_buffer_dynamic, .descriptor_count = 1000 },
.{ .type = .storage_buffer, .descriptor_count = 1000 },
.{ .type = .combined_image_sampler, .descriptor_count = 2000 },
.{ .type = .sampler, .descriptor_count = 1000 },
.{ .type = .sampled_image, .descriptor_count = 1000 },
.{ .type = .storage_image, .descriptor_count = 1000 },
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
};
pub const NeonVkSwapImage = struct {
image: vk.Image,
view: vk.ImageView,
imageIndex: usize,
pub fn deinit(self: *NeonVkSwapImage, vkd: vk_constants.DeviceDispatch, dev: vk.Device) void {
vkd.destroyImageView(dev, self.view, null);
}
};
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const vk_allocator = @import("../vk_allocator.zig");
const vk_constants = @import("../vk_constants.zig");
pub const NeonVkQueue = struct {
handle: vk.Queue,
family: u32,
pub fn init(vkd: vk_constants.DeviceDispatch, dev: vk.Device, family: u32, index: u32) @This() {
return .{
.handle = vkd.getDeviceQueue(dev, family, index),
.family = family,
};
}
};
pub const NeonVkFrameData = struct {
// descriptors
globalDescriptorSet: vk.DescriptorSet,
objectDescriptorSet: vk.DescriptorSet,
spriteDescriptorSet: vk.DescriptorSet,
// buffers
spriteBuffer: vk_allocator.NeonVkBuffer,
objectBuffer: vk_allocator.NeonVkBuffer,
animationsBuffer: vk_allocator.NeonVkBuffer,
cameraBuffer: vk_allocator.NeonVkBuffer,
};
pub const triangle_mesh_vert = @import("triangle_mesh_vert");
pub const NeonVkObjectDataGpu = triangle_mesh_vert.ObjectData;
pub const VertexBoneData = triangle_mesh_vert.VertexBoneData;
pub const NeonVkSceneDataGpu = struct {
fogColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
fogDistances: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
ambientColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightDirection: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
};
pub const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
.{ .type = .uniform_buffer, .descriptor_count = 1000 },
.{ .type = .uniform_buffer_dynamic, .descriptor_count = 1000 },
.{ .type = .storage_buffer, .descriptor_count = 1000 },
.{ .type = .combined_image_sampler, .descriptor_count = 2000 },
.{ .type = .sampler, .descriptor_count = 1000 },
.{ .type = .sampled_image, .descriptor_count = 1000 },
.{ .type = .storage_image, .descriptor_count = 1000 },
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
};
pub const NeonVkSwapImage = struct {
image: vk.Image,
view: vk.ImageView,
imageIndex: usize,
pub fn deinit(self: *NeonVkSwapImage, vkd: vk_constants.DeviceDispatch, dev: vk.Device) void {
vkd.destroyImageView(dev, self.view, null);
}
};

View File

@ -1,44 +1,44 @@
pub const SkeletalBuffers = struct {
descriptorSet: vk.DescriptorSet,
gc: *NeonVkContext,
vkAllocator: *NeonVkAllocator,
allocator: std.mem.Allocator,
finalsBuffer: [2]NeonVkBuffer = undefined,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.gc = gc,
.allocator = gc.allocator,
.vkAllocator = gc.vkAllocator,
.skeletalPipeData = undefined,
};
self.buildBuffers();
return self;
}
pub fn buildBuffers(self: *@This()) !void {
const vkAllocator: *NeonVkAllocator = self.vkAllocator;
// 100k animated skeletal mesh vertices ought to be enough for anyone right?
for (0..2) |i| {
self.finalsBuffer[i] = try vkAllocator.createSsboBuffer(@sizeOf(core.Mat) * vk_constants.MAX_SKIN_SLOTS, "bones buffer.");
}
}
};
const std = @import("std");
const core = @import("core");
const assets = @import("assets");
const graphics = @import("../graphics.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const VertexBoneData = vk_renderer_types.VertexBoneData;
const gpd = graphics.gpu_pipe_data;
const NeonVkContext = graphics.NeonVkContext;
const NeonVkBuffer = graphics.NeonVkBuffer;
const NeonVkAllocator = graphics.NeonVkAllocator;
const vk = @import("vulkan");
const vk_constants = @import("../vk_constants.zig");
pub const SkeletalBuffers = struct {
descriptorSet: vk.DescriptorSet,
gc: *NeonVkContext,
vkAllocator: *NeonVkAllocator,
allocator: std.mem.Allocator,
finalsBuffer: [2]NeonVkBuffer = undefined,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.gc = gc,
.allocator = gc.allocator,
.vkAllocator = gc.vkAllocator,
.skeletalPipeData = undefined,
};
self.buildBuffers();
return self;
}
pub fn buildBuffers(self: *@This()) !void {
const vkAllocator: *NeonVkAllocator = self.vkAllocator;
// 100k animated skeletal mesh vertices ought to be enough for anyone right?
for (0..2) |i| {
self.finalsBuffer[i] = try vkAllocator.createSsboBuffer(@sizeOf(core.Mat) * vk_constants.MAX_SKIN_SLOTS, "bones buffer.");
}
}
};
const std = @import("std");
const core = @import("core");
const assets = @import("assets");
const graphics = @import("../graphics.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const VertexBoneData = vk_renderer_types.VertexBoneData;
const gpd = graphics.gpu_pipe_data;
const NeonVkContext = graphics.NeonVkContext;
const NeonVkBuffer = graphics.NeonVkBuffer;
const NeonVkAllocator = graphics.NeonVkAllocator;
const vk = @import("vulkan");
const vk_constants = @import("../vk_constants.zig");

View File

@ -1,87 +1,87 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_constants = @import("../vk_constants.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const force_mailbox = core.BuildOption("force_mailbox");
pub fn findSurfaceFormat(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.SurfaceFormatKHR {
const preferred = vk.SurfaceFormatKHR{
.format = .b8g8r8a8_srgb,
.color_space = .srgb_nonlinear_khr,
};
var count: u32 = 0;
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, null);
const surface_formats = try allocator.alloc(vk.SurfaceFormatKHR, count);
defer allocator.free(surface_formats);
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, surface_formats.ptr);
for (surface_formats) |sfmt| {
if (std.meta.eql(sfmt, preferred)) {
return preferred;
}
}
const rv = surface_formats[0];
core.graphics_log("Selected surface format\n {any}", .{rv});
return rv;
}
pub fn findPresentMode(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.PresentModeKHR {
var count: u32 = undefined;
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, null);
const present_modes = try allocator.alloc(vk.PresentModeKHR, count);
defer allocator.free(present_modes);
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, present_modes.ptr);
const preferred = [_]vk.PresentModeKHR{
.fifo_khr,
.mailbox_khr,
.immediate_khr,
};
if (force_mailbox) {
return .mailbox_khr;
}
for (preferred) |mode| {
if (std.mem.indexOfScalar(vk.PresentModeKHR, present_modes, mode) != null) {
return mode;
}
}
return error.UnableToFindPresentMode;
}
pub fn findActualExtent(
extent: vk.Extent2D,
caps: vk.SurfaceCapabilitiesKHR,
) !vk.Extent2D {
if (caps.current_extent.width != 0xFFFF_FFFF) {
return caps.current_extent;
} else {
return .{
.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width),
.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height),
};
}
}
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_constants = @import("../vk_constants.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const force_mailbox = core.BuildOption("force_mailbox");
pub fn findSurfaceFormat(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.SurfaceFormatKHR {
const preferred = vk.SurfaceFormatKHR{
.format = .b8g8r8a8_srgb,
.color_space = .srgb_nonlinear_khr,
};
var count: u32 = 0;
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, null);
const surface_formats = try allocator.alloc(vk.SurfaceFormatKHR, count);
defer allocator.free(surface_formats);
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, surface_formats.ptr);
for (surface_formats) |sfmt| {
if (std.meta.eql(sfmt, preferred)) {
return preferred;
}
}
const rv = surface_formats[0];
core.graphics_log("Selected surface format\n {any}", .{rv});
return rv;
}
pub fn findPresentMode(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.PresentModeKHR {
var count: u32 = undefined;
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, null);
const present_modes = try allocator.alloc(vk.PresentModeKHR, count);
defer allocator.free(present_modes);
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, present_modes.ptr);
const preferred = [_]vk.PresentModeKHR{
.fifo_khr,
.mailbox_khr,
.immediate_khr,
};
if (force_mailbox) {
return .mailbox_khr;
}
for (preferred) |mode| {
if (std.mem.indexOfScalar(vk.PresentModeKHR, present_modes, mode) != null) {
return mode;
}
}
return error.UnableToFindPresentMode;
}
pub fn findActualExtent(
extent: vk.Extent2D,
caps: vk.SurfaceCapabilitiesKHR,
) !vk.Extent2D {
if (caps.current_extent.width != 0xFFFF_FFFF) {
return caps.current_extent;
} else {
return .{
.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width),
.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height),
};
}
}

View File

@ -1,128 +1,128 @@
// this implements the global texture list
const gTextureList: *TextureList = undefined;
pub const ArrayedTexture = struct {};
pub const TextureList = struct {
allocator: std.mem.Allocator,
textures: std.AutoHashMapUnmanaged(u32, *Texture),
gc: *NeonVkContext,
listSet: vk.DescriptorSet = undefined,
dsl: vk.DescriptorSetLayout = undefined,
// descriptorPool: vk.DescriptorPool = undefined,
// const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
// };
pub fn create(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.allocator = gc.allocator,
.textures = .{},
.gc = gc,
};
try self.initTextureList();
return self;
}
pub fn initTextureList(self: *@This()) !void {
// var poolInfo = vk.DescriptorPoolCreateInfo{
// .flags = .{},
// .max_sets = 1000,
// .pool_size_count = @intCast(descriptorPoolSizes.len),
// .p_pool_sizes = &descriptorPoolSizes,
// };
// self.descriptorPool = try vkd.createDescriptorPool(self.gc.dev, &poolInfo, null);
const bindings = [_]vk.DescriptorSetLayoutBinding{
.{
.binding = 0,
.descriptor_type = .storage_buffer,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 1,
.descriptor_type = .combined_image_sampler,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 2,
.descriptor_type = .storage_image,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
};
const flags = [_]vk.DescriptorBindingFlags{
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
};
const fci = vk.DescriptorSetLayoutBindingFlagsCreateInfo{ .binding_count = 3, .p_binding_flags = @ptrCast(&flags) };
const dsci = vk.DescriptorSetLayoutCreateInfo{
.flags = .{},
.binding_count = bindings.len,
.p_bindings = @ptrCast(&bindings),
.p_next = &fci,
};
self.dsl = try vkd.createDescriptorSetLayout(self.gc.dev, &dsci, null);
const dsai = vk.DescriptorSetAllocateInfo{
.descriptor_pool = self.gc.descriptorPool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast(&self.dsl),
};
try vkd.allocateDescriptorSets(self.gc.dev, &dsai, @ptrCast(&self.listSet));
}
pub fn destroy(self: *@This()) void {
vkd.destroyDescriptorSetLayout(self.gc.dev, self.dsl, null);
// vkd.destroyDescriptorPool(self.gc.dev, self.descriptorPool, null);
self.textures.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const graphics = @import("../graphics.zig");
const NeonVkContext = graphics.NeonVkContext;
const texture = @import("../texture.zig");
const Texture = texture.Texture;
const vk = @import("vulkan");
const std = @import("std");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
// this implements the global texture list
const gTextureList: *TextureList = undefined;
pub const ArrayedTexture = struct {};
pub const TextureList = struct {
allocator: std.mem.Allocator,
textures: std.AutoHashMapUnmanaged(u32, *Texture),
gc: *NeonVkContext,
listSet: vk.DescriptorSet = undefined,
dsl: vk.DescriptorSetLayout = undefined,
// descriptorPool: vk.DescriptorPool = undefined,
// const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
// };
pub fn create(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.allocator = gc.allocator,
.textures = .{},
.gc = gc,
};
try self.initTextureList();
return self;
}
pub fn initTextureList(self: *@This()) !void {
// var poolInfo = vk.DescriptorPoolCreateInfo{
// .flags = .{},
// .max_sets = 1000,
// .pool_size_count = @intCast(descriptorPoolSizes.len),
// .p_pool_sizes = &descriptorPoolSizes,
// };
// self.descriptorPool = try vkd.createDescriptorPool(self.gc.dev, &poolInfo, null);
const bindings = [_]vk.DescriptorSetLayoutBinding{
.{
.binding = 0,
.descriptor_type = .storage_buffer,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 1,
.descriptor_type = .combined_image_sampler,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 2,
.descriptor_type = .storage_image,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
};
const flags = [_]vk.DescriptorBindingFlags{
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
};
const fci = vk.DescriptorSetLayoutBindingFlagsCreateInfo{ .binding_count = 3, .p_binding_flags = @ptrCast(&flags) };
const dsci = vk.DescriptorSetLayoutCreateInfo{
.flags = .{},
.binding_count = bindings.len,
.p_bindings = @ptrCast(&bindings),
.p_next = &fci,
};
self.dsl = try vkd.createDescriptorSetLayout(self.gc.dev, &dsci, null);
const dsai = vk.DescriptorSetAllocateInfo{
.descriptor_pool = self.gc.descriptorPool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast(&self.dsl),
};
try vkd.allocateDescriptorSets(self.gc.dev, &dsai, @ptrCast(&self.listSet));
}
pub fn destroy(self: *@This()) void {
vkd.destroyDescriptorSetLayout(self.gc.dev, self.dsl, null);
// vkd.destroyDescriptorPool(self.gc.dev, self.descriptorPool, null);
self.textures.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const graphics = @import("../graphics.zig");
const NeonVkContext = graphics.NeonVkContext;
const texture = @import("../texture.zig");
const Texture = texture.Texture;
const vk = @import("vulkan");
const std = @import("std");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;

View File

@ -1,35 +1,35 @@
const vk_constants = @import("../vk_constants.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const vk = @import("vulkan");
const graphics = @import("../graphics.zig");
const NeonVkBuffer = graphics.NeonVkBuffer;
pub fn copyStagingSlice(
comptime Element: type,
cmd: vk.CommandBuffer,
params: struct {
src: *NeonVkBuffer,
dst: *NeonVkBuffer,
size: u32, // in element count
src_offset: u32 = 0, // in element counts
dst_offset: u32 = 0, // in element counts
},
) void {
const elementSize = @sizeOf(Element);
var copy = vk.BufferCopy{
.dst_offset = params.dst_offset * elementSize,
.src_offset = params.src_offset * elementSize,
.size = params.size * elementSize,
};
vkd.cmdCopyBuffer(
cmd,
params.src.buffer,
params.dst.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}
const vk_constants = @import("../vk_constants.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const vk = @import("vulkan");
const graphics = @import("../graphics.zig");
const NeonVkBuffer = graphics.NeonVkBuffer;
pub fn copyStagingSlice(
comptime Element: type,
cmd: vk.CommandBuffer,
params: struct {
src: *NeonVkBuffer,
dst: *NeonVkBuffer,
size: u32, // in element count
src_offset: u32 = 0, // in element counts
dst_offset: u32 = 0, // in element counts
},
) void {
const elementSize = @sizeOf(Element);
var copy = vk.BufferCopy{
.dst_offset = params.dst_offset * elementSize,
.src_offset = params.src_offset * elementSize,
.size = params.size * elementSize,
};
vkd.cmdCopyBuffer(
cmd,
params.src.buffer,
params.dst.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}

View File

@ -1,11 +1,11 @@
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics.zig");
const PixelBufferRGA8 = @import("PixelBufferRGBA8.zig");
pub fn updateTextureFromPixelsSync(
textureName: core.Name,
pixelBuffer: PixelBufferRGA8,
) void {
graphics.getContext().updateTextureFromPixelsSync(textureName, pixelBuffer);
}
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics.zig");
const PixelBufferRGA8 = @import("PixelBufferRGBA8.zig");
pub fn updateTextureFromPixelsSync(
textureName: core.Name,
pixelBuffer: PixelBufferRGA8,
) void {
graphics.getContext().updateTextureFromPixelsSync(textureName, pixelBuffer);
}

View File

@ -1,95 +1,95 @@
// REEEEEEEEEEEEEE
const std = @import("std");
const core = @import("core");
const vk = @import("vulkan");
const vma = @import("vma");
const vk_constants = @import("vk_constants.zig");
const vk_api = @import("vk_api.zig");
const vkd = vk_api.vkd;
pub fn transferDst_into_shaderReadOnly(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_shaderReadOnly");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toReadable = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .shader_read_only_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{ .transfer_write_bit = true },
.dst_access_mask = .{ .shader_read_bit = false },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .fragment_shader_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toReadable),
);
}
pub fn into_transferDst(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_transferDst");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toTransfer = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{},
.dst_access_mask = .{ .transfer_write_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .top_of_pipe_bit = true },
.{ .transfer_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toTransfer),
);
}
// REEEEEEEEEEEEEE
const std = @import("std");
const core = @import("core");
const vk = @import("vulkan");
const vma = @import("vma");
const vk_constants = @import("vk_constants.zig");
const vk_api = @import("vk_api.zig");
const vkd = vk_api.vkd;
pub fn transferDst_into_shaderReadOnly(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_shaderReadOnly");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toReadable = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .shader_read_only_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{ .transfer_write_bit = true },
.dst_access_mask = .{ .shader_read_bit = false },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .fragment_shader_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toReadable),
);
}
pub fn into_transferDst(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_transferDst");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toTransfer = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{},
.dst_access_mask = .{ .transfer_write_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .top_of_pipe_bit = true },
.{ .transfer_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toTransfer),
);
}

View File

@ -1,143 +1,143 @@
// Game: Deathwish
// Format: Standard
// entity 0
{
"classname" "worldspawn"
// brush 0
{
( -224 -32 -16 ) ( -224 -31 -16 ) ( -224 -32 -15 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -224 -32 -15 ) ( -223 -32 -16 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -223 -32 -16 ) ( -224 -31 -16 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -192 33 0 ) ( -191 32 0 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -191 32 0 ) ( -192 32 1 ) ProtoFloor 0 -16 0 1 1
( -64 32 0 ) ( -64 32 1 ) ( -64 33 0 ) ProtoFloor 0 -16 0 1 1
}
// brush 1
{
( -576 -160 -32 ) ( -576 -159 -32 ) ( -576 -160 -31 ) ProtoGrass 16 -16 0 1 1
( 0 -400 -32 ) ( 0 -400 -31 ) ( 1 -400 -32 ) ProtoGrass -16 -16 0 1 1
( 0 -160 -32 ) ( 1 -160 -32 ) ( 0 -159 -32 ) ProtoGrass -16 -16 0 1 1
( 48 -144 -16 ) ( 48 -143 -16 ) ( 49 -144 -16 ) ProtoGrass -16 -16 0 1 1
( 48 224 -16 ) ( 49 224 -16 ) ( 48 224 -15 ) ProtoGrass -16 -16 0 1 1
( 224 -144 -16 ) ( 224 -144 -15 ) ( 224 -143 -16 ) ProtoGrass 16 -16 0 1 1
}
// brush 2
{
( -48 -144 -32 ) ( -48 -143 -32 ) ( -48 -144 -31 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -48 -144 -31 ) ( -47 -144 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -47 -144 -32 ) ( -48 -143 -32 ) ProtoFloor 0 0 0 1 1
( 80 -64 0 ) ( 80 -63 0 ) ( 81 -64 0 ) ProtoFloor 0 0 0 1 1
( 80 -64 -16 ) ( 81 -64 -16 ) ( 80 -64 -15 ) ProtoFloor 0 -32 0 1 1
( 80 -64 -16 ) ( 80 -64 -15 ) ( 80 -63 -16 ) ProtoFloor 0 -32 0 1 1
}
// brush 3
{
( -48 -48 -32 ) ( -48 -47 -32 ) ( -48 -48 -31 ) ProtoFloor 32 -32 0 1 1
( -48 -48 -32 ) ( -48 -48 -31 ) ( -47 -48 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -48 -32 ) ( -47 -48 -32 ) ( -48 -47 -32 ) ProtoFloor 0 -32 0 1 1
( 80 32 16 ) ( 80 33 16 ) ( 81 32 16 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 81 32 -16 ) ( 80 32 -15 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 80 32 -15 ) ( 80 33 -16 ) ProtoFloor 32 -32 0 1 1
}
// brush 4
{
( -448 -96 -16 ) ( -448 -95 -16 ) ( -448 -96 -15 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -448 -96 -15 ) ( -447 -96 -16 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -447 -96 -16 ) ( -448 -95 -16 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 48 ) ( -432 81 48 ) ( -431 80 48 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -431 80 0 ) ( -432 80 1 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -432 80 1 ) ( -432 81 0 ) ProtoWallsGrey 0 0 0 1 1
}
// brush 5
{
( -416 -256 -16 ) ( -416 -255 -16 ) ( -416 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -496 -256 -15 ) ( -495 -256 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -495 -256 -16 ) ( -496 -255 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 96 ) ( -304 -239 96 ) ( -303 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -303 -240 0 ) ( -304 -240 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -304 -240 1 ) ( -304 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 6
{
( -320 -384 -16 ) ( -320 -383 -16 ) ( -320 -384 -15 ) ProtoWallsOrange 0 0 0 1 1
( -320 -320 -16 ) ( -320 -320 -15 ) ( -319 -320 -16 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 -16 ) ( -319 -384 -16 ) ( -320 -383 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 96 ) ( -304 -255 96 ) ( -303 -256 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -303 -256 0 ) ( -304 -256 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -304 -256 1 ) ( -304 -255 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 7
{
( -480 -256 80 ) ( -480 -255 80 ) ( -480 -256 81 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 80 ) ( -432 -256 81 ) ( -431 -256 80 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 64 ) ( -431 -256 64 ) ( -432 -255 64 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -239 96 ) ( -415 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -415 -240 96 ) ( -416 -240 97 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -240 97 ) ( -416 -239 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 8
{
( -576 -256 -16 ) ( -576 -255 -16 ) ( -576 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -672 -256 -16 ) ( -672 -256 -15 ) ( -671 -256 -16 ) ProtoWallsOrange -16 0 0 1 1
( -672 -256 -16 ) ( -671 -256 -16 ) ( -672 -255 -16 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 96 ) ( -480 -239 96 ) ( -479 -240 96 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -479 -240 0 ) ( -480 -240 1 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -480 -240 1 ) ( -480 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 9
{
( -416 -400 -16 ) ( -416 -399 -16 ) ( -416 -400 -15 ) ProtoWallsOrange 16 0 0 1 1
( -496 -400 -16 ) ( -496 -400 -15 ) ( -495 -400 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -400 -16 ) ( -495 -400 -16 ) ( -496 -399 -16 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 96 ) ( -304 -383 96 ) ( -303 -384 96 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 0 ) ( -303 -384 0 ) ( -304 -384 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -384 0 ) ( -304 -384 1 ) ( -304 -383 0 ) ProtoWallsOrange 16 0 0 1 1
}
// brush 10
{
( -320 -384 80 ) ( -320 -383 80 ) ( -320 -384 81 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 80 ) ( -320 -384 81 ) ( -319 -384 80 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 64 ) ( -319 -384 64 ) ( -320 -383 64 ) ProtoWallsOrange 0 0 0 1 1
( -288 -352 96 ) ( -288 -351 96 ) ( -287 -352 96 ) ProtoWallsOrange 0 0 0 1 1
( -288 -320 96 ) ( -287 -320 96 ) ( -288 -320 97 ) ProtoWallsOrange 0 0 0 1 1
( -304 -352 96 ) ( -304 -352 97 ) ( -304 -351 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 11
{
( -208 -304 -16 ) ( -208 -303 -16 ) ( -208 -304 -15 ) ProtoWallsGrey 32 0 0 1 1
( -208 -304 -16 ) ( -208 -304 -15 ) ( -207 -304 -16 ) ProtoWallsGrey -16 0 0 1 1
( -208 -304 -16 ) ( -207 -304 -16 ) ( -208 -303 -16 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -256 80 ) ( -144 -255 80 ) ( -143 -256 80 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -208 0 ) ( -143 -208 0 ) ( -144 -208 1 ) ProtoWallsGrey -16 0 0 1 1
( -32 -256 0 ) ( -32 -256 1 ) ( -32 -255 0 ) ProtoWallsGrey 32 0 0 1 1
}
// brush 12
{
( -544 -384 96 ) ( -544 -383 96 ) ( -544 -384 97 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -544 -384 97 ) ( -543 -384 96 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -543 -384 96 ) ( -544 -383 96 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -255 112 ) ( -303 -256 112 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -303 -256 112 ) ( -304 -256 113 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -256 113 ) ( -304 -255 112 ) ProtoWallsGrey 0 0 0 1 1
}
}
// entity 1
{
"classname" "enemy_spawn"
"origin" "-480 64 8"
}
// entity 2
{
"classname" "info_player_start"
"origin" "176 -224 8"
}
// entity 3
{
"classname" "enemy_spawn"
"origin" "-480 -16 8"
}
// entity 4
{
"classname" "enemy_spawn"
"origin" "-352 -288 8"
}
// Game: Deathwish
// Format: Standard
// entity 0
{
"classname" "worldspawn"
// brush 0
{
( -224 -32 -16 ) ( -224 -31 -16 ) ( -224 -32 -15 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -224 -32 -15 ) ( -223 -32 -16 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -223 -32 -16 ) ( -224 -31 -16 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -192 33 0 ) ( -191 32 0 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -191 32 0 ) ( -192 32 1 ) ProtoFloor 0 -16 0 1 1
( -64 32 0 ) ( -64 32 1 ) ( -64 33 0 ) ProtoFloor 0 -16 0 1 1
}
// brush 1
{
( -576 -160 -32 ) ( -576 -159 -32 ) ( -576 -160 -31 ) ProtoGrass 16 -16 0 1 1
( 0 -400 -32 ) ( 0 -400 -31 ) ( 1 -400 -32 ) ProtoGrass -16 -16 0 1 1
( 0 -160 -32 ) ( 1 -160 -32 ) ( 0 -159 -32 ) ProtoGrass -16 -16 0 1 1
( 48 -144 -16 ) ( 48 -143 -16 ) ( 49 -144 -16 ) ProtoGrass -16 -16 0 1 1
( 48 224 -16 ) ( 49 224 -16 ) ( 48 224 -15 ) ProtoGrass -16 -16 0 1 1
( 224 -144 -16 ) ( 224 -144 -15 ) ( 224 -143 -16 ) ProtoGrass 16 -16 0 1 1
}
// brush 2
{
( -48 -144 -32 ) ( -48 -143 -32 ) ( -48 -144 -31 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -48 -144 -31 ) ( -47 -144 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -47 -144 -32 ) ( -48 -143 -32 ) ProtoFloor 0 0 0 1 1
( 80 -64 0 ) ( 80 -63 0 ) ( 81 -64 0 ) ProtoFloor 0 0 0 1 1
( 80 -64 -16 ) ( 81 -64 -16 ) ( 80 -64 -15 ) ProtoFloor 0 -32 0 1 1
( 80 -64 -16 ) ( 80 -64 -15 ) ( 80 -63 -16 ) ProtoFloor 0 -32 0 1 1
}
// brush 3
{
( -48 -48 -32 ) ( -48 -47 -32 ) ( -48 -48 -31 ) ProtoFloor 32 -32 0 1 1
( -48 -48 -32 ) ( -48 -48 -31 ) ( -47 -48 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -48 -32 ) ( -47 -48 -32 ) ( -48 -47 -32 ) ProtoFloor 0 -32 0 1 1
( 80 32 16 ) ( 80 33 16 ) ( 81 32 16 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 81 32 -16 ) ( 80 32 -15 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 80 32 -15 ) ( 80 33 -16 ) ProtoFloor 32 -32 0 1 1
}
// brush 4
{
( -448 -96 -16 ) ( -448 -95 -16 ) ( -448 -96 -15 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -448 -96 -15 ) ( -447 -96 -16 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -447 -96 -16 ) ( -448 -95 -16 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 48 ) ( -432 81 48 ) ( -431 80 48 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -431 80 0 ) ( -432 80 1 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -432 80 1 ) ( -432 81 0 ) ProtoWallsGrey 0 0 0 1 1
}
// brush 5
{
( -416 -256 -16 ) ( -416 -255 -16 ) ( -416 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -496 -256 -15 ) ( -495 -256 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -495 -256 -16 ) ( -496 -255 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 96 ) ( -304 -239 96 ) ( -303 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -303 -240 0 ) ( -304 -240 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -304 -240 1 ) ( -304 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 6
{
( -320 -384 -16 ) ( -320 -383 -16 ) ( -320 -384 -15 ) ProtoWallsOrange 0 0 0 1 1
( -320 -320 -16 ) ( -320 -320 -15 ) ( -319 -320 -16 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 -16 ) ( -319 -384 -16 ) ( -320 -383 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 96 ) ( -304 -255 96 ) ( -303 -256 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -303 -256 0 ) ( -304 -256 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -304 -256 1 ) ( -304 -255 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 7
{
( -480 -256 80 ) ( -480 -255 80 ) ( -480 -256 81 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 80 ) ( -432 -256 81 ) ( -431 -256 80 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 64 ) ( -431 -256 64 ) ( -432 -255 64 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -239 96 ) ( -415 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -415 -240 96 ) ( -416 -240 97 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -240 97 ) ( -416 -239 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 8
{
( -576 -256 -16 ) ( -576 -255 -16 ) ( -576 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -672 -256 -16 ) ( -672 -256 -15 ) ( -671 -256 -16 ) ProtoWallsOrange -16 0 0 1 1
( -672 -256 -16 ) ( -671 -256 -16 ) ( -672 -255 -16 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 96 ) ( -480 -239 96 ) ( -479 -240 96 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -479 -240 0 ) ( -480 -240 1 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -480 -240 1 ) ( -480 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 9
{
( -416 -400 -16 ) ( -416 -399 -16 ) ( -416 -400 -15 ) ProtoWallsOrange 16 0 0 1 1
( -496 -400 -16 ) ( -496 -400 -15 ) ( -495 -400 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -400 -16 ) ( -495 -400 -16 ) ( -496 -399 -16 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 96 ) ( -304 -383 96 ) ( -303 -384 96 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 0 ) ( -303 -384 0 ) ( -304 -384 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -384 0 ) ( -304 -384 1 ) ( -304 -383 0 ) ProtoWallsOrange 16 0 0 1 1
}
// brush 10
{
( -320 -384 80 ) ( -320 -383 80 ) ( -320 -384 81 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 80 ) ( -320 -384 81 ) ( -319 -384 80 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 64 ) ( -319 -384 64 ) ( -320 -383 64 ) ProtoWallsOrange 0 0 0 1 1
( -288 -352 96 ) ( -288 -351 96 ) ( -287 -352 96 ) ProtoWallsOrange 0 0 0 1 1
( -288 -320 96 ) ( -287 -320 96 ) ( -288 -320 97 ) ProtoWallsOrange 0 0 0 1 1
( -304 -352 96 ) ( -304 -352 97 ) ( -304 -351 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 11
{
( -208 -304 -16 ) ( -208 -303 -16 ) ( -208 -304 -15 ) ProtoWallsGrey 32 0 0 1 1
( -208 -304 -16 ) ( -208 -304 -15 ) ( -207 -304 -16 ) ProtoWallsGrey -16 0 0 1 1
( -208 -304 -16 ) ( -207 -304 -16 ) ( -208 -303 -16 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -256 80 ) ( -144 -255 80 ) ( -143 -256 80 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -208 0 ) ( -143 -208 0 ) ( -144 -208 1 ) ProtoWallsGrey -16 0 0 1 1
( -32 -256 0 ) ( -32 -256 1 ) ( -32 -255 0 ) ProtoWallsGrey 32 0 0 1 1
}
// brush 12
{
( -544 -384 96 ) ( -544 -383 96 ) ( -544 -384 97 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -544 -384 97 ) ( -543 -384 96 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -543 -384 96 ) ( -544 -383 96 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -255 112 ) ( -303 -256 112 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -303 -256 112 ) ( -304 -256 113 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -256 113 ) ( -304 -255 112 ) ProtoWallsGrey 0 0 0 1 1
}
}
// entity 1
{
"classname" "enemy_spawn"
"origin" "-480 64 8"
}
// entity 2
{
"classname" "info_player_start"
"origin" "176 -224 8"
}
// entity 3
{
"classname" "enemy_spawn"
"origin" "-480 -16 8"
}
// entity 4
{
"classname" "enemy_spawn"
"origin" "-352 -288 8"
}

View File

@ -1,46 +1,46 @@
const std = @import("std");
const graphics = @import("graphics");
const core = @import("core");
const platform = @import("platform");
const QuakeMap = graphics.QuakeMap;
test "simple_integration" {
// this doesn't really do anything other than just a simple compile check
std.debug.print("sizeof NeonVkContext = {d}\n", .{@sizeOf(graphics.NeonVkContext)});
std.debug.print("sizeof triangle_mesh_vert.ObjectData = {d}\n", .{@sizeOf(graphics.vk_renderer.triangle_mesh_vert.ObjectData)});
}
test "renderthread queue" {
const allocator = std.testing.allocator;
try core.start_module(.{}, .{}, allocator);
defer core.shutdown_module(allocator);
}
test "quake map loading" {
// const TestMap = @embedFile("testmap.map");
const testmapFile =
\\{
\\"spawnflags" "0"
\\"classname" "worldspawn"
\\"wad" "E:\q1maps\Q.wad"
\\{
\\( 256 64 16 ) ( 256 64 0 ) ( 256 0 16 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 64 0 ) ( 0 0 16 ) mmetal1_2 0 0 0 1 1
\\( 64 256 16 ) ( 0 256 16 ) ( 64 256 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 0 16 ) ( 64 0 0 ) mmetal1_2 0 0 0 1 1
\\( 64 64 0 ) ( 64 0 0 ) ( 0 64 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 -64 ) ( 64 0 -64 ) ( 0 64 -64 ) mmetal1_2 0 0 0 1 1
\\}
\\}
\\{
\\"spawnflags" "0"
\\"classname" "info_player_start"
\\"origin" "32 32 24"
\\}
;
var err: QuakeMap.ErrorInfo = undefined;
var map = try QuakeMap.read(std.testing.allocator, testmapFile, &err);
defer map.deinit();
}
const std = @import("std");
const graphics = @import("graphics");
const core = @import("core");
const platform = @import("platform");
const QuakeMap = graphics.QuakeMap;
test "simple_integration" {
// this doesn't really do anything other than just a simple compile check
std.debug.print("sizeof NeonVkContext = {d}\n", .{@sizeOf(graphics.NeonVkContext)});
std.debug.print("sizeof triangle_mesh_vert.ObjectData = {d}\n", .{@sizeOf(graphics.vk_renderer.triangle_mesh_vert.ObjectData)});
}
test "renderthread queue" {
const allocator = std.testing.allocator;
try core.start_module(.{}, .{}, allocator);
defer core.shutdown_module(allocator);
}
test "quake map loading" {
// const TestMap = @embedFile("testmap.map");
const testmapFile =
\\{
\\"spawnflags" "0"
\\"classname" "worldspawn"
\\"wad" "E:\q1maps\Q.wad"
\\{
\\( 256 64 16 ) ( 256 64 0 ) ( 256 0 16 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 64 0 ) ( 0 0 16 ) mmetal1_2 0 0 0 1 1
\\( 64 256 16 ) ( 0 256 16 ) ( 64 256 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 0 16 ) ( 64 0 0 ) mmetal1_2 0 0 0 1 1
\\( 64 64 0 ) ( 64 0 0 ) ( 0 64 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 -64 ) ( 64 0 -64 ) ( 0 64 -64 ) mmetal1_2 0 0 0 1 1
\\}
\\}
\\{
\\"spawnflags" "0"
\\"classname" "info_player_start"
\\"origin" "32 32 24"
\\}
;
var err: QuakeMap.ErrorInfo = undefined;
var map = try QuakeMap.read(std.testing.allocator, testmapFile, &err);
defer map.deinit();
}

View File

@ -0,0 +1,2 @@
plunder these ones for features to
implement in rend

View File

@ -1,17 +1,17 @@
.{
.name = "ui",
.version = "0.0.0",
.dependencies = .{
.vulkan = .{ .path = "../../lib/vulkan" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.papyrus = .{ .path = "../papyrus" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
},
.paths = .{
"",
},
}
.{
.name = "ui",
.version = "0.0.0",
.dependencies = .{
.vulkan = .{ .path = "../../lib/vulkan" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.papyrus = .{ .path = "../papyrus" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
},
.paths = .{
"",
},
}

View File

@ -1,69 +1,69 @@
#version 460
layout (location = 0) in vec4 color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) flat in int instanceId;
layout (location = 3) in vec2 pixelPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "FontSDFShared.glsl"
#include "FragmentHelpers.glsl"
void main() {
vec4 tex = texture(tex, texCoord);
uint isSdf = fontBuffer.fontInfo[instanceId].isSdf;
vec2 position = fontBuffer.fontInfo[instanceId].position;
vec2 size = fontBuffer.fontInfo[instanceId].size;
if(!scissor(pixelPosition, position, size))
{
discard;
}
if(isSdf == 1)
{
float dist = tex.r;
float width = fwidth(dist);
vec4 textColor = clamp(color, 0.0, 1.0);
float outerEdge = 1.0f - (120.0f / 255.0f);
float alpha = contour(dist, outerEdge, width);
float dscale = 0.354; // half of 1/sqrt2; you can play with this
vec2 uv = texCoord.xy;
vec2 duv = dscale * (dFdx(uv) + dFdy(uv));
vec4 box = vec4(uv - duv, uv + duv);
float asum = getSample(box.xy, outerEdge, width)
+ getSample(box.zw, outerEdge, width)
+ getSample(box.xw, outerEdge, width)
+ getSample(box.zy, outerEdge, width);
// weighted average, with 4 extra points having 0.5 weight each,
// so 1 + 0.5*4 = 3 is the divisor
alpha = (alpha + 0.5 * asum) / 3.0;
textColor = vec4(color.xyz, alpha);//textColor.* alpha);
textColor.xyz = pow(textColor.xyz, vec3(2.2)); // gamma correction
// Premultiplied alpha output.
outFragColor = textColor;
}
else {
float alpha = 1.0;
float gray = dot(color.xyz, vec3(0.2126, 0.7152, 0.0722));
outFragColor = vec4(color.xyz , pow(tex.x / gray, 1/(2.2)) );//textColor.* alpha);
//outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
/* debug test.
if(!rect(pixelPosition, position, size))
{
outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
*/
}
#version 460
layout (location = 0) in vec4 color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) flat in int instanceId;
layout (location = 3) in vec2 pixelPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "FontSDFShared.glsl"
#include "FragmentHelpers.glsl"
void main() {
vec4 tex = texture(tex, texCoord);
uint isSdf = fontBuffer.fontInfo[instanceId].isSdf;
vec2 position = fontBuffer.fontInfo[instanceId].position;
vec2 size = fontBuffer.fontInfo[instanceId].size;
if(!scissor(pixelPosition, position, size))
{
discard;
}
if(isSdf == 1)
{
float dist = tex.r;
float width = fwidth(dist);
vec4 textColor = clamp(color, 0.0, 1.0);
float outerEdge = 1.0f - (120.0f / 255.0f);
float alpha = contour(dist, outerEdge, width);
float dscale = 0.354; // half of 1/sqrt2; you can play with this
vec2 uv = texCoord.xy;
vec2 duv = dscale * (dFdx(uv) + dFdy(uv));
vec4 box = vec4(uv - duv, uv + duv);
float asum = getSample(box.xy, outerEdge, width)
+ getSample(box.zw, outerEdge, width)
+ getSample(box.xw, outerEdge, width)
+ getSample(box.zy, outerEdge, width);
// weighted average, with 4 extra points having 0.5 weight each,
// so 1 + 0.5*4 = 3 is the divisor
alpha = (alpha + 0.5 * asum) / 3.0;
textColor = vec4(color.xyz, alpha);//textColor.* alpha);
textColor.xyz = pow(textColor.xyz, vec3(2.2)); // gamma correction
// Premultiplied alpha output.
outFragColor = textColor;
}
else {
float alpha = 1.0;
float gray = dot(color.xyz, vec3(0.2126, 0.7152, 0.0722));
outFragColor = vec4(color.xyz , pow(tex.x / gray, 1/(2.2)) );//textColor.* alpha);
//outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
/* debug test.
if(!rect(pixelPosition, position, size))
{
outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
*/
}

View File

@ -1,28 +1,28 @@
#version 460
layout(location = 0) in vec3 texPosition;
layout(location = 1) in vec3 texNormal;
layout(location = 2) in vec4 texColor;
layout(location = 3) in vec2 texCoord;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec2 texCoords;
layout (location = 2) out int instanceId;
layout (location = 3) out vec2 pixelPosition;
#include "FontSDFShared.glsl"
void main()
{
vec2 pos = fontBuffer.fontInfo[gl_BaseInstance].position;
vec2 size = fontBuffer.fontInfo[gl_BaseInstance].size;
vec2 t = texPosition.xy + pos;
pixelPosition = t;
//gl_Position = vec4(( (texPosition.xy + pos) / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
gl_Position = vec4((t / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
texCoords = texCoord;
fragColor = texColor;
instanceId = gl_BaseInstance;
}
#version 460
layout(location = 0) in vec3 texPosition;
layout(location = 1) in vec3 texNormal;
layout(location = 2) in vec4 texColor;
layout(location = 3) in vec2 texCoord;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec2 texCoords;
layout (location = 2) out int instanceId;
layout (location = 3) out vec2 pixelPosition;
#include "FontSDFShared.glsl"
void main()
{
vec2 pos = fontBuffer.fontInfo[gl_BaseInstance].position;
vec2 size = fontBuffer.fontInfo[gl_BaseInstance].size;
vec2 t = texPosition.xy + pos;
pixelPosition = t;
//gl_Position = vec4(( (texPosition.xy + pos) / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
gl_Position = vec4((t / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
texCoords = texCoord;
fragColor = texColor;
instanceId = gl_BaseInstance;
}

View File

@ -1,111 +1,111 @@
#version 460
//shader input
layout (location = 0) in vec4 fragColor;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec2 panelPixelPosition; // relative to the topleft
layout (location = 3) flat in int instanceId;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "PapyrusRectShared.glsl"
#include "FragmentHelpers.glsl"
void main()
{
vec2 imageSize = objectBuffer.objects[instanceId].imageSize;
vec4 rounding = objectBuffer.objects[instanceId].rounding;
vec4 borderColor = objectBuffer.objects[instanceId].borderColor;
float borderWidth = objectBuffer.objects[instanceId].borderWidth;
float alpha = fragColor.w;
uint usesImage = objectBuffer.objects[instanceId].flags & 1;
// check to discard topleft
vec3 color = fragColor.xyz;
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y < rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, rounding.x));
if(dist > (rounding.x ))
{
discard;
}
else if(somewhatEqual(dist, rounding.x))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// top right
if(panelPixelPosition.x > imageSize.x - rounding.y && panelPixelPosition.y < rounding.y )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.y, rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom Left
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y > imageSize.y - rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom right
if(panelPixelPosition.x > imageSize.x - rounding.a && imageSize.y - panelPixelPosition.y < rounding.a )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// check to discard topright
// determine border colors
if( panelPixelPosition.x < borderWidth
|| panelPixelPosition.x > imageSize.x - borderWidth
|| panelPixelPosition.y < borderWidth
|| panelPixelPosition.y > imageSize.y - borderWidth
)
{
color = borderColor.xyz;
alpha = borderColor.w;
}
// scale the color
if(usesImage > 0)
{
vec4 sampledColor = texture(tex, vec2(texCoord.x, 1 - texCoord.y));
outFragColor = vec4(sampledColor.rgb, sampledColor.a * alpha);
}
else
{
outFragColor = vec4(pow(color, vec3(2.2)), alpha);
}
}
#version 460
//shader input
layout (location = 0) in vec4 fragColor;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec2 panelPixelPosition; // relative to the topleft
layout (location = 3) flat in int instanceId;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "PapyrusRectShared.glsl"
#include "FragmentHelpers.glsl"
void main()
{
vec2 imageSize = objectBuffer.objects[instanceId].imageSize;
vec4 rounding = objectBuffer.objects[instanceId].rounding;
vec4 borderColor = objectBuffer.objects[instanceId].borderColor;
float borderWidth = objectBuffer.objects[instanceId].borderWidth;
float alpha = fragColor.w;
uint usesImage = objectBuffer.objects[instanceId].flags & 1;
// check to discard topleft
vec3 color = fragColor.xyz;
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y < rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, rounding.x));
if(dist > (rounding.x ))
{
discard;
}
else if(somewhatEqual(dist, rounding.x))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// top right
if(panelPixelPosition.x > imageSize.x - rounding.y && panelPixelPosition.y < rounding.y )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.y, rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom Left
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y > imageSize.y - rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom right
if(panelPixelPosition.x > imageSize.x - rounding.a && imageSize.y - panelPixelPosition.y < rounding.a )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// check to discard topright
// determine border colors
if( panelPixelPosition.x < borderWidth
|| panelPixelPosition.x > imageSize.x - borderWidth
|| panelPixelPosition.y < borderWidth
|| panelPixelPosition.y > imageSize.y - borderWidth
)
{
color = borderColor.xyz;
alpha = borderColor.w;
}
// scale the color
if(usesImage > 0)
{
vec4 sampledColor = texture(tex, vec2(texCoord.x, 1 - texCoord.y));
outFragColor = vec4(sampledColor.rgb, sampledColor.a * alpha);
}
else
{
outFragColor = vec4(pow(color, vec3(2.2)), alpha);
}
}

View File

@ -1,46 +1,46 @@
//we will be using glsl version 4.5 syntax
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec4 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec2 panelPixelPosition;
layout (location = 3) out int instanceId;
#include "PapyrusRectShared.glsl"
void main()
{
vec2 imagePosition = objectBuffer.objects[gl_BaseInstance].imagePosition;
vec2 imageSize = objectBuffer.objects[gl_BaseInstance].imageSize;
vec2 anchor = objectBuffer.objects[gl_BaseInstance].anchorPoint;
vec2 scale = objectBuffer.objects[gl_BaseInstance].scale;
float alpha = objectBuffer.objects[gl_BaseInstance].alpha;
vec4 baseColor = objectBuffer.objects[gl_BaseInstance].baseColor;
vec2 finalSize = (imageSize / PushConstants.extent);
//float zLevel = objectBuffer.objects[gl_BaseInstance].zLevel;
vec2 finalPos = ((imagePosition / PushConstants.extent) * 2 - 1) - anchor * finalSize * scale;
outColor = baseColor;
//outColor = vec3(vColor.x, vColor.y, vColor.z);
vec4 fp = vec4(
finalPos.x + ( vPosition.x * finalSize.x * scale.x),
finalPos.y + (-vPosition.y * finalSize.y * scale.y),
vPosition.z, 1.0
);
//1.0);
gl_Position = fp;
//gl_Position = vec4( ((position.x) - 1.3) * 0.3 * 1.3, (-position.y + 0.05) * 1.3, position.z, 1.0); // + vec4(imagePosition, 0.0f, 1.0f);
texCoord = vec2(1 - vTexCoord.x, vTexCoord.y);
panelPixelPosition = (vPosition.xy - anchor) / 2 * imageSize;
panelPixelPosition.y = imageSize.y - panelPixelPosition.y;
instanceId = gl_BaseInstance;
}
//we will be using glsl version 4.5 syntax
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec4 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec2 panelPixelPosition;
layout (location = 3) out int instanceId;
#include "PapyrusRectShared.glsl"
void main()
{
vec2 imagePosition = objectBuffer.objects[gl_BaseInstance].imagePosition;
vec2 imageSize = objectBuffer.objects[gl_BaseInstance].imageSize;
vec2 anchor = objectBuffer.objects[gl_BaseInstance].anchorPoint;
vec2 scale = objectBuffer.objects[gl_BaseInstance].scale;
float alpha = objectBuffer.objects[gl_BaseInstance].alpha;
vec4 baseColor = objectBuffer.objects[gl_BaseInstance].baseColor;
vec2 finalSize = (imageSize / PushConstants.extent);
//float zLevel = objectBuffer.objects[gl_BaseInstance].zLevel;
vec2 finalPos = ((imagePosition / PushConstants.extent) * 2 - 1) - anchor * finalSize * scale;
outColor = baseColor;
//outColor = vec3(vColor.x, vColor.y, vColor.z);
vec4 fp = vec4(
finalPos.x + ( vPosition.x * finalSize.x * scale.x),
finalPos.y + (-vPosition.y * finalSize.y * scale.y),
vPosition.z, 1.0
);
//1.0);
gl_Position = fp;
//gl_Position = vec4( ((position.x) - 1.3) * 0.3 * 1.3, (-position.y + 0.05) * 1.3, position.z, 1.0); // + vec4(imagePosition, 0.0f, 1.0f);
texCoord = vec2(1 - vTexCoord.x, vTexCoord.y);
panelPixelPosition = (vPosition.xy - anchor) / 2 * imageSize;
panelPixelPosition.y = imageSize.y - panelPixelPosition.y;
instanceId = gl_BaseInstance;
}

View File

@ -1,14 +1,14 @@
const std = @import("std");
const vk = @import("vulkan");
pub const VkCommand = union(enum(u8)) {
image: struct {
index: u32,
imageSet: ?vk.DescriptorSet,
},
text: struct {
index: u32,
small: bool,
ssbo: u32,
},
};
const std = @import("std");
const vk = @import("vulkan");
pub const VkCommand = union(enum(u8)) {
image: struct {
index: u32,
imageSet: ?vk.DescriptorSet,
},
text: struct {
index: u32,
small: bool,
ssbo: u32,
},
};

View File

@ -1,46 +1,46 @@
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics");
const memory = core.MemoryTracker;
pub const papyrus = @import("papyrus");
pub const HandlerError = papyrus.HandlerError;
pub const NodeHandle = papyrus.NodeHandle;
pub const LocText = papyrus.LocText;
pub const PressedType = papyrus.PressedType;
pub const PapyrusSystem = @import("PapyrusIntegration.zig");
var gPapyrus: *PapyrusSystem = undefined;
pub const Module: core.ModuleDescription = .{
.name = "ui",
.enabledByDefault = true,
};
pub fn getSystem() *PapyrusSystem {
return gPapyrus;
}
pub fn getContext() *papyrus.Context {
return gPapyrus.papyrusCtx;
}
pub fn start_module(comptime spec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
_ = spec;
_ = allocator;
// no initialization
if (core.isUtility()) {
return;
}
gPapyrus = try core.gEngine.createObject(PapyrusSystem, .{ .can_tick = true });
try gPapyrus.setup(graphics.getContext());
core.engine_logs("ui start_module");
memory.MTPrintStatsDelta();
}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator;
}
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics");
const memory = core.MemoryTracker;
pub const papyrus = @import("papyrus");
pub const HandlerError = papyrus.HandlerError;
pub const NodeHandle = papyrus.NodeHandle;
pub const LocText = papyrus.LocText;
pub const PressedType = papyrus.PressedType;
pub const PapyrusSystem = @import("PapyrusIntegration.zig");
var gPapyrus: *PapyrusSystem = undefined;
pub const Module: core.ModuleDescription = .{
.name = "ui",
.enabledByDefault = true,
};
pub fn getSystem() *PapyrusSystem {
return gPapyrus;
}
pub fn getContext() *papyrus.Context {
return gPapyrus.papyrusCtx;
}
pub fn start_module(comptime spec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
_ = spec;
_ = allocator;
// no initialization
if (core.isUtility()) {
return;
}
gPapyrus = try core.gEngine.createObject(PapyrusSystem, .{ .can_tick = true });
try gPapyrus.setup(graphics.getContext());
core.engine_logs("ui start_module");
memory.MTPrintStatsDelta();
}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator;
}

View File

@ -1,86 +1,86 @@
const std = @import("std");
// very tiny, not intended to build anything just to run tests linked with libc
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const mod = b.addModule("vkImgui", .{
.target = target,
.optimize = optimize,
.link_libc = true,
.root_source_file = b.path("src/vkImgui.zig"),
});
mod.addIncludePath(b.path("cimgui"));
mod.addIncludePath(b.path("cimplot"));
mod.addIncludePath(b.path("cimgui/imgui"));
mod.addIncludePath(b.path("cimgui/imgui/backends"));
const cimgui = b.addStaticLibrary(.{
.name = "cimgui",
.target = target,
.optimize = optimize,
});
cimgui.linkLibC();
if (target.result.abi != .msvc)
cimgui.linkLibCpp();
cimgui.addIncludePath(b.path("cimgui"));
cimgui.addIncludePath(b.path("cimplot"));
cimgui.addIncludePath(b.path("cimgui/imgui"));
cimgui.addIncludePath(b.path("cimgui/imgui/backends"));
cimgui.addCSourceFiles(.{
.root = b.path("cimgui/imgui"),
.files = &[_][]const u8{
"cimgui.cpp",
"cimgui_compat.cpp",
"imgui.cpp",
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
"backends/imgui_impl_vulkan.cpp",
"backends/imgui_impl_glfw.cpp",
},
});
cimgui.addCSourceFiles(.{
.root = b.path("cimplot"),
.files = &[_][]const u8{
"cimplot.cpp",
"implot/implot.cpp",
"implot/implot_demo.cpp",
"implot/implot_items.cpp",
},
});
const depList = [_][]const u8{
"core",
"graphics",
"platform",
"vulkan",
};
for (depList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
const depMod = dep.module(depName);
mod.addImport(depName, depMod);
}
mod.linkLibrary(cimgui);
// I could've made cimgui a seperate lib,
// I can seperate it out later if needed.
const test_step = b.step("test", "run unit tests for ui");
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tests/tests.zig"),
});
tests.root_module.addImport("vkImgui", mod);
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
b.installArtifact(tests);
}
const std = @import("std");
// very tiny, not intended to build anything just to run tests linked with libc
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const mod = b.addModule("vkImgui", .{
.target = target,
.optimize = optimize,
.link_libc = true,
.root_source_file = b.path("src/vkImgui.zig"),
});
mod.addIncludePath(b.path("cimgui"));
mod.addIncludePath(b.path("cimplot"));
mod.addIncludePath(b.path("cimgui/imgui"));
mod.addIncludePath(b.path("cimgui/imgui/backends"));
const cimgui = b.addStaticLibrary(.{
.name = "cimgui",
.target = target,
.optimize = optimize,
});
cimgui.linkLibC();
if (target.result.abi != .msvc)
cimgui.linkLibCpp();
cimgui.addIncludePath(b.path("cimgui"));
cimgui.addIncludePath(b.path("cimplot"));
cimgui.addIncludePath(b.path("cimgui/imgui"));
cimgui.addIncludePath(b.path("cimgui/imgui/backends"));
cimgui.addCSourceFiles(.{
.root = b.path("cimgui/imgui"),
.files = &[_][]const u8{
"cimgui.cpp",
"cimgui_compat.cpp",
"imgui.cpp",
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
"backends/imgui_impl_vulkan.cpp",
"backends/imgui_impl_glfw.cpp",
},
});
cimgui.addCSourceFiles(.{
.root = b.path("cimplot"),
.files = &[_][]const u8{
"cimplot.cpp",
"implot/implot.cpp",
"implot/implot_demo.cpp",
"implot/implot_items.cpp",
},
});
const depList = [_][]const u8{
"core",
"graphics",
"platform",
"vulkan",
};
for (depList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
const depMod = dep.module(depName);
mod.addImport(depName, depMod);
}
mod.linkLibrary(cimgui);
// I could've made cimgui a seperate lib,
// I can seperate it out later if needed.
const test_step = b.step("test", "run unit tests for ui");
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tests/tests.zig"),
});
tests.root_module.addImport("vkImgui", mod);
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
b.installArtifact(tests);
}

View File

@ -1,13 +1,13 @@
.{
.name = "imgui",
.version = "0.0.0",
.dependencies = .{
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
.platform = .{ .path = "../platform" },
.vulkan = .{ .path = "../../lib/vulkan" },
},
.paths = .{
"",
},
}
.{
.name = "imgui",
.version = "0.0.0",
.dependencies = .{
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
.platform = .{ .path = "../platform" },
.vulkan = .{ .path = "../../lib/vulkan" },
},
.paths = .{
"",
},
}

Some files were not shown because too many files have changed in this diff Show More