1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "entity_p.h"
5#include "entity_p_p.h"
6#include <Qt3DRender/private/managers_p.h>
7#include <Qt3DRender/private/nodemanagers_p.h>
8#include <Qt3DRender/qabstractlight.h>
9#include <Qt3DRender/qenvironmentlight.h>
10#include <Qt3DRender/qlayer.h>
11#include <Qt3DRender/qlevelofdetail.h>
12#include <Qt3DRender/qraycaster.h>
13#include <Qt3DRender/qscreenraycaster.h>
14#include <Qt3DRender/qmaterial.h>
15#include <Qt3DRender/qmesh.h>
16#include <Qt3DRender/private/renderlogging_p.h>
17#include <Qt3DRender/private/sphere_p.h>
18#include <Qt3DRender/qshaderdata.h>
19#include <Qt3DRender/qgeometryrenderer.h>
20#include <Qt3DRender/qpickingproxy.h>
21#include <Qt3DRender/qobjectpicker.h>
22#include <Qt3DRender/qcomputecommand.h>
23#include <Qt3DRender/private/geometryrenderermanager_p.h>
24#include <Qt3DRender/private/armature_p.h>
25
26#include <Qt3DRender/qcameralens.h>
27#include <Qt3DCore/qarmature.h>
28#include <Qt3DCore/qentity.h>
29#include <Qt3DCore/qtransform.h>
30#include <Qt3DCore/private/qentity_p.h>
31
32#include <QMatrix4x4>
33#include <QString>
34
35QT_BEGIN_NAMESPACE
36
37
38namespace Qt3DRender {
39namespace Render {
40
41using namespace Qt3DCore;
42
43EntityPrivate::EntityPrivate()
44 : Qt3DCore::QBackendNodePrivate(Entity::ReadOnly)
45{
46}
47
48EntityPrivate *EntityPrivate::get(Entity *node)
49{
50 return node->d_func();
51}
52
53void EntityPrivate::componentAdded(Qt3DCore::QNode *frontend)
54{
55 Q_Q(Entity);
56 const auto componentIdAndType = QNodeIdTypePair(frontend->id(), QNodePrivate::findStaticMetaObject(metaObject: frontend->metaObject()));
57 q->addComponent(idAndType: componentIdAndType);
58}
59
60void EntityPrivate::componentRemoved(Qt3DCore::QNode *frontend)
61{
62 Q_Q(Entity);
63 q->removeComponent(nodeId: frontend->id());
64}
65
66
67Entity::Entity()
68 : BackendNode(*new EntityPrivate)
69 , m_nodeManagers(nullptr)
70 , m_parentLessTransform(true)
71 , m_boundingDirty(false)
72 , m_treeEnabled(true)
73{
74}
75
76Entity::~Entity()
77{
78 cleanup();
79}
80
81void Entity::cleanup()
82{
83 if (m_nodeManagers != nullptr) {
84 m_nodeManagers->worldMatrixManager()->releaseResource(id: peerId());
85 qCDebug(Render::RenderNodes) << Q_FUNC_INFO;
86
87 removeFromParentChildHandles();
88
89 for (auto &childHandle : std::as_const(t&: m_childrenHandles)) {
90 auto child = m_nodeManagers->renderNodesManager()->data(handle: childHandle);
91 // children should always exist and have this as parent
92 // if they were destroyed, they would have removed themselves from our m_childrenHandles
93 Q_ASSERT(child);
94 Q_ASSERT(child->m_parentHandle == m_handle);
95 child->m_parentHandle = {};
96 }
97 }
98
99 m_worldTransform = HMatrix();
100 // Release all component will have to perform their own release when they receive the
101 // NodeDeleted notification
102 // Clear components
103 m_transformComponent = Qt3DCore::QNodeId();
104 m_cameraComponent = Qt3DCore::QNodeId();
105 m_materialComponent = Qt3DCore::QNodeId();
106 m_geometryRendererComponent = Qt3DCore::QNodeId();
107 m_pickingProxyComponent = Qt3DCore::QNodeId();
108 m_objectPickerComponent = QNodeId();
109 m_boundingVolumeDebugComponent = QNodeId();
110 m_computeComponent = QNodeId();
111 m_armatureComponent = QNodeId();
112 m_childrenHandles.clear();
113 m_layerComponents.clear();
114 m_levelOfDetailComponents.clear();
115 m_rayCasterComponents.clear();
116 m_shaderDataComponents.clear();
117 m_lightComponents.clear();
118 m_environmentLightComponents.clear();
119 m_localBoundingVolume.reset();
120 m_worldBoundingVolume.reset();
121 m_worldBoundingVolumeWithChildren.reset();
122 m_parentHandle = {};
123 m_boundingDirty = false;
124 QBackendNode::setEnabled(false);
125
126 // Ensure we rebuild caches when an Entity gets cleaned up
127 if (m_renderer)
128 markDirty(changes: AbstractRenderer::AllDirty);
129}
130
131void Entity::setParentHandle(HEntity parentHandle)
132{
133 Q_ASSERT(m_nodeManagers);
134
135 if (parentHandle == m_parentHandle)
136 return;
137
138 removeFromParentChildHandles();
139
140 m_parentHandle = parentHandle;
141 auto parent = m_nodeManagers->renderNodesManager()->data(handle: parentHandle);
142 if (parent != nullptr && !parent->m_childrenHandles.contains(t: m_handle))
143 parent->m_childrenHandles.append(t: m_handle);
144}
145
146void Entity::setNodeManagers(NodeManagers *manager)
147{
148 m_nodeManagers = manager;
149}
150
151void Entity::setHandle(HEntity handle)
152{
153 m_handle = handle;
154}
155
156void Entity::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
157{
158 const Qt3DCore::QEntity *node = qobject_cast<const Qt3DCore::QEntity *>(object: frontEnd);
159 if (!node)
160 return;
161
162 if (this->isEnabled() != node->isEnabled()) {
163 markDirty(changes: AbstractRenderer::EntityEnabledDirty);
164 // We let QBackendNode::syncFromFrontEnd change the enabled property
165 }
166
167 const auto parentID = node->parentEntity() ? node->parentEntity()->id() : Qt3DCore::QNodeId();
168 auto parentHandle = m_nodeManagers->renderNodesManager()->lookupHandle(id: parentID);
169
170 // All entity creation is done from top-down and always during the same frame, so
171 // we if we have a valid parent node, we should always be able to resolve the
172 // backend parent at this time
173 Q_ASSERT(!node->parentEntity() || (!parentHandle.isNull() && m_nodeManagers->renderNodesManager()->data(parentHandle)));
174
175 if (parentHandle != m_parentHandle) {
176 markDirty(changes: AbstractRenderer::AllDirty);
177 }
178
179 setParentHandle(parentHandle);
180
181 if (firstTime) {
182 m_worldTransform = m_nodeManagers->worldMatrixManager()->getOrAcquireHandle(id: peerId());
183
184 // TODO: Suboptimal -> Maybe have a Hash<QComponent, QEntityList> instead
185 m_transformComponent = QNodeId();
186 m_materialComponent = QNodeId();
187 m_cameraComponent = QNodeId();
188 m_geometryRendererComponent = QNodeId();
189 m_pickingProxyComponent = QNodeId();
190 m_objectPickerComponent = QNodeId();
191 m_boundingVolumeDebugComponent = QNodeId();
192 m_computeComponent = QNodeId();
193 m_layerComponents.clear();
194 m_levelOfDetailComponents.clear();
195 m_rayCasterComponents.clear();
196 m_shaderDataComponents.clear();
197 m_lightComponents.clear();
198 m_environmentLightComponents.clear();
199 m_localBoundingVolume = QSharedPointer<Sphere>::create(arguments: peerId());
200 m_worldBoundingVolume = QSharedPointer<Sphere>::create(arguments: peerId());
201 m_worldBoundingVolumeWithChildren = QSharedPointer<Sphere>::create(arguments: peerId());
202
203 const QComponentVector &components = node->components();
204 for (QComponent *c : components) {
205 const auto idAndType = QNodeIdTypePair(c->id(), QNodePrivate::findStaticMetaObject(metaObject: c->metaObject()));
206 addComponent(idAndType);
207 }
208 }
209
210 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
211}
212
213void Entity::dump() const
214{
215 static int depth = 0;
216 QString indent(2 * depth++, QChar::fromLatin1(c: ' '));
217 qCDebug(Backend) << indent + m_objectName;
218 const auto children_ = children();
219 for (const Entity *child : children_)
220 child->dump();
221 --depth;
222}
223
224Entity *Entity::parent() const
225{
226 return m_nodeManagers->renderNodesManager()->data(handle: m_parentHandle);
227}
228
229void Entity::removeFromParentChildHandles()
230{
231 // remove ourself from our parent's list of children.
232 auto p = parent();
233 if (p)
234 p->removeChildHandle(childHandle: m_handle);
235}
236
237QList<Entity *> Entity::children() const
238{
239 QList<Entity *> childrenVector;
240 childrenVector.reserve(asize: m_childrenHandles.size());
241 for (const HEntity &handle : m_childrenHandles) {
242 Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
243 if (child != nullptr)
244 childrenVector.append(t: child);
245 }
246 return childrenVector;
247}
248
249void Entity::traverse(const std::function<void(Entity *)> &operation)
250{
251 operation(this);
252 for (const HEntity &handle : std::as_const(t&: m_childrenHandles)) {
253 Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
254 if (child != nullptr)
255 child->traverse(operation);
256 }
257}
258
259void Entity::traverse(const std::function<void(const Entity *)> &operation) const
260{
261 operation(this);
262 for (const HEntity &handle : m_childrenHandles) {
263 const Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
264 if (child != nullptr)
265 child->traverse(operation);
266 }
267}
268
269Matrix4x4 *Entity::worldTransform()
270{
271 return m_nodeManagers->worldMatrixManager()->data(handle: m_worldTransform);
272}
273
274const Matrix4x4 *Entity::worldTransform() const
275{
276 return m_nodeManagers->worldMatrixManager()->data(handle: m_worldTransform);
277}
278
279void Entity::addComponent(Qt3DCore::QNodeIdTypePair idAndType)
280{
281 // The backend element is always created when this method is called
282 // If that's not the case something has gone wrong
283 const auto type = idAndType.type;
284 const auto id = idAndType.id;
285 qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "id =" << id << type->className();
286 if (type->inherits(metaObject: &Qt3DCore::QTransform::staticMetaObject)) {
287 m_transformComponent = id;
288 } else if (type->inherits(metaObject: &QCameraLens::staticMetaObject)) {
289 m_cameraComponent = id;
290 } else if (type->inherits(metaObject: &QLayer::staticMetaObject)) {
291 m_layerComponents.append(t: id);
292 } else if (type->inherits(metaObject: &QLevelOfDetail::staticMetaObject)) {
293 m_levelOfDetailComponents.append(t: id);
294 } else if (type->inherits(metaObject: &QRayCaster::staticMetaObject)) {
295 m_rayCasterComponents.append(t: id);
296 } else if (type->inherits(metaObject: &QScreenRayCaster::staticMetaObject)) {
297 m_rayCasterComponents.append(t: id);
298 } else if (type->inherits(metaObject: &QMaterial::staticMetaObject)) {
299 m_materialComponent = id;
300 } else if (type->inherits(metaObject: &QAbstractLight::staticMetaObject)) { // QAbstractLight subclasses QShaderData
301 m_lightComponents.append(t: id);
302 } else if (type->inherits(metaObject: &QEnvironmentLight::staticMetaObject)) {
303 m_environmentLightComponents.append(t: id);
304 } else if (type->inherits(metaObject: &QShaderData::staticMetaObject)) {
305 m_shaderDataComponents.append(t: id);
306 } else if (type->inherits(metaObject: &QGeometryRenderer::staticMetaObject)) {
307 m_geometryRendererComponent = id;
308 m_boundingDirty = true;
309 } else if (type->inherits(metaObject: &QPickingProxy::staticMetaObject)) {
310 m_pickingProxyComponent = id;
311 } else if (type->inherits(metaObject: &QObjectPicker::staticMetaObject)) {
312 m_objectPickerComponent = id;
313// } else if (type->inherits(&QBoundingVolumeDebug::staticMetaObject)) {
314// m_boundingVolumeDebugComponent = id;
315 } else if (type->inherits(metaObject: &QComputeCommand::staticMetaObject)) {
316 m_computeComponent = id;
317 } else if (type->inherits(metaObject: &QArmature::staticMetaObject)) {
318 m_armatureComponent = id;
319 }
320 markDirty(changes: AbstractRenderer::AllDirty);
321}
322
323void Entity::removeComponent(Qt3DCore::QNodeId nodeId)
324{
325 if (m_transformComponent == nodeId) {
326 m_transformComponent = QNodeId();
327 } else if (m_cameraComponent == nodeId) {
328 m_cameraComponent = QNodeId();
329 } else if (m_layerComponents.contains(t: nodeId)) {
330 m_layerComponents.removeAll(t: nodeId);
331 } else if (m_levelOfDetailComponents.contains(t: nodeId)) {
332 m_levelOfDetailComponents.removeAll(t: nodeId);
333 } else if (m_rayCasterComponents.contains(t: nodeId)) {
334 m_rayCasterComponents.removeAll(t: nodeId);
335 } else if (m_materialComponent == nodeId) {
336 m_materialComponent = QNodeId();
337 } else if (m_shaderDataComponents.contains(t: nodeId)) {
338 m_shaderDataComponents.removeAll(t: nodeId);
339 } else if (m_geometryRendererComponent == nodeId) {
340 m_geometryRendererComponent = QNodeId();
341 m_boundingDirty = true;
342 } else if (m_pickingProxyComponent == nodeId) {
343 m_pickingProxyComponent = QNodeId();
344 } else if (m_objectPickerComponent == nodeId) {
345 m_objectPickerComponent = QNodeId();
346// } else if (m_boundingVolumeDebugComponent == nodeId) {
347// m_boundingVolumeDebugComponent = QNodeId();
348 } else if (m_lightComponents.contains(t: nodeId)) {
349 m_lightComponents.removeAll(t: nodeId);
350 } else if (m_environmentLightComponents.contains(t: nodeId)) {
351 m_environmentLightComponents.removeAll(t: nodeId);
352 } else if (m_computeComponent == nodeId) {
353 m_computeComponent = QNodeId();
354 } else if (m_armatureComponent == nodeId) {
355 m_armatureComponent = QNodeId();
356 }
357 markDirty(changes: AbstractRenderer::AllDirty);
358}
359
360bool Entity::isBoundingVolumeDirty() const
361{
362 return m_boundingDirty;
363}
364
365void Entity::unsetBoundingVolumeDirty()
366{
367 m_boundingDirty = false;
368}
369
370void Entity::addRecursiveLayerId(const QNodeId layerId)
371{
372 if (!m_recursiveLayerComponents.contains(t: layerId) && !m_layerComponents.contains(t: layerId))
373 m_recursiveLayerComponents.push_back(t: layerId);
374}
375
376void Entity::removeRecursiveLayerId(const QNodeId layerId)
377{
378 m_recursiveLayerComponents.removeOne(t: layerId);
379}
380
381ENTITY_COMPONENT_TEMPLATE_IMPL(Material, HMaterial, MaterialManager, m_materialComponent)
382ENTITY_COMPONENT_TEMPLATE_IMPL(CameraLens, HCamera, CameraManager, m_cameraComponent)
383ENTITY_COMPONENT_TEMPLATE_IMPL(Transform, HTransform, TransformManager, m_transformComponent)
384ENTITY_COMPONENT_TEMPLATE_IMPL(GeometryRenderer, HGeometryRenderer, GeometryRendererManager, m_geometryRendererComponent)
385ENTITY_COMPONENT_TEMPLATE_IMPL(PickingProxy, HPickingProxy, PickingProxyManager, m_pickingProxyComponent)
386ENTITY_COMPONENT_TEMPLATE_IMPL(ObjectPicker, HObjectPicker, ObjectPickerManager, m_objectPickerComponent)
387ENTITY_COMPONENT_TEMPLATE_IMPL(ComputeCommand, HComputeCommand, ComputeCommandManager, m_computeComponent)
388ENTITY_COMPONENT_TEMPLATE_IMPL(Armature, HArmature, ArmatureManager, m_armatureComponent)
389ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(Layer, HLayer, LayerManager, m_layerComponents)
390ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(LevelOfDetail, HLevelOfDetail, LevelOfDetailManager, m_levelOfDetailComponents)
391ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(RayCaster, HRayCaster, RayCasterManager, m_rayCasterComponents)
392ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(ShaderData, HShaderData, ShaderDataManager, m_shaderDataComponents)
393ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(Light, HLight, LightManager, m_lightComponents)
394ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(EnvironmentLight, HEnvironmentLight, EnvironmentLightManager, m_environmentLightComponents)
395
396RenderEntityFunctor::RenderEntityFunctor(AbstractRenderer *renderer, NodeManagers *manager)
397 : m_nodeManagers(manager)
398 , m_renderer(renderer)
399{
400}
401
402Qt3DCore::QBackendNode *RenderEntityFunctor::create(Qt3DCore::QNodeId id) const
403{
404 HEntity renderNodeHandle = m_nodeManagers->renderNodesManager()->getOrAcquireHandle(id);
405 Entity *entity = m_nodeManagers->renderNodesManager()->data(handle: renderNodeHandle);
406 entity->setNodeManagers(m_nodeManagers);
407 entity->setHandle(renderNodeHandle);
408 entity->setRenderer(m_renderer);
409 return entity;
410}
411
412Qt3DCore::QBackendNode *RenderEntityFunctor::get(Qt3DCore::QNodeId id) const
413{
414 return m_nodeManagers->renderNodesManager()->lookupResource(id);
415}
416
417void RenderEntityFunctor::destroy(Qt3DCore::QNodeId id) const
418{
419 m_nodeManagers->renderNodesManager()->releaseResource(id);
420}
421
422} // namespace Render
423} // namespace Qt3DRender
424
425QT_END_NAMESPACE
426

source code of qt3d/src/render/backend/entity.cpp