Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08a6df4e68 |
@@ -8,6 +8,3 @@ lightkeeper/assets_runtime
|
|||||||
.idea/**
|
.idea/**
|
||||||
|
|
||||||
build/**
|
build/**
|
||||||
|
|
||||||
# Claude Code
|
|
||||||
CLAUDE.md
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
[submodule "destrum/third_party/imgui"]
|
[submodule "destrum/third_party/imgui"]
|
||||||
path = destrum/third_party/imgui
|
path = destrum/third_party/imgui
|
||||||
url = https://github.com/ocornut/imgui.git
|
url = https://github.com/ocornut/imgui.git
|
||||||
branch = docking
|
|
||||||
[submodule "destrum/third_party/jolt"]
|
[submodule "destrum/third_party/jolt"]
|
||||||
path = destrum/third_party/jolt
|
path = destrum/third_party/jolt
|
||||||
url = https://github.com/jrouwe/JoltPhysics.git
|
url = https://github.com/jrouwe/JoltPhysics.git
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ cmake --install build --config Release
|
|||||||
|
|
||||||
## Demo app
|
## Demo app
|
||||||
|
|
||||||
`lightkeeper` is the current demo application. It creates an SDL window, initializes the Destrum app, loads assets, sets up a scene, renders meshes, skyboxes, and a skinned animated character.
|
`lightkeeper` is the current game application. It launches **Star Catcher**, a small playable loop built from the engine's scene and rendering systems. Move the blue catcher with `A`/`D` or the arrow keys, catch orange stars, and press `R` to restart after three misses. Press `Escape` to quit.
|
||||||
|
|
||||||
## Development notes
|
## Development notes
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule TheChef updated: 38230f5092...14f7dd9423
@@ -5,7 +5,6 @@ set(SRC_FILES
|
|||||||
"src/Event.cpp"
|
"src/Event.cpp"
|
||||||
|
|
||||||
"src/Components/Animator.cpp"
|
"src/Components/Animator.cpp"
|
||||||
"src/Components/LightComponent.cpp"
|
|
||||||
"src/Components/MeshRendererComponent.cpp"
|
"src/Components/MeshRendererComponent.cpp"
|
||||||
"src/Components/Rotator.cpp"
|
"src/Components/Rotator.cpp"
|
||||||
"src/Components/Spinner.cpp"
|
"src/Components/Spinner.cpp"
|
||||||
@@ -38,18 +37,13 @@ set(SRC_FILES
|
|||||||
"src/Graphics/Managers/MemoryManager.cpp"
|
"src/Graphics/Managers/MemoryManager.cpp"
|
||||||
"src/Graphics/Managers/FrameManager.cpp"
|
"src/Graphics/Managers/FrameManager.cpp"
|
||||||
"src/Graphics/Managers/ImageManager.cpp"
|
"src/Graphics/Managers/ImageManager.cpp"
|
||||||
"src/Graphics/Managers/LineRenderingManager.cpp"
|
|
||||||
"src/Graphics/Managers/QuadRenderingManager.cpp"
|
|
||||||
|
|
||||||
"src/Graphics/Resources/GPUImage.cpp"
|
"src/Graphics/Resources/GPUImage.cpp"
|
||||||
"src/Graphics/Resources/NBuffer.cpp"
|
"src/Graphics/Resources/NBuffer.cpp"
|
||||||
"src/Graphics/Resources/Cubemap.cpp"
|
"src/Graphics/Resources/Cubemap.cpp"
|
||||||
|
|
||||||
"src/Graphics/Pipelines/MeshPipeline.cpp"
|
"src/Graphics/Pipelines/MeshPipeline.cpp"
|
||||||
"src/Graphics/Pipelines/LineRenderingPass.cpp"
|
|
||||||
"src/Graphics/Pipelines/QuadRendererPass.cpp"
|
|
||||||
"src/Graphics/Pipelines/SkyboxPipeline.cpp"
|
"src/Graphics/Pipelines/SkyboxPipeline.cpp"
|
||||||
"src/Graphics/Pipelines/LightCullingPipeline.cpp"
|
|
||||||
"src/Graphics/Pipelines/SkinningPipeline.cpp"
|
"src/Graphics/Pipelines/SkinningPipeline.cpp"
|
||||||
"src/Graphics/Pipelines/ImguiPass.cpp"
|
"src/Graphics/Pipelines/ImguiPass.cpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,154 +0,0 @@
|
|||||||
#version 460
|
|
||||||
|
|
||||||
#extension GL_GOOGLE_include_directive : require
|
|
||||||
#extension GL_EXT_buffer_reference : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
|
|
||||||
#include "bindless.glsl"
|
|
||||||
#include "scene_data.glsl"
|
|
||||||
|
|
||||||
// Forward+ light culling.
|
|
||||||
// One workgroup per LIGHT_TILE_SIZE x LIGHT_TILE_SIZE screen tile:
|
|
||||||
// 1. find the min/max depth of the tile from the depth pre-pass
|
|
||||||
// 2. build the tile frustum in view space
|
|
||||||
// 3. test every light against it and write the visible ones to the tile's light list
|
|
||||||
|
|
||||||
layout (push_constant, scalar) uniform constants
|
|
||||||
{
|
|
||||||
SceneDataBuffer sceneData;
|
|
||||||
uvec2 screenSize;
|
|
||||||
uint depthTexID;
|
|
||||||
uint padding;
|
|
||||||
} pcs;
|
|
||||||
|
|
||||||
layout (local_size_x = LIGHT_TILE_SIZE, local_size_y = LIGHT_TILE_SIZE, local_size_z = 1) in;
|
|
||||||
|
|
||||||
#define THREADS_PER_TILE (LIGHT_TILE_SIZE * LIGHT_TILE_SIZE)
|
|
||||||
|
|
||||||
shared uint tileMinDepth;
|
|
||||||
shared uint tileMaxDepth;
|
|
||||||
shared uint tileLightCount;
|
|
||||||
|
|
||||||
// left, right, bottom, top side planes (view space, through the camera origin)
|
|
||||||
shared vec4 tilePlanes[4];
|
|
||||||
// view space distances along -Z
|
|
||||||
shared float tileNearDistance;
|
|
||||||
shared float tileFarDistance;
|
|
||||||
|
|
||||||
vec3 unprojectToView(vec2 ndc, float depth)
|
|
||||||
{
|
|
||||||
vec4 p = pcs.sceneData.invProj * vec4(ndc, depth, 1.0);
|
|
||||||
return p.xyz / p.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plane through the camera origin and two points, normal facing towards `inside`
|
|
||||||
vec4 makeSidePlane(vec3 a, vec3 b, vec3 inside)
|
|
||||||
{
|
|
||||||
vec3 n = normalize(cross(a, b));
|
|
||||||
if (dot(n, inside) < 0.0) {
|
|
||||||
n = -n;
|
|
||||||
}
|
|
||||||
return vec4(n, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sphereInsideTile(vec3 center, float radius)
|
|
||||||
{
|
|
||||||
float distance = -center.z;
|
|
||||||
if (distance + radius < tileNearDistance || distance - radius > tileFarDistance) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
if (dot(tilePlanes[i].xyz, center) + tilePlanes[i].w < -radius) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
uint localIndex = gl_LocalInvocationIndex;
|
|
||||||
uvec2 tileID = gl_WorkGroupID.xy;
|
|
||||||
uint tileIndex = tileID.y * pcs.sceneData.tileCountX + tileID.x;
|
|
||||||
uint tileOffset = tileIndex * LIGHT_TILE_STRIDE;
|
|
||||||
|
|
||||||
if (localIndex == 0) {
|
|
||||||
tileMinDepth = 0xFFFFFFFFu;
|
|
||||||
tileMaxDepth = 0u;
|
|
||||||
tileLightCount = 0u;
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
|
|
||||||
// 1. min / max depth
|
|
||||||
uvec2 pixel = gl_GlobalInvocationID.xy;
|
|
||||||
if (all(lessThan(pixel, pcs.screenSize))) {
|
|
||||||
float depth = texelFetch(
|
|
||||||
sampler2D(textures[pcs.depthTexID], samplers[NEAREST_SAMPLER_ID]),
|
|
||||||
ivec2(pixel),
|
|
||||||
0).r;
|
|
||||||
|
|
||||||
// depth == 1.0 is the cleared far plane (sky), nothing there receives light.
|
|
||||||
// Depth is always positive, so its bits can be compared as uints.
|
|
||||||
if (depth < 1.0) {
|
|
||||||
uint depthBits = floatBitsToUint(depth);
|
|
||||||
atomicMin(tileMinDepth, depthBits);
|
|
||||||
atomicMax(tileMaxDepth, depthBits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
|
|
||||||
// 2. tile frustum
|
|
||||||
bool tileHasGeometry = tileMinDepth != 0xFFFFFFFFu;
|
|
||||||
|
|
||||||
if (localIndex == 0 && tileHasGeometry) {
|
|
||||||
vec2 screenSize = vec2(pcs.screenSize);
|
|
||||||
vec2 ndcMin = vec2(tileID * LIGHT_TILE_SIZE) / screenSize * 2.0 - 1.0;
|
|
||||||
vec2 ndcMax = vec2((tileID + 1) * LIGHT_TILE_SIZE) / screenSize * 2.0 - 1.0;
|
|
||||||
vec2 ndcCenter = (ndcMin + ndcMax) * 0.5;
|
|
||||||
|
|
||||||
vec3 corner00 = unprojectToView(vec2(ndcMin.x, ndcMin.y), 1.0);
|
|
||||||
vec3 corner10 = unprojectToView(vec2(ndcMax.x, ndcMin.y), 1.0);
|
|
||||||
vec3 corner11 = unprojectToView(vec2(ndcMax.x, ndcMax.y), 1.0);
|
|
||||||
vec3 corner01 = unprojectToView(vec2(ndcMin.x, ndcMax.y), 1.0);
|
|
||||||
vec3 center = unprojectToView(ndcCenter, 1.0);
|
|
||||||
|
|
||||||
tilePlanes[0] = makeSidePlane(corner01, corner00, center);
|
|
||||||
tilePlanes[1] = makeSidePlane(corner10, corner11, center);
|
|
||||||
tilePlanes[2] = makeSidePlane(corner00, corner10, center);
|
|
||||||
tilePlanes[3] = makeSidePlane(corner11, corner01, center);
|
|
||||||
|
|
||||||
tileNearDistance = -unprojectToView(ndcCenter, uintBitsToFloat(tileMinDepth)).z;
|
|
||||||
tileFarDistance = -unprojectToView(ndcCenter, uintBitsToFloat(tileMaxDepth)).z;
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
|
|
||||||
// 3. cull lights, every thread takes a slice of the light list
|
|
||||||
if (tileHasGeometry) {
|
|
||||||
for (uint i = localIndex; i < pcs.sceneData.numLights; i += THREADS_PER_TILE) {
|
|
||||||
Light light = pcs.sceneData.lights.data[i];
|
|
||||||
|
|
||||||
bool visible = light.type == TYPE_DIRECTIONAL_LIGHT;
|
|
||||||
if (!visible) {
|
|
||||||
vec3 viewPos = (pcs.sceneData.view * vec4(light.position, 1.0)).xyz;
|
|
||||||
visible = sphereInsideTile(viewPos, light.range);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
uint slot = atomicAdd(tileLightCount, 1u);
|
|
||||||
if (slot < MAX_LIGHTS_PER_TILE) {
|
|
||||||
pcs.sceneData.tileLightIndices.data[tileOffset + 1 + slot] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
|
|
||||||
if (localIndex == 0) {
|
|
||||||
pcs.sceneData.tileLightIndices.data[tileOffset] = min(tileLightCount, uint(MAX_LIGHTS_PER_TILE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#ifndef LIGHTING_GLSL
|
|
||||||
#define LIGHTING_GLSL
|
|
||||||
|
|
||||||
#include "lights.glsl"
|
|
||||||
|
|
||||||
#define PI 3.14159265359
|
|
||||||
|
|
||||||
// GGX / Trowbridge-Reitz normal distribution
|
|
||||||
float distributionGGX(float NoH, float roughness)
|
|
||||||
{
|
|
||||||
float a = roughness * roughness;
|
|
||||||
float a2 = a * a;
|
|
||||||
float d = NoH * NoH * (a2 - 1.0) + 1.0;
|
|
||||||
return a2 / (PI * d * d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smith-Schlick GGX, k remapped for direct lighting
|
|
||||||
float geometrySmithGGX(float NoV, float NoL, float roughness)
|
|
||||||
{
|
|
||||||
float r = roughness + 1.0;
|
|
||||||
float k = (r * r) / 8.0;
|
|
||||||
float ggxV = NoV / (NoV * (1.0 - k) + k);
|
|
||||||
float ggxL = NoL / (NoL * (1.0 - k) + k);
|
|
||||||
return ggxV * ggxL;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 fresnelSchlick(float VoH, vec3 f0)
|
|
||||||
{
|
|
||||||
return f0 + (1.0 - f0) * pow(1.0 - VoH, 5.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inverse square falloff, windowed so it reaches exactly 0 at the light range.
|
|
||||||
// The range is also the sphere the light culling uses, so no light gets cut off at a tile edge.
|
|
||||||
float calculateDistanceAttenuation(float distance, float range)
|
|
||||||
{
|
|
||||||
float window = clamp(1.0 - pow(distance / range, 4.0), 0.0, 1.0);
|
|
||||||
return (window * window) / max(distance * distance, 0.0001);
|
|
||||||
}
|
|
||||||
|
|
||||||
float calculateSpotAttenuation(Light light, vec3 l)
|
|
||||||
{
|
|
||||||
float cd = dot(normalize(light.direction), -l);
|
|
||||||
return smoothstep(light.spotOuterCos, light.spotInnerCos, cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
|
|
||||||
vec3 diffuseColor, float roughness, float metallic, vec3 f0)
|
|
||||||
{
|
|
||||||
vec3 l;
|
|
||||||
float attenuation = 1.0;
|
|
||||||
|
|
||||||
if (light.type == TYPE_DIRECTIONAL_LIGHT) {
|
|
||||||
l = normalize(-light.direction);
|
|
||||||
} else {
|
|
||||||
vec3 toLight = light.position - fragPos;
|
|
||||||
float distance = length(toLight);
|
|
||||||
l = toLight / max(distance, 0.0001);
|
|
||||||
|
|
||||||
attenuation = calculateDistanceAttenuation(distance, light.range);
|
|
||||||
if (light.type == TYPE_SPOT_LIGHT) {
|
|
||||||
attenuation *= calculateSpotAttenuation(light, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float NoL = clamp(dot(n, l), 0.0, 1.0);
|
|
||||||
if (NoL <= 0.0 || attenuation <= 0.0) {
|
|
||||||
return vec3(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 h = normalize(v + l);
|
|
||||||
float NoV = clamp(dot(n, v), 0.0001, 1.0);
|
|
||||||
float NoH = clamp(dot(n, h), 0.0, 1.0);
|
|
||||||
float VoH = clamp(dot(v, h), 0.0, 1.0);
|
|
||||||
|
|
||||||
float D = distributionGGX(NoH, roughness);
|
|
||||||
float G = geometrySmithGGX(NoV, NoL, roughness);
|
|
||||||
vec3 F = fresnelSchlick(VoH, f0);
|
|
||||||
|
|
||||||
vec3 specular = (D * G * F) / (4.0 * NoV * NoL + 0.0001);
|
|
||||||
vec3 kd = (vec3(1.0) - F) * (1.0 - metallic);
|
|
||||||
vec3 diffuse = kd * diffuseColor / PI;
|
|
||||||
|
|
||||||
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// blue -> green -> red, used to visualise how many lights a tile has
|
|
||||||
vec3 lightHeatmapColor(uint lightCount)
|
|
||||||
{
|
|
||||||
float t = clamp(float(lightCount) / 16.0, 0.0, 1.0);
|
|
||||||
if (t < 0.5) {
|
|
||||||
return mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), t * 2.0);
|
|
||||||
}
|
|
||||||
return mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (t - 0.5) * 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LIGHTING_GLSL
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#ifndef LIGHTS_GLSL
|
|
||||||
#define LIGHTS_GLSL
|
|
||||||
|
|
||||||
#extension GL_EXT_buffer_reference : require
|
|
||||||
|
|
||||||
#define TYPE_DIRECTIONAL_LIGHT 0
|
|
||||||
#define TYPE_POINT_LIGHT 1
|
|
||||||
#define TYPE_SPOT_LIGHT 2
|
|
||||||
|
|
||||||
// Forward+ tiling, must match LightCullingPipeline::TILE_SIZE / MAX_LIGHTS_PER_TILE
|
|
||||||
#define LIGHT_TILE_SIZE 16
|
|
||||||
#define MAX_LIGHTS_PER_TILE 255
|
|
||||||
// Every tile stores its light count first, followed by MAX_LIGHTS_PER_TILE light indices
|
|
||||||
#define LIGHT_TILE_STRIDE (MAX_LIGHTS_PER_TILE + 1)
|
|
||||||
|
|
||||||
// Must match GPULight in Light.h
|
|
||||||
struct Light {
|
|
||||||
vec3 position;
|
|
||||||
float range;
|
|
||||||
|
|
||||||
vec3 color;
|
|
||||||
float intensity;
|
|
||||||
|
|
||||||
// direction the light is pointing in (directional + spot lights)
|
|
||||||
vec3 direction;
|
|
||||||
uint type;
|
|
||||||
|
|
||||||
float spotInnerCos;
|
|
||||||
float spotOuterCos;
|
|
||||||
vec2 padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout (buffer_reference, std430) readonly buffer LightsBuffer {
|
|
||||||
Light data[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout (buffer_reference, std430) buffer TileLightIndicesBuffer {
|
|
||||||
uint data[];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // LIGHTS_GLSL
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#version 460
|
|
||||||
|
|
||||||
layout (location = 0) in vec4 inColor;
|
|
||||||
layout (location = 0) out vec4 outFragColor;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
outFragColor = inColor;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#version 460
|
|
||||||
|
|
||||||
layout (location = 0) in vec4 inPosition;
|
|
||||||
layout (location = 1) in vec4 inColor;
|
|
||||||
|
|
||||||
layout (location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
layout (push_constant) uniform LinePushConstants {
|
|
||||||
mat4 viewProjection;
|
|
||||||
} pcs;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
gl_Position = pcs.viewProjection * inPosition;
|
|
||||||
outColor = inColor;
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "bindless.glsl"
|
#include "bindless.glsl"
|
||||||
#include "scene_data.glsl"
|
#include "scene_data.glsl"
|
||||||
#include "mesh_pcs.glsl"
|
#include "mesh_pcs.glsl"
|
||||||
#include "lighting.glsl"
|
|
||||||
|
|
||||||
layout (location = 0) in vec3 inPos;
|
layout (location = 0) in vec3 inPos;
|
||||||
layout (location = 1) in vec2 inUV;
|
layout (location = 1) in vec2 inUV;
|
||||||
@@ -21,13 +20,16 @@ void main()
|
|||||||
uint samplerID = pcs.sceneData.materials.data[pcs.materialID].textureFilteringMode;
|
uint samplerID = pcs.sceneData.materials.data[pcs.materialID].textureFilteringMode;
|
||||||
|
|
||||||
vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor;
|
vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor;
|
||||||
vec3 baseColor = diffuse.rgb;
|
outFragColor = diffuse;
|
||||||
|
|
||||||
|
|
||||||
// if (diffuse.a < 0.1) {
|
// if (diffuse.a < 0.1) {
|
||||||
// discard;
|
// discard;
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
vec3 normal = normalize(inNormal);
|
// vec3 baseColor = material.baseColor.rgb * diffuse.rgb;
|
||||||
|
//
|
||||||
|
// vec3 normal = normalize(inNormal).rgb;
|
||||||
// if (inTangent != vec4(0.0)) {
|
// if (inTangent != vec4(0.0)) {
|
||||||
// // FIXME: sometimes Blender doesn't export tangents for some objects
|
// // FIXME: sometimes Blender doesn't export tangents for some objects
|
||||||
// // for some reason. When we will start computing tangents manually,
|
// // for some reason. When we will start computing tangents manually,
|
||||||
@@ -37,65 +39,91 @@ void main()
|
|||||||
// normal = inTBN * normalize(normal * 2.0 - 1.0);
|
// normal = inTBN * normalize(normal * 2.0 - 1.0);
|
||||||
// normal = normalize(normal);
|
// normal = normalize(normal);
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// #ifdef PBR
|
||||||
|
// float metallicF = material.metallicRoughnessEmissive.r;
|
||||||
|
// float roughnessF = material.metallicRoughnessEmissive.g;
|
||||||
|
//
|
||||||
|
// vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
||||||
|
//
|
||||||
|
// float roughness = roughnessF * metallicRoughness.g;
|
||||||
|
// // roughness *= roughness; // from perceptual to linear
|
||||||
|
// roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
||||||
|
//
|
||||||
|
// float metallic = metallicF * metallicRoughness.b;
|
||||||
|
//
|
||||||
|
// vec3 dielectricSpecular = vec3(0.04);
|
||||||
|
// vec3 black = vec3(0.0);
|
||||||
|
// vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
|
||||||
|
// vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
|
||||||
|
// #else
|
||||||
|
// vec3 diffuseColor = baseColor;
|
||||||
|
// float metallic = 0.f;
|
||||||
|
// float roughness = 1.f;
|
||||||
|
// vec3 f0 = vec3(0.0);
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
||||||
|
// vec3 fragPos = inPos.xyz;
|
||||||
|
// vec3 n = normal;
|
||||||
|
// vec3 v = normalize(cameraPos - fragPos);
|
||||||
|
//
|
||||||
|
// // diffuseColor = vec3(1.0);
|
||||||
|
// // baseColor = vec3(1.0);
|
||||||
|
//
|
||||||
|
// vec3 fragColor = vec3(0.0);
|
||||||
|
// for (int i = 0; i < pcs.sceneData.numLights; i++) {
|
||||||
|
// Light light = pcs.sceneData.lights.data[i];
|
||||||
|
//
|
||||||
|
// vec3 l = light.direction;
|
||||||
|
// if (light.type != TYPE_DIRECTIONAL_LIGHT) {
|
||||||
|
// l = normalize(light.position - fragPos);
|
||||||
|
// }
|
||||||
|
// float NoL = clamp(dot(n, l), 0.0, 1.0);
|
||||||
|
//
|
||||||
|
// float occlusion = 1.0;
|
||||||
|
// if (light.type == TYPE_DIRECTIONAL_LIGHT) {
|
||||||
|
// occlusion = calculateCSMOcclusion(
|
||||||
|
// fragPos, cameraPos, NoL,
|
||||||
|
// pcs.sceneData.csmShadowMapId,
|
||||||
|
// pcs.sceneData.cascadeFarPlaneZs,
|
||||||
|
// pcs.sceneData.csmLightSpaceTMs);
|
||||||
|
// } else if (light.type == TYPE_POINT_LIGHT && light.shadowMapID != 0) {
|
||||||
|
// occlusion = calculatePointShadow(
|
||||||
|
// fragPos, light.position, NoL,
|
||||||
|
// light.shadowMapID,
|
||||||
|
// pcs.sceneData.pointLightFarPlane);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fragColor += calculateLight(light, fragPos, n, v, l,
|
||||||
|
// diffuseColor, roughness, metallic, f0, occlusion);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // emissive
|
||||||
|
// float emissiveF = material.metallicRoughnessEmissive.b;
|
||||||
|
// vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
||||||
|
// fragColor += emissiveColor;
|
||||||
|
//
|
||||||
|
// // ambient
|
||||||
|
// fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||||
|
//
|
||||||
|
// #if 0
|
||||||
|
// // CSM DEBUG
|
||||||
|
// uint cascadeIndex = chooseCascade(inPos, cameraPos, pcs.sceneData.cascadeFarPlaneZs);
|
||||||
|
// fragColor *= debugShadowsFactor(cascadeIndex);
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// #if 0
|
||||||
|
// // TANGENT DEBUG
|
||||||
|
// if (inTangent == vec4(0.0)) {
|
||||||
|
// fragColor = vec3(1.0f, 0.0f, 0.0f);
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// #if 1
|
||||||
|
// // NORMAL DEBUG
|
||||||
|
// // fragColor = normal;
|
||||||
|
// #endif
|
||||||
|
|
||||||
float metallicF = material.metallicRoughnessEmissive.r;
|
// outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
float roughnessF = material.metallicRoughnessEmissive.g;
|
|
||||||
|
|
||||||
vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
|
||||||
|
|
||||||
float roughness = roughnessF * metallicRoughness.g;
|
|
||||||
// roughness *= roughness; // from perceptual to linear
|
|
||||||
roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
|
||||||
|
|
||||||
float metallic = metallicF * metallicRoughness.b;
|
|
||||||
|
|
||||||
vec3 dielectricSpecular = vec3(0.04);
|
|
||||||
vec3 black = vec3(0.0);
|
|
||||||
vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
|
|
||||||
vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
|
|
||||||
|
|
||||||
vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
|
||||||
vec3 fragPos = inPos.xyz;
|
|
||||||
vec3 n = normal;
|
|
||||||
vec3 v = normalize(cameraPos - fragPos);
|
|
||||||
|
|
||||||
// forward+: only loop over the lights the culling pass put in this pixel's tile
|
|
||||||
uvec2 tileID = uvec2(gl_FragCoord.xy) / LIGHT_TILE_SIZE;
|
|
||||||
uint tileOffset = (tileID.y * pcs.sceneData.tileCountX + tileID.x) * LIGHT_TILE_STRIDE;
|
|
||||||
uint tileLightCount = pcs.sceneData.tileLightIndices.data[tileOffset];
|
|
||||||
|
|
||||||
vec3 fragColor = vec3(0.0);
|
|
||||||
for (uint i = 0; i < tileLightCount; i++) {
|
|
||||||
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
|
|
||||||
Light light = pcs.sceneData.lights.data[lightIndex];
|
|
||||||
|
|
||||||
fragColor += calculateLight(light, fragPos, n, v,
|
|
||||||
diffuseColor, roughness, metallic, f0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// emissive
|
|
||||||
float emissiveF = material.metallicRoughnessEmissive.b;
|
|
||||||
vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
|
||||||
fragColor += emissiveColor;
|
|
||||||
|
|
||||||
// ambient
|
|
||||||
fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
|
||||||
|
|
||||||
if (pcs.sceneData.debugLightHeatmap != 0) {
|
|
||||||
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// TANGENT DEBUG
|
|
||||||
if (inTangent == vec4(0.0)) {
|
|
||||||
fragColor = vec3(1.0f, 0.0f, 0.0f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// NORMAL DEBUG
|
|
||||||
fragColor = normal;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
outFragColor = vec4(fragColor, diffuse.a);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,6 @@ layout (location = 2) out vec3 outNormal;
|
|||||||
layout (location = 3) out vec4 outTangent;
|
layout (location = 3) out vec4 outTangent;
|
||||||
layout (location = 4) out mat3 outTBN;
|
layout (location = 4) out mat3 outTBN;
|
||||||
|
|
||||||
// The depth pre-pass reuses this shader, positions have to match bit for bit
|
|
||||||
// so the main pass can depth test against it with LESS_OR_EQUAL
|
|
||||||
invariant gl_Position;
|
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex];
|
Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex];
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_GOOGLE_include_directive : require
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : enable
|
|
||||||
|
|
||||||
#include "bindless.glsl"
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 inUV;
|
|
||||||
layout(location = 1) in vec4 inColor;
|
|
||||||
layout(location = 2) flat in uint inTexId;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 outFragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texColor = sampleTexture2DLinear(inTexId, inUV);
|
|
||||||
outFragColor = texColor * inColor;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_GOOGLE_include_directive : require
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 inPosition;
|
|
||||||
layout(location = 1) in vec2 inUV;
|
|
||||||
layout(location = 2) in vec4 inColor;
|
|
||||||
layout(location = 3) in uint inTexId;
|
|
||||||
|
|
||||||
layout(location = 0) out vec2 outUV;
|
|
||||||
layout(location = 1) out vec4 outColor;
|
|
||||||
layout(location = 2) flat out uint outTexId;
|
|
||||||
|
|
||||||
layout(push_constant) uniform QuadPushConstants {
|
|
||||||
mat4 viewProjection;
|
|
||||||
} pcs;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = pcs.viewProjection * vec4(inPosition, 0.0, 1.0);
|
|
||||||
outUV = inUV;
|
|
||||||
outColor = inColor;
|
|
||||||
outTexId = inTexId;
|
|
||||||
}
|
|
||||||
@@ -6,14 +6,11 @@
|
|||||||
#extension GL_EXT_buffer_reference : require
|
#extension GL_EXT_buffer_reference : require
|
||||||
|
|
||||||
#include "materials.glsl"
|
#include "materials.glsl"
|
||||||
#include "lights.glsl"
|
|
||||||
|
|
||||||
// Must match GameRenderer::GPUSceneData
|
|
||||||
layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
||||||
// camera
|
// camera
|
||||||
mat4 view;
|
mat4 view;
|
||||||
mat4 proj;
|
mat4 proj;
|
||||||
mat4 invProj;
|
|
||||||
mat4 viewProj;
|
mat4 viewProj;
|
||||||
vec4 cameraPos;
|
vec4 cameraPos;
|
||||||
|
|
||||||
@@ -26,14 +23,6 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
|||||||
float fogDensity;
|
float fogDensity;
|
||||||
|
|
||||||
MaterialsBuffer materials;
|
MaterialsBuffer materials;
|
||||||
|
|
||||||
// forward+
|
|
||||||
LightsBuffer lights;
|
|
||||||
TileLightIndicesBuffer tileLightIndices;
|
|
||||||
uint numLights;
|
|
||||||
uint tileCountX;
|
|
||||||
uint debugLightHeatmap;
|
|
||||||
uint padding;
|
|
||||||
} sceneDataBuffer;
|
} sceneDataBuffer;
|
||||||
|
|
||||||
#endif // SCENE_DATA_GLSL
|
#endif // SCENE_DATA_GLSL
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ layout(location = 0) out vec4 outColor;
|
|||||||
layout(push_constant) uniform SkyboxPC {
|
layout(push_constant) uniform SkyboxPC {
|
||||||
mat4 invViewProj;
|
mat4 invViewProj;
|
||||||
vec4 skyboxRot[3];
|
vec4 skyboxRot[3];
|
||||||
|
vec3 cameraPos;
|
||||||
uint skyboxTextureId;
|
uint skyboxTextureId;
|
||||||
} pcs;
|
} pcs;
|
||||||
|
|
||||||
@@ -21,8 +22,7 @@ void main()
|
|||||||
vec4 world = pcs.invViewProj * ndc;
|
vec4 world = pcs.invViewProj * ndc;
|
||||||
vec3 worldPos = world.xyz / world.w;
|
vec3 worldPos = world.xyz / world.w;
|
||||||
|
|
||||||
// invViewProj has no translation, so the camera sits at the origin
|
vec3 dir = normalize(worldPos - pcs.cameraPos);
|
||||||
vec3 dir = normalize(worldPos);
|
|
||||||
dir.y *= -1.0;
|
dir.y *= -1.0;
|
||||||
|
|
||||||
mat3 skyboxRot = mat3(
|
mat3 skyboxRot = mat3(
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef LIGHTCOMPONENT_H
|
|
||||||
#define LIGHTCOMPONENT_H
|
|
||||||
|
|
||||||
#include <destrum/ObjectModel/Component.h>
|
|
||||||
#include <destrum/Graphics/Light.h>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
class LightComponent final : public Component {
|
|
||||||
public:
|
|
||||||
explicit LightComponent(GameObject& parent, LightType type = LightType::Point);
|
|
||||||
|
|
||||||
LightComponent(const LightComponent& other) = delete;
|
|
||||||
LightComponent(LightComponent&& other) noexcept = delete;
|
|
||||||
LightComponent& operator=(const LightComponent& other) = delete;
|
|
||||||
LightComponent& operator=(LightComponent&& other) noexcept = delete;
|
|
||||||
|
|
||||||
void Render(const RenderContext& ctx) override;
|
|
||||||
|
|
||||||
std::string GetTypeName() const override { return "LightComponent"; }
|
|
||||||
|
|
||||||
nlohmann::json Serialize() const override;
|
|
||||||
void Deserialize(const nlohmann::json& data) override;
|
|
||||||
void ImGuiInspector() override;
|
|
||||||
|
|
||||||
void SetType(LightType type) { m_Type = type; }
|
|
||||||
[[nodiscard]] LightType GetType() const { return m_Type; }
|
|
||||||
|
|
||||||
void SetColor(const glm::vec3& color) { m_Color = color; }
|
|
||||||
[[nodiscard]] const glm::vec3& GetColor() const { return m_Color; }
|
|
||||||
|
|
||||||
void SetIntensity(float intensity);
|
|
||||||
[[nodiscard]] float GetIntensity() const { return m_Intensity; }
|
|
||||||
|
|
||||||
void SetRange(float range);
|
|
||||||
[[nodiscard]] float GetRange() const { return m_Range; }
|
|
||||||
|
|
||||||
void SetConeAngles(float innerConeAngle, float outerConeAngle);
|
|
||||||
[[nodiscard]] float GetInnerConeAngle() const { return m_InnerConeAngle; }
|
|
||||||
[[nodiscard]] float GetOuterConeAngle() const { return m_OuterConeAngle; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
LightType m_Type{LightType::Point};
|
|
||||||
glm::vec3 m_Color{1.0f};
|
|
||||||
float m_Intensity{1.0f};
|
|
||||||
float m_Range{10.0f};
|
|
||||||
float m_InnerConeAngle{0.35f};
|
|
||||||
float m_OuterConeAngle{0.5f};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // LIGHTCOMPONENT_H
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#ifndef LIGHT_H
|
|
||||||
#define LIGHT_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <glm/vec2.hpp>
|
|
||||||
#include <glm/vec3.hpp>
|
|
||||||
|
|
||||||
enum class LightType : std::uint32_t {
|
|
||||||
Directional = 0,
|
|
||||||
Point = 1,
|
|
||||||
Spot = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
LightType type{LightType::Point};
|
|
||||||
|
|
||||||
glm::vec3 position{0.0f};
|
|
||||||
glm::vec3 direction{0.0f, -1.0f, 0.0f};
|
|
||||||
|
|
||||||
glm::vec3 color{1.0f};
|
|
||||||
float intensity{1.0f};
|
|
||||||
|
|
||||||
float range{10.0f};
|
|
||||||
|
|
||||||
float innerConeAngle{0.35f};
|
|
||||||
float outerConeAngle{0.5f};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GPULight {
|
|
||||||
glm::vec3 position;
|
|
||||||
float range;
|
|
||||||
|
|
||||||
glm::vec3 color;
|
|
||||||
float intensity;
|
|
||||||
|
|
||||||
glm::vec3 direction;
|
|
||||||
std::uint32_t type;
|
|
||||||
|
|
||||||
float spotInnerCos;
|
|
||||||
float spotOuterCos;
|
|
||||||
glm::vec2 padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl");
|
|
||||||
|
|
||||||
#endif //LIGHT_H
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#ifndef DESTRUM_LINERENDERINGMANAGER_H
|
|
||||||
#define DESTRUM_LINERENDERINGMANAGER_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/vec3.hpp>
|
|
||||||
#include <glm/vec4.hpp>
|
|
||||||
|
|
||||||
#include <destrum/Singleton.h>
|
|
||||||
|
|
||||||
struct Line {
|
|
||||||
glm::vec3 start{};
|
|
||||||
glm::vec3 end{};
|
|
||||||
glm::vec4 color{1.0f};
|
|
||||||
};
|
|
||||||
|
|
||||||
class LineRenderingManager final : public Singleton<LineRenderingManager> {
|
|
||||||
public:
|
|
||||||
friend class Singleton<LineRenderingManager>;
|
|
||||||
|
|
||||||
void SubmitLine(const Line& line);
|
|
||||||
void SubmitLine(
|
|
||||||
const glm::vec3& start,
|
|
||||||
const glm::vec3& end,
|
|
||||||
const glm::vec4& color = glm::vec4{1.0f});
|
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<Line>& GetLines() const { return lines; }
|
|
||||||
|
|
||||||
void ClearLines();
|
|
||||||
|
|
||||||
private:
|
|
||||||
LineRenderingManager() = default;
|
|
||||||
|
|
||||||
std::vector<Line> lines;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DESTRUM_LINERENDERINGMANAGER_H
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#ifndef DESTRUM_QUADRENDERINGMANAGER_H
|
|
||||||
#define DESTRUM_QUADRENDERINGMANAGER_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <destrum/Graphics/ids.h>
|
|
||||||
#include <destrum/Singleton.h>
|
|
||||||
|
|
||||||
struct QuadSubmission {
|
|
||||||
glm::vec2 position;
|
|
||||||
glm::vec2 size;
|
|
||||||
float rotation;
|
|
||||||
glm::vec4 color;
|
|
||||||
ImageID textureId;
|
|
||||||
};
|
|
||||||
|
|
||||||
class QuadRenderingManager final : public Singleton<QuadRenderingManager> {
|
|
||||||
public:
|
|
||||||
friend class Singleton<QuadRenderingManager>;
|
|
||||||
|
|
||||||
void SubmitQuad(
|
|
||||||
const glm::vec2& position,
|
|
||||||
const glm::vec2& size,
|
|
||||||
const glm::vec4& color = glm::vec4{1.0f},
|
|
||||||
ImageID textureId = NULL_IMAGE_ID);
|
|
||||||
|
|
||||||
void SubmitQuad(
|
|
||||||
const glm::vec2& position,
|
|
||||||
const glm::vec2& size,
|
|
||||||
float rotation,
|
|
||||||
const glm::vec4& color = glm::vec4{1.0f},
|
|
||||||
ImageID textureId = NULL_IMAGE_ID);
|
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<QuadSubmission>& GetQuads() const { return quads; }
|
|
||||||
void ClearQuads();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QuadRenderingManager() = default;
|
|
||||||
|
|
||||||
std::vector<QuadSubmission> quads;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DESTRUM_QUADRENDERINGMANAGER_H
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#ifndef LIGHTCULLINGPIPELINE_H
|
|
||||||
#define LIGHTCULLINGPIPELINE_H
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <destrum/Graphics/ComputePipeline.h>
|
|
||||||
#include <destrum/Graphics/Light.h>
|
|
||||||
#include <destrum/Graphics/ids.h>
|
|
||||||
#include <destrum/Graphics/Resources/AppendableBuffer.h>
|
|
||||||
#include <destrum/Graphics/Resources/Buffer.h>
|
|
||||||
|
|
||||||
class GfxDevice;
|
|
||||||
class RenderResources;
|
|
||||||
|
|
||||||
class LightCullingPipeline final {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static constexpr std::uint32_t TILE_SIZE = 16;
|
|
||||||
static constexpr std::uint32_t MAX_LIGHTS_PER_TILE = 255;
|
|
||||||
static constexpr std::uint32_t TILE_STRIDE = MAX_LIGHTS_PER_TILE + 1;
|
|
||||||
|
|
||||||
static constexpr std::size_t MAX_LIGHTS = 1024;
|
|
||||||
|
|
||||||
void init(GfxDevice& gfxDevice, RenderResources& resources, glm::ivec2 drawImageSize);
|
|
||||||
void cleanup(GfxDevice& gfxDevice);
|
|
||||||
|
|
||||||
void resize(GfxDevice& gfxDevice, glm::ivec2 drawImageSize);
|
|
||||||
|
|
||||||
void beginDrawing(std::size_t frameIndex);
|
|
||||||
void appendLight(const Light& light);
|
|
||||||
|
|
||||||
void flushCurrentFrame(VkCommandBuffer cmd, GfxDevice& gfxDevice);
|
|
||||||
|
|
||||||
void cullLights(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
ImageID depthImageId,
|
|
||||||
VkExtent2D renderExtent);
|
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceAddress getLightsBufferAddress() const;
|
|
||||||
[[nodiscard]] VkDeviceAddress getTileLightIndicesBufferAddress() const { return tileLightIndicesBuffer.address; }
|
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t getNumLights() const;
|
|
||||||
[[nodiscard]] glm::uvec2 getTileCount() const { return tileCount; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void createTileBuffer(GfxDevice& gfxDevice, glm::ivec2 drawImageSize);
|
|
||||||
|
|
||||||
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
|
||||||
std::unique_ptr<ComputePipeline> lightCullingPipeline;
|
|
||||||
|
|
||||||
struct PushConstants {
|
|
||||||
VkDeviceAddress sceneDataBuffer;
|
|
||||||
glm::uvec2 screenSize;
|
|
||||||
std::uint32_t depthTexId;
|
|
||||||
std::uint32_t padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PerFrameData {
|
|
||||||
AppendableBuffer<GPULight> lightsBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<PerFrameData, FRAMES_IN_FLIGHT> framesData;
|
|
||||||
std::size_t currentFrameIndex{0};
|
|
||||||
bool warnedAboutLightLimit{false};
|
|
||||||
|
|
||||||
GPUBuffer tileLightIndicesBuffer;
|
|
||||||
glm::uvec2 tileCount{0u, 0u};
|
|
||||||
|
|
||||||
PerFrameData& getCurrentFrameData();
|
|
||||||
const PerFrameData& getCurrentFrameData() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //LIGHTCULLINGPIPELINE_H
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#ifndef DESTRUM_LINERENDERINGPASS_H
|
|
||||||
#define DESTRUM_LINERENDERINGPASS_H
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
|
|
||||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
|
||||||
#include <destrum/Graphics/Resources/NBuffer.h>
|
|
||||||
|
|
||||||
class Camera;
|
|
||||||
class GfxDevice;
|
|
||||||
struct GPUImage;
|
|
||||||
class Pipeline;
|
|
||||||
|
|
||||||
class LineRenderingPass final {
|
|
||||||
public:
|
|
||||||
void init(GfxDevice& gfxDevice, VkFormat drawImageFormat);
|
|
||||||
void draw(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
const GPUImage& target,
|
|
||||||
const Camera& camera,
|
|
||||||
LineRenderingManager& lineManager);
|
|
||||||
void cleanup(GfxDevice& gfxDevice);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct LineVertex {
|
|
||||||
glm::vec4 position;
|
|
||||||
glm::vec4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ensureVertexCapacity(GfxDevice& gfxDevice, std::size_t requiredVertices);
|
|
||||||
|
|
||||||
VkPipelineLayout pipelineLayout{VK_NULL_HANDLE};
|
|
||||||
std::unique_ptr<Pipeline> pipeline;
|
|
||||||
NBuffer vertexBuffer;
|
|
||||||
std::size_t vertexCapacity{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DESTRUM_LINERENDERINGPASS_H
|
|
||||||
@@ -27,15 +27,6 @@ public:
|
|||||||
VkFormat drawImageFormat,
|
VkFormat drawImageFormat,
|
||||||
VkFormat depthImageFormat);
|
VkFormat depthImageFormat);
|
||||||
|
|
||||||
void drawDepthPrepass(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
VkExtent2D renderExtent,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const Camera& camera,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
|
||||||
const std::vector<std::size_t>& sortedDrawCommands);
|
|
||||||
|
|
||||||
void draw(
|
void draw(
|
||||||
VkCommandBuffer cmd,
|
VkCommandBuffer cmd,
|
||||||
VkExtent2D renderExtent,
|
VkExtent2D renderExtent,
|
||||||
@@ -64,20 +55,9 @@ private:
|
|||||||
std::uint32_t padding;
|
std::uint32_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
void drawMeshes(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
const Pipeline& pipeline,
|
|
||||||
VkExtent2D renderExtent,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const Camera& camera,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
|
||||||
const std::vector<std::size_t>& sortedDrawCommands);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
||||||
std::unique_ptr<Pipeline> m_pipeline;
|
std::unique_ptr<Pipeline> m_pipeline;
|
||||||
std::unique_ptr<Pipeline> m_depthPrepassPipeline;
|
|
||||||
|
|
||||||
bool m_renderWireframe{false};
|
bool m_renderWireframe{false};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
#ifndef DESTRUM_QUADRENDERERPASS_H
|
|
||||||
#define DESTRUM_QUADRENDERERPASS_H
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
|
|
||||||
#include <destrum/Graphics/Resources/Buffer.h>
|
|
||||||
|
|
||||||
|
|
||||||
class GfxDevice;
|
|
||||||
class Pipeline;
|
|
||||||
struct GPUImage;
|
|
||||||
class RenderResources;
|
|
||||||
|
|
||||||
class QuadRendererPass final {
|
|
||||||
public:
|
|
||||||
void init(GfxDevice& gfxDevice, RenderResources& resources, VkFormat drawImageFormat);
|
|
||||||
void draw(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
RenderResources& resources,
|
|
||||||
const GPUImage& target,
|
|
||||||
const glm::mat4& projection);
|
|
||||||
void cleanup(GfxDevice& gfxDevice);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct QuadVertex {
|
|
||||||
glm::vec2 position;
|
|
||||||
glm::vec2 uv;
|
|
||||||
glm::vec4 color;
|
|
||||||
std::uint32_t textureId;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ensureCapacity(GfxDevice& gfxDevice, std::size_t requiredQuads);
|
|
||||||
|
|
||||||
VkPipelineLayout pipelineLayout{VK_NULL_HANDLE};
|
|
||||||
std::unique_ptr<Pipeline> pipeline;
|
|
||||||
GPUBuffer vertexBuffer{};
|
|
||||||
GPUBuffer indexBuffer{};
|
|
||||||
std::size_t quadCapacity{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DESTRUM_QUADRENDERERPASS_H
|
|
||||||
@@ -35,6 +35,7 @@ private:
|
|||||||
struct SkyboxPushConstants {
|
struct SkyboxPushConstants {
|
||||||
glm::mat4 invViewProj;
|
glm::mat4 invViewProj;
|
||||||
glm::vec4 skyboxRot[3];
|
glm::vec4 skyboxRot[3];
|
||||||
|
glm::vec3 cameraPos;
|
||||||
std::uint32_t skyboxTextureId;
|
std::uint32_t skyboxTextureId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,7 @@ public:
|
|||||||
BindlessSetManager& getBindlessSetManager();
|
BindlessSetManager& getBindlessSetManager();
|
||||||
VkDescriptorSetLayout getBindlessDescSetLayout() const;
|
VkDescriptorSetLayout getBindlessDescSetLayout() const;
|
||||||
const VkDescriptorSet& getBindlessDescSet() const;
|
const VkDescriptorSet& getBindlessDescSet() const;
|
||||||
void bindBindlessDescSet(
|
void bindBindlessDescSet(VkCommandBuffer cmd, VkPipelineLayout layout) const;
|
||||||
VkCommandBuffer cmd,
|
|
||||||
VkPipelineLayout layout,
|
|
||||||
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS) const;
|
|
||||||
|
|
||||||
MeshCache& meshes() { return *meshCache; }
|
MeshCache& meshes() { return *meshCache; }
|
||||||
const MeshCache& meshes() const { return *meshCache; }
|
const MeshCache& meshes() const { return *meshCache; }
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
#ifndef RENDERER_H
|
#ifndef RENDERER_H
|
||||||
#define RENDERER_H
|
#define RENDERER_H
|
||||||
|
|
||||||
#include <glm/mat4x4.hpp>
|
|
||||||
#include <glm/vec3.hpp>
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
#include <destrum/Graphics/Camera.h>
|
#include <destrum/Graphics/Camera.h>
|
||||||
#include <destrum/Graphics/Light.h>
|
|
||||||
#include <destrum/Graphics/Pipelines/LightCullingPipeline.h>
|
|
||||||
#include <destrum/Graphics/Pipelines/LineRenderingPass.h>
|
|
||||||
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
|
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
|
||||||
#include <destrum/Graphics/Pipelines/QuadRendererPass.h>
|
|
||||||
#include <destrum/Graphics/ids.h>
|
#include <destrum/Graphics/ids.h>
|
||||||
#include <destrum/Graphics/MeshDrawCommand.h>
|
#include <destrum/Graphics/MeshDrawCommand.h>
|
||||||
#include <destrum/Graphics/Resources/NBuffer.h>
|
#include <destrum/Graphics/Resources/NBuffer.h>
|
||||||
@@ -22,11 +17,9 @@
|
|||||||
|
|
||||||
#include "Pipelines/SkyboxPipeline.h"
|
#include "Pipelines/SkyboxPipeline.h"
|
||||||
|
|
||||||
class GameRenderer
|
class GameRenderer {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
struct SceneData
|
struct SceneData {
|
||||||
{
|
|
||||||
const Camera& camera;
|
const Camera& camera;
|
||||||
glm::vec3 ambientColor;
|
glm::vec3 ambientColor;
|
||||||
float ambientIntensity;
|
float ambientIntensity;
|
||||||
@@ -44,15 +37,11 @@ public:
|
|||||||
void cleanup(GfxDevice& gfxDevice);
|
void cleanup(GfxDevice& gfxDevice);
|
||||||
|
|
||||||
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
|
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
|
||||||
void drawSkinnedMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, SkinnedMesh* skinnedMesh,
|
void drawSkinnedMesh(MeshID id, const glm::mat4& transform, MaterialID materialId, SkinnedMesh* skinnedMesh, std::size_t jointMatricesStartIndex);
|
||||||
std::size_t jointMatricesStartIndex);
|
|
||||||
void submitLight(const Light& light);
|
|
||||||
const GPUImage& getDrawImage() const;
|
const GPUImage& getDrawImage() const;
|
||||||
|
|
||||||
void resize(GfxDevice& gfxDevice, const glm::ivec2& newSize)
|
void resize(GfxDevice& gfxDevice, const glm::ivec2& newSize) {
|
||||||
{
|
|
||||||
createDrawImage(gfxDevice, newSize, false);
|
createDrawImage(gfxDevice, newSize, false);
|
||||||
lightCullingPipeline->resize(gfxDevice, newSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Material& getMaterialMutable(MaterialID id);
|
Material& getMaterialMutable(MaterialID id);
|
||||||
@@ -62,41 +51,23 @@ public:
|
|||||||
|
|
||||||
void flushMaterialUpdates(GfxDevice& gfxDevice);
|
void flushMaterialUpdates(GfxDevice& gfxDevice);
|
||||||
|
|
||||||
[[nodiscard]] SkinningPipeline& getSkinningPipeline() const
|
[[nodiscard]] SkinningPipeline& getSkinningPipeline() const {
|
||||||
{
|
|
||||||
return *skinningPipeline;
|
return *skinningPipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRenderWireframe(bool wireframe)
|
void setRenderWireframe(bool wireframe) {
|
||||||
{
|
|
||||||
meshPipeline->setRenderWireframe(wireframe);
|
meshPipeline->setRenderWireframe(wireframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getRenderWireframe()
|
bool getRenderWireframe() {
|
||||||
{
|
|
||||||
return meshPipeline->getRenderWireframe();
|
return meshPipeline->getRenderWireframe();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRenderLightHeatmap(bool heatmap)
|
|
||||||
{
|
|
||||||
renderLightHeatmap = heatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getRenderLightHeatmap() const
|
|
||||||
{
|
|
||||||
return renderLightHeatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResources* getResources()
|
RenderResources* getResources()
|
||||||
{
|
{
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawQuads(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
const glm::mat4& projection);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
||||||
|
|
||||||
@@ -117,41 +88,31 @@ private:
|
|||||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||||
|
|
||||||
|
|
||||||
struct GPUSceneData
|
struct GPUSceneData {
|
||||||
{
|
// camera
|
||||||
glm::mat4 view;
|
glm::mat4 view;
|
||||||
glm::mat4 proj;
|
glm::mat4 proj;
|
||||||
glm::mat4 invProj;
|
|
||||||
glm::mat4 viewProj;
|
glm::mat4 viewProj;
|
||||||
glm::vec4 cameraPos;
|
glm::vec4 cameraPos;
|
||||||
|
|
||||||
|
// ambient
|
||||||
glm::vec3 ambientColor;
|
glm::vec3 ambientColor;
|
||||||
float ambientIntensity;
|
float ambientIntensity;
|
||||||
|
|
||||||
|
// fog
|
||||||
glm::vec3 fogColor;
|
glm::vec3 fogColor;
|
||||||
float fogDensity;
|
float fogDensity;
|
||||||
|
|
||||||
VkDeviceAddress materialsBuffer;
|
VkDeviceAddress materialsBuffer;
|
||||||
|
|
||||||
VkDeviceAddress lightsBuffer;
|
|
||||||
VkDeviceAddress tileLightIndicesBuffer;
|
|
||||||
std::uint32_t numLights;
|
|
||||||
std::uint32_t tileCountX;
|
|
||||||
std::uint32_t debugLightHeatmap;
|
|
||||||
std::uint32_t padding;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NBuffer sceneDataBuffer;
|
NBuffer sceneDataBuffer;
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<MeshPipeline> meshPipeline;
|
std::unique_ptr<MeshPipeline> meshPipeline;
|
||||||
std::unique_ptr<SkyboxPipeline> skyboxPipeline;
|
std::unique_ptr<SkyboxPipeline> skyboxPipeline;
|
||||||
std::unique_ptr<LineRenderingPass> lineRenderingPass;
|
|
||||||
std::unique_ptr<QuadRendererPass> quadRendererPass;
|
|
||||||
|
|
||||||
std::unique_ptr<SkinningPipeline> skinningPipeline;
|
std::unique_ptr<SkinningPipeline> skinningPipeline;
|
||||||
std::unique_ptr<LightCullingPipeline> lightCullingPipeline;
|
|
||||||
|
|
||||||
bool renderLightHeatmap{false};
|
|
||||||
bool initialized{false};
|
bool initialized{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ public:
|
|||||||
|
|
||||||
int MouseX() const { return m_mouseX; }
|
int MouseX() const { return m_mouseX; }
|
||||||
int MouseY() const { return m_mouseY; }
|
int MouseY() const { return m_mouseY; }
|
||||||
int MouseDeltaX() const { return m_mouseRelDX; }
|
int MouseDeltaX() const { return m_mouseDX; }
|
||||||
int MouseDeltaY() const { return m_mouseRelDY; }
|
int MouseDeltaY() const { return m_mouseDY; }
|
||||||
|
|
||||||
int WheelX() const { return m_wheelX; }
|
int WheelX() const { return m_wheelX; }
|
||||||
int WheelY() const { return m_wheelY; }
|
int WheelY() const { return m_wheelY; }
|
||||||
@@ -81,9 +81,6 @@ public:
|
|||||||
|
|
||||||
void SetAxisDeadzone(int deadzone) { m_axisDeadzone = deadzone; } // 0..32767
|
void SetAxisDeadzone(int deadzone) { m_axisDeadzone = deadzone; } // 0..32767
|
||||||
|
|
||||||
void SetMouseCaptured(bool captured);
|
|
||||||
[[nodiscard]] bool IsMouseCaptured() const { return m_MouseCaptured; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Key states
|
// Key states
|
||||||
std::unordered_set<SDL_Scancode> m_keysDown;
|
std::unordered_set<SDL_Scancode> m_keysDown;
|
||||||
@@ -95,9 +92,10 @@ private:
|
|||||||
std::unordered_set<Uint8> m_mousePressed;
|
std::unordered_set<Uint8> m_mousePressed;
|
||||||
std::unordered_set<Uint8> m_mouseReleased;
|
std::unordered_set<Uint8> m_mouseReleased;
|
||||||
|
|
||||||
// Mouse position + relative delta (accumulated from SDL xrel/yrel)
|
// Mouse position + delta
|
||||||
int m_mouseX = 0, m_mouseY = 0;
|
int m_mouseX = 0, m_mouseY = 0;
|
||||||
int m_mouseRelDX = 0, m_mouseRelDY = 0;
|
int m_prevMouseX = 0, m_prevMouseY = 0;
|
||||||
|
int m_mouseDX = 0, m_mouseDY = 0;
|
||||||
|
|
||||||
int m_wheelX = 0, m_wheelY = 0;
|
int m_wheelX = 0, m_wheelY = 0;
|
||||||
|
|
||||||
@@ -127,8 +125,6 @@ private:
|
|||||||
|
|
||||||
int m_axisDeadzone = 8000; // typical deadzone
|
int m_axisDeadzone = 8000; // typical deadzone
|
||||||
|
|
||||||
bool m_MouseCaptured = false;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool QueryBinding(const Binding& b, ButtonState state) const;
|
bool QueryBinding(const Binding& b, ButtonState state) const;
|
||||||
|
|
||||||
|
|||||||
@@ -59,16 +59,6 @@ public:
|
|||||||
virtual void ResolveReferences(const ObjectMap&) {
|
virtual void ResolveReferences(const ObjectMap&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger callbacks called when this component's owner overlaps a sensor.
|
|
||||||
// Only called when the owner has a Collider set as a trigger (or overlaps one).
|
|
||||||
virtual void OnTriggerEnter(GameObject* other) {
|
|
||||||
(void)other;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void OnTriggerExit(GameObject* other) {
|
|
||||||
(void)other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasStarted{false};
|
bool HasStarted{false};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ public:
|
|||||||
float maxDistance,
|
float maxDistance,
|
||||||
PhysicsRaycastHit& hit) const override;
|
PhysicsRaycastHit& hit) const override;
|
||||||
|
|
||||||
std::vector<TriggerEvent> ConsumeTriggerEvents() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_Impl;
|
std::unique_ptr<Impl> m_Impl;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class PhysicsSceneBridge final {
|
|||||||
public:
|
public:
|
||||||
explicit PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world);
|
explicit PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world);
|
||||||
|
|
||||||
[[nodiscard]] bool IsValid() const { return m_World != nullptr; }
|
|
||||||
[[nodiscard]] PhysicsWorld& GetWorld() { return *m_World; }
|
[[nodiscard]] PhysicsWorld& GetWorld() { return *m_World; }
|
||||||
[[nodiscard]] const PhysicsWorld& GetWorld() const { return *m_World; }
|
[[nodiscard]] const PhysicsWorld& GetWorld() const { return *m_World; }
|
||||||
|
|
||||||
|
|||||||
@@ -116,9 +116,3 @@ struct PhysicsBodyDesc {
|
|||||||
bool useGravity{true};
|
bool useGravity{true};
|
||||||
bool allowSleep{true};
|
bool allowSleep{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TriggerEvent {
|
|
||||||
GameObject* owner{nullptr};
|
|
||||||
GameObject* other{nullptr};
|
|
||||||
bool entered{true};
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -51,9 +49,6 @@ public:
|
|||||||
float maxDistance,
|
float maxDistance,
|
||||||
PhysicsRaycastHit& hit) const = 0;
|
PhysicsRaycastHit& hit) const = 0;
|
||||||
|
|
||||||
// Trigger events accumulated during the last Step().
|
|
||||||
virtual std::vector<TriggerEvent> ConsumeTriggerEvents() { return {}; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_set<Rigidbody*> m_RegisteredRigidbodies;
|
std::unordered_set<Rigidbody*> m_RegisteredRigidbodies;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <destrum/Event.h>
|
#include <destrum/Event.h>
|
||||||
#include <destrum/ObjectModel/ObjectId.h>
|
#include <destrum/ObjectModel/ObjectId.h>
|
||||||
#include <destrum/Scene/SceneManager.h>
|
#include <destrum/Scene/SceneManager.h>
|
||||||
@@ -14,7 +12,6 @@
|
|||||||
|
|
||||||
class GameObject;
|
class GameObject;
|
||||||
class SceneSerializer;
|
class SceneSerializer;
|
||||||
class PhysicsWorld;
|
|
||||||
|
|
||||||
class Scene final {
|
class Scene final {
|
||||||
friend Scene& SceneManager::CreateScene(const std::string& name);
|
friend Scene& SceneManager::CreateScene(const std::string& name);
|
||||||
@@ -107,7 +104,6 @@ public:
|
|||||||
PhysicsSceneBridge& GetPhysics() { return m_Physics; }
|
PhysicsSceneBridge& GetPhysics() { return m_Physics; }
|
||||||
private:
|
private:
|
||||||
explicit Scene(const std::string& name);
|
explicit Scene(const std::string& name);
|
||||||
explicit Scene(const std::string& name, std::unique_ptr<PhysicsWorld> physicsWorld);
|
|
||||||
|
|
||||||
PhysicsSceneBridge m_Physics{std::make_unique<JoltPhysicsWorld>()};
|
PhysicsSceneBridge m_Physics{std::make_unique<JoltPhysicsWorld>()};
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public:
|
|||||||
Scene& CreateScene(const std::string& name);
|
Scene& CreateScene(const std::string& name);
|
||||||
|
|
||||||
Scene& GetCurrentScene() const;
|
Scene& GetCurrentScene() const;
|
||||||
const std::vector<std::shared_ptr<Scene>>& GetActiveScenes() const { return m_activeScenes; }
|
|
||||||
|
|
||||||
void Update(float dt);
|
void Update(float dt);
|
||||||
void FixedUpdate(float dt);
|
void FixedUpdate(float dt);
|
||||||
@@ -35,9 +34,7 @@ public:
|
|||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
void SwitchScene(int index);
|
void SwitchScene(int index);
|
||||||
void AddActiveScene(int index);
|
int GetActiveSceneId() const { return m_scenes.empty() ? -1 : m_ActiveSceneIndex; }
|
||||||
void RemoveActiveScene(int index);
|
|
||||||
int GetActiveSceneId() const;
|
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<std::shared_ptr<Scene>>& GetScenes() const { return m_scenes; }
|
[[nodiscard]] const std::vector<std::shared_ptr<Scene>>& GetScenes() const { return m_scenes; }
|
||||||
|
|
||||||
@@ -50,7 +47,8 @@ private:
|
|||||||
|
|
||||||
SceneManager() = default;
|
SceneManager() = default;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Scene>> m_activeScenes;
|
int m_ActiveSceneIndex{0};
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Scene>> m_scenes;
|
std::vector<std::shared_ptr<Scene>> m_scenes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#ifndef DESTRUM_ENGINECOMPONENTLIST_H
|
#ifndef DESTRUM_ENGINECOMPONENTLIST_H
|
||||||
#define DESTRUM_ENGINECOMPONENTLIST_H
|
#define DESTRUM_ENGINECOMPONENTLIST_H
|
||||||
|
|
||||||
|
// Keep the component manifest in one place. Consumers define X before
|
||||||
|
// expanding one of these lists to generate registration or other metadata.
|
||||||
#include <destrum/Components/Animator.h>
|
#include <destrum/Components/Animator.h>
|
||||||
#include <destrum/Components/LightComponent.h>
|
|
||||||
#include <destrum/Components/MeshRendererComponent.h>
|
#include <destrum/Components/MeshRendererComponent.h>
|
||||||
#include <destrum/Components/OrbitAndSpin.h>
|
#include <destrum/Components/OrbitAndSpin.h>
|
||||||
#include <destrum/Components/Rotator.h>
|
#include <destrum/Components/Rotator.h>
|
||||||
@@ -14,7 +15,6 @@
|
|||||||
|
|
||||||
#define DESTRUM_ENGINE_COMPONENTS(X) \
|
#define DESTRUM_ENGINE_COMPONENTS(X) \
|
||||||
X(MeshRendererComponent) \
|
X(MeshRendererComponent) \
|
||||||
X(LightComponent) \
|
|
||||||
X(Rotator) \
|
X(Rotator) \
|
||||||
X(Spinner) \
|
X(Spinner) \
|
||||||
X(OrbitAndSpin) \
|
X(OrbitAndSpin) \
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
X(Rigidbody) \
|
X(Rigidbody) \
|
||||||
X(BoxCollider) \
|
X(BoxCollider) \
|
||||||
X(SphereCollider) \
|
X(SphereCollider) \
|
||||||
X(CapsuleCollider) \
|
X(CapsuleCollider)
|
||||||
|
|
||||||
#endif // DESTRUM_ENGINECOMPONENTLIST_H
|
#endif // DESTRUM_ENGINECOMPONENTLIST_H
|
||||||
|
|||||||
@@ -2,34 +2,13 @@
|
|||||||
#define DESTRUM_SCENESERIALIZER_H
|
#define DESTRUM_SCENESERIALIZER_H
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
|
||||||
#include <future>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
class Scene;
|
class Scene;
|
||||||
|
|
||||||
class SceneSerializer final {
|
class SceneSerializer final {
|
||||||
public:
|
public:
|
||||||
struct LoadResult {
|
|
||||||
bool success{false};
|
|
||||||
std::string errorMessage;
|
|
||||||
nlohmann::json root;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool Save(Scene& scene, const std::filesystem::path& path);
|
static bool Save(Scene& scene, const std::filesystem::path& path);
|
||||||
|
static bool Load(Scene& scene, const std::filesystem::path& path);
|
||||||
static bool Load(Scene& scene, const std::filesystem::path& path,
|
|
||||||
std::function<void(float)> progressCallback = nullptr);
|
|
||||||
|
|
||||||
static LoadResult LoadSceneFile(const std::filesystem::path& path);
|
|
||||||
|
|
||||||
static bool ConstructScene(Scene& scene, LoadResult& result,
|
|
||||||
std::function<void(float)> progressCallback = nullptr);
|
|
||||||
|
|
||||||
// Parse the scene file on a background thread. Call LoadFromJson on the
|
|
||||||
// main thread once the future is ready.
|
|
||||||
static std::future<LoadResult> LoadSceneFileAsync(const std::filesystem::path& path);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //DESTRUM_SCENESERIALIZER_H
|
#endif //DESTRUM_SCENESERIALIZER_H
|
||||||
|
|||||||
+8
-8
@@ -131,12 +131,6 @@ void App::run()
|
|||||||
|
|
||||||
imguiPass.handleEvent(event);
|
imguiPass.handleEvent(event);
|
||||||
|
|
||||||
const bool mouseEvent =
|
|
||||||
event.type == SDL_MOUSEBUTTONDOWN ||
|
|
||||||
event.type == SDL_MOUSEBUTTONUP ||
|
|
||||||
event.type == SDL_MOUSEMOTION ||
|
|
||||||
event.type == SDL_MOUSEWHEEL;
|
|
||||||
|
|
||||||
if (event.type == SDL_QUIT)
|
if (event.type == SDL_QUIT)
|
||||||
{
|
{
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
@@ -155,13 +149,19 @@ void App::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool mouseEvent =
|
||||||
|
event.type == SDL_MOUSEBUTTONDOWN ||
|
||||||
|
event.type == SDL_MOUSEBUTTONUP ||
|
||||||
|
event.type == SDL_MOUSEMOTION ||
|
||||||
|
event.type == SDL_MOUSEWHEEL;
|
||||||
|
|
||||||
const bool keyboardEvent =
|
const bool keyboardEvent =
|
||||||
event.type == SDL_KEYDOWN ||
|
event.type == SDL_KEYDOWN ||
|
||||||
event.type == SDL_KEYUP ||
|
event.type == SDL_KEYUP ||
|
||||||
event.type == SDL_TEXTINPUT;
|
event.type == SDL_TEXTINPUT;
|
||||||
|
|
||||||
const bool capturedByImgui =
|
const bool capturedByImgui =
|
||||||
(mouseEvent && !InputManager::GetInstance().IsMouseCaptured() && imguiPass.wantsMouse()) ||
|
(mouseEvent && imguiPass.wantsMouse()) ||
|
||||||
(keyboardEvent && imguiPass.wantsKeyboard());
|
(keyboardEvent && imguiPass.wantsKeyboard());
|
||||||
|
|
||||||
if (!capturedByImgui)
|
if (!capturedByImgui)
|
||||||
@@ -176,7 +176,7 @@ void App::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isRunning) break;
|
if (!isRunning) break;
|
||||||
|
|
||||||
// Consume SDL events before updating the base camera so held and
|
// Consume SDL events before updating the base camera so held and
|
||||||
// newly pressed inputs are applied in the same frame.
|
// newly pressed inputs are applied in the same frame.
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
#include <destrum/Components/LightComponent.h>
|
|
||||||
|
|
||||||
#include <destrum/Graphics/RenderContext.h>
|
|
||||||
#include <destrum/ObjectModel/Transform.h>
|
|
||||||
|
|
||||||
#include <glm/gtc/constants.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
LightComponent::LightComponent(GameObject& parent, LightType type)
|
|
||||||
: Component(parent, "LightComponent")
|
|
||||||
, m_Type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::SetIntensity(float intensity)
|
|
||||||
{
|
|
||||||
m_Intensity = std::max(intensity, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::SetRange(float range)
|
|
||||||
{
|
|
||||||
m_Range = std::max(range, 0.01f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::SetConeAngles(float innerConeAngle, float outerConeAngle)
|
|
||||||
{
|
|
||||||
m_InnerConeAngle = std::clamp(innerConeAngle, 0.0f, glm::half_pi<float>());
|
|
||||||
m_OuterConeAngle = std::clamp(outerConeAngle, m_InnerConeAngle, glm::half_pi<float>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::Render(const RenderContext& ctx)
|
|
||||||
{
|
|
||||||
auto& transform = GetTransform();
|
|
||||||
|
|
||||||
ctx.renderer.submitLight(Light{
|
|
||||||
.type = m_Type,
|
|
||||||
.position = transform.GetWorldPosition(),
|
|
||||||
.direction = transform.GetWorldRotation() * glm::vec3{0.0f, 0.0f, -1.0f},
|
|
||||||
.color = m_Color,
|
|
||||||
.intensity = m_Intensity,
|
|
||||||
.range = m_Range,
|
|
||||||
.innerConeAngle = m_InnerConeAngle,
|
|
||||||
.outerConeAngle = m_OuterConeAngle,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json LightComponent::Serialize() const {
|
|
||||||
return {
|
|
||||||
{"type", static_cast<std::uint32_t>(m_Type)},
|
|
||||||
{"color", {m_Color.x, m_Color.y, m_Color.z}},
|
|
||||||
{"intensity", m_Intensity},
|
|
||||||
{"range", m_Range},
|
|
||||||
{"innerConeAngle", m_InnerConeAngle},
|
|
||||||
{"outerConeAngle", m_OuterConeAngle}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::Deserialize(const nlohmann::json& data) {
|
|
||||||
if (data.contains("type")) {
|
|
||||||
const auto type = data.at("type").get<std::uint32_t>();
|
|
||||||
if (type <= static_cast<std::uint32_t>(LightType::Spot)) {
|
|
||||||
m_Type = static_cast<LightType>(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.contains("color")) {
|
|
||||||
const auto& value = data.at("color");
|
|
||||||
m_Color = {value.at(0).get<float>(), value.at(1).get<float>(), value.at(2).get<float>()};
|
|
||||||
}
|
|
||||||
if (data.contains("intensity")) {
|
|
||||||
SetIntensity(data.at("intensity").get<float>());
|
|
||||||
}
|
|
||||||
if (data.contains("range")) {
|
|
||||||
SetRange(data.at("range").get<float>());
|
|
||||||
}
|
|
||||||
|
|
||||||
float innerConeAngle = m_InnerConeAngle;
|
|
||||||
float outerConeAngle = m_OuterConeAngle;
|
|
||||||
if (data.contains("innerConeAngle")) {
|
|
||||||
innerConeAngle = data.at("innerConeAngle").get<float>();
|
|
||||||
}
|
|
||||||
if (data.contains("outerConeAngle")) {
|
|
||||||
outerConeAngle = data.at("outerConeAngle").get<float>();
|
|
||||||
}
|
|
||||||
SetConeAngles(innerConeAngle, outerConeAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightComponent::ImGuiInspector() {
|
|
||||||
constexpr std::array typeNames{"Directional", "Point", "Spot"};
|
|
||||||
|
|
||||||
int type = static_cast<int>(m_Type);
|
|
||||||
if (ImGui::Combo("Type", &type, typeNames.data(), static_cast<int>(typeNames.size()))) {
|
|
||||||
SetType(static_cast<LightType>(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::ColorEdit3("Color", &m_Color.x);
|
|
||||||
|
|
||||||
float intensity = m_Intensity;
|
|
||||||
if (ImGui::DragFloat("Intensity", &intensity, 0.05f, 0.0f)) {
|
|
||||||
SetIntensity(intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Type == LightType::Directional) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float range = m_Range;
|
|
||||||
if (ImGui::DragFloat("Range", &range, 0.05f, 0.01f)) {
|
|
||||||
SetRange(range);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Type == LightType::Spot) {
|
|
||||||
float innerConeAngle = m_InnerConeAngle;
|
|
||||||
float outerConeAngle = m_OuterConeAngle;
|
|
||||||
bool changed = ImGui::SliderAngle("Inner Cone", &innerConeAngle, 0.0f, 90.0f);
|
|
||||||
changed |= ImGui::SliderAngle("Outer Cone", &outerConeAngle, 0.0f, 90.0f);
|
|
||||||
if (changed) {
|
|
||||||
SetConeAngles(innerConeAngle, outerConeAngle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -114,7 +114,7 @@ void Camera::SetRotation(const glm::vec2& yawPitchRadians) {
|
|||||||
SetRotation(yawPitchRadians.x, yawPitchRadians.y);
|
SetRotation(yawPitchRadians.x, yawPitchRadians.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::SetTarget(const glm::vec3&) {
|
void Camera::SetTarget(const glm::vec3& target) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::ClearTarget() {
|
void Camera::ClearTarget() {
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
|
||||||
|
|
||||||
void LineRenderingManager::SubmitLine(const Line& line)
|
|
||||||
{
|
|
||||||
lines.push_back(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingManager::SubmitLine(
|
|
||||||
const glm::vec3& start,
|
|
||||||
const glm::vec3& end,
|
|
||||||
const glm::vec4& color)
|
|
||||||
{
|
|
||||||
SubmitLine(Line{
|
|
||||||
.start = start,
|
|
||||||
.end = end,
|
|
||||||
.color = color,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingManager::ClearLines()
|
|
||||||
{
|
|
||||||
lines.clear();
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#include <destrum/Graphics/Managers/QuadRenderingManager.h>
|
|
||||||
|
|
||||||
void QuadRenderingManager::SubmitQuad(
|
|
||||||
const glm::vec2& position,
|
|
||||||
const glm::vec2& size,
|
|
||||||
const glm::vec4& color,
|
|
||||||
ImageID textureId)
|
|
||||||
{
|
|
||||||
SubmitQuad(position, size, 0.0f, color, textureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRenderingManager::SubmitQuad(
|
|
||||||
const glm::vec2& position,
|
|
||||||
const glm::vec2& size,
|
|
||||||
float rotation,
|
|
||||||
const glm::vec4& color,
|
|
||||||
ImageID textureId)
|
|
||||||
{
|
|
||||||
quads.push_back(QuadSubmission{
|
|
||||||
.position = position,
|
|
||||||
.size = size,
|
|
||||||
.rotation = rotation,
|
|
||||||
.color = color,
|
|
||||||
.textureId = textureId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRenderingManager::ClearQuads()
|
|
||||||
{
|
|
||||||
quads.clear();
|
|
||||||
}
|
|
||||||
@@ -124,11 +124,6 @@ void ImguiPass::beginFrame() {
|
|||||||
|
|
||||||
ImGui_ImplVulkan_NewFrame();
|
ImGui_ImplVulkan_NewFrame();
|
||||||
ImGui_ImplSDL2_NewFrame();
|
ImGui_ImplSDL2_NewFrame();
|
||||||
|
|
||||||
if (SDL_GetRelativeMouseMode() == SDL_TRUE) {
|
|
||||||
ImGui::GetIO().MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
frameBegun = true;
|
frameBegun = true;
|
||||||
|
|||||||
@@ -1,271 +0,0 @@
|
|||||||
#include <destrum/Graphics/Pipelines/LightCullingPipeline.h>
|
|
||||||
|
|
||||||
#include "destrum/FS/AssetFS.h"
|
|
||||||
#include "destrum/Graphics/GfxDevice.h"
|
|
||||||
#include "destrum/Graphics/RenderResources.h"
|
|
||||||
#include "destrum/Graphics/Util.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include "volk.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
void LightCullingPipeline::init(GfxDevice& gfxDevice, RenderResources& resources, glm::ivec2 drawImageSize)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto& device = gfxDevice.getDevice();
|
|
||||||
|
|
||||||
const auto pushConstant = VkPushConstantRange{
|
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
.offset = 0,
|
|
||||||
.size = sizeof(PushConstants),
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto pushConstants = std::array{pushConstant};
|
|
||||||
|
|
||||||
const auto layouts = std::array{
|
|
||||||
resources.getBindlessDescSetLayout()
|
|
||||||
};
|
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
|
||||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
||||||
pipelineLayoutInfo.setLayoutCount = static_cast<std::uint32_t>(layouts.size());
|
|
||||||
pipelineLayoutInfo.pSetLayouts = layouts.data();
|
|
||||||
pipelineLayoutInfo.pushConstantRangeCount = static_cast<std::uint32_t>(pushConstants.size());
|
|
||||||
pipelineLayoutInfo.pPushConstantRanges = pushConstants.data();
|
|
||||||
|
|
||||||
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Could not make light culling pipeline layout");
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePipelineConfigInfo pipelineConfig{};
|
|
||||||
pipelineConfig.name = "light culling compute pipeline";
|
|
||||||
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
|
||||||
|
|
||||||
const auto lightCullingShaderPath = AssetFS::GetInstance().GetCookedPathForFile(
|
|
||||||
"engine://shaders/light_culling.comp");
|
|
||||||
|
|
||||||
lightCullingPipeline = std::make_unique<ComputePipeline>(
|
|
||||||
gfxDevice,
|
|
||||||
lightCullingShaderPath.generic_string(),
|
|
||||||
pipelineConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < FRAMES_IN_FLIGHT; ++i)
|
|
||||||
{
|
|
||||||
auto& lightsBuffer = framesData[i].lightsBuffer;
|
|
||||||
lightsBuffer.capacity = MAX_LIGHTS;
|
|
||||||
lightsBuffer.buffer = gfxDevice.createBuffer(
|
|
||||||
MAX_LIGHTS * sizeof(GPULight),
|
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
|
||||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
|
||||||
vkutil::addDebugLabel(device, lightsBuffer.buffer.buffer, "lights");
|
|
||||||
}
|
|
||||||
|
|
||||||
createTileBuffer(gfxDevice, drawImageSize);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
cleanup(gfxDevice);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::cleanup(GfxDevice& gfxDevice)
|
|
||||||
{
|
|
||||||
for (auto& frame : framesData)
|
|
||||||
{
|
|
||||||
gfxDevice.destroyBuffer(frame.lightsBuffer.buffer);
|
|
||||||
frame.lightsBuffer.buffer = {};
|
|
||||||
frame.lightsBuffer.size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfxDevice.destroyBuffer(tileLightIndicesBuffer);
|
|
||||||
tileLightIndicesBuffer = {};
|
|
||||||
tileCount = glm::uvec2{0u, 0u};
|
|
||||||
|
|
||||||
if (m_pipelineLayout != VK_NULL_HANDLE && gfxDevice.getDevice() != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyPipelineLayout(gfxDevice.getDevice(), m_pipelineLayout, nullptr);
|
|
||||||
}
|
|
||||||
m_pipelineLayout = VK_NULL_HANDLE;
|
|
||||||
lightCullingPipeline.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::resize(GfxDevice& gfxDevice, glm::ivec2 drawImageSize)
|
|
||||||
{
|
|
||||||
gfxDevice.waitIdle();
|
|
||||||
|
|
||||||
gfxDevice.destroyBuffer(tileLightIndicesBuffer);
|
|
||||||
tileLightIndicesBuffer = {};
|
|
||||||
createTileBuffer(gfxDevice, drawImageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::createTileBuffer(GfxDevice& gfxDevice, glm::ivec2 drawImageSize)
|
|
||||||
{
|
|
||||||
assert(drawImageSize.x > 0 && drawImageSize.y > 0);
|
|
||||||
|
|
||||||
tileCount = glm::uvec2{
|
|
||||||
(static_cast<std::uint32_t>(drawImageSize.x) + TILE_SIZE - 1) / TILE_SIZE,
|
|
||||||
(static_cast<std::uint32_t>(drawImageSize.y) + TILE_SIZE - 1) / TILE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::size_t numTiles = static_cast<std::size_t>(tileCount.x) * tileCount.y;
|
|
||||||
tileLightIndicesBuffer = gfxDevice.createBuffer(
|
|
||||||
numTiles * TILE_STRIDE * sizeof(std::uint32_t),
|
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
|
|
||||||
vkutil::addDebugLabel(gfxDevice.getDevice(), tileLightIndicesBuffer.buffer, "tile light indices");
|
|
||||||
|
|
||||||
spdlog::debug("Forward+ light grid: {}x{} tiles", tileCount.x, tileCount.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::beginDrawing(std::size_t frameIndex)
|
|
||||||
{
|
|
||||||
currentFrameIndex = frameIndex;
|
|
||||||
getCurrentFrameData().lightsBuffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::appendLight(const Light& light)
|
|
||||||
{
|
|
||||||
auto& lightsBuffer = getCurrentFrameData().lightsBuffer;
|
|
||||||
if (lightsBuffer.size >= lightsBuffer.capacity)
|
|
||||||
{
|
|
||||||
if (!warnedAboutLightLimit)
|
|
||||||
{
|
|
||||||
spdlog::warn("Light limit of {} reached, extra lights are ignored", MAX_LIGHTS);
|
|
||||||
warnedAboutLightLimit = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float directionLength = glm::length(light.direction);
|
|
||||||
const glm::vec3 direction = directionLength > 1e-6f
|
|
||||||
? light.direction / directionLength
|
|
||||||
: glm::vec3{0.0f, -1.0f, 0.0f};
|
|
||||||
|
|
||||||
const auto gpuLight = GPULight{
|
|
||||||
.position = light.position,
|
|
||||||
.range = light.range,
|
|
||||||
.color = light.color,
|
|
||||||
.intensity = light.intensity,
|
|
||||||
.direction = direction,
|
|
||||||
.type = static_cast<std::uint32_t>(light.type),
|
|
||||||
.spotInnerCos = std::cos(light.innerConeAngle),
|
|
||||||
.spotOuterCos = std::cos(light.outerConeAngle),
|
|
||||||
.padding = glm::vec2{0.0f},
|
|
||||||
};
|
|
||||||
lightsBuffer.append(gpuLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::flushCurrentFrame(VkCommandBuffer cmd, GfxDevice& gfxDevice)
|
|
||||||
{
|
|
||||||
auto& buffer = getCurrentFrameData().lightsBuffer.buffer;
|
|
||||||
gfxDevice.getMemoryManager().flushAllocation(buffer, 0, VK_WHOLE_SIZE);
|
|
||||||
vkutil::bufferHostWriteToShaderReadBarrier(
|
|
||||||
cmd,
|
|
||||||
buffer.buffer,
|
|
||||||
0,
|
|
||||||
static_cast<VkDeviceSize>(MAX_LIGHTS * sizeof(GPULight)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightCullingPipeline::cullLights(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
ImageID depthImageId,
|
|
||||||
VkExtent2D renderExtent)
|
|
||||||
{
|
|
||||||
if (!lightCullingPipeline)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// previous frame's mesh pass might still be reading the tile lists
|
|
||||||
const VkBufferMemoryBarrier2 tileBufferBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.buffer = tileLightIndicesBuffer.buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.size = VK_WHOLE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkDependencyInfo dependencyInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
||||||
.bufferMemoryBarrierCount = 1,
|
|
||||||
.pBufferMemoryBarriers = &tileBufferBarrier,
|
|
||||||
};
|
|
||||||
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
lightCullingPipeline->bind(cmd);
|
|
||||||
resources.bindBindlessDescSet(cmd, m_pipelineLayout, VK_PIPELINE_BIND_POINT_COMPUTE);
|
|
||||||
|
|
||||||
const auto cs = PushConstants{
|
|
||||||
.sceneDataBuffer = sceneDataBuffer.address,
|
|
||||||
.screenSize = glm::uvec2{renderExtent.width, renderExtent.height},
|
|
||||||
.depthTexId = static_cast<std::uint32_t>(depthImageId),
|
|
||||||
.padding = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
vkCmdPushConstants(cmd,
|
|
||||||
m_pipelineLayout,
|
|
||||||
VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
0,
|
|
||||||
sizeof(PushConstants),
|
|
||||||
&cs);
|
|
||||||
|
|
||||||
// one workgroup per tile
|
|
||||||
vkCmdDispatch(cmd, tileCount.x, tileCount.y, 1);
|
|
||||||
|
|
||||||
const VkBufferMemoryBarrier2 tileBufferBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.buffer = tileLightIndicesBuffer.buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.size = VK_WHOLE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkDependencyInfo dependencyInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
||||||
.bufferMemoryBarrierCount = 1,
|
|
||||||
.pBufferMemoryBarriers = &tileBufferBarrier,
|
|
||||||
};
|
|
||||||
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkDeviceAddress LightCullingPipeline::getLightsBufferAddress() const
|
|
||||||
{
|
|
||||||
return getCurrentFrameData().lightsBuffer.buffer.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t LightCullingPipeline::getNumLights() const
|
|
||||||
{
|
|
||||||
return static_cast<std::uint32_t>(getCurrentFrameData().lightsBuffer.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
LightCullingPipeline::PerFrameData& LightCullingPipeline::getCurrentFrameData()
|
|
||||||
{
|
|
||||||
return framesData[currentFrameIndex % FRAMES_IN_FLIGHT];
|
|
||||||
}
|
|
||||||
|
|
||||||
const LightCullingPipeline::PerFrameData& LightCullingPipeline::getCurrentFrameData() const
|
|
||||||
{
|
|
||||||
return framesData[currentFrameIndex % FRAMES_IN_FLIGHT];
|
|
||||||
}
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
#include <destrum/Graphics/Pipelines/LineRenderingPass.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <destrum/FS/AssetFS.h>
|
|
||||||
#include <destrum/Graphics/Camera.h>
|
|
||||||
#include <destrum/Graphics/GPUImage.h>
|
|
||||||
#include <destrum/Graphics/GfxDevice.h>
|
|
||||||
#include <destrum/Graphics/Pipeline.h>
|
|
||||||
#include <destrum/Graphics/Util.h>
|
|
||||||
|
|
||||||
#include "volk.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr std::size_t InitialVertexCapacity = 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingPass::init(GfxDevice& gfxDevice, VkFormat drawImageFormat)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
vertexBuffer.init(
|
|
||||||
gfxDevice,
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
||||||
InitialVertexCapacity * sizeof(LineVertex),
|
|
||||||
"line vertices");
|
|
||||||
vertexCapacity = InitialVertexCapacity;
|
|
||||||
|
|
||||||
const auto vertexShader =
|
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/line.vert");
|
|
||||||
const auto fragmentShader =
|
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/line.frag");
|
|
||||||
|
|
||||||
const auto pushConstantRange = VkPushConstantRange{
|
|
||||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
.offset = 0,
|
|
||||||
.size = sizeof(glm::mat4),
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto pipelineLayoutInfo = VkPipelineLayoutCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
||||||
.pushConstantRangeCount = 1,
|
|
||||||
.pPushConstantRanges = &pushConstantRange,
|
|
||||||
};
|
|
||||||
|
|
||||||
VK_CHECK(vkCreatePipelineLayout(
|
|
||||||
gfxDevice.getDevice(),
|
|
||||||
&pipelineLayoutInfo,
|
|
||||||
nullptr,
|
|
||||||
&pipelineLayout));
|
|
||||||
|
|
||||||
PipelineConfigInfo pipelineConfig{};
|
|
||||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
|
||||||
pipelineConfig.name = "Line Rendering Pipeline";
|
|
||||||
pipelineConfig.pipelineLayout = pipelineLayout;
|
|
||||||
pipelineConfig.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
|
||||||
pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
|
||||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
|
||||||
pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED;
|
|
||||||
|
|
||||||
pipelineConfig.vertexBindingDescriptions = {
|
|
||||||
VkVertexInputBindingDescription{
|
|
||||||
.binding = 0,
|
|
||||||
.stride = sizeof(LineVertex),
|
|
||||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pipelineConfig.vertexAttributeDescriptions = {
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 0,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
|
||||||
.offset = offsetof(LineVertex, position),
|
|
||||||
},
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 1,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
|
||||||
.offset = offsetof(LineVertex, color),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pipelineConfig.colorBlendAttachment.blendEnable = VK_TRUE;
|
|
||||||
pipelineConfig.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.dstColorBlendFactor =
|
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
|
||||||
pipelineConfig.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
||||||
pipelineConfig.colorBlendAttachment.dstAlphaBlendFactor =
|
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
||||||
|
|
||||||
pipeline = std::make_unique<Pipeline>(
|
|
||||||
gfxDevice,
|
|
||||||
vertexShader.string(),
|
|
||||||
fragmentShader.string(),
|
|
||||||
pipelineConfig);
|
|
||||||
} catch (...) {
|
|
||||||
cleanup(gfxDevice);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingPass::draw(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
const GPUImage& target,
|
|
||||||
const Camera& camera,
|
|
||||||
LineRenderingManager& lineManager)
|
|
||||||
{
|
|
||||||
const auto& lines = lineManager.GetLines();
|
|
||||||
if (!pipeline || lines.empty()) {
|
|
||||||
lineManager.ClearLines();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines.size() > std::numeric_limits<std::size_t>::max() / 2) {
|
|
||||||
throw std::overflow_error("Too many lines submitted for rendering");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<LineVertex> vertices;
|
|
||||||
vertices.reserve(lines.size() * 2);
|
|
||||||
for (const Line& line : lines) {
|
|
||||||
vertices.push_back(LineVertex{
|
|
||||||
.position = glm::vec4{line.start, 1.0f},
|
|
||||||
.color = line.color,
|
|
||||||
});
|
|
||||||
vertices.push_back(LineVertex{
|
|
||||||
.position = glm::vec4{line.end, 1.0f},
|
|
||||||
.color = line.color,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vertices.size() > std::numeric_limits<std::uint32_t>::max()) {
|
|
||||||
throw std::overflow_error("Too many line vertices submitted for rendering");
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureVertexCapacity(gfxDevice, vertices.size());
|
|
||||||
vertexBuffer.uploadNewData(
|
|
||||||
cmd,
|
|
||||||
gfxDevice.getCurrentFrameIndex(),
|
|
||||||
vertices.data(),
|
|
||||||
vertices.size() * sizeof(LineVertex));
|
|
||||||
|
|
||||||
auto renderInfo = vkutil::createRenderingInfo({
|
|
||||||
.renderExtent = target.getExtent2D(),
|
|
||||||
.colorImageView = target.imageView,
|
|
||||||
});
|
|
||||||
|
|
||||||
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
|
|
||||||
|
|
||||||
pipeline->bind(cmd);
|
|
||||||
|
|
||||||
const auto viewport = VkViewport{
|
|
||||||
.x = 0.0f,
|
|
||||||
.y = 0.0f,
|
|
||||||
.width = static_cast<float>(target.extent.width),
|
|
||||||
.height = static_cast<float>(target.extent.height),
|
|
||||||
.minDepth = 0.0f,
|
|
||||||
.maxDepth = 1.0f,
|
|
||||||
};
|
|
||||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
|
||||||
|
|
||||||
const auto scissor = VkRect2D{
|
|
||||||
.offset = {},
|
|
||||||
.extent = target.getExtent2D(),
|
|
||||||
};
|
|
||||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
|
||||||
vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL);
|
|
||||||
|
|
||||||
const glm::mat4 viewProjection = camera.GetViewProjectionMatrix();
|
|
||||||
vkCmdPushConstants(
|
|
||||||
cmd,
|
|
||||||
pipelineLayout,
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
0,
|
|
||||||
sizeof(viewProjection),
|
|
||||||
&viewProjection);
|
|
||||||
|
|
||||||
const VkBuffer vertexBufferHandle = vertexBuffer.getBuffer().buffer;
|
|
||||||
const VkDeviceSize vertexBufferOffset = 0;
|
|
||||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBufferHandle, &vertexBufferOffset);
|
|
||||||
vkCmdDraw(cmd, static_cast<std::uint32_t>(vertices.size()), 1, 0, 0);
|
|
||||||
|
|
||||||
vkCmdEndRendering(cmd);
|
|
||||||
lineManager.ClearLines();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingPass::cleanup(GfxDevice& gfxDevice)
|
|
||||||
{
|
|
||||||
pipeline.reset();
|
|
||||||
|
|
||||||
if (pipelineLayout != VK_NULL_HANDLE && gfxDevice.getDevice() != VK_NULL_HANDLE) {
|
|
||||||
vkDestroyPipelineLayout(gfxDevice.getDevice(), pipelineLayout, nullptr);
|
|
||||||
}
|
|
||||||
pipelineLayout = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
vertexBuffer.cleanup(gfxDevice);
|
|
||||||
vertexCapacity = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineRenderingPass::ensureVertexCapacity(
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
std::size_t requiredVertices)
|
|
||||||
{
|
|
||||||
if (requiredVertices <= vertexCapacity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t newCapacity = std::max(vertexCapacity, InitialVertexCapacity);
|
|
||||||
while (newCapacity < requiredVertices) {
|
|
||||||
if (newCapacity > std::numeric_limits<std::size_t>::max() / 2) {
|
|
||||||
newCapacity = requiredVertices;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newCapacity *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfxDevice.waitIdle();
|
|
||||||
vertexBuffer.cleanup(gfxDevice);
|
|
||||||
vertexBuffer.init(
|
|
||||||
gfxDevice,
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
||||||
newCapacity * sizeof(LineVertex),
|
|
||||||
"line vertices");
|
|
||||||
vertexCapacity = newCapacity;
|
|
||||||
}
|
|
||||||
@@ -23,8 +23,7 @@ void MeshPipeline::init(
|
|||||||
VkFormat drawImageFormat,
|
VkFormat drawImageFormat,
|
||||||
VkFormat depthImageFormat)
|
VkFormat depthImageFormat)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
const auto vertexShader =
|
const auto vertexShader =
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.vert");
|
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.vert");
|
||||||
|
|
||||||
@@ -60,29 +59,6 @@ void MeshPipeline::init(
|
|||||||
throw std::runtime_error("Could not create mesh pipeline layout");
|
throw std::runtime_error("Could not create mesh pipeline layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
PipelineConfigInfo pipelineConfig{};
|
|
||||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
|
||||||
|
|
||||||
pipelineConfig.name = "Mesh Depth Prepass Pipeline";
|
|
||||||
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
|
||||||
|
|
||||||
pipelineConfig.vertexAttributeDescriptions = {};
|
|
||||||
pipelineConfig.vertexBindingDescriptions = {};
|
|
||||||
|
|
||||||
pipelineConfig.colorAttachments = {};
|
|
||||||
pipelineConfig.colorBlendInfo.attachmentCount = 0;
|
|
||||||
pipelineConfig.depthAttachment = depthImageFormat;
|
|
||||||
|
|
||||||
m_depthPrepassPipeline = std::make_unique<Pipeline>(
|
|
||||||
gfxDevice,
|
|
||||||
vertexShader.string(),
|
|
||||||
"",
|
|
||||||
pipelineConfig
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
PipelineConfigInfo pipelineConfig{};
|
PipelineConfigInfo pipelineConfig{};
|
||||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||||
|
|
||||||
@@ -95,50 +71,18 @@ void MeshPipeline::init(
|
|||||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
pipelineConfig.colorAttachments = {drawImageFormat};
|
||||||
pipelineConfig.depthAttachment = depthImageFormat;
|
pipelineConfig.depthAttachment = depthImageFormat;
|
||||||
|
|
||||||
// depth was already written by the pre-pass, only shade the visible surface
|
|
||||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
|
||||||
|
|
||||||
m_pipeline = std::make_unique<Pipeline>(
|
m_pipeline = std::make_unique<Pipeline>(
|
||||||
gfxDevice,
|
gfxDevice,
|
||||||
vertexShader.string(),
|
vertexShader.string(),
|
||||||
fragShader.string(),
|
fragShader.string(),
|
||||||
pipelineConfig
|
pipelineConfig
|
||||||
);
|
);
|
||||||
}
|
} catch (...) {
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
cleanup(gfxDevice.getDevice());
|
cleanup(gfxDevice.getDevice());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPipeline::drawDepthPrepass(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
VkExtent2D renderExtent,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const Camera& camera,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
|
||||||
const std::vector<std::size_t>& sortedDrawCommands)
|
|
||||||
{
|
|
||||||
if (!m_depthPrepassPipeline)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawMeshes(
|
|
||||||
cmd,
|
|
||||||
*m_depthPrepassPipeline,
|
|
||||||
renderExtent,
|
|
||||||
resources,
|
|
||||||
camera,
|
|
||||||
sceneDataBuffer,
|
|
||||||
drawCommands,
|
|
||||||
sortedDrawCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeshPipeline::draw(
|
void MeshPipeline::draw(
|
||||||
VkCommandBuffer cmd,
|
VkCommandBuffer cmd,
|
||||||
VkExtent2D renderExtent,
|
VkExtent2D renderExtent,
|
||||||
@@ -148,35 +92,13 @@ void MeshPipeline::draw(
|
|||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
const std::vector<MeshDrawCommand>& drawCommands,
|
||||||
const std::vector<std::size_t>& sortedDrawCommands)
|
const std::vector<std::size_t>& sortedDrawCommands)
|
||||||
{
|
{
|
||||||
if (!m_pipeline)
|
if (!m_pipeline) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawMeshes(
|
|
||||||
cmd,
|
|
||||||
*m_pipeline,
|
|
||||||
renderExtent,
|
|
||||||
resources,
|
|
||||||
camera,
|
|
||||||
sceneDataBuffer,
|
|
||||||
drawCommands,
|
|
||||||
sortedDrawCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeshPipeline::drawMeshes(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
const Pipeline& pipeline,
|
|
||||||
VkExtent2D renderExtent,
|
|
||||||
const RenderResources& resources,
|
|
||||||
const Camera& camera,
|
|
||||||
const GPUBuffer& sceneDataBuffer,
|
|
||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
|
||||||
const std::vector<std::size_t>& sortedDrawCommands)
|
|
||||||
{
|
|
||||||
const MeshCache& meshCache = resources.meshes();
|
const MeshCache& meshCache = resources.meshes();
|
||||||
|
|
||||||
pipeline.bind(cmd);
|
m_pipeline->bind(cmd);
|
||||||
|
|
||||||
resources.bindBindlessDescSet(cmd, m_pipelineLayout);
|
resources.bindBindlessDescSet(cmd, m_pipelineLayout);
|
||||||
|
|
||||||
@@ -208,10 +130,8 @@ void MeshPipeline::drawMeshes(
|
|||||||
|
|
||||||
const auto frustum = edge::createFrustumFromCamera(camera);
|
const auto frustum = edge::createFrustumFromCamera(camera);
|
||||||
|
|
||||||
const auto drawOne = [&](const MeshDrawCommand& dc)
|
const auto drawOne = [&](const MeshDrawCommand& dc) {
|
||||||
{
|
if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
|
||||||
if (!edge::isInFrustum(frustum, dc.worldBoundingSphere))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +139,7 @@ void MeshPipeline::drawMeshes(
|
|||||||
|
|
||||||
const auto& mesh = meshCache.getMesh(dc.meshId);
|
const auto& mesh = meshCache.getMesh(dc.meshId);
|
||||||
|
|
||||||
if (dc.meshId != prevMeshId)
|
if (dc.meshId != prevMeshId) {
|
||||||
{
|
|
||||||
prevMeshId = dc.meshId;
|
prevMeshId = dc.meshId;
|
||||||
|
|
||||||
vkCmdBindIndexBuffer(
|
vkCmdBindIndexBuffer(
|
||||||
@@ -256,30 +175,26 @@ void MeshPipeline::drawMeshes(
|
|||||||
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!sortedDrawCommands.empty())
|
if (!sortedDrawCommands.empty()) {
|
||||||
{
|
for (std::size_t drawIndex : sortedDrawCommands) {
|
||||||
for (std::size_t drawIndex : sortedDrawCommands)
|
|
||||||
{
|
|
||||||
assert(drawIndex < drawCommands.size());
|
assert(drawIndex < drawCommands.size());
|
||||||
drawOne(drawCommands[drawIndex]);
|
drawOne(drawCommands[drawIndex]);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
for (const MeshDrawCommand& dc : drawCommands) {
|
||||||
{
|
|
||||||
for (const MeshDrawCommand& dc : drawCommands)
|
|
||||||
{
|
|
||||||
drawOne(dc);
|
drawOne(dc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional:
|
||||||
|
// spdlog::debug("Actual mesh draw calls: {}", actualDrawCalls);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPipeline::cleanup(VkDevice device)
|
void MeshPipeline::cleanup(VkDevice device)
|
||||||
{
|
{
|
||||||
m_pipeline.reset();
|
m_pipeline.reset();
|
||||||
m_depthPrepassPipeline.reset();
|
|
||||||
|
|
||||||
if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE)
|
if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
|
||||||
{
|
|
||||||
vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr);
|
vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr);
|
||||||
}
|
}
|
||||||
m_pipelineLayout = VK_NULL_HANDLE;
|
m_pipelineLayout = VK_NULL_HANDLE;
|
||||||
|
|||||||
@@ -1,334 +0,0 @@
|
|||||||
#include <destrum/Graphics/Pipelines/QuadRendererPass.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
#include <destrum/FS/AssetFS.h>
|
|
||||||
#include <destrum/Graphics/GfxDevice.h>
|
|
||||||
#include <destrum/Graphics/GPUImage.h>
|
|
||||||
#include <destrum/Graphics/Pipeline.h>
|
|
||||||
#include <destrum/Graphics/RenderResources.h>
|
|
||||||
#include <destrum/Graphics/Util.h>
|
|
||||||
#include <destrum/Graphics/Managers/QuadRenderingManager.h>
|
|
||||||
|
|
||||||
#include "volk.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr std::size_t InitialQuadCapacity = 256;
|
|
||||||
constexpr std::size_t VerticesPerQuad = 4;
|
|
||||||
constexpr std::size_t IndicesPerQuad = 6;
|
|
||||||
|
|
||||||
glm::vec2 rotatePoint(const glm::vec2& point, float cosA, float sinA) {
|
|
||||||
return glm::vec2{
|
|
||||||
point.x * cosA - point.y * sinA,
|
|
||||||
point.x * sinA + point.y * cosA,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRendererPass::init(GfxDevice& gfxDevice, RenderResources& resources, VkFormat drawImageFormat)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const std::size_t vertexBytes = InitialQuadCapacity * VerticesPerQuad * sizeof(QuadVertex);
|
|
||||||
vertexBuffer = gfxDevice.createBuffer(
|
|
||||||
vertexBytes,
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
|
||||||
|
|
||||||
const std::size_t indexBytes = InitialQuadCapacity * IndicesPerQuad * sizeof(std::uint32_t);
|
|
||||||
indexBuffer = gfxDevice.createBuffer(
|
|
||||||
indexBytes,
|
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
|
||||||
|
|
||||||
quadCapacity = InitialQuadCapacity;
|
|
||||||
|
|
||||||
const auto vertexShader =
|
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/quad.vert");
|
|
||||||
const auto fragmentShader =
|
|
||||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/quad.frag");
|
|
||||||
|
|
||||||
const auto pushConstantRange = VkPushConstantRange{
|
|
||||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
.offset = 0,
|
|
||||||
.size = sizeof(glm::mat4),
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto layouts = std::array{
|
|
||||||
resources.getBindlessDescSetLayout()
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto pipelineLayoutInfo = VkPipelineLayoutCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
||||||
.setLayoutCount = static_cast<std::uint32_t>(layouts.size()),
|
|
||||||
.pSetLayouts = layouts.data(),
|
|
||||||
.pushConstantRangeCount = 1,
|
|
||||||
.pPushConstantRanges = &pushConstantRange,
|
|
||||||
};
|
|
||||||
|
|
||||||
VK_CHECK(vkCreatePipelineLayout(
|
|
||||||
gfxDevice.getDevice(),
|
|
||||||
&pipelineLayoutInfo,
|
|
||||||
nullptr,
|
|
||||||
&pipelineLayout));
|
|
||||||
|
|
||||||
PipelineConfigInfo pipelineConfig{};
|
|
||||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
|
||||||
pipelineConfig.name = "Quad Rendering Pipeline";
|
|
||||||
pipelineConfig.pipelineLayout = pipelineLayout;
|
|
||||||
pipelineConfig.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
||||||
pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
|
||||||
pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
|
||||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
|
||||||
pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED;
|
|
||||||
|
|
||||||
pipelineConfig.vertexBindingDescriptions = {
|
|
||||||
VkVertexInputBindingDescription{
|
|
||||||
.binding = 0,
|
|
||||||
.stride = sizeof(QuadVertex),
|
|
||||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pipelineConfig.vertexAttributeDescriptions = {
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 0,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
|
||||||
.offset = offsetof(QuadVertex, position),
|
|
||||||
},
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 1,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
|
||||||
.offset = offsetof(QuadVertex, uv),
|
|
||||||
},
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 2,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
|
||||||
.offset = offsetof(QuadVertex, color),
|
|
||||||
},
|
|
||||||
VkVertexInputAttributeDescription{
|
|
||||||
.location = 3,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32_UINT,
|
|
||||||
.offset = offsetof(QuadVertex, textureId),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pipelineConfig.colorBlendAttachment.blendEnable = VK_TRUE;
|
|
||||||
pipelineConfig.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
|
||||||
pipelineConfig.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
||||||
pipelineConfig.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
||||||
pipelineConfig.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
||||||
|
|
||||||
pipeline = std::make_unique<Pipeline>(
|
|
||||||
gfxDevice,
|
|
||||||
vertexShader.string(),
|
|
||||||
fragmentShader.string(),
|
|
||||||
pipelineConfig);
|
|
||||||
} catch (...) {
|
|
||||||
cleanup(gfxDevice);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRendererPass::draw(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
RenderResources& resources,
|
|
||||||
const GPUImage& target,
|
|
||||||
const glm::mat4& projection)
|
|
||||||
{
|
|
||||||
auto& quadManager = QuadRenderingManager::GetInstance();
|
|
||||||
const auto& quads = quadManager.GetQuads();
|
|
||||||
if (!pipeline || quads.empty()) {
|
|
||||||
quadManager.ClearQuads();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::size_t numQuads = quads.size();
|
|
||||||
if (numQuads > std::numeric_limits<std::size_t>::max() / VerticesPerQuad) {
|
|
||||||
throw std::overflow_error("Too many quads submitted for rendering");
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::size_t numVertices = numQuads * VerticesPerQuad;
|
|
||||||
const std::size_t numIndices = numQuads * IndicesPerQuad;
|
|
||||||
|
|
||||||
if (numVertices > std::numeric_limits<std::uint32_t>::max()) {
|
|
||||||
throw std::overflow_error("Too many quad vertices submitted for rendering");
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureCapacity(gfxDevice, numQuads);
|
|
||||||
|
|
||||||
const ImageID whiteTextureId = resources.getWhiteTextureID();
|
|
||||||
|
|
||||||
auto* vertexMapped = reinterpret_cast<std::uint8_t*>(vertexBuffer.info.pMappedData);
|
|
||||||
auto* indexMapped = reinterpret_cast<std::uint8_t*>(indexBuffer.info.pMappedData);
|
|
||||||
|
|
||||||
constexpr std::array<glm::vec2, 4> localCorners = {{
|
|
||||||
glm::vec2{-0.5f, -0.5f},
|
|
||||||
glm::vec2{ 0.5f, -0.5f},
|
|
||||||
glm::vec2{ 0.5f, 0.5f},
|
|
||||||
glm::vec2{-0.5f, 0.5f},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr std::array<glm::vec2, 4> uvs = {{
|
|
||||||
glm::vec2{0.0f, 1.0f},
|
|
||||||
glm::vec2{1.0f, 1.0f},
|
|
||||||
glm::vec2{1.0f, 0.0f},
|
|
||||||
glm::vec2{0.0f, 0.0f},
|
|
||||||
}};
|
|
||||||
|
|
||||||
auto* vertWrite = reinterpret_cast<QuadVertex*>(vertexMapped);
|
|
||||||
auto* idxWrite = reinterpret_cast<std::uint32_t*>(indexMapped);
|
|
||||||
std::uint32_t vertexBase = 0;
|
|
||||||
|
|
||||||
for (const auto& quad : quads) {
|
|
||||||
const float cosA = std::cos(quad.rotation);
|
|
||||||
const float sinA = std::sin(quad.rotation);
|
|
||||||
const glm::vec2 halfSize = quad.size * 0.5f;
|
|
||||||
const ImageID texId = (quad.textureId == NULL_IMAGE_ID) ? whiteTextureId : quad.textureId;
|
|
||||||
|
|
||||||
for (std::size_t v = 0; v < VerticesPerQuad; ++v) {
|
|
||||||
glm::vec2 localPos = localCorners[v] * halfSize;
|
|
||||||
glm::vec2 rotatedPos = rotatePoint(localPos, cosA, sinA);
|
|
||||||
glm::vec2 worldPos = quad.position + rotatedPos;
|
|
||||||
|
|
||||||
vertWrite[vertexBase + v] = QuadVertex{
|
|
||||||
.position = worldPos,
|
|
||||||
.uv = uvs[v],
|
|
||||||
.color = quad.color,
|
|
||||||
.textureId = static_cast<std::uint32_t>(texId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::uint32_t idxOffset = vertexBase / 4 * 6;
|
|
||||||
idxWrite[idxOffset + 0] = vertexBase + 0;
|
|
||||||
idxWrite[idxOffset + 1] = vertexBase + 1;
|
|
||||||
idxWrite[idxOffset + 2] = vertexBase + 2;
|
|
||||||
idxWrite[idxOffset + 3] = vertexBase + 0;
|
|
||||||
idxWrite[idxOffset + 4] = vertexBase + 2;
|
|
||||||
idxWrite[idxOffset + 5] = vertexBase + 3;
|
|
||||||
vertexBase += VerticesPerQuad;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& memMgr = gfxDevice.getMemoryManager();
|
|
||||||
memMgr.flushAllocation(vertexBuffer, 0, numVertices * sizeof(QuadVertex));
|
|
||||||
memMgr.flushAllocation(indexBuffer, 0, numIndices * sizeof(std::uint32_t));
|
|
||||||
|
|
||||||
auto renderInfo = vkutil::createRenderingInfo({
|
|
||||||
.renderExtent = target.getExtent2D(),
|
|
||||||
.colorImageView = target.imageView,
|
|
||||||
});
|
|
||||||
|
|
||||||
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
|
|
||||||
|
|
||||||
pipeline->bind(cmd);
|
|
||||||
resources.bindBindlessDescSet(cmd, pipelineLayout);
|
|
||||||
|
|
||||||
const auto viewport = VkViewport{
|
|
||||||
.x = 0.0f,
|
|
||||||
.y = 0.0f,
|
|
||||||
.width = static_cast<float>(target.extent.width),
|
|
||||||
.height = static_cast<float>(target.extent.height),
|
|
||||||
.minDepth = 0.0f,
|
|
||||||
.maxDepth = 1.0f,
|
|
||||||
};
|
|
||||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
|
||||||
|
|
||||||
const auto scissor = VkRect2D{
|
|
||||||
.offset = {},
|
|
||||||
.extent = target.getExtent2D(),
|
|
||||||
};
|
|
||||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
|
||||||
vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL);
|
|
||||||
|
|
||||||
vkCmdPushConstants(
|
|
||||||
cmd,
|
|
||||||
pipelineLayout,
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
0,
|
|
||||||
sizeof(glm::mat4),
|
|
||||||
&projection);
|
|
||||||
|
|
||||||
const VkBuffer vertexBufferHandle = vertexBuffer.buffer;
|
|
||||||
const VkDeviceSize vertexBufferOffset = 0;
|
|
||||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBufferHandle, &vertexBufferOffset);
|
|
||||||
|
|
||||||
const VkBuffer indexBufferHandle = indexBuffer.buffer;
|
|
||||||
vkCmdBindIndexBuffer(cmd, indexBufferHandle, 0, VK_INDEX_TYPE_UINT32);
|
|
||||||
|
|
||||||
vkCmdDrawIndexed(cmd, static_cast<std::uint32_t>(numIndices), 1, 0, 0, 0);
|
|
||||||
|
|
||||||
vkCmdEndRendering(cmd);
|
|
||||||
quadManager.ClearQuads();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRendererPass::cleanup(GfxDevice& gfxDevice)
|
|
||||||
{
|
|
||||||
pipeline.reset();
|
|
||||||
|
|
||||||
if (pipelineLayout != VK_NULL_HANDLE && gfxDevice.getDevice() != VK_NULL_HANDLE) {
|
|
||||||
vkDestroyPipelineLayout(gfxDevice.getDevice(), pipelineLayout, nullptr);
|
|
||||||
}
|
|
||||||
pipelineLayout = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
if (vertexBuffer.buffer != VK_NULL_HANDLE) {
|
|
||||||
gfxDevice.destroyBuffer(vertexBuffer);
|
|
||||||
}
|
|
||||||
if (indexBuffer.buffer != VK_NULL_HANDLE) {
|
|
||||||
gfxDevice.destroyBuffer(indexBuffer);
|
|
||||||
}
|
|
||||||
quadCapacity = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadRendererPass::ensureCapacity(
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
std::size_t requiredQuads)
|
|
||||||
{
|
|
||||||
if (requiredQuads <= quadCapacity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t newCapacity = std::max(quadCapacity, InitialQuadCapacity);
|
|
||||||
while (newCapacity < requiredQuads) {
|
|
||||||
if (newCapacity > std::numeric_limits<std::size_t>::max() / 2) {
|
|
||||||
newCapacity = requiredQuads;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newCapacity *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfxDevice.waitIdle();
|
|
||||||
|
|
||||||
if (vertexBuffer.buffer != VK_NULL_HANDLE) {
|
|
||||||
gfxDevice.destroyBuffer(vertexBuffer);
|
|
||||||
}
|
|
||||||
vertexBuffer = gfxDevice.createBuffer(
|
|
||||||
newCapacity * VerticesPerQuad * sizeof(QuadVertex),
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
|
||||||
|
|
||||||
if (indexBuffer.buffer != VK_NULL_HANDLE) {
|
|
||||||
gfxDevice.destroyBuffer(indexBuffer);
|
|
||||||
}
|
|
||||||
indexBuffer = gfxDevice.createBuffer(
|
|
||||||
newCapacity * IndicesPerQuad * sizeof(std::uint32_t),
|
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
|
||||||
|
|
||||||
quadCapacity = newCapacity;
|
|
||||||
}
|
|
||||||
@@ -119,16 +119,14 @@ void SkyboxPipeline::draw(
|
|||||||
|
|
||||||
const glm::mat3 r = glm::mat3(skyboxRotation);
|
const glm::mat3 r = glm::mat3(skyboxRotation);
|
||||||
|
|
||||||
// rotation only, translation in the inverse makes the sky jitter far from the origin
|
|
||||||
const glm::mat4 viewRotation = glm::mat4(glm::mat3(camera.GetViewMatrix()));
|
|
||||||
|
|
||||||
const auto pcs = SkyboxPushConstants{
|
const auto pcs = SkyboxPushConstants{
|
||||||
.invViewProj = glm::inverse(camera.GetProjectionMatrix() * viewRotation),
|
.invViewProj = glm::inverse(camera.GetViewProjectionMatrix()),
|
||||||
.skyboxRot = {
|
.skyboxRot = {
|
||||||
glm::vec4(r[0], 0.f),
|
glm::vec4(r[0], 0.f),
|
||||||
glm::vec4(r[1], 0.f),
|
glm::vec4(r[1], 0.f),
|
||||||
glm::vec4(r[2], 0.f)
|
glm::vec4(r[2], 0.f)
|
||||||
},
|
},
|
||||||
|
.cameraPos = camera.GetPosition(),
|
||||||
.skyboxTextureId = static_cast<std::uint32_t>(skyboxTextureId),
|
.skyboxTextureId = static_cast<std::uint32_t>(skyboxTextureId),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -266,13 +266,10 @@ const VkDescriptorSet& RenderResources::getBindlessDescSet() const {
|
|||||||
return imageCache->bindlessSetManager.getDescSet();
|
return imageCache->bindlessSetManager.getDescSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderResources::bindBindlessDescSet(
|
void RenderResources::bindBindlessDescSet(VkCommandBuffer cmd, VkPipelineLayout layout) const {
|
||||||
VkCommandBuffer cmd,
|
|
||||||
VkPipelineLayout layout,
|
|
||||||
VkPipelineBindPoint bindPoint) const {
|
|
||||||
vkCmdBindDescriptorSets(
|
vkCmdBindDescriptorSets(
|
||||||
cmd,
|
cmd,
|
||||||
bindPoint,
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
layout,
|
layout,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
#include <destrum/Graphics/Renderer.h>
|
#include <destrum/Graphics/Renderer.h>
|
||||||
|
|
||||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
|
||||||
#include <destrum/Graphics/Managers/QuadRenderingManager.h>
|
|
||||||
#include <destrum/Graphics/Util.h>
|
#include <destrum/Graphics/Util.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
#include "volk.h"
|
#include "volk.h"
|
||||||
#include "destrum/Util/GameState.h"
|
#include "destrum/Util/GameState.h"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
@@ -21,8 +17,7 @@ GameRenderer::GameRenderer()
|
|||||||
|
|
||||||
void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::ivec2 drawImageSize)
|
void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::ivec2 drawImageSize)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
resources = &_resources;
|
resources = &_resources;
|
||||||
sceneDataBuffer.init(
|
sceneDataBuffer.init(
|
||||||
gfxDevice,
|
gfxDevice,
|
||||||
@@ -41,20 +36,9 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
|
|||||||
skinningPipeline = std::make_unique<SkinningPipeline>();
|
skinningPipeline = std::make_unique<SkinningPipeline>();
|
||||||
skinningPipeline->init(gfxDevice);
|
skinningPipeline->init(gfxDevice);
|
||||||
|
|
||||||
lightCullingPipeline = std::make_unique<LightCullingPipeline>();
|
|
||||||
lightCullingPipeline->init(gfxDevice, _resources, drawImageSize);
|
|
||||||
|
|
||||||
lineRenderingPass = std::make_unique<LineRenderingPass>();
|
|
||||||
lineRenderingPass->init(gfxDevice, drawImageFormat);
|
|
||||||
|
|
||||||
quadRendererPass = std::make_unique<QuadRendererPass>();
|
|
||||||
quadRendererPass->init(gfxDevice, _resources, drawImageFormat);
|
|
||||||
|
|
||||||
GameState::GetInstance().SetRenderer(this);
|
GameState::GetInstance().SetRenderer(this);
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
} catch (...) {
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
cleanup(gfxDevice);
|
cleanup(gfxDevice);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@@ -65,8 +49,6 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
|
|||||||
flushMaterialUpdates(gfxDevice);
|
flushMaterialUpdates(gfxDevice);
|
||||||
meshDrawCommands.clear();
|
meshDrawCommands.clear();
|
||||||
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||||
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
|
||||||
QuadRenderingManager::GetInstance().ClearQuads();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::endDrawing()
|
void GameRenderer::endDrawing()
|
||||||
@@ -77,16 +59,13 @@ void GameRenderer::endDrawing()
|
|||||||
std::stable_sort(
|
std::stable_sort(
|
||||||
sortedMeshDrawCommands.begin(),
|
sortedMeshDrawCommands.begin(),
|
||||||
sortedMeshDrawCommands.end(),
|
sortedMeshDrawCommands.end(),
|
||||||
[this](std::size_t left, std::size_t right)
|
[this](std::size_t left, std::size_t right) {
|
||||||
{
|
|
||||||
const auto& lhs = meshDrawCommands[left];
|
const auto& lhs = meshDrawCommands[left];
|
||||||
const auto& rhs = meshDrawCommands[right];
|
const auto& rhs = meshDrawCommands[right];
|
||||||
if (lhs.meshId != rhs.meshId)
|
if (lhs.meshId != rhs.meshId) {
|
||||||
{
|
|
||||||
return lhs.meshId < rhs.meshId;
|
return lhs.meshId < rhs.meshId;
|
||||||
}
|
}
|
||||||
if (lhs.materialId != rhs.materialId)
|
if (lhs.materialId != rhs.materialId) {
|
||||||
{
|
|
||||||
return lhs.materialId < rhs.materialId;
|
return lhs.materialId < rhs.materialId;
|
||||||
}
|
}
|
||||||
return (lhs.skinnedMesh != nullptr) < (rhs.skinnedMesh != nullptr);
|
return (lhs.skinnedMesh != nullptr) < (rhs.skinnedMesh != nullptr);
|
||||||
@@ -116,17 +95,9 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush");
|
|
||||||
|
|
||||||
lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: there has to be a better way to do this
|
|
||||||
const auto gpuSceneData = GPUSceneData{
|
const auto gpuSceneData = GPUSceneData{
|
||||||
.view = sceneData.camera.GetViewMatrix(),
|
.view = sceneData.camera.GetViewMatrix(),
|
||||||
.proj = sceneData.camera.GetProjectionMatrix(),
|
.proj = sceneData.camera.GetProjectionMatrix(),
|
||||||
.invProj = glm::inverse(sceneData.camera.GetProjectionMatrix()),
|
|
||||||
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
|
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
|
||||||
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
|
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
|
||||||
.ambientColor = {sceneData.ambientColor},
|
.ambientColor = {sceneData.ambientColor},
|
||||||
@@ -134,12 +105,6 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
.fogColor = {sceneData.fogColor},
|
.fogColor = {sceneData.fogColor},
|
||||||
.fogDensity = sceneData.fogDensity,
|
.fogDensity = sceneData.fogDensity,
|
||||||
.materialsBuffer = resources->materials().getMaterialDataBufferAddress(),
|
.materialsBuffer = resources->materials().getMaterialDataBufferAddress(),
|
||||||
.lightsBuffer = lightCullingPipeline->getLightsBufferAddress(),
|
|
||||||
.tileLightIndicesBuffer = lightCullingPipeline->getTileLightIndicesBufferAddress(),
|
|
||||||
.numLights = lightCullingPipeline->getNumLights(),
|
|
||||||
.tileCountX = lightCullingPipeline->getTileCount().x,
|
|
||||||
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
|
|
||||||
.padding = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -189,60 +154,12 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
|
|
||||||
|
|
||||||
auto depthPrepassInfo = vkutil::createRenderingInfo({
|
|
||||||
.renderExtent = drawImage.getExtent2D(),
|
|
||||||
.depthImageView = depthImage.imageView,
|
|
||||||
.depthImageClearValue = 1.f,
|
|
||||||
});
|
|
||||||
depthPrepassInfo.renderingInfo.pDepthAttachment = &depthPrepassInfo.depthAttachment;
|
|
||||||
|
|
||||||
vkCmdBeginRendering(cmd, &depthPrepassInfo.renderingInfo);
|
|
||||||
|
|
||||||
meshPipeline->drawDepthPrepass(
|
|
||||||
cmd,
|
|
||||||
drawImage.getExtent2D(),
|
|
||||||
*resources,
|
|
||||||
camera,
|
|
||||||
sceneDataBuffer.getBuffer(),
|
|
||||||
meshDrawCommands,
|
|
||||||
sortedMeshDrawCommands);
|
|
||||||
|
|
||||||
vkCmdEndRendering(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling");
|
|
||||||
|
|
||||||
vkutil::transitionImage(
|
|
||||||
cmd,
|
|
||||||
depthImage.image,
|
|
||||||
depthImage.layout,
|
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
depthImage.setLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
|
|
||||||
lightCullingPipeline->cullLights(
|
|
||||||
cmd,
|
|
||||||
*resources,
|
|
||||||
sceneDataBuffer.getBuffer(),
|
|
||||||
depthImageId,
|
|
||||||
drawImage.getExtent2D());
|
|
||||||
|
|
||||||
vkutil::transitionImage(
|
|
||||||
cmd,
|
|
||||||
depthImage.image,
|
|
||||||
depthImage.layout,
|
|
||||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
|
||||||
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto renderInfo = vkutil::createRenderingInfo({
|
auto renderInfo = vkutil::createRenderingInfo({
|
||||||
.renderExtent = drawImage.getExtent2D(),
|
.renderExtent = drawImage.getExtent2D(),
|
||||||
.colorImageView = drawImage.imageView,
|
.colorImageView = drawImage.imageView,
|
||||||
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
|
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
|
||||||
.depthImageView = depthImage.imageView,
|
.depthImageView = depthImage.imageView,
|
||||||
|
.depthImageClearValue = 1.f,
|
||||||
});
|
});
|
||||||
renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment;
|
renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment;
|
||||||
renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment;
|
renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment;
|
||||||
@@ -277,64 +194,20 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
|||||||
|
|
||||||
vkCmdEndRendering(cmd);
|
vkCmdEndRendering(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "LineRenderingPass::draw");
|
|
||||||
|
|
||||||
lineRenderingPass->draw(
|
|
||||||
cmd,
|
|
||||||
gfxDevice,
|
|
||||||
drawImage,
|
|
||||||
camera,
|
|
||||||
LineRenderingManager::GetInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw");
|
|
||||||
|
|
||||||
quadRendererPass->draw(
|
|
||||||
cmd,
|
|
||||||
gfxDevice,
|
|
||||||
*resources,
|
|
||||||
drawImage,
|
|
||||||
glm::ortho(0.0f,
|
|
||||||
static_cast<float>(drawImage.extent.width),
|
|
||||||
static_cast<float>(drawImage.extent.height),
|
|
||||||
0.0f, -1.0f, 1.0f));
|
|
||||||
}
|
|
||||||
// vkutil::cmdEndLabel(cmd);
|
// vkutil::cmdEndLabel(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::drawQuads(
|
|
||||||
VkCommandBuffer cmd,
|
|
||||||
GfxDevice& gfxDevice,
|
|
||||||
const glm::mat4& projection)
|
|
||||||
{
|
|
||||||
const auto& drawImage = resources->getImage(drawImageId);
|
|
||||||
quadRendererPass->draw(cmd, gfxDevice, *resources, drawImage, projection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||||
{
|
{
|
||||||
VkDevice device = gfxDevice.getDevice();
|
VkDevice device = gfxDevice.getDevice();
|
||||||
|
|
||||||
if (device != VK_NULL_HANDLE)
|
if (device != VK_NULL_HANDLE) {
|
||||||
{
|
|
||||||
vkDeviceWaitIdle(device);
|
vkDeviceWaitIdle(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinningPipeline)
|
if (skinningPipeline)
|
||||||
skinningPipeline->cleanup(gfxDevice);
|
skinningPipeline->cleanup(gfxDevice);
|
||||||
|
|
||||||
if (lineRenderingPass)
|
|
||||||
lineRenderingPass->cleanup(gfxDevice);
|
|
||||||
|
|
||||||
if (quadRendererPass)
|
|
||||||
quadRendererPass->cleanup(gfxDevice);
|
|
||||||
|
|
||||||
if (lightCullingPipeline)
|
|
||||||
lightCullingPipeline->cleanup(gfxDevice);
|
|
||||||
|
|
||||||
if (skyboxPipeline)
|
if (skyboxPipeline)
|
||||||
skyboxPipeline->cleanup(device);
|
skyboxPipeline->cleanup(device);
|
||||||
|
|
||||||
@@ -355,9 +228,6 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
|||||||
meshPipeline.reset();
|
meshPipeline.reset();
|
||||||
skyboxPipeline.reset();
|
skyboxPipeline.reset();
|
||||||
skinningPipeline.reset();
|
skinningPipeline.reset();
|
||||||
lineRenderingPass.reset();
|
|
||||||
quadRendererPass.reset();
|
|
||||||
lightCullingPipeline.reset();
|
|
||||||
pendingMaterialUploads.clear();
|
pendingMaterialUploads.clear();
|
||||||
meshDrawCommands.clear();
|
meshDrawCommands.clear();
|
||||||
sortedMeshDrawCommands.clear();
|
sortedMeshDrawCommands.clear();
|
||||||
@@ -400,11 +270,6 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRenderer::submitLight(const Light& light)
|
|
||||||
{
|
|
||||||
lightCullingPipeline->appendLight(light);
|
|
||||||
}
|
|
||||||
|
|
||||||
const GPUImage& GameRenderer::getDrawImage() const
|
const GPUImage& GameRenderer::getDrawImage() const
|
||||||
{
|
{
|
||||||
assert(resources);
|
assert(resources);
|
||||||
@@ -431,8 +296,7 @@ void GameRenderer::setSkyboxTexture(ImageID skyboxImageId)
|
|||||||
|
|
||||||
void GameRenderer::flushMaterialUpdates(GfxDevice& gfxDevice)
|
void GameRenderer::flushMaterialUpdates(GfxDevice& gfxDevice)
|
||||||
{
|
{
|
||||||
if (!pendingMaterialUploads.empty())
|
if (!pendingMaterialUploads.empty()) {
|
||||||
{
|
|
||||||
// Material data is currently shared by all frame contexts. Wait before
|
// Material data is currently shared by all frame contexts. Wait before
|
||||||
// mutating it so an older in-flight frame cannot read the overwritten
|
// mutating it so an older in-flight frame cannot read the overwritten
|
||||||
// range. This can later be replaced with per-frame material buffers.
|
// range. This can later be replaced with per-frame material buffers.
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ void InputManager::BeginFrame() {
|
|||||||
|
|
||||||
m_textInput.clear();
|
m_textInput.clear();
|
||||||
|
|
||||||
// Relative mouse accumulators are filled during SDL event processing
|
// Update mouse delta based on last known position
|
||||||
// and consumed by game code each frame. Reset them here (before events
|
m_mouseDX = m_mouseX - m_prevMouseX;
|
||||||
// are processed) so this frame accumulates fresh deltas.
|
m_mouseDY = m_mouseY - m_prevMouseY;
|
||||||
m_mouseRelDX = 0;
|
m_prevMouseX = m_mouseX;
|
||||||
m_mouseRelDY = 0;
|
m_prevMouseY = m_mouseY;
|
||||||
|
|
||||||
// Clear controller "edge" sets + snapshot axes
|
// Clear controller "edge" sets + snapshot axes
|
||||||
for (auto& [id, pad]: m_pads) {
|
for (auto& [id, pad]: m_pads) {
|
||||||
@@ -60,12 +60,6 @@ void InputManager::BeginFrame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::SetMouseCaptured(bool captured) {
|
|
||||||
if (captured == m_MouseCaptured) return;
|
|
||||||
m_MouseCaptured = captured;
|
|
||||||
SDL_SetRelativeMouseMode(captured ? SDL_TRUE : SDL_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputManager::AddController(int deviceIndex) {
|
void InputManager::AddController(int deviceIndex) {
|
||||||
if (!SDL_IsGameController(deviceIndex)) return;
|
if (!SDL_IsGameController(deviceIndex)) return;
|
||||||
|
|
||||||
@@ -140,8 +134,6 @@ bool InputManager::ProcessEvent(const SDL_Event& e) {
|
|||||||
case SDL_MOUSEMOTION: {
|
case SDL_MOUSEMOTION: {
|
||||||
m_mouseX = e.motion.x;
|
m_mouseX = e.motion.x;
|
||||||
m_mouseY = e.motion.y;
|
m_mouseY = e.motion.y;
|
||||||
m_mouseRelDX += e.motion.xrel;
|
|
||||||
m_mouseRelDY += e.motion.yrel;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,9 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
@@ -279,84 +276,6 @@ namespace
|
|||||||
throw std::runtime_error("Cannot create Jolt body without a valid shape.");
|
throw std::runtime_error("Cannot create Jolt body without a valid shape.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SensorContactListener final : public ContactListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using BodyPair = std::pair<BodyID, BodyID>;
|
|
||||||
|
|
||||||
struct PairHash {
|
|
||||||
std::size_t operator()(const BodyPair& p) const {
|
|
||||||
return std::hash<uint32_t>{}(
|
|
||||||
p.first.GetIndexAndSequenceNumber() ^
|
|
||||||
(p.second.GetIndexAndSequenceNumber() << 7));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PairData {
|
|
||||||
GameObject* objA{nullptr};
|
|
||||||
GameObject* objB{nullptr};
|
|
||||||
bool sensorA{false};
|
|
||||||
bool sensorB{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
ValidateResult OnContactValidate(const Body&, const Body&, RVec3Arg,
|
|
||||||
const CollideShapeResult&) override
|
|
||||||
{
|
|
||||||
return ValidateResult::AcceptAllContactsForThisBodyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnContactAdded(const Body& body1, const Body& body2,
|
|
||||||
const ContactManifold&, ContactSettings& ioSettings) override
|
|
||||||
{
|
|
||||||
if (body1.IsSensor() || body2.IsSensor())
|
|
||||||
{
|
|
||||||
PairData data;
|
|
||||||
data.objA = reinterpret_cast<GameObject*>(body1.GetUserData());
|
|
||||||
data.objB = reinterpret_cast<GameObject*>(body2.GetUserData());
|
|
||||||
data.sensorA = body1.IsSensor();
|
|
||||||
data.sensorB = body2.IsSensor();
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
m_Overlaps.insert_or_assign(MakePair(body1.GetID(), body2.GetID()), data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnContactPersisted(const Body& body1, const Body& body2,
|
|
||||||
const ContactManifold&, ContactSettings&) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnContactRemoved(const SubShapeIDPair& subShapePair) override
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
m_Overlaps.erase(MakePair(subShapePair.GetBody1ID(), subShapePair.GetBody2ID()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
m_Overlaps.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<BodyPair, PairData, PairHash> SwapOverlaps()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
return std::move(m_Overlaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static BodyPair MakePair(BodyID a, BodyID b)
|
|
||||||
{
|
|
||||||
if (a.GetIndexAndSequenceNumber() < b.GetIndexAndSequenceNumber())
|
|
||||||
{
|
|
||||||
return {a, b};
|
|
||||||
}
|
|
||||||
return {b, a};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mutex m_Mutex;
|
|
||||||
std::unordered_map<BodyPair, PairData, PairHash> m_Overlaps;
|
|
||||||
};
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class JoltPhysicsWorld::Impl
|
class JoltPhysicsWorld::Impl
|
||||||
@@ -388,7 +307,6 @@ public:
|
|||||||
m_ObjectVsBroadPhaseLayerFilter,
|
m_ObjectVsBroadPhaseLayerFilter,
|
||||||
m_ObjectLayerPairFilter);
|
m_ObjectLayerPairFilter);
|
||||||
|
|
||||||
m_PhysicsSystem.SetContactListener(&m_SensorListener);
|
|
||||||
m_PhysicsSystem.SetGravity(ToJoltVec3(settings.gravity));
|
m_PhysicsSystem.SetGravity(ToJoltVec3(settings.gravity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,15 +426,6 @@ public:
|
|||||||
collisionSteps,
|
collisionSteps,
|
||||||
m_TempAllocator.get(),
|
m_TempAllocator.get(),
|
||||||
m_JobSystem.get());
|
m_JobSystem.get());
|
||||||
|
|
||||||
ProcessSensorOverlaps();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TriggerEvent> ConsumeTriggerEvents()
|
|
||||||
{
|
|
||||||
std::vector<TriggerEvent> events;
|
|
||||||
m_TriggerEvents.swap(events);
|
|
||||||
return events;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncKinematicBodiesToPhysics()
|
void SyncKinematicBodiesToPhysics()
|
||||||
@@ -717,43 +626,6 @@ private:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessSensorOverlaps()
|
|
||||||
{
|
|
||||||
const auto currentOverlaps = m_SensorListener.SwapOverlaps();
|
|
||||||
|
|
||||||
for (const auto& [pair, data] : currentOverlaps)
|
|
||||||
{
|
|
||||||
if (m_PreviousSensorOverlaps.find(pair) == m_PreviousSensorOverlaps.end())
|
|
||||||
{
|
|
||||||
if (data.sensorA && data.objA)
|
|
||||||
{
|
|
||||||
m_TriggerEvents.push_back({data.objA, data.objB, true});
|
|
||||||
}
|
|
||||||
if (data.sensorB && data.objB)
|
|
||||||
{
|
|
||||||
m_TriggerEvents.push_back({data.objB, data.objA, true});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& [pair, data] : m_PreviousSensorOverlaps)
|
|
||||||
{
|
|
||||||
if (currentOverlaps.find(pair) == currentOverlaps.end())
|
|
||||||
{
|
|
||||||
if (data.sensorA && data.objA)
|
|
||||||
{
|
|
||||||
m_TriggerEvents.push_back({data.objA, data.objB, false});
|
|
||||||
}
|
|
||||||
if (data.sensorB && data.objB)
|
|
||||||
{
|
|
||||||
m_TriggerEvents.push_back({data.objB, data.objA, false});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_PreviousSensorOverlaps = std::move(currentOverlaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings m_Settings{};
|
Settings m_Settings{};
|
||||||
|
|
||||||
BPLayerInterfaceImpl m_BPLayerInterface{};
|
BPLayerInterfaceImpl m_BPLayerInterface{};
|
||||||
@@ -767,10 +639,6 @@ private:
|
|||||||
|
|
||||||
std::unordered_map<std::uint32_t, BodyRecord> m_Bodies;
|
std::unordered_map<std::uint32_t, BodyRecord> m_Bodies;
|
||||||
std::uint32_t m_NextHandle{0};
|
std::uint32_t m_NextHandle{0};
|
||||||
|
|
||||||
SensorContactListener m_SensorListener;
|
|
||||||
std::unordered_map<SensorContactListener::BodyPair, SensorContactListener::PairData, SensorContactListener::PairHash> m_PreviousSensorOverlaps;
|
|
||||||
std::vector<TriggerEvent> m_TriggerEvents;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
JoltPhysicsWorld::JoltPhysicsWorld(const Settings& settings)
|
JoltPhysicsWorld::JoltPhysicsWorld(const Settings& settings)
|
||||||
@@ -842,8 +710,3 @@ bool JoltPhysicsWorld::Raycast(const glm::vec3& origin,
|
|||||||
{
|
{
|
||||||
return m_Impl->Raycast(origin, direction, maxDistance, hit);
|
return m_Impl->Raycast(origin, direction, maxDistance, hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TriggerEvent> JoltPhysicsWorld::ConsumeTriggerEvents()
|
|
||||||
{
|
|
||||||
return m_Impl->ConsumeTriggerEvents();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <destrum/Physics/PhysicsSceneBridge.h>
|
#include <destrum/Physics/PhysicsSceneBridge.h>
|
||||||
|
|
||||||
#include <destrum/Components/Physics/Rigidbody.h>
|
#include <destrum/Components/Physics/Rigidbody.h>
|
||||||
#include <destrum/ObjectModel/Component.h>
|
|
||||||
#include <destrum/ObjectModel/GameObject.h>
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
|
||||||
PhysicsSceneBridge::PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world)
|
PhysicsSceneBridge::PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world)
|
||||||
@@ -9,16 +8,14 @@ PhysicsSceneBridge::PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSceneBridge::RegisterGameObject(GameObject& object) {
|
void PhysicsSceneBridge::RegisterGameObject(GameObject& object) {
|
||||||
if (!m_World) return;
|
|
||||||
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
||||||
if (!rb->HasPhysicsBody() || rb->GetPhysicsWorld() != m_World.get()) {
|
if (!rb->HasPhysicsBody() || rb->GetPhysicsWorld() != &GetWorld()) {
|
||||||
m_World->RegisterRigidbody(*rb);
|
m_World->RegisterRigidbody(*rb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
|
void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
|
||||||
if (!m_World) return;
|
|
||||||
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
||||||
if (rb->GetPhysicsWorld() == m_World.get()) {
|
if (rb->GetPhysicsWorld() == m_World.get()) {
|
||||||
m_World->UnregisterRigidbody(*rb);
|
m_World->UnregisterRigidbody(*rb);
|
||||||
@@ -27,29 +24,13 @@ void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSceneBridge::RefreshGameObject(GameObject& object) {
|
void PhysicsSceneBridge::RefreshGameObject(GameObject& object) {
|
||||||
if (!m_World) return;
|
|
||||||
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
||||||
m_World->RefreshRigidbody(*rb);
|
m_World->RefreshRigidbody(*rb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSceneBridge::FixedUpdate(float fixedDt) {
|
void PhysicsSceneBridge::FixedUpdate(float fixedDt) {
|
||||||
if (!m_World) return;
|
|
||||||
m_World->SyncKinematicBodiesToPhysics();
|
m_World->SyncKinematicBodiesToPhysics();
|
||||||
m_World->Step(fixedDt);
|
m_World->Step(fixedDt);
|
||||||
|
|
||||||
for (const auto& event : m_World->ConsumeTriggerEvents()) {
|
|
||||||
if (event.owner == nullptr) continue;
|
|
||||||
for (const auto& component : event.owner->GetComponents()) {
|
|
||||||
if (component && !component->IsBeingDestroyed() && component->isEnabled()) {
|
|
||||||
if (event.entered) {
|
|
||||||
component->OnTriggerEnter(event.other);
|
|
||||||
} else {
|
|
||||||
component->OnTriggerExit(event.other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_World->SyncDynamicBodiesToTransforms();
|
m_World->SyncDynamicBodiesToTransforms();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scene::Scene(const std::string& name)
|
Scene::Scene(const std::string& name)
|
||||||
: Scene(name, std::make_unique<JoltPhysicsWorld>()) {
|
: m_name(name),
|
||||||
}
|
|
||||||
|
|
||||||
Scene::Scene(const std::string& name, std::unique_ptr<PhysicsWorld> physicsWorld)
|
|
||||||
: m_Physics(std::move(physicsWorld)),
|
|
||||||
m_name(name),
|
|
||||||
m_id(++m_idCounter) {
|
m_id(++m_idCounter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,40 +8,50 @@
|
|||||||
#include <destrum/Util/DeltaTime.h>
|
#include <destrum/Util/DeltaTime.h>
|
||||||
|
|
||||||
Scene& SceneManager::GetCurrentScene() const {
|
Scene& SceneManager::GetCurrentScene() const {
|
||||||
if (m_activeScenes.empty()) {
|
if (m_scenes.empty()) {
|
||||||
throw std::out_of_range("No active scenes are available");
|
throw std::out_of_range("No scenes are available");
|
||||||
}
|
}
|
||||||
return *m_activeScenes.front();
|
|
||||||
|
if (m_ActiveSceneIndex < 0 || m_ActiveSceneIndex >= static_cast<int>(m_scenes.size())) {
|
||||||
|
throw std::out_of_range("Active scene index is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
return *m_scenes[static_cast<std::size_t>(m_ActiveSceneIndex)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::Update(float dt) {
|
void SceneManager::Update(float dt) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
if (m_scenes.empty()) return;
|
||||||
|
(void)GetCurrentScene();
|
||||||
|
const auto scene = m_scenes.at(static_cast<std::size_t>(m_ActiveSceneIndex));
|
||||||
scene->Update(dt);
|
scene->Update(dt);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::FixedUpdate(float dt) {
|
void SceneManager::FixedUpdate(float dt) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
if (m_scenes.empty()) return;
|
||||||
|
(void)GetCurrentScene();
|
||||||
|
const auto scene = m_scenes.at(static_cast<std::size_t>(m_ActiveSceneIndex));
|
||||||
scene->FixedUpdate(dt);
|
scene->FixedUpdate(dt);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::LateUpdate(float dt) {
|
void SceneManager::LateUpdate(float dt) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
if (m_scenes.empty()) return;
|
||||||
|
(void)GetCurrentScene();
|
||||||
|
const auto scene = m_scenes.at(static_cast<std::size_t>(m_ActiveSceneIndex));
|
||||||
scene->LateUpdate(dt);
|
scene->LateUpdate(dt);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::Render(const RenderContext& ctx) {
|
void SceneManager::Render(const RenderContext& ctx) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
if (m_scenes.empty()) return;
|
||||||
|
(void)GetCurrentScene();
|
||||||
|
const auto scene = m_scenes.at(static_cast<std::size_t>(m_ActiveSceneIndex));
|
||||||
scene->Render(ctx);
|
scene->Render(ctx);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::RenderImgui() {
|
void SceneManager::RenderImgui() {
|
||||||
for (const auto& scene : m_activeScenes) {
|
if (m_scenes.empty()) return;
|
||||||
|
(void)GetCurrentScene();
|
||||||
|
const auto scene = m_scenes.at(static_cast<std::size_t>(m_ActiveSceneIndex));
|
||||||
scene->RenderImgui();
|
scene->RenderImgui();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::HandleGameObjectDestroy() {
|
void SceneManager::HandleGameObjectDestroy() {
|
||||||
@@ -51,7 +61,7 @@ void SceneManager::HandleGameObjectDestroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::DestroyGameObjects() {
|
void SceneManager::DestroyGameObjects() {
|
||||||
for (const auto& scene : m_scenes) {
|
for (const auto& scene: m_scenes) {
|
||||||
scene->DestroyGameObjects();
|
scene->DestroyGameObjects();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,22 +73,37 @@ void SceneManager::UnloadAllScenes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::HandleSceneDestroy() {
|
void SceneManager::HandleSceneDestroy() {
|
||||||
|
const std::shared_ptr<Scene> activeScene =
|
||||||
|
m_ActiveSceneIndex >= 0 &&
|
||||||
|
m_ActiveSceneIndex < static_cast<int>(m_scenes.size())
|
||||||
|
? m_scenes[static_cast<std::size_t>(m_ActiveSceneIndex)]
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
for (auto it = m_scenes.begin(); it != m_scenes.end();) {
|
for (auto it = m_scenes.begin(); it != m_scenes.end();) {
|
||||||
if ((*it)->IsBeingUnloaded()) {
|
if ((*it)->IsBeingUnloaded()) {
|
||||||
const auto activeIt = std::find(m_activeScenes.begin(), m_activeScenes.end(), *it);
|
|
||||||
if (activeIt != m_activeScenes.end()) {
|
|
||||||
m_activeScenes.erase(activeIt);
|
|
||||||
}
|
|
||||||
it = m_scenes.erase(it);
|
it = m_scenes.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any stale active scenes that are no longer in the scene list.
|
if (m_scenes.empty()) {
|
||||||
std::erase_if(m_activeScenes, [this](const auto& scene) {
|
m_ActiveSceneIndex = 0;
|
||||||
return std::find(m_scenes.begin(), m_scenes.end(), scene) == m_scenes.end();
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (activeScene != nullptr) {
|
||||||
|
const auto activeIt = std::find(m_scenes.begin(), m_scenes.end(), activeScene);
|
||||||
|
if (activeIt != m_scenes.end()) {
|
||||||
|
m_ActiveSceneIndex = static_cast<int>(std::distance(m_scenes.begin(), activeIt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ActiveSceneIndex = std::clamp(
|
||||||
|
m_ActiveSceneIndex,
|
||||||
|
0,
|
||||||
|
static_cast<int>(m_scenes.size()) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::HandleScene() {
|
void SceneManager::HandleScene() {
|
||||||
@@ -88,6 +113,7 @@ void SceneManager::HandleScene() {
|
|||||||
|
|
||||||
void SceneManager::Destroy() {
|
void SceneManager::Destroy() {
|
||||||
if (m_scenes.empty()) {
|
if (m_scenes.empty()) {
|
||||||
|
m_ActiveSceneIndex = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,60 +124,25 @@ void SceneManager::Destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::SwitchScene(int index) {
|
void SceneManager::SwitchScene(int index) {
|
||||||
|
// InputManager::GetInstance().RemoveAllBindings();
|
||||||
|
|
||||||
if (index < 0 || index >= static_cast<int>(m_scenes.size())) {
|
if (index < 0 || index >= static_cast<int>(m_scenes.size())) {
|
||||||
throw std::out_of_range("Scene index out of range");
|
throw std::out_of_range("Scene index out of range");
|
||||||
}
|
}
|
||||||
|
if (index == m_ActiveSceneIndex) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
return;
|
||||||
scene->UnloadBindings();
|
|
||||||
}
|
}
|
||||||
m_activeScenes.clear();
|
|
||||||
m_activeScenes.push_back(m_scenes[static_cast<std::size_t>(index)]);
|
m_scenes[static_cast<std::size_t>(m_ActiveSceneIndex)]->UnloadBindings();
|
||||||
m_activeScenes.back()->LoadBindings();
|
m_ActiveSceneIndex = index;
|
||||||
|
m_scenes[static_cast<std::size_t>(m_ActiveSceneIndex)]->LoadBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::AddActiveScene(int index) {
|
Scene &SceneManager::CreateScene(const std::string &name) {
|
||||||
if (index < 0 || index >= static_cast<int>(m_scenes.size())) {
|
|
||||||
throw std::out_of_range("Scene index out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& scene = m_scenes[static_cast<std::size_t>(index)];
|
|
||||||
if (std::find(m_activeScenes.begin(), m_activeScenes.end(), scene) == m_activeScenes.end()) {
|
|
||||||
scene->LoadBindings();
|
|
||||||
m_activeScenes.push_back(scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::RemoveActiveScene(int index) {
|
|
||||||
if (index < 0 || index >= static_cast<int>(m_scenes.size())) {
|
|
||||||
throw std::out_of_range("Scene index out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& scene = m_scenes[static_cast<std::size_t>(index)];
|
|
||||||
const auto it = std::find(m_activeScenes.begin(), m_activeScenes.end(), scene);
|
|
||||||
if (it != m_activeScenes.end()) {
|
|
||||||
(*it)->UnloadBindings();
|
|
||||||
m_activeScenes.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SceneManager::GetActiveSceneId() const {
|
|
||||||
if (m_scenes.empty() || m_activeScenes.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto it = std::find(m_scenes.begin(), m_scenes.end(), m_activeScenes.front());
|
|
||||||
if (it == m_scenes.end()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<int>(std::distance(m_scenes.begin(), it));
|
|
||||||
}
|
|
||||||
|
|
||||||
Scene& SceneManager::CreateScene(const std::string& name) {
|
|
||||||
const auto scene = std::shared_ptr<Scene>(new Scene(name));
|
const auto scene = std::shared_ptr<Scene>(new Scene(name));
|
||||||
m_scenes.push_back(scene);
|
m_scenes.push_back(scene);
|
||||||
if (m_scenes.size() == 1) {
|
if (m_scenes.size() == 1) {
|
||||||
m_activeScenes.push_back(scene);
|
m_ActiveSceneIndex = 0;
|
||||||
}
|
}
|
||||||
return *scene;
|
return *scene;
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,8 @@
|
|||||||
#include <destrum/ObjectModel/GameObject.h>
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
#include <destrum/ObjectModel/ObjectId.h>
|
#include <destrum/ObjectModel/ObjectId.h>
|
||||||
#include <destrum/ObjectModel/Transform.h>
|
#include <destrum/ObjectModel/Transform.h>
|
||||||
#include <destrum/Assets/AssetReference.h>
|
|
||||||
#include <destrum/Components/Physics/Collider.h>
|
#include <destrum/Components/Physics/Collider.h>
|
||||||
#include <destrum/Components/Physics/Rigidbody.h>
|
#include <destrum/Components/Physics/Rigidbody.h>
|
||||||
#include <destrum/FS/AssetFS.h>
|
|
||||||
#include <destrum/Serialization/ComponentFactory.h>
|
#include <destrum/Serialization/ComponentFactory.h>
|
||||||
#include <destrum/Serialization/ComponentRegistry.h>
|
#include <destrum/Serialization/ComponentRegistry.h>
|
||||||
|
|
||||||
@@ -203,78 +201,6 @@ namespace {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool AssetFileExists(const std::string& path) {
|
|
||||||
if (path.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path fullPath;
|
|
||||||
if (path.find("://") != std::string::npos) {
|
|
||||||
try {
|
|
||||||
fullPath = AssetFS::GetInstance().GetFullPath(path);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fullPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::filesystem::exists(fullPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ValidateAssetReferences(const json& root, const std::filesystem::path& scenePath) {
|
|
||||||
const auto report = [&](const std::string& objectName, const std::string& detail) {
|
|
||||||
std::cerr << "Scene asset warning (" << scenePath << "): " << objectName
|
|
||||||
<< " references \"" << detail << "\" which could not be found\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& objectJson : root.at("objects")) {
|
|
||||||
const std::string objectName = objectJson.value("name", "GameObject");
|
|
||||||
if (!objectJson.contains("components")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& componentJson : objectJson.at("components")) {
|
|
||||||
if (!componentJson.contains("data")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto& data = componentJson.at("data");
|
|
||||||
|
|
||||||
// MeshRendererComponent: meshKey / materialKey are asset cache keys.
|
|
||||||
for (const char* key : {"meshKey", "materialKey"}) {
|
|
||||||
if (data.contains(key) && data.at(key).is_string()) {
|
|
||||||
const std::string cacheKey = data.at(key).get<std::string>();
|
|
||||||
if (cacheKey.empty()) continue;
|
|
||||||
const auto asset = AssetReference::fromCacheKey(cacheKey);
|
|
||||||
if (asset && !AssetFileExists(asset->path)) {
|
|
||||||
report(objectName, cacheKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic: any nested object with a "path" field is an AssetReference.
|
|
||||||
std::function<void(const json&)> walk = [&](const json& node) {
|
|
||||||
if (node.is_object()) {
|
|
||||||
if (node.contains("path") && node.at("path").is_string()) {
|
|
||||||
const std::string path = node.at("path").get<std::string>();
|
|
||||||
if (!path.empty() && !AssetFileExists(path)) {
|
|
||||||
report(objectName, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
|
||||||
walk(it.value());
|
|
||||||
}
|
|
||||||
} else if (node.is_array()) {
|
|
||||||
for (const auto& element : node) {
|
|
||||||
walk(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
walk(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSerializer::Save(Scene& scene, const std::filesystem::path& path) {
|
bool SceneSerializer::Save(Scene& scene, const std::filesystem::path& path) {
|
||||||
@@ -364,45 +290,30 @@ bool SceneSerializer::Save(Scene& scene, const std::filesystem::path& path) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSerializer::LoadResult SceneSerializer::LoadSceneFile(const std::filesystem::path& path) {
|
bool SceneSerializer::Load(Scene& scene, const std::filesystem::path& path) {
|
||||||
LoadResult result;
|
|
||||||
|
|
||||||
std::ifstream file(path);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
result.errorMessage = "Failed to open scene file for reading: " + path.string();
|
|
||||||
std::cerr << result.errorMessage << '\n';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
file >> result.root;
|
|
||||||
if (!ValidateSceneJson(result.root)) {
|
|
||||||
result.errorMessage = "Invalid scene data: " + path.string();
|
|
||||||
std::cerr << result.errorMessage << '\n';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
result.errorMessage = "Failed to parse scene file: " + std::string(exception.what());
|
|
||||||
std::cerr << result.errorMessage << '\n';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ValidateAssetReferences(result.root, path);
|
|
||||||
result.success = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|
||||||
std::function<void(float)> progressCallback) {
|
|
||||||
if (scene.IsIterating()) {
|
if (scene.IsIterating()) {
|
||||||
std::cerr << "Cannot load a scene during an update or render phase: "
|
std::cerr << "Cannot load a scene during an update or render phase: "
|
||||||
<< scene.GetName() << '\n';
|
<< scene.GetName() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.success) {
|
RegisterEngineComponents();
|
||||||
std::cerr << "Cannot construct scene from a failed load result: "
|
|
||||||
<< result.errorMessage << '\n';
|
std::ifstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Failed to open scene file for reading: " << path << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
json root;
|
||||||
|
try {
|
||||||
|
file >> root;
|
||||||
|
if (!ValidateSceneJson(root)) {
|
||||||
|
std::cerr << "Invalid scene data: " << path << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& exception) {
|
||||||
|
std::cerr << "Failed to parse scene file: " << exception.what() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,16 +322,9 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterEngineComponents();
|
|
||||||
if (progressCallback) progressCallback(0.35f);
|
|
||||||
|
|
||||||
const json& root = result.root;
|
|
||||||
|
|
||||||
// Build the replacement separately. The current scene is not touched
|
// Build the replacement separately. The current scene is not touched
|
||||||
// until all objects, transforms, and components have loaded successfully.
|
// until all objects, transforms, and components have loaded successfully.
|
||||||
// No physics world is created for the staging scene - physics bodies are
|
Scene stagingScene(scene.GetName());
|
||||||
// registered directly in the live scene's world once loading succeeds.
|
|
||||||
Scene stagingScene(scene.GetName(), nullptr);
|
|
||||||
std::unordered_map<ObjectId, GameObject*> idMap;
|
std::unordered_map<ObjectId, GameObject*> idMap;
|
||||||
std::vector<const json*> objectJsonList;
|
std::vector<const json*> objectJsonList;
|
||||||
std::vector<GameObject*> registeredObjects;
|
std::vector<GameObject*> registeredObjects;
|
||||||
@@ -439,16 +343,7 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
objectJsonList.push_back(&objectJson);
|
objectJsonList.push_back(&objectJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.4f);
|
stagingScene.CommitPendingAdditions();
|
||||||
|
|
||||||
// Move objects from pending to objects list without physics registration
|
|
||||||
// (staging scene has no physics world).
|
|
||||||
for (auto& pending : stagingScene.m_pendingAdditions) {
|
|
||||||
if (pending) {
|
|
||||||
stagingScene.m_objects.emplace_back(std::move(pending));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stagingScene.m_pendingAdditions.clear();
|
|
||||||
|
|
||||||
for (const json* objectJson : objectJsonList) {
|
for (const json* objectJson : objectJsonList) {
|
||||||
const ObjectId id = objectJson->at("id").get<ObjectId>();
|
const ObjectId id = objectJson->at("id").get<ObjectId>();
|
||||||
@@ -497,8 +392,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.5f);
|
|
||||||
|
|
||||||
for (const json* objectJson : objectJsonList) {
|
for (const json* objectJson : objectJsonList) {
|
||||||
GameObject* object = idMap.at(objectJson->at("id").get<ObjectId>());
|
GameObject* object = idMap.at(objectJson->at("id").get<ObjectId>());
|
||||||
if (!objectJson->contains("components")) {
|
if (!objectJson->contains("components")) {
|
||||||
@@ -519,8 +412,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.6f);
|
|
||||||
|
|
||||||
for (const auto& objectPtr : stagingScene.GetObjects()) {
|
for (const auto& objectPtr : stagingScene.GetObjects()) {
|
||||||
if (!objectPtr) {
|
if (!objectPtr) {
|
||||||
continue;
|
continue;
|
||||||
@@ -533,8 +424,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.7f);
|
|
||||||
|
|
||||||
// Register the replacement bodies in the live physics world before
|
// Register the replacement bodies in the live physics world before
|
||||||
// touching the current scene. If any body fails, the old scene and
|
// touching the current scene. If any body fails, the old scene and
|
||||||
// its physics state can remain intact.
|
// its physics state can remain intact.
|
||||||
@@ -549,8 +438,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
registeredObjects.push_back(objectPtr.get());
|
registeredObjects.push_back(objectPtr.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.8f);
|
|
||||||
} catch (const std::exception& exception) {
|
} catch (const std::exception& exception) {
|
||||||
std::cerr << "Failed to load scene: " << exception.what() << '\n';
|
std::cerr << "Failed to load scene: " << exception.what() << '\n';
|
||||||
for (GameObject* object : registeredObjects) {
|
for (GameObject* object : registeredObjects) {
|
||||||
@@ -562,7 +449,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.UnloadBindings();
|
|
||||||
scene.RemoveAll();
|
scene.RemoveAll();
|
||||||
scene.m_objects = std::move(stagingScene.m_objects);
|
scene.m_objects = std::move(stagingScene.m_objects);
|
||||||
scene.m_pendingAdditions = std::move(stagingScene.m_pendingAdditions);
|
scene.m_pendingAdditions = std::move(stagingScene.m_pendingAdditions);
|
||||||
@@ -570,8 +456,6 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
scene.m_name = root.at("name").get<std::string>();
|
scene.m_name = root.at("name").get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.9f);
|
|
||||||
|
|
||||||
for (const auto& object : scene.m_objects) {
|
for (const auto& object : scene.m_objects) {
|
||||||
if (object) {
|
if (object) {
|
||||||
object->SetScene(&scene);
|
object->SetScene(&scene);
|
||||||
@@ -583,27 +467,5 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback(1.0f);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSerializer::Load(Scene& scene, const std::filesystem::path& path,
|
|
||||||
std::function<void(float)> progressCallback) {
|
|
||||||
if (progressCallback) progressCallback(0.0f);
|
|
||||||
|
|
||||||
auto result = LoadSceneFile(path);
|
|
||||||
if (!result.success) {
|
|
||||||
if (progressCallback) progressCallback(1.0f);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progressCallback) progressCallback(0.3f);
|
|
||||||
return ConstructScene(scene, result, std::move(progressCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<SceneSerializer::LoadResult> SceneSerializer::LoadSceneFileAsync(
|
|
||||||
const std::filesystem::path& path) {
|
|
||||||
return std::async(std::launch::async, [path]() {
|
|
||||||
return LoadSceneFile(path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
+1
-1
Submodule destrum/third_party/imgui updated: 7e1b65d26d...d15966ff6c
@@ -6,11 +6,6 @@ set(GAME_SRC
|
|||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
|
||||||
src/Lightkeeper.cpp
|
src/Lightkeeper.cpp
|
||||||
|
|
||||||
src/components/GameComponentRegistry.cpp
|
|
||||||
src/components/FirstPersonController.cpp
|
|
||||||
src/components/PrintComponent.cpp
|
|
||||||
src/components/TriggerLoggerComponent.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(lightkeeper ${GAME_SRC})
|
add_executable(lightkeeper ${GAME_SRC})
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#ifndef LIGHTKEEPER_H
|
#ifndef LIGHTKEEPER_H
|
||||||
#define LIGHTKEEPER_H
|
#define LIGHTKEEPER_H
|
||||||
|
|
||||||
#include <string>
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <destrum/App.h>
|
#include <destrum/App.h>
|
||||||
#include <destrum/Scene/SceneManager.h>
|
|
||||||
#include <destrum/Graphics/RenderResources.h>
|
#include <destrum/Graphics/RenderResources.h>
|
||||||
|
|
||||||
#include "destrum/Graphics/Resources/Cubemap.h"
|
#include "destrum/Graphics/Resources/Cubemap.h"
|
||||||
#include "destrum/ObjectModel/GameObject.h"
|
#include "destrum/ObjectModel/GameObject.h"
|
||||||
|
|
||||||
class FirstPersonController;
|
|
||||||
class LightKeeper final : public App {
|
class LightKeeper final : public App {
|
||||||
public:
|
public:
|
||||||
LightKeeper();
|
LightKeeper();
|
||||||
@@ -24,31 +23,33 @@ public:
|
|||||||
void onWindowResize(int newWidth, int newHeight) override;
|
void onWindowResize(int newWidth, int newHeight) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawDebugMenuBar();
|
struct FallingStar {
|
||||||
void saveCurrentScene();
|
GameObject* object{};
|
||||||
void loadScene();
|
float speed{};
|
||||||
void createEmptyScene();
|
};
|
||||||
void submitBoxColliderDebugLines();
|
|
||||||
|
void resetGame();
|
||||||
|
void respawnStar(FallingStar& star, float minimumHeight);
|
||||||
|
void drawHud();
|
||||||
|
|
||||||
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
||||||
|
|
||||||
MeshID sphereMesh;
|
MeshID sphereMesh{NULL_MESH_ID};
|
||||||
MaterialID sphereMaterial;
|
MaterialID playerMaterial{NULL_MATERIAL_ID};
|
||||||
|
MaterialID starMaterial{NULL_MATERIAL_ID};
|
||||||
ImageID texID;
|
|
||||||
|
|
||||||
std::unique_ptr<CubeMap> skyboxCubemap;
|
std::unique_ptr<CubeMap> skyboxCubemap;
|
||||||
|
|
||||||
GameObject* capybara = nullptr;
|
GameObject* player{nullptr};
|
||||||
GameObject* m_Player = nullptr;
|
std::vector<FallingStar> stars;
|
||||||
FirstPersonController* m_FPSController = nullptr;
|
std::mt19937 randomEngine{0xD35A1234u};
|
||||||
|
std::uniform_real_distribution<float> spawnX{-5.5f, 5.5f};
|
||||||
|
std::uniform_real_distribution<float> spawnHeight{0.0f, 2.0f};
|
||||||
|
std::uniform_real_distribution<float> spawnSpeed{2.8f, 4.2f};
|
||||||
|
|
||||||
char scenePath[512]{"scenes/debug_scene.json"};
|
int score{0};
|
||||||
char newSceneName[128]{"EmptyScene"};
|
int misses{0};
|
||||||
std::string sceneStatus;
|
bool gameOver{false};
|
||||||
bool renderBoxColliders{false};
|
|
||||||
bool renderQuads{true};
|
|
||||||
bool renderDebugLines{true};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //LIGHTKEEPER_H
|
#endif //LIGHTKEEPER_H
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
#ifndef LIGHTKEEPER_FIRSTPERSONCONTROLLER_H
|
|
||||||
#define LIGHTKEEPER_FIRSTPERSONCONTROLLER_H
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <destrum/Input/InputManager.h>
|
|
||||||
#include <destrum/ObjectModel/Component.h>
|
|
||||||
|
|
||||||
class Rigidbody;
|
|
||||||
class CapsuleCollider;
|
|
||||||
|
|
||||||
class FirstPersonController final : public Component {
|
|
||||||
public:
|
|
||||||
explicit FirstPersonController(GameObject& parent);
|
|
||||||
|
|
||||||
std::string GetTypeName() const override { return "FirstPersonController"; }
|
|
||||||
|
|
||||||
void Start() override;
|
|
||||||
void Update(float dt) override;
|
|
||||||
void FixedUpdate(float fixedDt) override;
|
|
||||||
void ImGuiInspector() override;
|
|
||||||
nlohmann::json Serialize() const override;
|
|
||||||
void Deserialize(const nlohmann::json& data) override;
|
|
||||||
|
|
||||||
[[nodiscard]] float GetYaw() const { return m_Yaw; }
|
|
||||||
[[nodiscard]] float GetPitch() const { return m_Pitch; }
|
|
||||||
[[nodiscard]] float GetEyeHeight() const { return m_EyeHeight; }
|
|
||||||
|
|
||||||
void SetMovementSpeed(float speed) { m_MovementSpeed = speed; }
|
|
||||||
[[nodiscard]] float GetMovementSpeed() const { return m_MovementSpeed; }
|
|
||||||
|
|
||||||
void SetMouseSensitivity(float sens) { m_MouseSensitivity = sens; }
|
|
||||||
[[nodiscard]] float GetMouseSensitivity() const { return m_MouseSensitivity; }
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsMouseCaptured() const { return InputManager::GetInstance().IsMouseCaptured(); }
|
|
||||||
void SetMouseCaptured(bool captured);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Rigidbody* m_Rigidbody{nullptr};
|
|
||||||
CapsuleCollider* m_Capsule{nullptr};
|
|
||||||
|
|
||||||
float m_Yaw{0.0f};
|
|
||||||
float m_Pitch{0.0f};
|
|
||||||
float m_MovementSpeed{5.0f};
|
|
||||||
float m_MouseSensitivity{0.002f};
|
|
||||||
float m_EyeHeight{1.6f};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef LIGHTKEEPER_GAMECOMPONENTLIST_H
|
|
||||||
#define LIGHTKEEPER_GAMECOMPONENTLIST_H
|
|
||||||
|
|
||||||
#include <components/FirstPersonController.h>
|
|
||||||
#include <components/PrintComponent.h>
|
|
||||||
#include <components/TriggerLoggerComponent.h>
|
|
||||||
|
|
||||||
#define LIGHTKEEPER_GAME_COMPONENTS(X) \
|
|
||||||
X(FirstPersonController) \
|
|
||||||
X(PrintComponent) \
|
|
||||||
X(TriggerLoggerComponent)
|
|
||||||
|
|
||||||
#endif // LIGHTKEEPER_GAMECOMPONENTLIST_H
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef LIGHTKEEPER_GAMECOMPONENTREGISTRY_H
|
|
||||||
#define LIGHTKEEPER_GAMECOMPONENTREGISTRY_H
|
|
||||||
|
|
||||||
void RegisterGameComponents();
|
|
||||||
|
|
||||||
#endif // LIGHTKEEPER_GAMECOMPONENTREGISTRY_H
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef DESTRUM_PRINTCOMPONENT_H
|
|
||||||
#define DESTRUM_PRINTCOMPONENT_H
|
|
||||||
#include "destrum/ObjectModel/Component.h"
|
|
||||||
|
|
||||||
class PrintComponent: public Component {
|
|
||||||
public:
|
|
||||||
|
|
||||||
PrintComponent(GameObject& parent);
|
|
||||||
|
|
||||||
std::string GetTypeName() const override { return "PrintComponent"; }
|
|
||||||
void Start() override;
|
|
||||||
void Update(float dt) override;
|
|
||||||
void ImGuiInspector() override;
|
|
||||||
nlohmann::json Serialize() const override;
|
|
||||||
void Deserialize(const nlohmann::json&) override;
|
|
||||||
|
|
||||||
void SetMessage(std::string_view message)
|
|
||||||
{
|
|
||||||
m_message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_message{};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //DESTRUM_PRINTCOMPONENT_H
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef DESTRUM_TRIGGERLOGGERCOMPONENT_H
|
|
||||||
#define DESTRUM_TRIGGERLOGGERCOMPONENT_H
|
|
||||||
#include "destrum/ObjectModel/Component.h"
|
|
||||||
|
|
||||||
class TriggerLoggerComponent: public Component {
|
|
||||||
public:
|
|
||||||
TriggerLoggerComponent(GameObject& parent);
|
|
||||||
|
|
||||||
std::string GetTypeName() const override { return "TriggerLoggerComponent"; }
|
|
||||||
|
|
||||||
void OnTriggerEnter(GameObject* other) override;
|
|
||||||
void OnTriggerExit(GameObject* other) override;
|
|
||||||
|
|
||||||
nlohmann::json Serialize() const override;
|
|
||||||
void Deserialize(const nlohmann::json&) override;
|
|
||||||
|
|
||||||
int triggerEnterCount{0};
|
|
||||||
int triggerExitCount{0};
|
|
||||||
std::string lastOtherName;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //DESTRUM_TRIGGERLOGGERCOMPONENT_H
|
|
||||||
+194
-703
@@ -1,62 +1,35 @@
|
|||||||
#include "Lightkeeper.h"
|
#include "Lightkeeper.h"
|
||||||
|
|
||||||
#include <array>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <destrum/FS/AssetFS.h>
|
|
||||||
#include <destrum/Assets/AssetManager.h>
|
|
||||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
|
||||||
#include <destrum/Graphics/Managers/QuadRenderingManager.h>
|
|
||||||
#include "glm/gtc/constants.hpp"
|
|
||||||
#include "glm/gtx/transform.hpp"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include <destrum/Components/Physics/Rigidbody.h>
|
|
||||||
#include <destrum/Components/Physics/BoxCollider.h>
|
|
||||||
#include <destrum/Scene/Scene.h>
|
|
||||||
#include <destrum/Serialization/SceneSerializer.h>
|
|
||||||
|
|
||||||
#include "destrum/Components/MeshRendererComponent.h"
|
|
||||||
#include "destrum/Components/Rotator.h"
|
|
||||||
#include "destrum/Components/Spinner.h"
|
|
||||||
#include "destrum/Components/OrbitAndSpin.h"
|
|
||||||
#include "destrum/ObjectModel/GameObject.h"
|
|
||||||
#include "destrum/Util/ModelDoc.h"
|
|
||||||
#include "destrum/Components/Animator.h"
|
|
||||||
#include "destrum/Components/LightComponent.h"
|
|
||||||
#include <components/FirstPersonController.h>
|
|
||||||
#include <components/TriggerLoggerComponent.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "imgui.h"
|
#include <SDL.h>
|
||||||
#include "components/GameComponentRegistry.h"
|
#include <glm/gtc/constants.hpp>
|
||||||
#include "components/PrintComponent.h"
|
#include <glm/gtx/transform.hpp>
|
||||||
#include "destrum/Components/Physics/SphereCollider.h"
|
#include <imgui.h>
|
||||||
#include "destrum/Util/ModelDocUtils.h"
|
|
||||||
|
#include <destrum/Assets/AssetManager.h>
|
||||||
|
#include <destrum/Components/MeshRendererComponent.h>
|
||||||
|
#include <destrum/FS/AssetFS.h>
|
||||||
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
#include <destrum/Scene/Scene.h>
|
||||||
|
#include <destrum/Scene/SceneManager.h>
|
||||||
|
#include <destrum/Util/ModelDoc.h>
|
||||||
|
#include <destrum/Util/ModelDocUtils.h>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::filesystem::path ResolveScenePath(
|
constexpr int kStarCount = 3;
|
||||||
std::string_view value,
|
constexpr int kMaxMisses = 3;
|
||||||
const std::filesystem::path& exeDir) {
|
constexpr float kPlayerY = -5.0f;
|
||||||
if (value.empty()) {
|
constexpr float kCatchY = -4.25f;
|
||||||
throw std::invalid_argument("Scene path cannot be empty");
|
constexpr float kPlayerSpeed = 7.0f;
|
||||||
}
|
constexpr float kPlayerHalfWidth = 1.15f;
|
||||||
|
|
||||||
if (value.find("://") != std::string_view::npos) {
|
|
||||||
return AssetFS::GetInstance().GetFullPath(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::filesystem::path path{value};
|
|
||||||
return path.is_absolute() ? path : exeDir / path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LightKeeper::LightKeeper() : App()
|
LightKeeper::LightKeeper() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
LightKeeper::~LightKeeper()
|
LightKeeper::~LightKeeper()
|
||||||
{
|
{
|
||||||
@@ -70,653 +43,185 @@ LightKeeper::~LightKeeper()
|
|||||||
|
|
||||||
void LightKeeper::customInit()
|
void LightKeeper::customInit()
|
||||||
{
|
{
|
||||||
RegisterGameComponents();
|
|
||||||
|
|
||||||
resources.init(gfxDevice);
|
resources.init(gfxDevice);
|
||||||
renderer.init(gfxDevice, resources, m_params.renderSize);
|
renderer.init(gfxDevice, resources, m_params.renderSize);
|
||||||
|
|
||||||
const float aspectRatio = static_cast<float>(m_params.renderSize.x) / static_cast<float>(m_params.renderSize.y);
|
camera.m_position = glm::vec3{0.0f, 0.0f, -10.0f};
|
||||||
camera.setAspectRatio(aspectRatio);
|
camera.SetRotation(glm::radians(90.0f), 0.0f);
|
||||||
|
camera.setAspectRatio(
|
||||||
|
static_cast<float>(m_params.renderSize.x) /
|
||||||
|
static_cast<float>(m_params.renderSize.y));
|
||||||
|
|
||||||
ModelDoc::LoadOptions staticModelOptions{};
|
const auto skyboxID =
|
||||||
staticModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr");
|
||||||
staticModelOptions.loadMaterials = true;
|
const auto vertShaderPath =
|
||||||
staticModelOptions.loadSkeleton = false;
|
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert");
|
||||||
staticModelOptions.loadAnimations = false;
|
const auto fragShaderPath =
|
||||||
|
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag");
|
||||||
|
|
||||||
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
|
||||||
|
|
||||||
auto& scene = SceneManager::GetInstance().CreateScene("Main");
|
|
||||||
|
|
||||||
// scene.Add(testCube);
|
|
||||||
|
|
||||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg");
|
|
||||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/mars.jpg");
|
|
||||||
const auto skyboxID = AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr");
|
|
||||||
//
|
|
||||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png");
|
|
||||||
//
|
|
||||||
const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert");
|
|
||||||
const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag");
|
|
||||||
//
|
|
||||||
skyboxCubemap = std::make_unique<CubeMap>();
|
skyboxCubemap = std::make_unique<CubeMap>();
|
||||||
skyboxCubemap->LoadCubeMap(gfxDevice, resources, skyboxID.generic_string());
|
skyboxCubemap->LoadCubeMap(gfxDevice, resources, skyboxID);
|
||||||
skyboxCubemap->InitCubemapPipeline(gfxDevice, resources, vertShaderPath.generic_string(), fragShaderPath.generic_string());
|
skyboxCubemap->InitCubemapPipeline(
|
||||||
|
gfxDevice,
|
||||||
|
resources,
|
||||||
|
vertShaderPath.generic_string(),
|
||||||
|
fragShaderPath.generic_string());
|
||||||
skyboxCubemap->CreateCubeMap(gfxDevice, resources);
|
skyboxCubemap->CreateCubeMap(gfxDevice, resources);
|
||||||
|
|
||||||
renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID());
|
renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID());
|
||||||
|
|
||||||
const auto planeObj = scene.CreateGameObject("GroundPlane");
|
ModelDoc::LoadOptions modelOptions{};
|
||||||
const auto planeMeshComp = planeObj->AddComponent<MeshRendererComponent>();
|
modelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||||
|
modelOptions.loadMaterials = true;
|
||||||
|
modelOptions.loadSkeleton = false;
|
||||||
|
modelOptions.loadAnimations = false;
|
||||||
|
|
||||||
auto planeModel = ModelDoc::LoadModel(
|
const auto sphereModel = AssetManager::GetInstance().LoadModel(
|
||||||
AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string(),
|
"engine://sphere.fbx",
|
||||||
staticModelOptions
|
modelOptions);
|
||||||
);
|
const auto& spherePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(
|
||||||
ModelDocUtils::LogModelDocSummary(planeModel, "plane.glb");
|
sphereModel,
|
||||||
|
"engine://sphere.fbx");
|
||||||
const auto& planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb");
|
|
||||||
const auto planeMeshID = resources.meshes().addMesh(gfxDevice, planePrimitive.mesh);
|
|
||||||
|
|
||||||
const auto planeTexturePath = ModelDocUtils::PickTexturePath(
|
|
||||||
planeModel,
|
|
||||||
planePrimitive,
|
|
||||||
AssetFS::GetInstance().GetFullPath("game://grass.png")
|
|
||||||
);
|
|
||||||
|
|
||||||
const auto planeTextureID = resources.loadImageFromFile(gfxDevice, planeTexturePath);
|
|
||||||
const auto planeMaterialID = resources.materials().addMaterial({
|
|
||||||
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
|
||||||
planeModel, planePrimitive),
|
|
||||||
.textureFilteringMode = TextureFilteringMode::Nearest,
|
|
||||||
.diffuseTexture = planeTextureID,
|
|
||||||
.name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
planeModel, planePrimitive, "GroundPlaneMaterial"),
|
|
||||||
});
|
|
||||||
|
|
||||||
planeMeshComp->SetMeshID(planeMeshID);
|
|
||||||
planeMeshComp->SetMaterialID(planeMaterialID);
|
|
||||||
planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f));
|
|
||||||
planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f));
|
|
||||||
planeObj->AddComponent<BoxCollider>(glm::vec3{10.0f, 0.5f, 10.0f});
|
|
||||||
|
|
||||||
auto* floorRb = planeObj->AddComponent<Rigidbody>();
|
|
||||||
floorRb->SetType(RigidbodyType::Static);
|
|
||||||
scene.GetPhysics().RegisterGameObject(*planeObj);
|
|
||||||
|
|
||||||
const auto CharObj = scene.CreateGameObject("Character");
|
|
||||||
capybara = CharObj;
|
|
||||||
|
|
||||||
ModelDoc::LoadOptions characterModelOptions{};
|
|
||||||
characterModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
|
||||||
characterModelOptions.loadMaterials = true;
|
|
||||||
characterModelOptions.loadSkeleton = true;
|
|
||||||
characterModelOptions.loadAnimations = true;
|
|
||||||
|
|
||||||
// auto charModel = ModelDoc::LoadModel(
|
|
||||||
// AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
|
|
||||||
// characterModelOptions
|
|
||||||
// );
|
|
||||||
// ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
|
|
||||||
//
|
|
||||||
// const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
|
||||||
// charModel,
|
|
||||||
// "engine://cotw-capybara-male/source/capybara.fbx"
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
|
||||||
//
|
|
||||||
// const auto charTexturePath = ModelDocUtils::PickTexturePath(
|
|
||||||
// charModel,
|
|
||||||
// charPrimitive,
|
|
||||||
// AssetFS::GetInstance().GetFullPath(
|
|
||||||
// "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath);
|
|
||||||
// // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
|
||||||
// const auto charMaterialID = resources.materials().addMaterial({
|
|
||||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
|
||||||
// charModel, charPrimitive),
|
|
||||||
// .diffuseTexture = charTextureID,
|
|
||||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
// charModel, charPrimitive, "CharacterMaterial"),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
|
||||||
// charMeshComp->SetMeshID(charMeshID);
|
|
||||||
// charMeshComp->SetMaterialID(charMaterialID);
|
|
||||||
//
|
|
||||||
// const auto animator = CharObj->AddComponent<Animator>();
|
|
||||||
// animator->setSkeleton(std::move(charModel.skeleton));
|
|
||||||
//
|
|
||||||
// std::string firstAnimationName;
|
|
||||||
//
|
|
||||||
// for (auto& clip : charModel.animations)
|
|
||||||
// {
|
|
||||||
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
|
||||||
//
|
|
||||||
// if (firstAnimationName.empty())
|
|
||||||
// firstAnimationName = clip.name;
|
|
||||||
//
|
|
||||||
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!firstAnimationName.empty())
|
|
||||||
// {
|
|
||||||
// spdlog::info("Playing animation: '{}'", firstAnimationName);
|
|
||||||
// animator->play(firstAnimationName);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
|
|
||||||
//
|
|
||||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
|
||||||
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
|
||||||
|
|
||||||
// ModelDoc::LoadOptions options{};
|
|
||||||
// options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
|
||||||
//
|
|
||||||
// auto model = ModelDoc::LoadModel(
|
|
||||||
// AssetFS::GetInstance()
|
|
||||||
// .GetFullPath("engine://multiple_object_test.fbx")
|
|
||||||
// .generic_string(),
|
|
||||||
// options
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// ModelDocUtils::LogModelDocSummary(model, "engine://multiple_object_test.fbx");
|
|
||||||
//
|
|
||||||
// auto root = std::make_shared<GameObject>("MultiMeshModel");
|
|
||||||
// root->GetTransform().SetWorldPosition(glm::vec3(0.1f, 0.1f, 0.1f));
|
|
||||||
// scene.Add(root);
|
|
||||||
//
|
|
||||||
// for (std::size_t i = 0; i < model.primitives.size(); ++i) {
|
|
||||||
// const auto &primitive = model.primitives[i];
|
|
||||||
//
|
|
||||||
// const auto meshID = meshCache.addMesh(gfxDevice, primitive.mesh);
|
|
||||||
//
|
|
||||||
// const auto texturePath = ModelDocUtils::PickTexturePath(
|
|
||||||
// model,
|
|
||||||
// primitive,
|
|
||||||
// AssetFS::GetInstance().GetFullPath("engine://textures/white.png")
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto textureID = gfxDevice.loadImageFromFile(texturePath);
|
|
||||||
//
|
|
||||||
// const auto materialID = materialCache.addMaterial(gfxDevice, {
|
|
||||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
|
||||||
// model, primitive),
|
|
||||||
// .diffuseTexture = textureID,
|
|
||||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
// model,
|
|
||||||
// primitive,
|
|
||||||
// primitive.mesh.name + "_Material"
|
|
||||||
// ),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// auto part = std::make_shared<GameObject>(
|
|
||||||
// primitive.mesh.name.empty()
|
|
||||||
// ? "Primitive_" + std::to_string(i)
|
|
||||||
// : primitive.mesh.name
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// auto meshComp = part->AddComponent<MeshRendererComponent>();
|
|
||||||
// meshComp->SetMeshID(meshID);
|
|
||||||
// meshComp->SetMaterialID(materialID);
|
|
||||||
//
|
|
||||||
// scene.Add(part);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// {
|
|
||||||
// const auto CharObj = scene.CreateGameObject("Character");
|
|
||||||
//
|
|
||||||
// ModelDoc::LoadOptions characterOptions{};
|
|
||||||
// characterOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
|
||||||
// characterOptions.loadMaterials = true;
|
|
||||||
// characterOptions.loadSkeleton = true;
|
|
||||||
// characterOptions.loadAnimations = false;
|
|
||||||
//
|
|
||||||
// auto charModel = AssetManager::GetInstance().LoadModel("engine://characterMedium.fbx", characterOptions);
|
|
||||||
//
|
|
||||||
// const auto& charPrimitive =
|
|
||||||
// ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
|
||||||
// charModel,
|
|
||||||
// "engine://characterMedium.fbx"
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
|
||||||
//
|
|
||||||
// const auto charTextureID = resources.loadImageFromFile(gfxDevice,
|
|
||||||
// AssetFS::GetInstance().GetFullPath("engine://textures/criminalMaleA.png")
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto charMaterialID = resources.materials().addMaterial({
|
|
||||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
|
||||||
// charModel, charPrimitive),
|
|
||||||
// .diffuseTexture = charTextureID,
|
|
||||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
// charModel,
|
|
||||||
// charPrimitive,
|
|
||||||
// "CharacterMaterial"
|
|
||||||
// ),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
|
||||||
// charMeshComp->SetMeshID(charMeshID);
|
|
||||||
// charMeshComp->SetMaterialID(charMaterialID);
|
|
||||||
//
|
|
||||||
// const auto animator = CharObj->AddComponent<Animator>();
|
|
||||||
// animator->setSkeleton(charModel.skeleton);
|
|
||||||
//
|
|
||||||
// auto runClips = AssetManager::GetInstance().LoadAnimationClips(
|
|
||||||
// "engine://run.fbx",
|
|
||||||
// charModel.skeleton
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// for (auto& clip : runClips)
|
|
||||||
// {
|
|
||||||
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
|
||||||
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!runClips.empty())
|
|
||||||
// {
|
|
||||||
// animator->play("Root|Run");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f));
|
|
||||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(5, 0, 0));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// auto cubeModel = ModelDoc::LoadModel(
|
|
||||||
// AssetFS::GetInstance()
|
|
||||||
// .GetFullPath("engine://cube.fbx")
|
|
||||||
// .generic_string(),
|
|
||||||
// staticModelOptions
|
|
||||||
// );
|
|
||||||
// ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx");
|
|
||||||
//
|
|
||||||
// const auto& cubePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(cubeModel, "game://cube.fbx");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// const auto eliasTextuerPath = ModelDocUtils::PickTexturePath(
|
|
||||||
// cubeModel,
|
|
||||||
// cubePrimitive,
|
|
||||||
// AssetFS::GetInstance().GetFullPath("game://grass.png")
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto eliasTextueID = resources.loadImageFromFile(gfxDevice, eliasTextuerPath);
|
|
||||||
// const auto eliasMaterialID = resources.materials().addMaterial({
|
|
||||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
|
||||||
// planeModel, planePrimitive),
|
|
||||||
// .textureFilteringMode =
|
|
||||||
// TextureFilteringMode::Anisotropic,
|
|
||||||
// .diffuseTexture = eliasTextueID,
|
|
||||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
// planeModel, planePrimitive, "GroundPlaneMaterial"),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const auto cubeMeshID = resources.meshes().addMesh(gfxDevice, cubePrimitive.mesh);
|
|
||||||
// //
|
|
||||||
// // for (int i{0}; i < 100; i++)
|
|
||||||
// // {
|
|
||||||
// // auto cube = std::make_shared<GameObject>("Cube");
|
|
||||||
// //
|
|
||||||
// // cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
|
|
||||||
// // cube->AddComponent<Rigidbody>();
|
|
||||||
// //
|
|
||||||
// // auto meshComp = cube->AddComponent<MeshRendererComponent>();
|
|
||||||
// // meshComp->SetMeshID(cubeMeshID);
|
|
||||||
// // meshComp->SetMaterialID(eliasMaterialID);
|
|
||||||
// //
|
|
||||||
// // cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, i, 0.0f));
|
|
||||||
// // cube->GetTransform().SetWorldScale(glm::vec3(0.005f));
|
|
||||||
// //
|
|
||||||
// // scene.Add(cube);
|
|
||||||
// // scene.GetPhysics().RegisterGameObject(*cube);
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// // const int cubeCount = 10;
|
|
||||||
// // const float spacing = 1.0f;
|
|
||||||
// //
|
|
||||||
// // for (int x = 0; x < cubeCount; x++)
|
|
||||||
// // {
|
|
||||||
// // for (int y = 0; y < 3; y++)
|
|
||||||
// // {
|
|
||||||
// // for (int z = 0; z < cubeCount; z++)
|
|
||||||
// // {
|
|
||||||
// // auto cube = std::make_shared<GameObject>("Cube");
|
|
||||||
// //
|
|
||||||
// // cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
|
|
||||||
// // cube->AddComponent<Rigidbody>();
|
|
||||||
// //
|
|
||||||
// // auto meshComp = cube->AddComponent<MeshRendererComponent>();
|
|
||||||
// // meshComp->SetMeshID(cubeMeshID);
|
|
||||||
// // meshComp->SetMaterialID(eliasMaterialID);
|
|
||||||
// //
|
|
||||||
// // cube->GetTransform().SetWorldPosition(glm::vec3(
|
|
||||||
// // (x - cubeCount / 2.0f) * spacing,
|
|
||||||
// // y * spacing + 0,
|
|
||||||
// // (z - cubeCount / 2.0f) * spacing
|
|
||||||
// // ));
|
|
||||||
// //
|
|
||||||
// // cube->GetTransform().SetWorldScale(glm::vec3(0.005f));
|
|
||||||
// //
|
|
||||||
// // scene.Add(cube);
|
|
||||||
// // scene.GetPhysics().RegisterGameObject(*cube);
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
|
|
||||||
|
|
||||||
// {
|
|
||||||
auto sphereModel = AssetManager::GetInstance().LoadModel("engine://sphere.fbx", staticModelOptions);
|
|
||||||
ModelDocUtils::LogModelDocSummary(sphereModel, "sphere.fbx");
|
|
||||||
//
|
|
||||||
const auto& spherePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(sphereModel, "game://sphere.fbx");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// const auto sphereTexturePath = ModelDocUtils::PickTexturePath(
|
|
||||||
// sphereModel,
|
|
||||||
// spherePrimitive,
|
|
||||||
// AssetFS::GetInstance().GetFullPath("game://238882.png")
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// const auto sphereTextureID = resources.loadImageFromFile(gfxDevice, sphereTexturePath);
|
|
||||||
// sphereMaterial = resources.materials().addMaterial({
|
|
||||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(planeModel, planePrimitive),
|
|
||||||
// .textureFilteringMode =TextureFilteringMode::Anisotropic,
|
|
||||||
// .diffuseTexture = sphereTextureID,
|
|
||||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
|
||||||
// planeModel, planePrimitive,
|
|
||||||
// "GroundPlaneMaterial"),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
sphereMesh = resources.meshes().addMesh(gfxDevice, spherePrimitive.mesh);
|
sphereMesh = resources.meshes().addMesh(gfxDevice, spherePrimitive.mesh);
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
sphereMaterial = resources.materials().addSimpleColorMaterial({0.5f, 0.3f, 0.8f}, "Blue");
|
playerMaterial = resources.materials().addSimpleColorMaterial(
|
||||||
|
{0.08f, 0.75f, 1.0f},
|
||||||
|
"Catcher Blue");
|
||||||
|
starMaterial = resources.materials().addSimpleColorMaterial(
|
||||||
|
{1.0f, 0.55f, 0.05f},
|
||||||
|
"Falling Star Orange");
|
||||||
|
|
||||||
|
auto& scene = SceneManager::GetInstance().CreateScene("Star Catcher");
|
||||||
|
|
||||||
// Trigger zone: a static box with a TriggerLoggerComponent.
|
player = scene.CreateGameObject("Catcher");
|
||||||
// Spheres spawned via the "Spawn ball" button fall through it.
|
auto* playerRenderer = player->AddComponent<MeshRendererComponent>();
|
||||||
auto triggerObj = scene.CreateGameObject("TriggerBox");
|
playerRenderer->SetMeshID(sphereMesh);
|
||||||
auto triggerBox = triggerObj->AddComponent<BoxCollider>(glm::vec3{3.0f, 3.0f, 3.0f});
|
playerRenderer->SetMaterialID(playerMaterial);
|
||||||
triggerBox->SetTrigger(true);
|
player->GetTransform().SetLocalPosition({0.0f, kPlayerY, 0.0f});
|
||||||
auto triggerRb = triggerObj->AddComponent<Rigidbody>();
|
player->GetTransform().SetLocalScale(glm::vec3{0.012f, 0.0035f, 0.009f});
|
||||||
triggerRb->SetType(RigidbodyType::Static);
|
|
||||||
triggerObj->AddComponent<TriggerLoggerComponent>();
|
|
||||||
triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f});
|
|
||||||
|
|
||||||
// Forward+ test lights: a dim sun and a ring of colored point lights orbiting the origin
|
stars.reserve(kStarCount);
|
||||||
auto sunObj = scene.CreateGameObject("Sun");
|
for (int index = 0; index < kStarCount; ++index) {
|
||||||
auto* sun = sunObj->AddComponent<LightComponent>(LightType::Directional);
|
auto* starObject = scene.CreateGameObject(
|
||||||
sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f});
|
"Star " + std::to_string(index + 1));
|
||||||
sun->SetIntensity(1.0f);
|
auto* starRenderer = starObject->AddComponent<MeshRendererComponent>();
|
||||||
sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.0f});
|
starRenderer->SetMeshID(sphereMesh);
|
||||||
|
starRenderer->SetMaterialID(starMaterial);
|
||||||
constexpr int pointLightCount = 32;
|
starObject->GetTransform().SetLocalScale(glm::vec3{0.0045f});
|
||||||
constexpr float pointLightRingRadius = 6.0f;
|
stars.push_back({starObject, 0.0f});
|
||||||
for (int i = 0; i < pointLightCount; i++)
|
|
||||||
{
|
|
||||||
const float t = static_cast<float>(i) / static_cast<float>(pointLightCount);
|
|
||||||
const float angle = t * glm::two_pi<float>();
|
|
||||||
|
|
||||||
auto lightObj = scene.CreateGameObject("PointLight " + std::to_string(i));
|
|
||||||
lightObj->GetTransform().SetWorldPosition(glm::vec3{
|
|
||||||
std::cos(angle) * pointLightRingRadius,
|
|
||||||
-0.25f,
|
|
||||||
std::sin(angle) * pointLightRingRadius
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* light = lightObj->AddComponent<LightComponent>(LightType::Point);
|
|
||||||
light->SetColor(glm::vec3{
|
|
||||||
0.5f + 0.5f * std::cos(glm::two_pi<float>() * t),
|
|
||||||
0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.33f)),
|
|
||||||
0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.67f))
|
|
||||||
});
|
|
||||||
light->SetIntensity(1.0f);
|
|
||||||
light->SetRange(3.0f);
|
|
||||||
|
|
||||||
auto* orbit = lightObj->AddComponent<Rotator>(pointLightRingRadius, 0.5f);
|
|
||||||
orbit->SetAxis(glm::vec3{0.0f, 1.0f, 0.0f});
|
|
||||||
orbit->SetPivotPosition(glm::vec3{0.0f, -0.25f, 0.0f});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto playerObj = scene.CreateGameObject("Player");
|
resetGame();
|
||||||
playerObj->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, -5.0f));
|
}
|
||||||
auto* fps = playerObj->AddComponent<FirstPersonController>();
|
|
||||||
m_Player = playerObj;
|
|
||||||
m_FPSController = fps;
|
|
||||||
scene.GetPhysics().RegisterGameObject(*playerObj);
|
|
||||||
|
|
||||||
const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png");
|
void LightKeeper::respawnStar(FallingStar& star, float minimumHeight)
|
||||||
texID = resources.loadImageFromFile(gfxDevice, texPath);
|
{
|
||||||
|
star.speed = spawnSpeed(randomEngine) + static_cast<float>(score) * 0.08f;
|
||||||
|
star.object->SetActive(true);
|
||||||
|
star.object->GetTransform().SetLocalPosition({
|
||||||
|
spawnX(randomEngine),
|
||||||
|
minimumHeight + spawnHeight(randomEngine),
|
||||||
|
0.0f});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightKeeper::resetGame()
|
||||||
|
{
|
||||||
|
score = 0;
|
||||||
|
misses = 0;
|
||||||
|
gameOver = false;
|
||||||
|
|
||||||
|
if (player != nullptr) {
|
||||||
|
player->SetActive(true);
|
||||||
|
player->GetTransform().SetLocalPosition({0.0f, kPlayerY, 0.0f});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < stars.size(); ++index) {
|
||||||
|
respawnStar(stars[index], 6.5f + static_cast<float>(index) * 0.85f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightKeeper::customUpdate(float dt)
|
void LightKeeper::customUpdate(float dt)
|
||||||
{
|
{
|
||||||
// LightKeeper owns a camera that hides App::camera; update this instance
|
auto& input = InputManager::GetInstance();
|
||||||
// after SDL events have been consumed.
|
|
||||||
if (m_FPSController != nullptr && m_FPSController->IsMouseCaptured())
|
if (input.WasKeyPressed(SDL_SCANCODE_ESCAPE)) {
|
||||||
{
|
isRunning = false;
|
||||||
auto& playerTransform = m_Player->GetTransform();
|
return;
|
||||||
const glm::vec3 playerPos = playerTransform.GetWorldPosition();
|
|
||||||
camera.SetRotation(m_FPSController->GetYaw(), m_FPSController->GetPitch());
|
|
||||||
camera.m_position = playerPos + glm::vec3(0.0f, m_FPSController->GetEyeHeight(), 0.0f);
|
|
||||||
camera.CalculateViewMatrix();
|
|
||||||
camera.CalculateProjectionMatrix();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if (input.WasKeyPressed(SDL_SCANCODE_R)) {
|
||||||
camera.Update(dt);
|
resetGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gameOver) {
|
||||||
|
float moveDirection = 0.0f;
|
||||||
|
if (input.IsKeyDown(SDL_SCANCODE_A) || input.IsKeyDown(SDL_SCANCODE_LEFT)) {
|
||||||
|
moveDirection += 1.0f;
|
||||||
|
}
|
||||||
|
if (input.IsKeyDown(SDL_SCANCODE_D) || input.IsKeyDown(SDL_SCANCODE_RIGHT)) {
|
||||||
|
moveDirection -= 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 playerPosition = player->GetTransform().GetLocalPosition();
|
||||||
|
playerPosition.x = std::clamp(
|
||||||
|
playerPosition.x + moveDirection * kPlayerSpeed * dt,
|
||||||
|
-5.8f,
|
||||||
|
5.8f);
|
||||||
|
player->GetTransform().SetLocalPosition(playerPosition);
|
||||||
|
|
||||||
|
for (auto& star : stars) {
|
||||||
|
glm::vec3 starPosition = star.object->GetTransform().GetLocalPosition();
|
||||||
|
starPosition.y -= star.speed * dt;
|
||||||
|
star.object->GetTransform().SetLocalPosition(starPosition);
|
||||||
|
|
||||||
|
if (starPosition.y > kCatchY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool caught = std::abs(starPosition.x - playerPosition.x) <=
|
||||||
|
kPlayerHalfWidth;
|
||||||
|
if (caught) {
|
||||||
|
++score;
|
||||||
|
} else {
|
||||||
|
++misses;
|
||||||
|
if (misses >= kMaxMisses) {
|
||||||
|
gameOver = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameOver) {
|
||||||
|
respawnStar(star, 7.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SceneManager::GetInstance().Update(dt);
|
SceneManager::GetInstance().Update(dt);
|
||||||
SceneManager::GetInstance().LateUpdate(dt);
|
SceneManager::GetInstance().LateUpdate(dt);
|
||||||
|
drawHud();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_params.showDebugUi) {
|
void LightKeeper::drawHud()
|
||||||
drawDebugMenuBar();
|
{
|
||||||
}
|
constexpr ImGuiWindowFlags flags =
|
||||||
|
ImGuiWindowFlags_NoDecoration |
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize |
|
||||||
|
ImGuiWindowFlags_NoSavedSettings |
|
||||||
|
ImGuiWindowFlags_NoNav;
|
||||||
|
|
||||||
LineRenderingManager::GetInstance().SubmitLine({0, 0, 0}, {0, 10, 0}, {255, 0, 0, 255});
|
ImGui::SetNextWindowPos({24.0f, 24.0f}, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||||
|
ImGui::Begin("Star Catcher HUD", nullptr, flags);
|
||||||
|
ImGui::Text("STAR CATCHER");
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Score %03d", score);
|
||||||
|
ImGui::Text("Lives %d", kMaxMisses - misses);
|
||||||
|
ImGui::TextDisabled("A/D or arrow keys to move");
|
||||||
|
|
||||||
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1))
|
if (gameOver) {
|
||||||
{
|
ImGui::Separator();
|
||||||
renderer.setRenderWireframe(!renderer.getRenderWireframe());
|
ImGui::Text("GAME OVER");
|
||||||
}
|
ImGui::Text("Press R to play again");
|
||||||
|
|
||||||
if (m_params.showDebugUi) {
|
|
||||||
ImGui::Begin("Test");
|
|
||||||
if (ImGui::Button("SPawn ball"))
|
|
||||||
{
|
|
||||||
auto sphere = SceneManager::GetInstance().GetCurrentScene().CreateGameObject("Sphere");
|
|
||||||
sphere->AddComponent<SphereCollider>(1.5f);
|
|
||||||
auto rb = sphere->AddComponent<Rigidbody>();
|
|
||||||
rb->SetMass(1000);
|
|
||||||
|
|
||||||
auto meshRenderComp = sphere->AddComponent<MeshRendererComponent>();
|
|
||||||
meshRenderComp->SetMaterialID(sphereMaterial);
|
|
||||||
meshRenderComp->SetMeshID(sphereMesh);
|
|
||||||
|
|
||||||
sphere->GetTransform().SetWorldPosition((std::rand() % 2) - 0.5f, 100, (std::rand() % 2) - 0.5f);
|
|
||||||
sphere->GetTransform().SetWorldScale(glm::vec3{0.015f});
|
|
||||||
|
|
||||||
|
|
||||||
SceneManager::GetInstance().GetCurrentScene().GetPhysics().RefreshGameObject(*sphere);
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightKeeper::drawDebugMenuBar()
|
|
||||||
{
|
|
||||||
if (!ImGui::BeginMainMenuBar()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("File")) {
|
|
||||||
ImGui::TextDisabled("Scene");
|
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(320.0f);
|
|
||||||
ImGui::InputText("Scene path", scenePath, sizeof(scenePath));
|
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(320.0f);
|
|
||||||
ImGui::InputText("New scene name", newSceneName, sizeof(newSceneName));
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
if (ImGui::MenuItem("New Empty Scene")) {
|
|
||||||
createEmptyScene();
|
|
||||||
}
|
|
||||||
if (ImGui::MenuItem("Load")) {
|
|
||||||
loadScene();
|
|
||||||
}
|
|
||||||
if (ImGui::MenuItem("Save")) {
|
|
||||||
saveCurrentScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sceneStatus.empty()) {
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::TextWrapped("%s", sceneStatus.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Render")) {
|
|
||||||
ImGui::MenuItem("Box Colliders", nullptr, &renderBoxColliders);
|
|
||||||
ImGui::MenuItem("2D Quads", nullptr, &renderQuads);
|
|
||||||
ImGui::MenuItem("Debug Lines", nullptr, &renderDebugLines);
|
|
||||||
|
|
||||||
bool lightHeatmap = renderer.getRenderLightHeatmap();
|
|
||||||
if (ImGui::MenuItem("Forward+ Light Heatmap", nullptr, &lightHeatmap)) {
|
|
||||||
renderer.setRenderLightHeatmap(lightHeatmap);
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMainMenuBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightKeeper::saveCurrentScene()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const auto path = ResolveScenePath(scenePath, m_params.exeDir);
|
|
||||||
const auto parent = path.parent_path();
|
|
||||||
if (!parent.empty()) {
|
|
||||||
std::filesystem::create_directories(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SceneSerializer::Save(
|
|
||||||
SceneManager::GetInstance().GetCurrentScene(),
|
|
||||||
path)) {
|
|
||||||
sceneStatus = "Saved scene to " + path.string();
|
|
||||||
} else {
|
|
||||||
sceneStatus = "Failed to save scene to " + path.string();
|
|
||||||
}
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
sceneStatus = std::string{"Save failed: "} + exception.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightKeeper::loadScene()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const auto path = ResolveScenePath(scenePath, m_params.exeDir);
|
|
||||||
if (SceneSerializer::Load(
|
|
||||||
SceneManager::GetInstance().GetCurrentScene(),
|
|
||||||
path)) {
|
|
||||||
sceneStatus = "Loaded scene from " + path.string();
|
|
||||||
} else {
|
|
||||||
sceneStatus = "Failed to load scene from " + path.string();
|
|
||||||
}
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
sceneStatus = std::string{"Load failed: "} + exception.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightKeeper::createEmptyScene()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const std::string name = newSceneName[0] != '\0'
|
|
||||||
? newSceneName
|
|
||||||
: "EmptyScene";
|
|
||||||
auto& sceneManager = SceneManager::GetInstance();
|
|
||||||
const int newSceneIndex = sceneManager.GetSceneCount();
|
|
||||||
sceneManager.CreateScene(name);
|
|
||||||
sceneManager.SwitchScene(newSceneIndex);
|
|
||||||
sceneStatus = "Created and switched to scene '" + name + "'";
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
sceneStatus = std::string{"New scene failed: "} + exception.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightKeeper::submitBoxColliderDebugLines()
|
|
||||||
{
|
|
||||||
if (!renderBoxColliders) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& sceneManager = SceneManager::GetInstance();
|
|
||||||
if (sceneManager.GetSceneCount() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scene& scene = sceneManager.GetCurrentScene();
|
|
||||||
auto& lineManager = LineRenderingManager::GetInstance();
|
|
||||||
constexpr std::array<std::array<std::size_t, 2>, 12> edges{{
|
|
||||||
{{0, 1}}, {{1, 3}}, {{3, 2}}, {{2, 0}},
|
|
||||||
{{4, 5}}, {{5, 7}}, {{7, 6}}, {{6, 4}},
|
|
||||||
{{0, 4}}, {{1, 5}}, {{2, 6}}, {{3, 7}},
|
|
||||||
}};
|
|
||||||
|
|
||||||
for (const auto& objectPtr : scene.GetObjects()) {
|
|
||||||
if (!objectPtr || objectPtr->IsBeingDestroyed() ||
|
|
||||||
!objectPtr->IsActiveInHierarchy()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* boxCollider = objectPtr->GetComponent<BoxCollider>();
|
|
||||||
if (boxCollider == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collider dimensions are already in world units. Physics does not
|
|
||||||
// apply the GameObject scale when constructing the shape.
|
|
||||||
const glm::vec3 center = boxCollider->GetCenterOffset();
|
|
||||||
const glm::vec3 halfExtents = boxCollider->GetHalfExtents();
|
|
||||||
const std::array<glm::vec3, 8> localCorners{
|
|
||||||
center + glm::vec3{-halfExtents.x, -halfExtents.y, -halfExtents.z},
|
|
||||||
center + glm::vec3{ halfExtents.x, -halfExtents.y, -halfExtents.z},
|
|
||||||
center + glm::vec3{-halfExtents.x, halfExtents.y, -halfExtents.z},
|
|
||||||
center + glm::vec3{ halfExtents.x, halfExtents.y, -halfExtents.z},
|
|
||||||
center + glm::vec3{-halfExtents.x, -halfExtents.y, halfExtents.z},
|
|
||||||
center + glm::vec3{ halfExtents.x, -halfExtents.y, halfExtents.z},
|
|
||||||
center + glm::vec3{-halfExtents.x, halfExtents.y, halfExtents.z},
|
|
||||||
center + glm::vec3{ halfExtents.x, halfExtents.y, halfExtents.z},
|
|
||||||
};
|
|
||||||
|
|
||||||
Transform& transform = objectPtr->GetTransform();
|
|
||||||
const glm::vec3 worldPosition = transform.GetWorldPosition();
|
|
||||||
const glm::quat worldRotation = transform.GetWorldRotation();
|
|
||||||
std::array<glm::vec3, 8> worldCorners{};
|
|
||||||
for (std::size_t index = 0; index < localCorners.size(); ++index) {
|
|
||||||
worldCorners[index] = worldPosition + worldRotation * localCorners[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const glm::vec4 color = boxCollider->IsTrigger()
|
|
||||||
? glm::vec4{1.0f, 0.75f, 0.1f, 1.0f}
|
|
||||||
: glm::vec4{0.1f, 0.8f, 1.0f, 1.0f};
|
|
||||||
for (const auto& edge : edges) {
|
|
||||||
lineManager.SubmitLine(
|
|
||||||
worldCorners[edge[0]],
|
|
||||||
worldCorners[edge[1]],
|
|
||||||
color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightKeeper::customDraw()
|
void LightKeeper::customDraw()
|
||||||
@@ -727,59 +232,42 @@ void LightKeeper::customDraw()
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderer.beginDrawing(gfxDevice);
|
renderer.beginDrawing(gfxDevice);
|
||||||
submitBoxColliderDebugLines();
|
|
||||||
|
|
||||||
QuadRenderingManager::GetInstance().SubmitQuad(
|
const GameRenderer::SceneData sceneData{
|
||||||
glm::vec2{100.0f, 100.0f}, glm::vec2{200.0f, 200.0f}, 0.0f,
|
.camera = camera,
|
||||||
glm::vec4{1.0f}, texID);
|
.ambientColor = glm::vec3{0.1f},
|
||||||
|
.ambientIntensity = 0.5f,
|
||||||
const RenderContext ctx{
|
.fogColor = glm::vec3{0.02f, 0.03f, 0.08f},
|
||||||
|
.fogDensity = 0.01f};
|
||||||
|
const RenderContext context{
|
||||||
.renderer = renderer,
|
.renderer = renderer,
|
||||||
.camera = camera,
|
.camera = camera,
|
||||||
.sceneData = {
|
.sceneData = sceneData};
|
||||||
.camera = camera,
|
|
||||||
.ambientColor = glm::vec3(0.1f),
|
|
||||||
.ambientIntensity = 0.5f,
|
|
||||||
.fogColor = glm::vec3(0.5f),
|
|
||||||
.fogDensity = 0.01f
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SceneManager::GetInstance().Render(ctx);
|
SceneManager::GetInstance().Render(context);
|
||||||
renderer.endDrawing();
|
renderer.endDrawing();
|
||||||
|
|
||||||
const auto& drawImage = renderer.getDrawImage();
|
renderer.draw(cmd, gfxDevice, camera, sceneData);
|
||||||
|
|
||||||
renderer.draw(
|
|
||||||
cmd, gfxDevice, camera, GameRenderer::SceneData{
|
|
||||||
camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f
|
|
||||||
});
|
|
||||||
|
|
||||||
gfxDevice.endFrame(
|
gfxDevice.endFrame(
|
||||||
cmd, drawImage, {
|
cmd,
|
||||||
.clearColor = {{0.f, 0.f, 0.5f, 1.f}},
|
renderer.getDrawImage(),
|
||||||
|
{
|
||||||
|
.clearColor = {{0.005f, 0.008f, 0.03f, 1.0f}},
|
||||||
.drawImageBlitRect = glm::ivec4{},
|
.drawImageBlitRect = glm::ivec4{},
|
||||||
.imguiPass = &imguiPass,
|
.imguiPass = &imguiPass});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightKeeper::customCleanup()
|
void LightKeeper::customCleanup()
|
||||||
{
|
{
|
||||||
// auto device = gfxDevice.getDevice().device;
|
|
||||||
|
|
||||||
// vkDeviceWaitIdle(device);
|
|
||||||
|
|
||||||
gfxDevice.waitIdle();
|
gfxDevice.waitIdle();
|
||||||
|
|
||||||
SceneManager::GetInstance().Destroy();
|
stars.clear();
|
||||||
|
player = nullptr;
|
||||||
|
|
||||||
if (skyboxCubemap)
|
if (skyboxCubemap) {
|
||||||
{
|
|
||||||
skyboxCubemap->cleanup(gfxDevice);
|
skyboxCubemap->cleanup(gfxDevice);
|
||||||
skyboxCubemap.reset();
|
skyboxCubemap.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.cleanup(gfxDevice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightKeeper::customFixedUpdate(float dt)
|
void LightKeeper::customFixedUpdate(float dt)
|
||||||
@@ -789,7 +277,10 @@ void LightKeeper::customFixedUpdate(float dt)
|
|||||||
|
|
||||||
void LightKeeper::onWindowResize(int newWidth, int newHeight)
|
void LightKeeper::onWindowResize(int newWidth, int newHeight)
|
||||||
{
|
{
|
||||||
renderer.resize(gfxDevice, glm::ivec2{newWidth, newHeight});
|
if (newWidth <= 0 || newHeight <= 0) {
|
||||||
const float aspectRatio = static_cast<float>(newWidth) / static_cast<float>(newHeight);
|
return;
|
||||||
camera.setAspectRatio(aspectRatio);
|
}
|
||||||
|
|
||||||
|
renderer.resize(gfxDevice, {newWidth, newHeight});
|
||||||
|
camera.setAspectRatio(static_cast<float>(newWidth) / static_cast<float>(newHeight));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
#include <components/FirstPersonController.h>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
|
|
||||||
#include <destrum/Components/Physics/CapsuleCollider.h>
|
|
||||||
#include <destrum/Components/Physics/Rigidbody.h>
|
|
||||||
#include <destrum/Input/InputManager.h>
|
|
||||||
#include <destrum/ObjectModel/GameObject.h>
|
|
||||||
#include <destrum/Physics/PhysicsWorld.h>
|
|
||||||
#include <destrum/Scene/Scene.h>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
FirstPersonController::FirstPersonController(GameObject& parent)
|
|
||||||
: Component(parent, "FirstPersonController")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::SetMouseCaptured(bool captured)
|
|
||||||
{
|
|
||||||
InputManager::GetInstance().SetMouseCaptured(captured);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::Start()
|
|
||||||
{
|
|
||||||
auto* obj = GetGameObject();
|
|
||||||
m_Capsule = obj->AddComponent<CapsuleCollider>(0.5f, 2.0f);
|
|
||||||
m_Rigidbody = obj->AddComponent<Rigidbody>();
|
|
||||||
m_Rigidbody->SetMass(80.0f);
|
|
||||||
m_Rigidbody->SetAllowSleep(false);
|
|
||||||
m_Rigidbody->SetUseGravity(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::Update(float)
|
|
||||||
{
|
|
||||||
const auto& input = InputManager::GetInstance();
|
|
||||||
|
|
||||||
if (input.WasKeyPressed(SDL_SCANCODE_F5))
|
|
||||||
{
|
|
||||||
SetMouseCaptured(!IsMouseCaptured());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsMouseCaptured())
|
|
||||||
{
|
|
||||||
if (input.WasMousePressed(SDL_BUTTON_LEFT) && !ImGui::GetIO().WantCaptureMouse)
|
|
||||||
{
|
|
||||||
SetMouseCaptured(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.WasKeyPressed(SDL_SCANCODE_ESCAPE))
|
|
||||||
{
|
|
||||||
SetMouseCaptured(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.WasKeyPressed(SDL_SCANCODE_SPACE) && m_Rigidbody->HasPhysicsBody())
|
|
||||||
{
|
|
||||||
m_Rigidbody->AddImpulse(glm::vec3(0.0f, 400.0f, 0.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Yaw += static_cast<float>(input.MouseDeltaX()) * m_MouseSensitivity;
|
|
||||||
m_Pitch -= static_cast<float>(input.MouseDeltaY()) * m_MouseSensitivity;
|
|
||||||
m_Pitch = glm::clamp(m_Pitch, glm::radians(-89.0f), glm::radians(89.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::FixedUpdate(float)
|
|
||||||
{
|
|
||||||
if (!IsMouseCaptured() || m_Rigidbody == nullptr || !m_Rigidbody->HasPhysicsBody())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto& input = InputManager::GetInstance();
|
|
||||||
|
|
||||||
const float cosY = glm::cos(m_Yaw);
|
|
||||||
const float sinY = glm::sin(m_Yaw);
|
|
||||||
const glm::vec3 forward{cosY, 0.0f, sinY};
|
|
||||||
const glm::vec3 right{-sinY, 0.0f, cosY};
|
|
||||||
|
|
||||||
glm::vec3 moveDir(0.0f);
|
|
||||||
if (input.IsKeyDown(SDL_SCANCODE_W)) moveDir += forward;
|
|
||||||
if (input.IsKeyDown(SDL_SCANCODE_S)) moveDir -= forward;
|
|
||||||
if (input.IsKeyDown(SDL_SCANCODE_D)) moveDir += right;
|
|
||||||
if (input.IsKeyDown(SDL_SCANCODE_A)) moveDir -= right;
|
|
||||||
|
|
||||||
glm::vec3 velocity(0.0f);
|
|
||||||
if (glm::length2(moveDir) > 0.0f)
|
|
||||||
{
|
|
||||||
moveDir = glm::normalize(moveDir);
|
|
||||||
velocity = moveDir * m_MovementSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
velocity.y = m_Rigidbody->GetLinearVelocity().y;
|
|
||||||
m_Rigidbody->SetLinearVelocity(velocity);
|
|
||||||
|
|
||||||
auto* world = m_Rigidbody->GetPhysicsWorld();
|
|
||||||
if (world != nullptr)
|
|
||||||
{
|
|
||||||
PhysicsTransform xform = world->GetBodyTransform(m_Rigidbody->GetBody());
|
|
||||||
xform.rotation = glm::quat(glm::vec3(0.0f, m_Yaw, 0.0f));
|
|
||||||
world->SetBodyTransform(m_Rigidbody->GetBody(), xform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::ImGuiInspector()
|
|
||||||
{
|
|
||||||
Component::ImGuiInspector();
|
|
||||||
ImGui::DragFloat("Movement Speed", &m_MovementSpeed, 0.1f, 0.0f, 100.0f);
|
|
||||||
ImGui::DragFloat("Mouse Sensitivity", &m_MouseSensitivity, 0.0001f, 0.0f, 0.1f);
|
|
||||||
ImGui::DragFloat("Eye Height", &m_EyeHeight, 0.01f, 0.0f, 5.0f);
|
|
||||||
ImGui::Text("Yaw: %.1f deg", glm::degrees(m_Yaw));
|
|
||||||
ImGui::Text("Pitch: %.1f deg", glm::degrees(m_Pitch));
|
|
||||||
bool captured = IsMouseCaptured();
|
|
||||||
ImGui::Checkbox("Mouse Captured", &captured);
|
|
||||||
if (captured != IsMouseCaptured()) {
|
|
||||||
SetMouseCaptured(captured);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json FirstPersonController::Serialize() const
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
{"movementSpeed", m_MovementSpeed},
|
|
||||||
{"mouseSensitivity", m_MouseSensitivity},
|
|
||||||
{"eyeHeight", m_EyeHeight},
|
|
||||||
{"yaw", m_Yaw},
|
|
||||||
{"pitch", m_Pitch},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void FirstPersonController::Deserialize(const nlohmann::json& data)
|
|
||||||
{
|
|
||||||
if (data.contains("movementSpeed")) m_MovementSpeed = data.at("movementSpeed").get<float>();
|
|
||||||
if (data.contains("mouseSensitivity")) m_MouseSensitivity = data.at("mouseSensitivity").get<float>();
|
|
||||||
if (data.contains("eyeHeight")) m_EyeHeight = data.at("eyeHeight").get<float>();
|
|
||||||
if (data.contains("yaw")) m_Yaw = data.at("yaw").get<float>();
|
|
||||||
if (data.contains("pitch")) m_Pitch = data.at("pitch").get<float>();
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#include <components/GameComponentRegistry.h>
|
|
||||||
|
|
||||||
#include <destrum/Serialization/ComponentFactory.h>
|
|
||||||
|
|
||||||
#include <components/GameComponentList.h>
|
|
||||||
|
|
||||||
void RegisterGameComponents()
|
|
||||||
{
|
|
||||||
static bool registered = false;
|
|
||||||
if (registered) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
registered = true;
|
|
||||||
|
|
||||||
#define LIGHTKEEPER_REGISTER_COMPONENT(ComponentType) \
|
|
||||||
ComponentFactory::Register<ComponentType>(#ComponentType);
|
|
||||||
LIGHTKEEPER_GAME_COMPONENTS(LIGHTKEEPER_REGISTER_COMPONENT)
|
|
||||||
#undef LIGHTKEEPER_REGISTER_COMPONENT
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include <components/PrintComponent.h>
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
PrintComponent::PrintComponent(GameObject& parent) : Component(parent, "PrintComponent")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintComponent::Start()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintComponent::Update(float dt)
|
|
||||||
{
|
|
||||||
spdlog::debug(m_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintComponent::ImGuiInspector()
|
|
||||||
{
|
|
||||||
Component::ImGuiInspector();
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json PrintComponent::Serialize() const
|
|
||||||
{
|
|
||||||
return {"message", m_message};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintComponent::Deserialize(const nlohmann::json& basic_jsons)
|
|
||||||
{
|
|
||||||
if (basic_jsons.contains("message"))
|
|
||||||
{
|
|
||||||
m_message = basic_jsons.at("message").get<std::string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#include <components/TriggerLoggerComponent.h>
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include <destrum/ObjectModel/GameObject.h>
|
|
||||||
|
|
||||||
TriggerLoggerComponent::TriggerLoggerComponent(GameObject& parent)
|
|
||||||
: Component(parent, "TriggerLoggerComponent") {}
|
|
||||||
|
|
||||||
void TriggerLoggerComponent::OnTriggerEnter(GameObject* other) {
|
|
||||||
++triggerEnterCount;
|
|
||||||
lastOtherName = other ? other->GetName() : "null";
|
|
||||||
spdlog::info(
|
|
||||||
"TriggerLogger: ENTER {} -> {} (enters: {}, exits: {})",
|
|
||||||
GetGameObject()->GetName(),
|
|
||||||
lastOtherName,
|
|
||||||
triggerEnterCount,
|
|
||||||
triggerExitCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TriggerLoggerComponent::OnTriggerExit(GameObject* other) {
|
|
||||||
++triggerExitCount;
|
|
||||||
lastOtherName = other ? other->GetName() : "null";
|
|
||||||
spdlog::info(
|
|
||||||
"TriggerLogger: EXIT {} -> {} (enters: {}, exits: {})",
|
|
||||||
GetGameObject()->GetName(),
|
|
||||||
lastOtherName,
|
|
||||||
triggerEnterCount,
|
|
||||||
triggerExitCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json TriggerLoggerComponent::Serialize() const {
|
|
||||||
return {
|
|
||||||
{"triggerEnterCount", triggerEnterCount},
|
|
||||||
{"triggerExitCount", triggerExitCount}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TriggerLoggerComponent::Deserialize(const nlohmann::json& data) {
|
|
||||||
if (data.contains("triggerEnterCount")) {
|
|
||||||
triggerEnterCount = data.at("triggerEnterCount").get<int>();
|
|
||||||
}
|
|
||||||
if (data.contains("triggerExitCount")) {
|
|
||||||
triggerExitCount = data.at("triggerExitCount").get<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,9 +25,10 @@ int main(int argc, char* argv[]) {
|
|||||||
app.init({
|
app.init({
|
||||||
.windowSize = {1200, 800},
|
.windowSize = {1200, 800},
|
||||||
.renderSize = {1200, 800},
|
.renderSize = {1200, 800},
|
||||||
.appName = "Destrum Engine",
|
.appName = "Destrum Star Catcher",
|
||||||
.windowTitle = "Lightkeeper",
|
.windowTitle = "Star Catcher",
|
||||||
.exeDir = exeDir,
|
.exeDir = exeDir,
|
||||||
|
.showDebugUi = false,
|
||||||
});
|
});
|
||||||
app.run();
|
app.run();
|
||||||
app.cleanup();
|
app.cleanup();
|
||||||
|
|||||||
@@ -20,12 +20,9 @@
|
|||||||
#include <destrum/Physics/JoltPhysicsWorld.h>
|
#include <destrum/Physics/JoltPhysicsWorld.h>
|
||||||
#include <destrum/Components/Physics/SphereCollider.h>
|
#include <destrum/Components/Physics/SphereCollider.h>
|
||||||
#include <destrum/Components/Physics/BoxCollider.h>
|
#include <destrum/Components/Physics/BoxCollider.h>
|
||||||
#include <destrum/Components/Physics/Collider.h>
|
|
||||||
#include <destrum/Components/Animator.h>
|
#include <destrum/Components/Animator.h>
|
||||||
#include <destrum/Components/LightComponent.h>
|
|
||||||
#include <destrum/Components/Physics/Rigidbody.h>
|
#include <destrum/Components/Physics/Rigidbody.h>
|
||||||
#include <destrum/Graphics/Material.h>
|
#include <destrum/Graphics/Material.h>
|
||||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class TestComponent final : public Component {
|
class TestComponent final : public Component {
|
||||||
@@ -172,7 +169,6 @@ namespace {
|
|||||||
|
|
||||||
const char* canonicalNames[] = {
|
const char* canonicalNames[] = {
|
||||||
"MeshRendererComponent",
|
"MeshRendererComponent",
|
||||||
"LightComponent",
|
|
||||||
"Rotator",
|
"Rotator",
|
||||||
"Spinner",
|
"Spinner",
|
||||||
"OrbitAndSpin",
|
"OrbitAndSpin",
|
||||||
@@ -193,29 +189,6 @@ namespace {
|
|||||||
"RigidBody legacy alias must not be registered");
|
"RigidBody legacy alias must not be registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLineRenderingManager()
|
|
||||||
{
|
|
||||||
auto& lineManager = LineRenderingManager::GetInstance();
|
|
||||||
lineManager.ClearLines();
|
|
||||||
|
|
||||||
lineManager.SubmitLine(
|
|
||||||
glm::vec3{0.0f},
|
|
||||||
glm::vec3{1.0f, 0.0f, 0.0f},
|
|
||||||
glm::vec4{1.0f, 0.0f, 0.0f, 1.0f});
|
|
||||||
lineManager.SubmitLine(Line{
|
|
||||||
.start = glm::vec3{1.0f},
|
|
||||||
.end = glm::vec3{2.0f},
|
|
||||||
.color = glm::vec4{0.0f, 1.0f, 0.0f, 1.0f},
|
|
||||||
});
|
|
||||||
|
|
||||||
check(lineManager.GetLines().size() == 2,
|
|
||||||
"line rendering manager must retain submitted lines");
|
|
||||||
|
|
||||||
lineManager.ClearLines();
|
|
||||||
check(lineManager.GetLines().empty(),
|
|
||||||
"line rendering manager must clear transient lines");
|
|
||||||
}
|
|
||||||
|
|
||||||
void testAssetPathValidation()
|
void testAssetPathValidation()
|
||||||
{
|
{
|
||||||
const auto root = std::filesystem::temp_directory_path() / "destrum_asset_test";
|
const auto root = std::filesystem::temp_directory_path() / "destrum_asset_test";
|
||||||
@@ -449,39 +422,6 @@ namespace {
|
|||||||
"Animator scene data must not embed animation keyframes");
|
"Animator scene data must not embed animation keyframes");
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLightComponentRoundTrip()
|
|
||||||
{
|
|
||||||
GameObject object{"light"};
|
|
||||||
auto* light = object.AddComponent<LightComponent>(LightType::Spot);
|
|
||||||
light->SetColor(glm::vec3{1.0f, 0.5f, 0.25f});
|
|
||||||
light->SetIntensity(4.0f);
|
|
||||||
light->SetRange(7.5f);
|
|
||||||
light->SetConeAngles(0.2f, 0.4f);
|
|
||||||
|
|
||||||
const auto data = light->Serialize();
|
|
||||||
|
|
||||||
GameObject loadedObject{"loaded light"};
|
|
||||||
auto* loaded = loadedObject.AddComponent<LightComponent>();
|
|
||||||
loaded->Deserialize(data);
|
|
||||||
|
|
||||||
check(loaded->GetType() == LightType::Spot,
|
|
||||||
"LightComponent must round-trip its type");
|
|
||||||
check(loaded->GetColor() == glm::vec3{1.0f, 0.5f, 0.25f},
|
|
||||||
"LightComponent must round-trip its color");
|
|
||||||
check(loaded->GetIntensity() == 4.0f && loaded->GetRange() == 7.5f,
|
|
||||||
"LightComponent must round-trip intensity and range");
|
|
||||||
check(loaded->GetInnerConeAngle() == 0.2f && loaded->GetOuterConeAngle() == 0.4f,
|
|
||||||
"LightComponent must round-trip its cone angles");
|
|
||||||
|
|
||||||
loaded->SetConeAngles(0.5f, 0.1f);
|
|
||||||
check(loaded->GetOuterConeAngle() >= loaded->GetInnerConeAngle(),
|
|
||||||
"LightComponent outer cone must never be smaller than the inner cone");
|
|
||||||
|
|
||||||
loaded->SetRange(0.0f);
|
|
||||||
check(loaded->GetRange() > 0.0f,
|
|
||||||
"LightComponent range must stay positive");
|
|
||||||
}
|
|
||||||
|
|
||||||
void testSimpleColorMaterial()
|
void testSimpleColorMaterial()
|
||||||
{
|
{
|
||||||
const auto material = Material::SimpleColor(
|
const auto material = Material::SimpleColor(
|
||||||
@@ -519,65 +459,6 @@ namespace {
|
|||||||
check(!AssetReference::fromCacheKey("CharacterMedium.fbx").has_value(),
|
check(!AssetReference::fromCacheKey("CharacterMedium.fbx").has_value(),
|
||||||
"plain cache names must not be treated as file-backed assets");
|
"plain cache names must not be treated as file-backed assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
class TriggerTracker final : public Component {
|
|
||||||
public:
|
|
||||||
explicit TriggerTracker(GameObject& owner)
|
|
||||||
: Component(owner, "TriggerTracker") {}
|
|
||||||
|
|
||||||
void Update(float) override {}
|
|
||||||
std::string GetTypeName() const override { return "TriggerTracker"; }
|
|
||||||
|
|
||||||
void OnTriggerEnter(GameObject* other) override {
|
|
||||||
++enterCount;
|
|
||||||
lastOther = other;
|
|
||||||
enterObjectName = other ? other->GetName() : "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTriggerExit(GameObject* other) override {
|
|
||||||
++exitCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int enterCount{0};
|
|
||||||
int exitCount{0};
|
|
||||||
GameObject* lastOther{nullptr};
|
|
||||||
std::string enterObjectName;
|
|
||||||
};
|
|
||||||
|
|
||||||
void testTriggerZone()
|
|
||||||
{
|
|
||||||
Scene& scene = SceneManager::GetInstance().CreateScene("trigger-test");
|
|
||||||
|
|
||||||
// Trigger box at origin (Static rigidbody + BoxCollider as trigger).
|
|
||||||
GameObject* triggerObj = scene.CreateGameObject("TriggerZone");
|
|
||||||
triggerObj->AddComponent<BoxCollider>(glm::vec3{3.0f, 3.0f, 3.0f});
|
|
||||||
triggerObj->GetComponent<BoxCollider>()->SetTrigger(true);
|
|
||||||
auto* triggerRb = triggerObj->AddComponent<Rigidbody>();
|
|
||||||
triggerRb->SetType(RigidbodyType::Static);
|
|
||||||
auto* tracker = triggerObj->AddComponent<TriggerTracker>();
|
|
||||||
|
|
||||||
// Falling sphere above the trigger.
|
|
||||||
GameObject* sphere = scene.CreateGameObject("FallingSphere");
|
|
||||||
sphere->AddComponent<SphereCollider>(0.5f);
|
|
||||||
sphere->AddComponent<Rigidbody>();
|
|
||||||
sphere->GetTransform().SetWorldPosition({0.0f, 8.0f, 0.0f});
|
|
||||||
|
|
||||||
scene.CommitPendingAdditions();
|
|
||||||
|
|
||||||
// Run physics for enough steps that the sphere falls into the trigger.
|
|
||||||
for (int step = 0; step < 120; ++step) {
|
|
||||||
scene.FixedUpdate(1.0f / 60.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
check(tracker->enterCount >= 1,
|
|
||||||
"falling sphere must trigger OnTriggerEnter at least once");
|
|
||||||
check(tracker->exitCount >= 1,
|
|
||||||
"falling sphere passing through trigger must exit");
|
|
||||||
check(tracker->enterObjectName == "FallingSphere",
|
|
||||||
"OnTriggerEnter must report the correct entering object");
|
|
||||||
|
|
||||||
SceneManager::GetInstance().Destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -588,7 +469,6 @@ int main()
|
|||||||
testEventRemoval();
|
testEventRemoval();
|
||||||
testComponentRemoval();
|
testComponentRemoval();
|
||||||
testEngineComponentRegistration();
|
testEngineComponentRegistration();
|
||||||
testLineRenderingManager();
|
|
||||||
testAssetPathValidation();
|
testAssetPathValidation();
|
||||||
testLegacySceneLoad();
|
testLegacySceneLoad();
|
||||||
testSceneLoadRollback();
|
testSceneLoadRollback();
|
||||||
@@ -596,10 +476,8 @@ int main()
|
|||||||
testJoltSphereCollisions();
|
testJoltSphereCollisions();
|
||||||
testSceneJoltSphereCollisions();
|
testSceneJoltSphereCollisions();
|
||||||
testAnimatorAssetReferences();
|
testAnimatorAssetReferences();
|
||||||
testLightComponentRoundTrip();
|
|
||||||
testSimpleColorMaterial();
|
testSimpleColorMaterial();
|
||||||
testAssetReferenceCacheKeys();
|
testAssetReferenceCacheKeys();
|
||||||
testTriggerZone();
|
|
||||||
std::cout << "destrum tests passed\n";
|
std::cout << "destrum tests passed\n";
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user