Revision control

++ // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "entity_p.h"
++ #include "entity_p_p.h"
++ #include <Qt3DRender/private/managers_p.h>
++ #include <Qt3DRender/private/nodemanagers_p.h>
++ #include <Qt3DRender/qabstractlight.h>
++ #include <Qt3DRender/qenvironmentlight.h>
++ #include <Qt3DRender/qlayer.h>
++ #include <Qt3DRender/qlevelofdetail.h>
++ #include <Qt3DRender/qraycaster.h>
++ #include <Qt3DRender/qscreenraycaster.h>
++ #include <Qt3DRender/qmaterial.h>
++ #include <Qt3DRender/qmesh.h>
++ #include <Qt3DRender/private/renderlogging_p.h>
++ #include <Qt3DRender/private/sphere_p.h>
++ #include <Qt3DRender/qshaderdata.h>
++ #include <Qt3DRender/qgeometryrenderer.h>
++ #include <Qt3DRender/qpickingproxy.h>
++ #include <Qt3DRender/qobjectpicker.h>
++ #include <Qt3DRender/qcomputecommand.h>
++ #include <Qt3DRender/private/geometryrenderermanager_p.h>
++ #include <Qt3DRender/private/armature_p.h>
++
++ #include <Qt3DRender/qcameralens.h>
++ #include <Qt3DCore/qarmature.h>
++ #include <Qt3DCore/qentity.h>
++ #include <Qt3DCore/qtransform.h>
++ #include <Qt3DCore/private/qentity_p.h>
++
++ #include <QMatrix4x4>
++ #include <QString>
++
++ QT_BEGIN_NAMESPACE
++
++
++ namespace Qt3DRender {
++ namespace Render {
++
++ using namespace Qt3DCore;
++
++ EntityPrivate::EntityPrivate()
++ : Qt3DCore::QBackendNodePrivate(Entity::ReadOnly)
++ {
++ }
++
++ EntityPrivate *EntityPrivate::get(Entity *node)
++ {
++ return node->d_func();
++ }
++
++ void EntityPrivate::componentAdded(Qt3DCore::QNode *frontend)
++ {
++ Q_Q(Entity);
++ const auto componentIdAndType = QNodeIdTypePair(frontend->id(), QNodePrivate::findStaticMetaObject(frontend->metaObject()));
++ q->addComponent(componentIdAndType);
++ }
++
++ void EntityPrivate::componentRemoved(Qt3DCore::QNode *frontend)
++ {
++ Q_Q(Entity);
++ q->removeComponent(frontend->id());
++ }
++
++
++ Entity::Entity()
++ : BackendNode(*new EntityPrivate)
++ , m_nodeManagers(nullptr)
++ , m_parentLessTransform(true)
++ , m_boundingDirty(false)
++ , m_treeEnabled(true)
++ {
++ }
++
++ Entity::~Entity()
++ {
++ cleanup();
++ }
++
++ void Entity::cleanup()
++ {
++ if (m_nodeManagers != nullptr) {
++ m_nodeManagers->worldMatrixManager()->releaseResource(peerId());
++ qCDebug(Render::RenderNodes) << Q_FUNC_INFO;
++
++ removeFromParentChildHandles();
++
++ for (auto &childHandle : std::as_const(m_childrenHandles)) {
++ auto child = m_nodeManagers->renderNodesManager()->data(childHandle);
++ // children should always exist and have this as parent
++ // if they were destroyed, they would have removed themselves from our m_childrenHandles
++ Q_ASSERT(child);
++ Q_ASSERT(child->m_parentHandle == m_handle);
++ child->m_parentHandle = {};
++ }
++ }
++
++ m_worldTransform = HMatrix();
++ // Release all component will have to perform their own release when they receive the
++ // NodeDeleted notification
++ // Clear components
++ m_transformComponent = Qt3DCore::QNodeId();
++ m_cameraComponent = Qt3DCore::QNodeId();
++ m_materialComponent = Qt3DCore::QNodeId();
++ m_geometryRendererComponent = Qt3DCore::QNodeId();
++ m_pickingProxyComponent = Qt3DCore::QNodeId();
++ m_objectPickerComponent = QNodeId();
++ m_boundingVolumeDebugComponent = QNodeId();
++ m_computeComponent = QNodeId();
++ m_armatureComponent = QNodeId();
++ m_childrenHandles.clear();
++ m_layerComponents.clear();
++ m_levelOfDetailComponents.clear();
++ m_rayCasterComponents.clear();
++ m_shaderDataComponents.clear();
++ m_lightComponents.clear();
++ m_environmentLightComponents.clear();
++ m_localBoundingVolume.reset();
++ m_worldBoundingVolume.reset();
++ m_worldBoundingVolumeWithChildren.reset();
++ m_parentHandle = {};
++ m_boundingDirty = false;
++ QBackendNode::setEnabled(false);
++
++ // Ensure we rebuild caches when an Entity gets cleaned up
++ if (m_renderer)
++ markDirty(AbstractRenderer::AllDirty);
++ }
++
++ void Entity::setParentHandle(HEntity parentHandle)
++ {
++ Q_ASSERT(m_nodeManagers);
++
++ if (parentHandle == m_parentHandle)
++ return;
++
++ removeFromParentChildHandles();
++
++ m_parentHandle = parentHandle;
++ auto parent = m_nodeManagers->renderNodesManager()->data(parentHandle);
++ if (parent != nullptr && !parent->m_childrenHandles.contains(m_handle))
++ parent->m_childrenHandles.append(m_handle);
++ }
++
++ void Entity::setNodeManagers(NodeManagers *manager)
++ {
++ m_nodeManagers = manager;
++ }
++
++ void Entity::setHandle(HEntity handle)
++ {
++ m_handle = handle;
++ }
++
++ void Entity::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
++ {
++ const Qt3DCore::QEntity *node = qobject_cast<const Qt3DCore::QEntity *>(frontEnd);
++ if (!node)
++ return;
++
++ if (this->isEnabled() != node->isEnabled()) {
++ markDirty(AbstractRenderer::EntityEnabledDirty);
++ // We let QBackendNode::syncFromFrontEnd change the enabled property
++ }
++
++ const auto parentID = node->parentEntity() ? node->parentEntity()->id() : Qt3DCore::QNodeId();
++ auto parentHandle = m_nodeManagers->renderNodesManager()->lookupHandle(parentID);
++
++ // All entity creation is done from top-down and always during the same frame, so
++ // we if we have a valid parent node, we should always be able to resolve the
++ // backend parent at this time
++ Q_ASSERT(!node->parentEntity() || (!parentHandle.isNull() && m_nodeManagers->renderNodesManager()->data(parentHandle)));
++
++ if (parentHandle != m_parentHandle) {
++ markDirty(AbstractRenderer::AllDirty);
++ }
++
++ setParentHandle(parentHandle);
++
++ if (firstTime) {
++ m_worldTransform = m_nodeManagers->worldMatrixManager()->getOrAcquireHandle(peerId());
++
++ // TODO: Suboptimal -> Maybe have a Hash<QComponent, QEntityList> instead
++ m_transformComponent = QNodeId();
++ m_materialComponent = QNodeId();
++ m_cameraComponent = QNodeId();
++ m_geometryRendererComponent = QNodeId();
++ m_pickingProxyComponent = QNodeId();
++ m_objectPickerComponent = QNodeId();
++ m_boundingVolumeDebugComponent = QNodeId();
++ m_computeComponent = QNodeId();
++ m_layerComponents.clear();
++ m_levelOfDetailComponents.clear();
++ m_rayCasterComponents.clear();
++ m_shaderDataComponents.clear();
++ m_lightComponents.clear();
++ m_environmentLightComponents.clear();
++ m_localBoundingVolume = QSharedPointer<Sphere>::create(peerId());
++ m_worldBoundingVolume = QSharedPointer<Sphere>::create(peerId());
++ m_worldBoundingVolumeWithChildren = QSharedPointer<Sphere>::create(peerId());
++
++ const QComponentVector &components = node->components();
++ for (QComponent *c : components) {
++ const auto idAndType = QNodeIdTypePair(c->id(), QNodePrivate::findStaticMetaObject(c->metaObject()));
++ addComponent(idAndType);
++ }
++ }
++
++ BackendNode::syncFromFrontEnd(frontEnd, firstTime);
++ }
++
++ void Entity::dump() const
++ {
++ static int depth = 0;
++ QString indent(2 * depth++, QChar::fromLatin1(' '));
++ qCDebug(Backend) << indent + m_objectName;
++ const auto children_ = children();
++ for (const Entity *child : children_)
++ child->dump();
++ --depth;
++ }
++
++ Entity *Entity::parent() const
++ {
++ return m_nodeManagers->renderNodesManager()->data(m_parentHandle);
++ }
++
++ void Entity::removeFromParentChildHandles()
++ {
++ // remove ourself from our parent's list of children.
++ auto p = parent();
++ if (p)
++ p->removeChildHandle(m_handle);
++ }
++
++ QList<Entity *> Entity::children() const
++ {
++ QList<Entity *> childrenVector;
++ childrenVector.reserve(m_childrenHandles.size());
++ for (const HEntity &handle : m_childrenHandles) {
++ Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
++ if (child != nullptr)
++ childrenVector.append(child);
++ }
++ return childrenVector;
++ }
++
++ void Entity::traverse(const std::function<void(Entity *)> &operation)
++ {
++ operation(this);
++ for (const HEntity &handle : std::as_const(m_childrenHandles)) {
++ Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
++ if (child != nullptr)
++ child->traverse(operation);
++ }
++ }
++
++ void Entity::traverse(const std::function<void(const Entity *)> &operation) const
++ {
++ operation(this);
++ for (const HEntity &handle : m_childrenHandles) {
++ const Entity *child = m_nodeManagers->renderNodesManager()->data(handle);
++ if (child != nullptr)
++ child->traverse(operation);
++ }
++ }
++
++ Matrix4x4 *Entity::worldTransform()
++ {
++ return m_nodeManagers->worldMatrixManager()->data(m_worldTransform);
++ }
++
++ const Matrix4x4 *Entity::worldTransform() const
++ {
++ return m_nodeManagers->worldMatrixManager()->data(m_worldTransform);
++ }
++
++ void Entity::addComponent(Qt3DCore::QNodeIdTypePair idAndType)
++ {
++ // The backend element is always created when this method is called
++ // If that's not the case something has gone wrong
++ const auto type = idAndType.type;
++ const auto id = idAndType.id;
++ qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "id =" << id << type->className();
++ if (type->inherits(&Qt3DCore::QTransform::staticMetaObject)) {
++ m_transformComponent = id;
++ } else if (type->inherits(&QCameraLens::staticMetaObject)) {
++ m_cameraComponent = id;
++ } else if (type->inherits(&QLayer::staticMetaObject)) {
++ m_layerComponents.append(id);
++ } else if (type->inherits(&QLevelOfDetail::staticMetaObject)) {
++ m_levelOfDetailComponents.append(id);
++ } else if (type->inherits(&QRayCaster::staticMetaObject)) {
++ m_rayCasterComponents.append(id);
++ } else if (type->inherits(&QScreenRayCaster::staticMetaObject)) {
++ m_rayCasterComponents.append(id);
++ } else if (type->inherits(&QMaterial::staticMetaObject)) {
++ m_materialComponent = id;
++ } else if (type->inherits(&QAbstractLight::staticMetaObject)) { // QAbstractLight subclasses QShaderData
++ m_lightComponents.append(id);
++ } else if (type->inherits(&QEnvironmentLight::staticMetaObject)) {
++ m_environmentLightComponents.append(id);
++ } else if (type->inherits(&QShaderData::staticMetaObject)) {
++ m_shaderDataComponents.append(id);
++ } else if (type->inherits(&QGeometryRenderer::staticMetaObject)) {
++ m_geometryRendererComponent = id;
++ m_boundingDirty = true;
++ } else if (type->inherits(&QPickingProxy::staticMetaObject)) {
++ m_pickingProxyComponent = id;
++ } else if (type->inherits(&QObjectPicker::staticMetaObject)) {
++ m_objectPickerComponent = id;
++ // } else if (type->inherits(&QBoundingVolumeDebug::staticMetaObject)) {
++ // m_boundingVolumeDebugComponent = id;
++ } else if (type->inherits(&QComputeCommand::staticMetaObject)) {
++ m_computeComponent = id;
++ } else if (type->inherits(&QArmature::staticMetaObject)) {
++ m_armatureComponent = id;
++ }
++ markDirty(AbstractRenderer::AllDirty);
++ }
++
++ void Entity::removeComponent(Qt3DCore::QNodeId nodeId)
++ {
++ if (m_transformComponent == nodeId) {
++ m_transformComponent = QNodeId();
++ } else if (m_cameraComponent == nodeId) {
++ m_cameraComponent = QNodeId();
++ } else if (m_layerComponents.contains(nodeId)) {
++ m_layerComponents.removeAll(nodeId);
++ } else if (m_levelOfDetailComponents.contains(nodeId)) {
++ m_levelOfDetailComponents.removeAll(nodeId);
++ } else if (m_rayCasterComponents.contains(nodeId)) {
++ m_rayCasterComponents.removeAll(nodeId);
++ } else if (m_materialComponent == nodeId) {
++ m_materialComponent = QNodeId();
++ } else if (m_shaderDataComponents.contains(nodeId)) {
++ m_shaderDataComponents.removeAll(nodeId);
++ } else if (m_geometryRendererComponent == nodeId) {
++ m_geometryRendererComponent = QNodeId();
++ m_boundingDirty = true;
++ } else if (m_pickingProxyComponent == nodeId) {
++ m_pickingProxyComponent = QNodeId();
++ } else if (m_objectPickerComponent == nodeId) {
++ m_objectPickerComponent = QNodeId();
++ // } else if (m_boundingVolumeDebugComponent == nodeId) {
++ // m_boundingVolumeDebugComponent = QNodeId();
++ } else if (m_lightComponents.contains(nodeId)) {
++ m_lightComponents.removeAll(nodeId);
++ } else if (m_environmentLightComponents.contains(nodeId)) {
++ m_environmentLightComponents.removeAll(nodeId);
++ } else if (m_computeComponent == nodeId) {
++ m_computeComponent = QNodeId();
++ } else if (m_armatureComponent == nodeId) {
++ m_armatureComponent = QNodeId();
++ }
++ markDirty(AbstractRenderer::AllDirty);
++ }
++
++ bool Entity::isBoundingVolumeDirty() const
++ {
++ return m_boundingDirty;
++ }
++
++ void Entity::unsetBoundingVolumeDirty()
++ {
++ m_boundingDirty = false;
++ }
++
++ void Entity::addRecursiveLayerId(const QNodeId layerId)
++ {
++ if (!m_recursiveLayerComponents.contains(layerId) && !m_layerComponents.contains(layerId))
++ m_recursiveLayerComponents.push_back(layerId);
++ }
++
++ void Entity::removeRecursiveLayerId(const QNodeId layerId)
++ {
++ m_recursiveLayerComponents.removeOne(layerId);
++ }
++
++ ENTITY_COMPONENT_TEMPLATE_IMPL(Material, HMaterial, MaterialManager, m_materialComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(CameraLens, HCamera, CameraManager, m_cameraComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(Transform, HTransform, TransformManager, m_transformComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(GeometryRenderer, HGeometryRenderer, GeometryRendererManager, m_geometryRendererComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(PickingProxy, HPickingProxy, PickingProxyManager, m_pickingProxyComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(ObjectPicker, HObjectPicker, ObjectPickerManager, m_objectPickerComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(ComputeCommand, HComputeCommand, ComputeCommandManager, m_computeComponent)
++ ENTITY_COMPONENT_TEMPLATE_IMPL(Armature, HArmature, ArmatureManager, m_armatureComponent)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(Layer, HLayer, LayerManager, m_layerComponents)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(LevelOfDetail, HLevelOfDetail, LevelOfDetailManager, m_levelOfDetailComponents)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(RayCaster, HRayCaster, RayCasterManager, m_rayCasterComponents)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(ShaderData, HShaderData, ShaderDataManager, m_shaderDataComponents)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(Light, HLight, LightManager, m_lightComponents)
++ ENTITY_COMPONENT_LIST_TEMPLATE_IMPL(EnvironmentLight, HEnvironmentLight, EnvironmentLightManager, m_environmentLightComponents)
++
++ RenderEntityFunctor::RenderEntityFunctor(AbstractRenderer *renderer, NodeManagers *manager)
++ : m_nodeManagers(manager)
++ , m_renderer(renderer)
++ {
++ }
++
++ Qt3DCore::QBackendNode *RenderEntityFunctor::create(Qt3DCore::QNodeId id) const
++ {
++ HEntity renderNodeHandle = m_nodeManagers->renderNodesManager()->getOrAcquireHandle(id);
++ Entity *entity = m_nodeManagers->renderNodesManager()->data(renderNodeHandle);
++ entity->setNodeManagers(m_nodeManagers);
++ entity->setHandle(renderNodeHandle);
++ entity->setRenderer(m_renderer);
++ return entity;
++ }
++
++ Qt3DCore::QBackendNode *RenderEntityFunctor::get(Qt3DCore::QNodeId id) const
++ {
++ return m_nodeManagers->renderNodesManager()->lookupResource(id);
++ }
++
++ void RenderEntityFunctor::destroy(Qt3DCore::QNodeId id) const
++ {
++ m_nodeManagers->renderNodesManager()->releaseResource(id);
++ }
++
++ } // namespace Render
++ } // namespace Qt3DRender
++
++ QT_END_NAMESPACE