feat: add camera culling masks
This commit is contained in:
@@ -46,6 +46,9 @@ public:
|
||||
CameraClearMode GetClearMode() const { return m_clearMode; }
|
||||
void SetClearMode(CameraClearMode value) { m_clearMode = value; }
|
||||
|
||||
uint32_t GetCullingMask() const { return m_cullingMask; }
|
||||
void SetCullingMask(uint32_t value) { m_cullingMask = value; }
|
||||
|
||||
const Math::Color& GetClearColor() const { return m_clearColor; }
|
||||
void SetClearColor(const Math::Color& value) { m_clearColor = value; }
|
||||
|
||||
@@ -61,6 +64,7 @@ private:
|
||||
float m_depth = 0.0f;
|
||||
bool m_primary = true;
|
||||
CameraClearMode m_clearMode = CameraClearMode::Auto;
|
||||
uint32_t m_cullingMask = 0xFFFFFFFFu;
|
||||
Math::Color m_clearColor = Math::Color(0.192f, 0.302f, 0.475f, 1.0f);
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ public:
|
||||
uint64_t GetUUID() const { return m_uuid; }
|
||||
const std::string& GetName() const { return m_name; }
|
||||
void SetName(const std::string& name) { m_name = name; }
|
||||
uint8_t GetLayer() const { return m_layer; }
|
||||
void SetLayer(uint8_t layer) { m_layer = std::min<uint8_t>(layer, 31u); }
|
||||
|
||||
void DetachFromParent();
|
||||
|
||||
@@ -217,6 +219,7 @@ private:
|
||||
std::string m_name;
|
||||
bool m_activeSelf = true;
|
||||
bool m_started = false;
|
||||
uint8_t m_layer = 0;
|
||||
|
||||
GameObject* m_parent = nullptr;
|
||||
std::vector<GameObject*> m_children;
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
void ExtractVisibleItems(
|
||||
Components::GameObject* gameObject,
|
||||
const Math::Vector3& cameraPosition,
|
||||
uint32_t cullingMask,
|
||||
std::vector<VisibleRenderItem>& visibleItems) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ void CameraComponent::Serialize(std::ostream& os) const {
|
||||
os << "depth=" << m_depth << ";";
|
||||
os << "primary=" << (m_primary ? 1 : 0) << ";";
|
||||
os << "clearMode=" << static_cast<int>(m_clearMode) << ";";
|
||||
os << "cullingMask=" << m_cullingMask << ";";
|
||||
os << "clearColor=" << m_clearColor.r << "," << m_clearColor.g << "," << m_clearColor.b << "," << m_clearColor.a << ";";
|
||||
}
|
||||
|
||||
@@ -68,6 +69,8 @@ void CameraComponent::Deserialize(std::istream& is) {
|
||||
m_primary = (std::stoi(value) != 0);
|
||||
} else if (key == "clearMode") {
|
||||
m_clearMode = static_cast<CameraClearMode>(std::stoi(value));
|
||||
} else if (key == "cullingMask") {
|
||||
m_cullingMask = static_cast<uint32_t>(std::stoul(value));
|
||||
} else if (key == "clearColor") {
|
||||
std::replace(value.begin(), value.end(), ',', ' ');
|
||||
std::istringstream ss(value);
|
||||
|
||||
@@ -317,6 +317,7 @@ void GameObject::Destroy() {
|
||||
void GameObject::Serialize(std::ostream& os) const {
|
||||
os << "name=" << m_name << ";";
|
||||
os << "active=" << (m_activeSelf ? "1" : "0") << ";";
|
||||
os << "layer=" << static_cast<uint32_t>(m_layer) << ";";
|
||||
os << "id=" << m_id << ";";
|
||||
os << "uuid=" << m_uuid << ";";
|
||||
os << "transform=";
|
||||
@@ -346,6 +347,11 @@ void GameObject::Deserialize(std::istream& is) {
|
||||
is.get(val);
|
||||
m_activeSelf = (val == '1');
|
||||
if (is.peek() == ';') is.get();
|
||||
} else if (strcmp(key, "layer") == 0) {
|
||||
uint32_t layer = 0;
|
||||
is >> layer;
|
||||
SetLayer(static_cast<uint8_t>(layer));
|
||||
if (is.peek() == ';') is.get();
|
||||
} else if (strcmp(key, "id") == 0) {
|
||||
is >> m_id;
|
||||
if (is.peek() == ';') is.get();
|
||||
|
||||
@@ -64,10 +64,11 @@ RenderSceneData RenderSceneExtractor::Extract(
|
||||
|
||||
sceneData.cameraData = BuildCameraData(*sceneData.camera, viewportWidth, viewportHeight);
|
||||
const Math::Vector3 cameraPosition = sceneData.cameraData.worldPosition;
|
||||
const uint32_t cullingMask = sceneData.camera->GetCullingMask();
|
||||
|
||||
const std::vector<Components::GameObject*> rootGameObjects = scene.GetRootGameObjects();
|
||||
for (Components::GameObject* rootGameObject : rootGameObjects) {
|
||||
ExtractVisibleItems(rootGameObject, cameraPosition, sceneData.visibleItems);
|
||||
ExtractVisibleItems(rootGameObject, cameraPosition, cullingMask, sceneData.visibleItems);
|
||||
}
|
||||
|
||||
std::stable_sort(
|
||||
@@ -92,10 +93,11 @@ RenderSceneData RenderSceneExtractor::ExtractForCamera(
|
||||
sceneData.camera = &camera;
|
||||
sceneData.cameraData = BuildCameraData(camera, viewportWidth, viewportHeight);
|
||||
const Math::Vector3 cameraPosition = sceneData.cameraData.worldPosition;
|
||||
const uint32_t cullingMask = camera.GetCullingMask();
|
||||
|
||||
const std::vector<Components::GameObject*> rootGameObjects = scene.GetRootGameObjects();
|
||||
for (Components::GameObject* rootGameObject : rootGameObjects) {
|
||||
ExtractVisibleItems(rootGameObject, cameraPosition, sceneData.visibleItems);
|
||||
ExtractVisibleItems(rootGameObject, cameraPosition, cullingMask, sceneData.visibleItems);
|
||||
}
|
||||
|
||||
std::stable_sort(
|
||||
@@ -224,15 +226,20 @@ void RenderSceneExtractor::ExtractLighting(
|
||||
void RenderSceneExtractor::ExtractVisibleItems(
|
||||
Components::GameObject* gameObject,
|
||||
const Math::Vector3& cameraPosition,
|
||||
uint32_t cullingMask,
|
||||
std::vector<VisibleRenderItem>& visibleItems) const {
|
||||
if (gameObject == nullptr || !gameObject->IsActiveInHierarchy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t gameObjectLayerMask = 1u << gameObject->GetLayer();
|
||||
const bool isVisibleInCameraMask = (cullingMask & gameObjectLayerMask) != 0;
|
||||
|
||||
auto* meshFilter = gameObject->GetComponent<Components::MeshFilterComponent>();
|
||||
auto* meshRenderer = gameObject->GetComponent<Components::MeshRendererComponent>();
|
||||
if (meshFilter != nullptr &&
|
||||
meshRenderer != nullptr &&
|
||||
isVisibleInCameraMask &&
|
||||
meshFilter->IsEnabled() &&
|
||||
meshRenderer->IsEnabled()) {
|
||||
Resources::Mesh* mesh = meshFilter->GetMesh();
|
||||
@@ -279,7 +286,7 @@ void RenderSceneExtractor::ExtractVisibleItems(
|
||||
}
|
||||
|
||||
for (Components::GameObject* child : gameObject->GetChildren()) {
|
||||
ExtractVisibleItems(child, cameraPosition, visibleItems);
|
||||
ExtractVisibleItems(child, cameraPosition, cullingMask, visibleItems);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ TEST(CameraComponent_Test, DefaultValues) {
|
||||
EXPECT_FLOAT_EQ(camera.GetFarClipPlane(), 1000.0f);
|
||||
EXPECT_TRUE(camera.IsPrimary());
|
||||
EXPECT_EQ(camera.GetClearMode(), CameraClearMode::Auto);
|
||||
EXPECT_EQ(camera.GetCullingMask(), 0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
TEST(CameraComponent_Test, SetterClamping) {
|
||||
@@ -38,6 +39,7 @@ TEST(CameraComponent_Test, SetterClamping) {
|
||||
TEST(CameraComponent_Test, SerializeRoundTripPreservesClearMode) {
|
||||
CameraComponent source;
|
||||
source.SetClearMode(CameraClearMode::DepthOnly);
|
||||
source.SetCullingMask(0x0000000Fu);
|
||||
|
||||
std::stringstream stream;
|
||||
source.Serialize(stream);
|
||||
@@ -46,6 +48,7 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesClearMode) {
|
||||
target.Deserialize(stream);
|
||||
|
||||
EXPECT_EQ(target.GetClearMode(), CameraClearMode::DepthOnly);
|
||||
EXPECT_EQ(target.GetCullingMask(), 0x0000000Fu);
|
||||
}
|
||||
|
||||
TEST(LightComponent_Test, DefaultValues) {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace XCEngine::Components;
|
||||
using namespace XCEngine::Math;
|
||||
|
||||
@@ -64,6 +66,20 @@ TEST(GameObject_Test, NamedConstructor) {
|
||||
EXPECT_EQ(go.GetName(), "TestObject");
|
||||
}
|
||||
|
||||
TEST(GameObject_Test, Layer_GetSetAndSerializeRoundTrip) {
|
||||
GameObject source("LayeredObject");
|
||||
source.SetLayer(7);
|
||||
|
||||
std::stringstream stream;
|
||||
source.Serialize(stream);
|
||||
|
||||
GameObject target;
|
||||
target.Deserialize(stream);
|
||||
|
||||
EXPECT_EQ(source.GetLayer(), 7u);
|
||||
EXPECT_EQ(target.GetLayer(), 7u);
|
||||
}
|
||||
|
||||
TEST(GameObject_Test, AddComponent_Single) {
|
||||
GameObject go;
|
||||
|
||||
|
||||
@@ -180,6 +180,41 @@ TEST(RenderSceneExtractor_Test, ExtractsBrightestDirectionalLightAsMainLight) {
|
||||
mainLightObject->GetTransform()->GetForward().Normalized() * -1.0f);
|
||||
}
|
||||
|
||||
TEST(RenderSceneExtractor_Test, FiltersVisibleItemsByCameraCullingMask) {
|
||||
Scene scene("CullingMaskScene");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetCullingMask(1u << 5);
|
||||
|
||||
GameObject* visibleObject = scene.CreateGameObject("VisibleLayer5");
|
||||
visibleObject->SetLayer(5);
|
||||
auto* visibleMeshFilter = visibleObject->AddComponent<MeshFilterComponent>();
|
||||
visibleObject->AddComponent<MeshRendererComponent>();
|
||||
Mesh* visibleMesh = CreateTestMesh("Meshes/layer5.mesh");
|
||||
visibleMeshFilter->SetMesh(visibleMesh);
|
||||
|
||||
GameObject* hiddenObject = scene.CreateGameObject("HiddenLayer0");
|
||||
hiddenObject->SetLayer(0);
|
||||
auto* hiddenMeshFilter = hiddenObject->AddComponent<MeshFilterComponent>();
|
||||
hiddenObject->AddComponent<MeshRendererComponent>();
|
||||
Mesh* hiddenMesh = CreateTestMesh("Meshes/layer0.mesh");
|
||||
hiddenMeshFilter->SetMesh(hiddenMesh);
|
||||
|
||||
RenderSceneExtractor extractor;
|
||||
const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600);
|
||||
|
||||
ASSERT_EQ(sceneData.visibleItems.size(), 1u);
|
||||
EXPECT_EQ(sceneData.visibleItems[0].gameObject, visibleObject);
|
||||
EXPECT_EQ(sceneData.visibleItems[0].mesh, visibleMesh);
|
||||
|
||||
visibleMeshFilter->ClearMesh();
|
||||
hiddenMeshFilter->ClearMesh();
|
||||
delete visibleMesh;
|
||||
delete hiddenMesh;
|
||||
}
|
||||
|
||||
TEST(RenderSceneExtractor_Test, ExtractsSectionLevelVisibleItemsAndSortsByRenderQueue) {
|
||||
Scene scene("SectionScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user