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"
40 Cogs::Geometry::BoundingBox getTransformedBounds(
const Cogs::Geometry::BoundingBox& bbox,
const glm::mat4 & m)
42 std::array<glm::vec3, 8> points;
43 Cogs::Geometry::corners(bbox, points);
45 for (glm::vec3& p : points) {
46 p = Cogs::Geometry::transform(p, m);
49 return Cogs::Geometry::computeBoundingBox(points.begin(), points.end());
62 const bool workParallel = context->
engine->workParallel();
73 Cogs::Geometry::BoundingBox& localBounds = getLocalBounds(&renderComponent);
75 if (!data.transformComponent) {
82 if ((data.flags & MeshRenderDataFlags::LocalBoundsOverride) == 0) {
95 data.instanceMeshBoundsGeneration != instanceMesh->
getGeneration() ||
100 if (!isEmpty(mesh->boundingBox) && !mesh->isInitialized()) {
105 Geometry::BoundingBox instanceBounds;
107 if (mesh->boundingBox.min.x <= mesh->boundingBox.max.x) {
108 instanceBounds = mesh->boundingBox;
113 localBounds = Cogs::Geometry::BoundingBox();
115 if (isEmpty(instanceMesh->boundingBox)) {
119 for (
size_t i = 0; i < instanceCount; ++i) {
120 const glm::vec3 & instancePosition = *
reinterpret_cast<glm::vec3 *
>(pPos + pStride * (renderComponent.
startInstance + i));
122 localBounds.min = glm::min(localBounds.min, instanceBounds.min + instancePosition);
123 localBounds.max = glm::max(localBounds.max, instanceBounds.max + instancePosition);
127 localBounds.min = glm::min(localBounds.min, instanceBounds.min + instanceMesh->boundingBox.min);
128 localBounds.max = glm::max(localBounds.max, instanceBounds.max + instanceMesh->boundingBox.max);
131 data.meshBoundsGeneration =
static_cast<uint8_t
>(mesh->
getGeneration());
132 data.instanceMeshBoundsGeneration =
static_cast<uint8_t
>(instanceMesh->
getGeneration());
133 ++data.localBoundsGeneration;
142 if (data.worldBoundsGeneration != data.localBoundsGeneration || transformSystem->hasChanged(transform)) {
143 data.localToWorld = transformSystem->getLocalToWorld(transform);
145 if (!isEmpty(localBounds)) {
146 getWorldBounds(&renderComponent) = getTransformedBounds(localBounds, data.localToWorld);
148 getWorldBounds(&renderComponent) = localBounds;
151 if (data.worldBoundsGeneration != data.localBoundsGeneration) {
152 data.worldBoundsGeneration = data.localBoundsGeneration;
157 data.cullingIndex = NoCullingIndex;
164 CpuInstrumentationScope(SCOPE_SYSTEMS,
"InstancedMeshRenderSystem::update");
166 Parallel::processComponents(context, pool,
"InstancedMeshRenderSystem::updateComponent", updateComponent, geometryGroup);
170 Serial::processComponents(pool, updateComponent);
185 if (geometryGroup.isValid()) {
196 if (pool.size() == 1u) {
197 assert(bounds ==
nullptr);
198 bounds = std::make_unique<InstancedMeshRenderBounds>(
this);
199 context->bounds->addBoundsExtension(bounds.get());
201 assert(picker ==
nullptr);
202 picker = std::make_unique<InstancedMeshPicker>(
this);
203 context->rayPicking->addPickable(picker.get());
210 base_type::destroyComponent(component);
212 if (pool.size() == 0u) {
214 context->bounds->removeBoundsExtension(bounds.get());
219 context->rayPicking->removePickable(picker.get());
225void Cogs::Core::InstancedMeshRenderSystem::initializeCulling(
CullingSource* cullSource)
227 const size_t offset = cullSource->count;
228 const size_t count = pool.size();
229 const size_t total = offset + count;
232 cullSource->count = total;
233 cullSource->bbMinWorld.resize(total);
234 cullSource->bbMaxWorld.resize(total);
237 for (
SizeType i = 0; i < count; ++i) {
240 meshData.cullingIndex = j;
241 cullSource->bbMinWorld[j] = getWorldBounds(&pool[i]).min;
242 cullSource->bbMaxWorld[j] = getWorldBounds(&pool[i]).max;
246 auto scope = Parallel::forEach(context, count, [
this, offset, cullSource](
size_t i) {
249 meshData.cullingIndex = j;
250 cullSource->bbMinWorld[j] = getWorldBounds(&pool[(
SizeType)i]).min;
251 cullSource->bbMaxWorld[j] = getWorldBounds(&pool[(
SizeType)i]).max;
252 },
"InstancedMeshRenderSystem::initializeCulling");
263 const Cogs::Geometry::BoundingBox bbox = system->getWorldBounds(&component);
272 if (!component)
return false;
274 if (!ignoreVisibility) {
280 const Cogs::Geometry::BoundingBox bbox = system->getWorldBounds(component);
293 const glm::mat4& worldPickMatrix,
294 const glm::mat4& rawViewProjection,
295 const glm::mat4& viewMatrix,
299 std::vector<RayPicking::RayPickHit>& hits)
303 bool hitSomething =
false;
317 if (!renderData.meshComponent) {
continue; }
321 if (!instanceMesh || !instanceMesh->isActive()) {
continue; }
326 const Geometry::BoundingBox& bboxWorld = system->getWorldBounds(&comp);
330 for (
size_t i = 0; i < instanceCount; ++i) {
331 const glm::vec3& instancePosition = *
reinterpret_cast<const glm::vec3*
>(pPos + pStride * (comp.
startInstance + i));
332 const glm::mat4 instanceTransform = renderData.localToWorld * glm::translate(glm::mat4(1.0f), instancePosition);
337 if (isEmpty(bboxWorld)) {
341 if (frustumClassifyBoundingBox(worldPickMatrix, bboxWorld.min, bboxWorld.max) != FrustumPlanes::InsideAll) {
347 glm::mat4 localPickMatrix = worldPickMatrix * instanceTransform;
349 std::vector<RayIntersectionHit> meshHits;
350 std::vector<FrustumPlanes> scratch;
351 if (!intersectMesh(meshHits,
357 static_cast<uint32_t
>(-1))) {
361 if (meshHits.empty()) {
366 const glm::vec4 position = instanceTransform * glm::vec4(meshHit.pos_, 1.f);
369 const ClipShapeData& clipData = context->clipShapeSystem->getData(clipComp);
370 if (clippedByClipComp(clipData, position)) {
375 const glm::vec4 clipPos = rawViewProjection * glm::vec4(position.x, position.y, position.z, 1.f);
376 if ((-clipPos.w < clipPos.z) && (clipPos.z < clipPos.w)) {
378 glm::vec4 viewPos = viewMatrix * position;
379 float viewDist = -viewPos.z;
382 if (viewDist < hits[0].distance) {
383 glm::vec2 textureCoords = getTextureCoords(texcoordInfo, meshHit, pickingFlags);
384 hits[0] = {comp, returnChildEntity, position, viewDist, textureCoords};
390 glm::vec2 textureCoords = getTextureCoords(texcoordInfo, meshHit, pickingFlags);
391 hits.emplace_back(comp, returnChildEntity, position, viewDist, textureCoords);
400 std::sort(hits.begin(), hits.end());
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.
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.
MeshHandle instanceMesh
Mesh containing instancing data.
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.
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.
@ AllSorted
Return all hits sorted based on distance from the ray start, closest first.
@ 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.
@ 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 ...
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.