1#include "InstancedMeshRenderSystem.h"
5#include "Components/Core/TransformComponent.h"
6#include "Components/Core/MeshComponent.h"
7#include "Components/Core/NearLimitComponent.h"
8#include "Components/Core/SceneComponent.h"
9#include "Components/Core/ClipShapeComponent.h"
11#include "Scene/PickingFlags.h"
12#include "Scene/RayPick.h"
13#include "Systems/Core/RenderSystem.h"
14#include "Systems/Core/TransformSystem.h"
15#include "Systems/Core/CameraSystem.h"
16#include "Systems/Core/ClipShapeSystem.h"
18#include "Resources/Mesh.h"
19#include "Resources/MeshManager.h"
20#include "Resources/Buffer.h"
22#include "Services/Variables.h"
23#include "Services/TaskManager.h"
25#include "Utilities/Parallel.h"
26#include "Math/RayIntersection.h"
28#include "Foundation/Geometry/Glm.hpp"
29#include "Foundation/Logging/Logger.h"
38 Cogs::Geometry::BoundingBox getTransformedBounds(
const Cogs::Geometry::BoundingBox& bbox,
const glm::mat4 & m)
40 const glm::vec3 c000 = glm::vec3(m * glm::vec4(bbox.min.x, bbox.min.y, bbox.min.z, 1.f));
41 const glm::vec3 c001 = glm::vec3(m * glm::vec4(bbox.min.x, bbox.min.y, bbox.max.z, 1.f));
42 const glm::vec3 c010 = glm::vec3(m * glm::vec4(bbox.min.x, bbox.max.y, bbox.min.z, 1.f));
43 const glm::vec3 c011 = glm::vec3(m * glm::vec4(bbox.min.x, bbox.max.y, bbox.max.z, 1.f));
44 const glm::vec3 c100 = glm::vec3(m * glm::vec4(bbox.max.x, bbox.min.y, bbox.min.z, 1.f));
45 const glm::vec3 c101 = glm::vec3(m * glm::vec4(bbox.max.x, bbox.min.y, bbox.max.z, 1.f));
46 const glm::vec3 c110 = glm::vec3(m * glm::vec4(bbox.max.x, bbox.max.y, bbox.min.z, 1.f));
47 const glm::vec3 c111 = glm::vec3(m * glm::vec4(bbox.max.x, bbox.max.y, bbox.max.z, 1.f));
48 return Cogs::Geometry::BoundingBox {
49 glm::min(glm::min(glm::min(c000, c001), glm::min(c010, c011)),
50 glm::min(glm::min(c100, c101), glm::min(c110, c111))),
51 glm::max(glm::max(glm::max(c000, c001), glm::max(c010, c011)),
52 glm::max(glm::max(c100, c101), glm::max(c110, c111)))
66 const bool workParallel = context->
engine->workParallel();
77 Cogs::Geometry::BoundingBox& localBounds = getLocalBounds(&renderComponent);
79 if (!data.transformComponent) {
86 if ((data.flags & MeshRenderDataFlags::LocalBoundsOverride) == 0) {
99 data.instanceMeshBoundsGeneration != instanceMesh->
getGeneration() ||
104 if (!isEmpty(mesh->boundingBox) && !mesh->isInitialized()) {
109 Geometry::BoundingBox instanceBounds;
111 if (mesh->boundingBox.min.x <= mesh->boundingBox.max.x) {
112 instanceBounds = mesh->boundingBox;
117 localBounds = Cogs::Geometry::BoundingBox();
119 if (isEmpty(instanceMesh->boundingBox)) {
123 for (
size_t i = 0; i < instanceCount; ++i) {
124 Cogs::Geometry::BoundingBox b = getTransformedBounds(instanceBounds, instancedMatrix.getDataAt<
const glm::mat4>(renderComponent.
startInstance + i));
125 localBounds.min = glm::min(localBounds.min, b.min);
126 localBounds.max = glm::max(localBounds.max, b.max);
132 for (
size_t i = 0; i < instanceCount; ++i) {
133 const glm::vec3& pos = instancedPosition.getDataAt<
const glm::vec3>(renderComponent.
startInstance + i);
134 localBounds.min = glm::min(localBounds.min, instanceBounds.min + pos);
135 localBounds.max = glm::max(localBounds.max, instanceBounds.max + pos);
144 localBounds.min = glm::min(localBounds.min, instanceBounds.min + instanceMesh->boundingBox.min);
145 localBounds.max = glm::max(localBounds.max, instanceBounds.max + instanceMesh->boundingBox.max);
148 data.meshBoundsGeneration =
static_cast<uint8_t
>(mesh->
getGeneration());
149 data.instanceMeshBoundsGeneration =
static_cast<uint8_t
>(instanceMesh->
getGeneration());
150 ++data.localBoundsGeneration;
159 if (data.worldBoundsGeneration != data.localBoundsGeneration || transformSystem->hasChanged(transform)) {
160 data.localToWorld = transformSystem->getLocalToWorld(transform);
162 if (!isEmpty(localBounds)) {
163 getWorldBounds(&renderComponent) = getTransformedBounds(localBounds, data.localToWorld);
165 getWorldBounds(&renderComponent) = localBounds;
168 if (data.worldBoundsGeneration != data.localBoundsGeneration) {
169 data.worldBoundsGeneration = data.localBoundsGeneration;
174 data.cullingIndex = NoCullingIndex;
181 CpuInstrumentationScope(SCOPE_SYSTEMS,
"InstancedMeshRenderSystem::update");
183 Parallel::processComponents(context, pool,
"InstancedMeshRenderSystem::updateComponent", updateComponent, geometryGroup);
187 Serial::processComponents(pool, updateComponent);
202 if (geometryGroup.isValid()) {
213 if (pool.size() == 1u) {
214 assert(bounds ==
nullptr);
215 bounds = std::make_unique<InstancedMeshRenderBounds>(
this);
216 context->bounds->addBoundsExtension(bounds.get());
218 assert(picker ==
nullptr);
219 picker = std::make_unique<InstancedMeshPicker>(
this);
220 context->rayPicking->addPickable(picker.get());
227 base_type::destroyComponent(component);
229 if (pool.size() == 0u) {
231 context->bounds->removeBoundsExtension(bounds.get());
236 context->rayPicking->removePickable(picker.get());
242void Cogs::Core::InstancedMeshRenderSystem::initializeCulling(
CullingSource* cullSource)
244 const size_t offset = cullSource->count;
245 const size_t count = pool.size();
246 const size_t total = offset + count;
249 cullSource->count = total;
250 cullSource->bbMinWorld.resize(total);
251 cullSource->bbMaxWorld.resize(total);
254 for (
SizeType i = 0; i < count; ++i) {
257 meshData.cullingIndex = j;
258 cullSource->bbMinWorld[j] = getWorldBounds(&pool[i]).min;
259 cullSource->bbMaxWorld[j] = getWorldBounds(&pool[i]).max;
263 auto scope = Parallel::forEach(context, count, [
this, offset, cullSource](
size_t i) {
266 meshData.cullingIndex = j;
267 cullSource->bbMinWorld[j] = getWorldBounds(&pool[(
SizeType)i]).min;
268 cullSource->bbMaxWorld[j] = getWorldBounds(&pool[(
SizeType)i]).max;
269 },
"InstancedMeshRenderSystem::initializeCulling");
280 const Cogs::Geometry::BoundingBox bbox = system->getWorldBounds(&component);
289 if (!component)
return false;
291 if (!ignoreVisibility) {
297 const Cogs::Geometry::BoundingBox bbox = system->getWorldBounds(component);
310 const glm::mat4& worldPickMatrix,
311 const glm::mat4& rawViewProjection,
312 const glm::mat4& viewMatrix,
316 std::vector<RayPicking::RayPickHit>& hits)
320 bool hitSomething =
false;
338 if (!renderData.meshComponent) {
continue; }
342 if (!instanceMesh || !instanceMesh->isActive()) {
continue; }
344 const Geometry::BoundingBox& bboxWorld = system->getWorldBounds(&comp);
349 if (isEmpty(bboxWorld)) {
353 if (boundingBoxOutsideFrustum(worldPickMatrix, bboxWorld.min, bboxWorld.max)) {
358 uint32_t instanceCount = 0;
373 for (
size_t i = 0; i < instanceCount; ++i) {
375 glm::mat4 instanceTransform;
376 if (instancedMatrix.ptr) {
377 const glm::mat4& mat = instancedMatrix.getDataAt<
const glm::mat4>(comp.
startInstance + i);
378 instanceTransform = renderData.localToWorld * mat;
380 else if (instancedPosition.ptr) {
381 const glm::vec3& pos = instancedPosition.getDataAt<
const glm::vec3>(comp.
startInstance + i);
382 instanceTransform = renderData.localToWorld * glm::translate(glm::mat4(1.0f), pos);
391 glm::mat4 localPickMatrix = worldPickMatrix * instanceTransform;
394 if (!isEmpty(mesh->boundingBox) && boundingBoxOutsideFrustum(localPickMatrix, mesh->boundingBox.min, mesh->boundingBox.max)) {
400 std::vector<RayIntersectionHit> meshHits;
401 std::vector<FrustumPlanes> scratch;
402 if (!intersectMesh(meshHits,
408 static_cast<uint32_t
>(-1))) {
412 if (meshHits.empty()) {
417 const glm::vec4 position = instanceTransform * glm::vec4(meshHit.pos_, 1.f);
420 const ClipShapeData& clipData = context->clipShapeSystem->getData(clipComp);
421 if (clippedByClipComp(clipData, position)) {
426 const glm::vec4 clipPos = rawViewProjection * glm::vec4(position.x, position.y, position.z, 1.f);
427 if ((-clipPos.w < clipPos.z) && (clipPos.z < clipPos.w)) {
429 glm::vec4 viewPos = viewMatrix * position;
430 float viewDist = -viewPos.z;
433 if (hits[0].isBehind(ordinal, viewDist)) {
434 glm::vec2 textureCoords = getTextureCoords(texcoordInfo, meshHit, pickingFlags);
435 if (instanceIdMultiplier) {
436 textureCoords.x =
static_cast<float>(
static_cast<uint32_t
>(textureCoords.x) + instanceIdMultiplier * i);
438 hits[0] = {comp, returnChildEntity, position, ordinal, viewDist, textureCoords};
444 glm::vec2 textureCoords = getTextureCoords(texcoordInfo, meshHit, pickingFlags);
445 if (instanceIdMultiplier) {
446 textureCoords.x =
static_cast<float>(
static_cast<uint32_t
>(textureCoords.x) + instanceIdMultiplier * i);
448 hits.emplace_back(comp, returnChildEntity, position, ordinal, viewDist, textureCoords);
ComponentType * getComponent() const
ComponentHandle getComponentHandle() const
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Sets up a clipping shape that can be used by multiple entities.
Context * context
Pointer to the Context instance the system lives in.
void postUpdate()
Perform post update logic in the system.
virtual void initialize(Context *context)
Initialize the system.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Engine > engine
Engine instance.
virtual void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds) override
Expand bounds including bounds of all entities in this system in world coordinates.
Renders a mesh using instancing.
uint32_t startIndex
Start vertex index to render from.
uint32_t vertexCount
Number of vertexes to draw. uint32_t(-1) to draw all remaining vertices.
uint32_t instanceCount
Instance count. uint32_t(-1) to draw all remaining instances.
uint32_t instanceIdMultiplier
static uint32_t getRenderCount(uint32_t startIndex, uint32_t instanceCount, uint32_t meshCount)
Get number of instanced Meshes to render.
uint32_t startInstance
Start instance.
ComponentHandle createComponent() override
Create a new component instance.
void destroyComponent(ComponentHandle component) override
Destroy the component held by the given handle.
void initialize(Context *context) override
Initialize the system.
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
ComponentModel::ComponentHandle clipShapeComponent
Handle to the currently active clip component, if any.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
ComponentModel::ComponentHandle customRayPickHandling
Handle to the component that manages custom raypicking.
constexpr bool isVisible() const
Check if the entity is visible or not.
constexpr bool isRenderFlagSet(RenderFlags flag) const
Check if the given flag is currently set.
constexpr bool isPickable() const
Check if the entity is pickable or not.
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
PicksReturned
* Options for returning picking hits.
@ Closest
Return just the closest hit.
@ ForcePickable
Ensure component is pickable though it is not rendered.
PickingFlags
Options for COGS picking.
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
Cogs::Geometry::BoundingBox COGSCORE_DLL_API calculateBounds(Mesh *mesh)
Calculate a bounding box for the given mesh.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
ComponentIndex SizeType
Type used to track the size of pools.
@ InstanceMatrix
Instance matrix semantic.
@ InstanceVector
Instance vector semantic.
Handle to a Component instance.
ComponentType * resolveComponent() const
bool pickImpl(Context *context, const glm::mat4 &worldPickMatrix, const glm::mat4 &rawViewProjection, const glm::mat4 &viewMatrix, const RayPicking::RayPickFilter &filter, PickingFlags pickingFlags, PicksReturned returnFlag, std::vector< RayPicking::RayPickHit > &hits) override
Each mesh rendering system should implement this function that goes through all components and calls ...
Utility structure containing reference to a data stream in a mesh.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
StreamReference getSemanticStream(ElementSemantic semantic, DataFormat format)
Get the data of the stream containing data with the given semantic, format and minimum element size.
bool boundsDirty() const
Gets if the mesh bounds need to be updated before use.
uint32_t getCount() const
Get the vertex count of the mesh.
bool isUnwantedType(const ComponentModel::Component &comp) const
Helper function used to determine if a given component belongs to an accepted entity type.
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
uint8_t currentLod
The assigned LoD of the current component.
uint8_t selectedLod
The selected LoD of the composite entity.
uint32_t getGeneration() const
Get the generation count.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.