summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2019-05-17 17:55:40 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2019-05-23 16:48:10 +0300
commit1cc2c2fa919c6f2cf2e1ac836248184258fe779e (patch)
treee037c1ca216b212fead0aaf9353c4f255009c76e
parentecdb627cf0fd529186f774299a3a8bd2bf72d953 (diff)
Add C++ API to create meshes dynamically
Meshes can now be created at runtime via Q3DSPresentation API. Single vertex buffer is supported, and the vertex data must be properly interleaved by the user. Task-number: QT3DS-3378 Change-Id: I93a14db4201a24357c48e815747d4089dcba41ba Reviewed-by: Janne Kangas <janne.kangas@qt.io> Reviewed-by: Jere Tuliniemi <jere.tuliniemi@qt.io> Reviewed-by: Pasi Keränen <pasi.keranen@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--src/Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp1
-rw-r--r--src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro3
-rw-r--r--src/Runtime/Source/engine/Qt3DSRuntimeView.cpp12
-rw-r--r--src/Runtime/Source/engine/Qt3DSRuntimeView.h5
-rw-r--r--src/Runtime/Source/runtime/Qt3DSIScriptBridge.h7
-rw-r--r--src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp12
-rw-r--r--src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp484
-rw-r--r--src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h5
-rw-r--r--src/Runtime/Source/viewer/Qt3DSViewerApp.cpp119
-rw-r--r--src/Runtime/Source/viewer/Qt3DSViewerApp.h102
-rw-r--r--src/Runtime/api/studio3d/q3dscommandqueue.cpp8
-rw-r--r--src/Runtime/api/studio3d/q3dscommandqueue_p.h1
-rw-r--r--src/Runtime/api/studio3d/q3dsgeometry.cpp152
-rw-r--r--src/Runtime/api/studio3d/q3dsgeometry.h117
-rw-r--r--src/Runtime/api/studio3d/q3dsgeometry_p.h67
-rw-r--r--src/Runtime/api/studio3d/q3dspresentation.cpp37
-rw-r--r--src/Runtime/api/studio3d/q3dspresentation.h4
-rw-r--r--src/Runtime/api/studio3d/studio3d.pro7
-rw-r--r--src/Runtime/api/studio3dqml/q3dsrenderer.cpp13
-rw-r--r--src/Runtime/api/studio3dqml/q3dsrenderer.h1
-rw-r--r--src/Runtime/api/studio3dqml/q3dsstudio3d.cpp2
-rw-r--r--tests/auto/viewer/tst_qt3dsviewer.cpp175
-rw-r--r--tests/auto/viewer/tst_qt3dsviewer.h2
-rw-r--r--tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip10
24 files changed, 1104 insertions, 242 deletions
diff --git a/src/Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp b/src/Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp
index 976fc29b..e8c6d92e 100644
--- a/src/Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp
+++ b/src/Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp
@@ -29,7 +29,6 @@
#include "Qt3DSImportLibPrecompile.h"
#include "Qt3DSImportMesh.h"
#include "foundation/Qt3DSMemoryBuffer.h"
-#include "Qt3DSFileTools.h"
#include "Qt3DSImportContainers.h"
using namespace qt3dsimp;
diff --git a/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro b/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro
index 1a76455a..7e446406 100644
--- a/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro
+++ b/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro
@@ -12,7 +12,7 @@ linux {
QMAKE_LFLAGS += -lrt
}
-DEFINES += QT3DS_BUILDING_LIBRARY
+DEFINES += QT3DS_BUILDING_LIBRARY DISABLE_MESH_OPTIMIZATION
QT += qml
QT += quick-private
@@ -593,6 +593,7 @@ HEADERS += \
SOURCES += \
../Source/datamodel/Qt3DSMetadata.cpp \
../../Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMesh.cpp \
+ ../../Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportMeshBuilder.cpp \
../../Authoring/QT3DSIMP/Qt3DSImportLib/Qt3DSImportPath.cpp \
../../Authoring/QT3DSDM/Systems/Qt3DSDMMetaData.cpp \
../../Authoring/QT3DSDM/Systems/Qt3DSDMXML.cpp \
diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
index 9485a67b..2849cd39 100644
--- a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
+++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
@@ -47,6 +47,7 @@
#include "Qt3DSRenderer.h"
#include "Qt3DSRenderBufferManager.h"
#include "Qt3DSRenderRuntimeBindingImpl.h"
+#include "Qt3DSImportMesh.h"
#include "Qt3DSDLLManager.h"
#include "foundation/Qt3DSSimpleTypes.h"
@@ -217,6 +218,7 @@ public:
void deleteElements(const QStringList &elementPaths) override;
void createMaterials(const QString &elementPath,
const QStringList &materialDefinitions) override;
+ void createMesh(const QString &name, qt3dsimp::Mesh *mesh) override;
void SetAttribute(const char *elementPath, const char *attributeName,
const char *value) override;
bool GetAttribute(const char *elementPath, const char *attributeName, void *value) override;
@@ -668,6 +670,16 @@ void CRuntimeView::createMaterials(const QString &elementPath,
}
}
+void CRuntimeView::createMesh(const QString &name, qt3dsimp::Mesh *mesh)
+{
+ if (m_Application) {
+ Q3DStudio::CQmlEngine &theBridgeEngine
+ = static_cast<Q3DStudio::CQmlEngine &>(m_RuntimeFactoryCore->GetScriptEngineQml());
+ theBridgeEngine.createMesh(
+ name, mesh, &m_RuntimeFactory->GetQt3DSRenderContext().GetBufferManager());
+ }
+}
+
void CRuntimeView::SetAttribute(const char *elementPath, const char *attributeName,
const char *value)
{
diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.h b/src/Runtime/Source/engine/Qt3DSRuntimeView.h
index ecf7b252..5273d635 100644
--- a/src/Runtime/Source/engine/Qt3DSRuntimeView.h
+++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.h
@@ -50,6 +50,10 @@
typedef void (*qml_Function)(void *inUserData);
+namespace qt3dsimp {
+ struct Mesh;
+}
+
class QRuntimeViewSignalProxy : public QObject
{
Q_OBJECT
@@ -201,6 +205,7 @@ public:
virtual void deleteElements(const QStringList &elementPaths) = 0;
virtual void createMaterials(const QString &elementPath,
const QStringList &materialDefinitions) = 0;
+ virtual void createMesh(const QString &name, qt3dsimp::Mesh *mesh) = 0;
virtual void SetAttribute(const char *elementPath, const char *attributeName,
const char *value) = 0;
virtual bool GetAttribute(const char *elementPath, const char *attributeName, void *value) = 0;
diff --git a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h
index 69a4be66..d570a392 100644
--- a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h
+++ b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h
@@ -39,6 +39,10 @@
#include <QtCore/qvector.h>
#include <QtCore/qstringlist.h>
+namespace qt3dsimp {
+ struct Mesh;
+}
+
namespace qt3ds {
namespace runtime {
class IApplication;
@@ -57,6 +61,7 @@ namespace render {
class IQt3DSRenderer;
class ICustomMaterialSystem;
class IDynamicObjectSystem;
+ class IBufferManager;
}
}
@@ -172,6 +177,8 @@ public: // Elements
qt3ds::render::ICustomMaterialSystem *customMaterialSystem,
qt3ds::render::IDynamicObjectSystem *dynamicObjectSystem,
qt3ds::render::IQt3DSRenderer *renderer) = 0;
+ virtual void createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager) = 0;
public: // Components
virtual void GotoSlide(const char *component, const char *slideName,
diff --git a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
index ac1c994a..02bfbd5b 100644
--- a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
@@ -68,7 +68,8 @@
#include "Qt3DSQmlElementHelper.h"
#include "q3dsqmlscript.h"
#include "Qt3DSRenderRuntimeBindingImpl.h"
-#include "Qt3DSRenderBufferManager.h" // TODO: Needed for adding meshes dynamically (QT3DS-3378)
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSImportMesh.h"
#include "Qt3DSRenderer.h"
#include "q3dsmaterialdefinitionparser.h"
#include "Qt3DSRenderCustomMaterialSystem.h"
@@ -436,6 +437,8 @@ public:
qt3ds::render::ICustomMaterialSystem *customMaterialSystem,
IDynamicObjectSystem *dynamicObjectSystem,
qt3ds::render::IQt3DSRenderer *renderer) override;
+ void createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager) override;
void GotoSlide(const char *component, const char *slideName,
const SScriptEngineGotoSlideArgs &inArgs) override;
@@ -1642,6 +1645,13 @@ void CQmlEngineImpl::createMaterials(const QString &elementPath,
handleError();
}
+void CQmlEngineImpl::createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager)
+{
+ // Add the custom meshes to buffer manager.
+ bufferManager->loadCustomMesh(name, mesh);
+}
+
void CQmlEngineImpl::GotoSlide(const char *component, const char *slideName,
const SScriptEngineGotoSlideArgs &inArgs)
{
diff --git a/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
index 638e8458..eb23f3d0 100644
--- a/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
+++ b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
@@ -585,19 +585,22 @@ struct SBufferManager : public IBufferManager
return qt3dsimp::SMultiLoadResult();
}
- virtual NVConstDataRef<QT3DSU8> CreatePackedPositionDataArray(qt3dsimp::SMultiLoadResult *inResult)
+ virtual NVConstDataRef<QT3DSU8> CreatePackedPositionDataArray(
+ const qt3dsimp::SMultiLoadResult &inResult)
{
// we assume a position consists of 3 floats
- QT3DSU32 vertexCount = inResult->m_Mesh->m_VertexBuffer.m_Data.size()
- / inResult->m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 vertexCount = inResult.m_Mesh->m_VertexBuffer.m_Data.size()
+ / inResult.m_Mesh->m_VertexBuffer.m_Stride;
QT3DSU32 dataSize = vertexCount * 3 * sizeof(QT3DSF32);
- QT3DSF32 *posData = (QT3DSF32 *)QT3DS_ALLOC(m_Context->GetAllocator(), dataSize,
- "SRenderMesh::CreatePackedPositionDataArray");
- QT3DSU8 *baseOffset = reinterpret_cast<QT3DSU8 *>(inResult->m_Mesh);
+ QT3DSF32 *posData = static_cast<QT3DSF32 *>(
+ QT3DS_ALLOC(m_Context->GetAllocator(), dataSize,
+ "SRenderMesh::CreatePackedPositionDataArray"));
+ QT3DSU8 *baseOffset = reinterpret_cast<QT3DSU8 *>(inResult.m_Mesh);
// copy position data
if (posData) {
- QT3DSF32 *srcData = (QT3DSF32 *)inResult->m_Mesh->m_VertexBuffer.m_Data.begin(baseOffset);
- QT3DSU32 srcStride = inResult->m_Mesh->m_VertexBuffer.m_Stride / sizeof(QT3DSF32);
+ QT3DSF32 *srcData = reinterpret_cast<QT3DSF32 *>(
+ inResult.m_Mesh->m_VertexBuffer.m_Data.begin(baseOffset));
+ QT3DSU32 srcStride = inResult.m_Mesh->m_VertexBuffer.m_Stride / sizeof(QT3DSF32);
QT3DSF32 *dstData = posData;
QT3DSU32 dstStride = 3;
@@ -610,257 +613,272 @@ struct SBufferManager : public IBufferManager
srcData += srcStride;
}
- return toConstDataRef((const qt3ds::QT3DSU8 *)posData, dataSize);
+ return toConstDataRef(reinterpret_cast<const qt3ds::QT3DSU8 *>(posData), dataSize);
}
return NVConstDataRef<QT3DSU8>();
}
+ SRenderMesh *createRenderMesh(const qt3dsimp::SMultiLoadResult &result)
+ {
+ SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)(
+ qt3ds::render::NVRenderDrawMode::Triangles,
+ qt3ds::render::NVRenderWinding::CounterClockwise, result.m_Id,
+ m_Context->GetAllocator());
+ QT3DSU8 *baseAddress = reinterpret_cast<QT3DSU8 *>(result.m_Mesh);
+ NVConstDataRef<QT3DSU8> theVBufData(
+ result.m_Mesh->m_VertexBuffer.m_Data.begin(baseAddress),
+ result.m_Mesh->m_VertexBuffer.m_Data.size());
+
+ NVRenderVertexBuffer *theVertexBuffer = m_Context->CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static,
+ result.m_Mesh->m_VertexBuffer.m_Data.m_Size,
+ result.m_Mesh->m_VertexBuffer.m_Stride, theVBufData);
+
+ // create a tight packed position data VBO
+ // this should improve our depth pre pass rendering
+ NVRenderVertexBuffer *thePosVertexBuffer = nullptr;
+ NVConstDataRef<QT3DSU8> posData = CreatePackedPositionDataArray(result);
+ if (posData.size()) {
+ thePosVertexBuffer
+ = m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static,
+ posData.size(), 3 * sizeof(QT3DSF32), posData);
+ }
+
+ NVRenderIndexBuffer *theIndexBuffer = nullptr;
+ if (result.m_Mesh->m_IndexBuffer.m_Data.size()) {
+ using qt3ds::render::NVRenderComponentTypes;
+ QT3DSU32 theIndexBufferSize = result.m_Mesh->m_IndexBuffer.m_Data.size();
+ NVRenderComponentTypes::Enum bufComponentType =
+ result.m_Mesh->m_IndexBuffer.m_ComponentType;
+ QT3DSU32 sizeofType
+ = qt3ds::render::NVRenderComponentTypes::getSizeofType(bufComponentType);
+
+ if (sizeofType == 2 || sizeofType == 4) {
+ // Ensure type is unsigned; else things will fail in rendering pipeline.
+ if (bufComponentType == NVRenderComponentTypes::QT3DSI16)
+ bufComponentType = NVRenderComponentTypes::QT3DSU16;
+ if (bufComponentType == NVRenderComponentTypes::QT3DSI32)
+ bufComponentType = NVRenderComponentTypes::QT3DSU32;
+
+ NVConstDataRef<QT3DSU8> theIBufData(
+ result.m_Mesh->m_IndexBuffer.m_Data.begin(baseAddress),
+ result.m_Mesh->m_IndexBuffer.m_Data.size());
+ theIndexBuffer = m_Context->CreateIndexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static, bufComponentType,
+ theIndexBufferSize, theIBufData);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ nvvector<qt3ds::render::NVRenderVertexBufferEntry> &theEntryBuffer(m_EntryBuffer);
+ theEntryBuffer.resize(result.m_Mesh->m_VertexBuffer.m_Entries.size());
+ for (QT3DSU32 entryIdx = 0,
+ entryEnd = result.m_Mesh->m_VertexBuffer.m_Entries.size();
+ entryIdx < entryEnd; ++entryIdx) {
+ theEntryBuffer[entryIdx]
+ = result.m_Mesh->m_VertexBuffer.m_Entries.index(baseAddress, entryIdx)
+ .ToVertexBufferEntry(baseAddress);
+ }
+ // create our attribute layout
+ NVRenderAttribLayout *theAttribLayout
+ = m_Context->CreateAttributeLayout(theEntryBuffer);
+ // create our attribute layout for depth pass
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3),
+ };
+ NVRenderAttribLayout *theAttribLayoutDepth
+ = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1));
+
+ // create input assembler object
+ QT3DSU32 strides = result.m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 offsets = 0;
+ NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler(
+ theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer,
+ toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1),
+ result.m_Mesh->m_DrawMode);
+
+ // create depth input assembler object
+ QT3DSU32 posStrides = thePosVertexBuffer ? 3 * sizeof(QT3DSF32) : strides;
+ NVRenderInputAssembler *theInputAssemblerDepth = m_Context->CreateInputAssembler(
+ theAttribLayoutDepth,
+ toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1),
+ theIndexBuffer, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
+ result.m_Mesh->m_DrawMode);
+
+ NVRenderInputAssembler *theInputAssemblerPoints = m_Context->CreateInputAssembler(
+ theAttribLayoutDepth,
+ toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1),
+ nullptr, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
+ NVRenderDrawMode::Points);
+
+ if (!theInputAssembler || !theInputAssemblerDepth || !theInputAssemblerPoints) {
+ QT3DS_ASSERT(false);
+ return nullptr;
+ }
+ theNewMesh->m_Joints.resize(result.m_Mesh->m_Joints.size());
+ for (QT3DSU32 jointIdx = 0, jointEnd = result.m_Mesh->m_Joints.size();
+ jointIdx < jointEnd; ++jointIdx) {
+ const qt3dsimp::Joint &theImportJoint(
+ result.m_Mesh->m_Joints.index(baseAddress, jointIdx));
+ SRenderJoint &theNewJoint(theNewMesh->m_Joints[jointIdx]);
+ theNewJoint.m_JointID = theImportJoint.m_JointID;
+ theNewJoint.m_ParentID = theImportJoint.m_ParentID;
+ memCopy(theNewJoint.m_invBindPose, theImportJoint.m_invBindPose,
+ 16 * sizeof(QT3DSF32));
+ memCopy(theNewJoint.m_localToGlobalBoneSpace,
+ theImportJoint.m_localToGlobalBoneSpace, 16 * sizeof(QT3DSF32));
+ }
+
+ for (QT3DSU32 subsetIdx = 0, subsetEnd = result.m_Mesh->m_Subsets.size();
+ subsetIdx < subsetEnd; ++subsetIdx) {
+ SRenderSubset theSubset(m_Context->GetAllocator());
+ const qt3dsimp::MeshSubset &source(
+ result.m_Mesh->m_Subsets.index(baseAddress, subsetIdx));
+ theSubset.m_Bounds = source.m_Bounds;
+ theSubset.m_Count = source.m_Count;
+ theSubset.m_Offset = source.m_Offset;
+ theSubset.m_Joints = theNewMesh->m_Joints;
+ theSubset.m_Name = m_StrTable->RegisterStr(source.m_Name.begin(baseAddress));
+ theVertexBuffer->addRef();
+ theSubset.m_VertexBuffer = theVertexBuffer;
+ if (thePosVertexBuffer) {
+ thePosVertexBuffer->addRef();
+ theSubset.m_PosVertexBuffer = thePosVertexBuffer;
+ }
+ if (theIndexBuffer) {
+ theIndexBuffer->addRef();
+ theSubset.m_IndexBuffer = theIndexBuffer;
+ }
+ theSubset.m_InputAssembler = theInputAssembler;
+ theSubset.m_InputAssemblerDepth = theInputAssemblerDepth;
+ theSubset.m_InputAssemblerPoints = theInputAssemblerPoints;
+ theSubset.m_PrimitiveType = result.m_Mesh->m_DrawMode;
+ theInputAssembler->addRef();
+ theInputAssemblerDepth->addRef();
+ theSubset.m_InputAssemblerPoints->addRef();
+ theNewMesh->m_Subsets.push_back(theSubset);
+ }
+ // If we want to, we can break up models into sub-subsets.
+ // These are assumed to use the same material as the outer subset but have fewer triangles
+ // and should have a more exact bounding box. This sort of thing helps with using the
+ // frustum culling system but it is really done incorrectly.
+ // It should be done via some sort of oct-tree mechanism and so that the sub-subsets
+ // are spatially sorted and it should only be done upon save-to-binary with the
+ // results saved out to disk. As you can see, doing it properly requires some real
+ // engineering effort so it is somewhat unlikely it will ever happen.
+ // Or it could be done on import if someone really wants to change the mesh buffer
+ // format. Either way it isn't going to happen here and it isn't going to happen this way
+ // but this is a working example of using the technique.
+#ifdef QT3DS_RENDER_GENERATE_SUB_SUBSETS
+ Option<qt3ds::render::NVRenderVertexBufferEntry> thePosAttrOpt
+ = theVertexBuffer->GetEntryByName("attr_pos");
+ bool hasPosAttr = thePosAttrOpt.hasValue()
+ && thePosAttrOpt->m_ComponentType == qt3ds::render::NVRenderComponentTypes::QT3DSF32
+ && thePosAttrOpt->m_NumComponents == 3;
+
+ for (size_t subsetIdx = 0, subsetEnd = theNewMesh->m_Subsets.size();
+ subsetIdx < subsetEnd; ++subsetIdx) {
+ SRenderSubset &theOuterSubset = theNewMesh->m_Subsets[subsetIdx];
+ if (theOuterSubset.m_Count && theIndexBuffer
+ && theIndexBuffer->GetComponentType()
+ == qt3ds::render::NVRenderComponentTypes::QT3DSU16
+ && theNewMesh->m_DrawMode == NVRenderDrawMode::Triangles && hasPosAttr) {
+ // Num tris in a sub subset.
+ QT3DSU32 theSubsetSize = 3334 * 3; // divisible by three.
+ size_t theNumSubSubsets = ((theOuterSubset.m_Count - 1) / theSubsetSize) + 1;
+ QT3DSU32 thePosAttrOffset = thePosAttrOpt->m_FirstItemOffset;
+ const QT3DSU8 *theVertData = result.m_Mesh->m_VertexBuffer.m_Data.begin();
+ const QT3DSU8 *theIdxData = result.m_Mesh->m_IndexBuffer.m_Data.begin();
+ QT3DSU32 theVertStride = result.m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 theOffset = theOuterSubset.m_Offset;
+ QT3DSU32 theCount = theOuterSubset.m_Count;
+ for (size_t subSubsetIdx = 0, subSubsetEnd = theNumSubSubsets;
+ subSubsetIdx < subSubsetEnd; ++subSubsetIdx) {
+ SRenderSubsetBase theBase;
+ theBase.m_Offset = theOffset;
+ theBase.m_Count = NVMin(theSubsetSize, theCount);
+ theBase.m_Bounds.setEmpty();
+ theCount -= theBase.m_Count;
+ theOffset += theBase.m_Count;
+ // Create new bounds.
+ // Offset is in item size, not bytes.
+ const QT3DSU16 *theSubsetIdxData
+ = reinterpret_cast<const QT3DSU16 *>(theIdxData + theBase.m_Offset * 2);
+ for (size_t theIdxIdx = 0, theIdxEnd = theBase.m_Count;
+ theIdxIdx < theIdxEnd; ++theIdxIdx) {
+ QT3DSU32 theVertOffset = theSubsetIdxData[theIdxIdx] * theVertStride;
+ theVertOffset += thePosAttrOffset;
+ QT3DSVec3 thePos = *(
+ reinterpret_cast<const QT3DSVec3 *>(theVertData + theVertOffset));
+ theBase.m_Bounds.include(thePos);
+ }
+ theOuterSubset.m_SubSubsets.push_back(theBase);
+ }
+ } else {
+ SRenderSubsetBase theBase;
+ theBase.m_Bounds = theOuterSubset.m_Bounds;
+ theBase.m_Count = theOuterSubset.m_Count;
+ theBase.m_Offset = theOuterSubset.m_Offset;
+ theOuterSubset.m_SubSubsets.push_back(theBase);
+ }
+ }
+#endif
+ if (posData.size()) {
+ m_Context->GetAllocator().deallocate(
+ static_cast<void *>(const_cast<qt3ds::QT3DSU8 *>(posData.begin())));
+ }
+
+ return theNewMesh;
+ }
+
+ void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) override
+ {
+ if (!name.isEmpty() && mesh) {
+ CRegisteredString meshName = m_StrTable->RegisterStr(name);
+ pair<TMeshMap::iterator, bool> theMesh
+ = m_MeshMap.insert({ meshName, static_cast<SRenderMesh *>(nullptr) });
+ // Only create the mesh if it doesn't yet exist
+ if (theMesh.second) {
+ qt3dsimp::SMultiLoadResult result;
+ result.m_Mesh = mesh;
+ theMesh.first->second = createRenderMesh(result);
+ }
+ }
+ }
+
SRenderMesh *LoadMesh(CRegisteredString inMeshPath) override
{
if (inMeshPath.IsValid() == false)
- return NULL;
+ return nullptr;
pair<TMeshMap::iterator, bool> theMesh =
- m_MeshMap.insert(make_pair(inMeshPath, (SRenderMesh *)NULL));
- if (theMesh.second == true) {
- // check to see if this is primitive
-
+ m_MeshMap.insert(make_pair(inMeshPath, static_cast<SRenderMesh *>(nullptr)));
+ if (theMesh.second) {
+ // Check to see if this is primitive
qt3dsimp::SMultiLoadResult theResult = LoadPrimitive(inMeshPath);
// Attempt a load from the filesystem if this mesh isn't a primitive.
- if (theResult.m_Mesh == NULL) {
+ if (!theResult.m_Mesh) {
m_PathBuilder = inMeshPath;
TStr::size_type pound = m_PathBuilder.rfind('#');
QT3DSU32 id = 0;
if (pound != TStr::npos) {
- id = atoi(m_PathBuilder.c_str() + pound + 1);
+ id = QT3DSU32(atoi(m_PathBuilder.c_str() + pound + 1));
m_PathBuilder.erase(m_PathBuilder.begin() + pound, m_PathBuilder.end());
}
NVScopedRefCounted<IRefCountedInputStream> theStream(
m_InputStreamFactory->GetStreamForFile(m_PathBuilder.c_str()));
if (theStream) {
- theResult = qt3dsimp::Mesh::LoadMulti(m_Context->GetAllocator(), *theStream, id);
+ theResult = qt3dsimp::Mesh::LoadMulti(
+ m_Context->GetAllocator(), *theStream, id);
}
- if (theResult.m_Mesh == NULL)
+ if (!theResult.m_Mesh)
qCWarning(WARNING, "Failed to load mesh: %s", m_PathBuilder.c_str());
}
if (theResult.m_Mesh) {
- SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)(
- qt3ds::render::NVRenderDrawMode::Triangles,
- qt3ds::render::NVRenderWinding::CounterClockwise, theResult.m_Id,
- m_Context->GetAllocator());
- QT3DSU8 *baseAddress = reinterpret_cast<QT3DSU8 *>(theResult.m_Mesh);
- theMesh.first->second = theNewMesh;
- NVConstDataRef<QT3DSU8> theVBufData(
- theResult.m_Mesh->m_VertexBuffer.m_Data.begin(baseAddress),
- theResult.m_Mesh->m_VertexBuffer.m_Data.size());
-
- NVRenderVertexBuffer *theVertexBuffer = m_Context->CreateVertexBuffer(
- qt3ds::render::NVRenderBufferUsageType::Static,
- theResult.m_Mesh->m_VertexBuffer.m_Data.m_Size,
- theResult.m_Mesh->m_VertexBuffer.m_Stride, theVBufData);
-
- // create a tight packed position data VBO
- // this should improve our depth pre pass rendering
- NVRenderVertexBuffer *thePosVertexBuffer = NULL;
- NVConstDataRef<QT3DSU8> posData = CreatePackedPositionDataArray(&theResult);
- if (posData.size()) {
- thePosVertexBuffer =
- m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static,
- posData.size(), 3 * sizeof(QT3DSF32), posData);
- }
-
- NVRenderIndexBuffer *theIndexBuffer = NULL;
- if (theResult.m_Mesh->m_IndexBuffer.m_Data.size()) {
- using qt3ds::render::NVRenderComponentTypes;
- QT3DSU32 theIndexBufferSize = theResult.m_Mesh->m_IndexBuffer.m_Data.size();
- NVRenderComponentTypes::Enum bufComponentType =
- theResult.m_Mesh->m_IndexBuffer.m_ComponentType;
- QT3DSU32 sizeofType =
- qt3ds::render::NVRenderComponentTypes::getSizeofType(bufComponentType);
-
- if (sizeofType == 2 || sizeofType == 4) {
- // Ensure type is unsigned; else things will fail in rendering pipeline.
- if (bufComponentType == NVRenderComponentTypes::QT3DSI16)
- bufComponentType = NVRenderComponentTypes::QT3DSU16;
- if (bufComponentType == NVRenderComponentTypes::QT3DSI32)
- bufComponentType = NVRenderComponentTypes::QT3DSU32;
-
- NVConstDataRef<QT3DSU8> theIBufData(
- theResult.m_Mesh->m_IndexBuffer.m_Data.begin(baseAddress),
- theResult.m_Mesh->m_IndexBuffer.m_Data.size());
- theIndexBuffer = m_Context->CreateIndexBuffer(
- qt3ds::render::NVRenderBufferUsageType::Static, bufComponentType,
- theIndexBufferSize, theIBufData);
- } else {
- QT3DS_ASSERT(false);
- }
- }
- nvvector<qt3ds::render::NVRenderVertexBufferEntry> &theEntryBuffer(m_EntryBuffer);
- theEntryBuffer.resize(theResult.m_Mesh->m_VertexBuffer.m_Entries.size());
- for (QT3DSU32 entryIdx = 0,
- entryEnd = theResult.m_Mesh->m_VertexBuffer.m_Entries.size();
- entryIdx < entryEnd; ++entryIdx) {
- theEntryBuffer[entryIdx] =
- theResult.m_Mesh->m_VertexBuffer.m_Entries.index(baseAddress, entryIdx)
- .ToVertexBufferEntry(baseAddress);
- }
- // create our attribute layout
- NVRenderAttribLayout *theAttribLayout =
- m_Context->CreateAttributeLayout(theEntryBuffer);
- // create our attribute layout for depth pass
- qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
- qt3ds::render::NVRenderVertexBufferEntry(
- "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3),
- };
- NVRenderAttribLayout *theAttribLayoutDepth =
- m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1));
-
- // create input assembler object
- QT3DSU32 strides = theResult.m_Mesh->m_VertexBuffer.m_Stride;
- QT3DSU32 offsets = 0;
- NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler(
- theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer,
- toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1),
- theResult.m_Mesh->m_DrawMode);
-
- // create depth input assembler object
- QT3DSU32 posStrides = (thePosVertexBuffer) ? 3 * sizeof(QT3DSF32) : strides;
- NVRenderInputAssembler *theInputAssemblerDepth = m_Context->CreateInputAssembler(
- theAttribLayoutDepth,
- toConstDataRef((thePosVertexBuffer) ? &thePosVertexBuffer : &theVertexBuffer,
- 1),
- theIndexBuffer, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
- theResult.m_Mesh->m_DrawMode);
-
- NVRenderInputAssembler *theInputAssemblerPoints = m_Context->CreateInputAssembler(
- theAttribLayoutDepth,
- toConstDataRef((thePosVertexBuffer) ? &thePosVertexBuffer : &theVertexBuffer,
- 1),
- NULL, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
- NVRenderDrawMode::Points);
-
- if (!theInputAssembler || !theInputAssemblerDepth || !theInputAssemblerPoints) {
- QT3DS_ASSERT(false);
- return NULL;
- }
- theNewMesh->m_Joints.resize(theResult.m_Mesh->m_Joints.size());
- for (QT3DSU32 jointIdx = 0, jointEnd = theResult.m_Mesh->m_Joints.size();
- jointIdx < jointEnd; ++jointIdx) {
- const qt3dsimp::Joint &theImportJoint(
- theResult.m_Mesh->m_Joints.index(baseAddress, jointIdx));
- SRenderJoint &theNewJoint(theNewMesh->m_Joints[jointIdx]);
- theNewJoint.m_JointID = theImportJoint.m_JointID;
- theNewJoint.m_ParentID = theImportJoint.m_ParentID;
- memCopy(theNewJoint.m_invBindPose, theImportJoint.m_invBindPose,
- 16 * sizeof(QT3DSF32));
- memCopy(theNewJoint.m_localToGlobalBoneSpace,
- theImportJoint.m_localToGlobalBoneSpace, 16 * sizeof(QT3DSF32));
- }
-
- for (QT3DSU32 subsetIdx = 0, subsetEnd = theResult.m_Mesh->m_Subsets.size();
- subsetIdx < subsetEnd; ++subsetIdx) {
- SRenderSubset theSubset(m_Context->GetAllocator());
- const qt3dsimp::MeshSubset &source(
- theResult.m_Mesh->m_Subsets.index(baseAddress, subsetIdx));
- theSubset.m_Bounds = source.m_Bounds;
- theSubset.m_Count = source.m_Count;
- theSubset.m_Offset = source.m_Offset;
- theSubset.m_Joints = theNewMesh->m_Joints;
- theSubset.m_Name = m_StrTable->RegisterStr(source.m_Name.begin(baseAddress));
- theVertexBuffer->addRef();
- theSubset.m_VertexBuffer = theVertexBuffer;
- if (thePosVertexBuffer) {
- thePosVertexBuffer->addRef();
- theSubset.m_PosVertexBuffer = thePosVertexBuffer;
- }
- if (theIndexBuffer) {
- theIndexBuffer->addRef();
- theSubset.m_IndexBuffer = theIndexBuffer;
- }
- theSubset.m_InputAssembler = theInputAssembler;
- theSubset.m_InputAssemblerDepth = theInputAssemblerDepth;
- theSubset.m_InputAssemblerPoints = theInputAssemblerPoints;
- theSubset.m_PrimitiveType = theResult.m_Mesh->m_DrawMode;
- theInputAssembler->addRef();
- theInputAssemblerDepth->addRef();
- theSubset.m_InputAssemblerPoints->addRef();
- theNewMesh->m_Subsets.push_back(theSubset);
- }
-// If we want to, we can an in a quite stupid way break up modes into sub-subsets.
-// These are assumed to use the same material as the outer subset but have fewer tris
-// and should have a more exact bounding box. This sort of thing helps with using the frustum
-// culling
-// system but it is really done incorrectly. It should be done via some sort of oct-tree mechanism
-// and it
-// so that the sub-subsets spatially sorted and it should only be done upon save-to-binary with the
-// results
-// saved out to disk. As you can see, doing it properly requires some real engineering effort so it
-// is somewhat
-// unlikely it will ever happen. Or it could be done on import if someone really wants to change
-// the mesh buffer
-// format. Either way it isn't going to happen here and it isn't going to happen this way but this
-// is a working
-// example of using the technique.
-#ifdef QT3DS_RENDER_GENERATE_SUB_SUBSETS
- Option<qt3ds::render::NVRenderVertexBufferEntry> thePosAttrOpt =
- theVertexBuffer->GetEntryByName("attr_pos");
- bool hasPosAttr = thePosAttrOpt.hasValue()
- && thePosAttrOpt->m_ComponentType == qt3ds::render::NVRenderComponentTypes::QT3DSF32
- && thePosAttrOpt->m_NumComponents == 3;
-
- for (size_t subsetIdx = 0, subsetEnd = theNewMesh->m_Subsets.size();
- subsetIdx < subsetEnd; ++subsetIdx) {
- SRenderSubset &theOuterSubset = theNewMesh->m_Subsets[subsetIdx];
- if (theOuterSubset.m_Count && theIndexBuffer
- && theIndexBuffer->GetComponentType()
- == qt3ds::render::NVRenderComponentTypes::QT3DSU16
- && theNewMesh->m_DrawMode == NVRenderDrawMode::Triangles && hasPosAttr) {
- // Num tris in a sub subset.
- QT3DSU32 theSubsetSize = 3334 * 3; // divisible by three.
- size_t theNumSubSubsets =
- ((theOuterSubset.m_Count - 1) / theSubsetSize) + 1;
- QT3DSU32 thePosAttrOffset = thePosAttrOpt->m_FirstItemOffset;
- const QT3DSU8 *theVertData = theResult.m_Mesh->m_VertexBuffer.m_Data.begin();
- const QT3DSU8 *theIdxData = theResult.m_Mesh->m_IndexBuffer.m_Data.begin();
- QT3DSU32 theVertStride = theResult.m_Mesh->m_VertexBuffer.m_Stride;
- QT3DSU32 theOffset = theOuterSubset.m_Offset;
- QT3DSU32 theCount = theOuterSubset.m_Count;
- for (size_t subSubsetIdx = 0, subSubsetEnd = theNumSubSubsets;
- subSubsetIdx < subSubsetEnd; ++subSubsetIdx) {
- SRenderSubsetBase theBase;
- theBase.m_Offset = theOffset;
- theBase.m_Count = NVMin(theSubsetSize, theCount);
- theBase.m_Bounds.setEmpty();
- theCount -= theBase.m_Count;
- theOffset += theBase.m_Count;
- // Create new bounds.
- // Offset is in item size, not bytes.
- const QT3DSU16 *theSubsetIdxData =
- reinterpret_cast<const QT3DSU16 *>(theIdxData + theBase.m_Offset * 2);
- for (size_t theIdxIdx = 0, theIdxEnd = theBase.m_Count;
- theIdxIdx < theIdxEnd; ++theIdxIdx) {
- QT3DSU32 theVertOffset = theSubsetIdxData[theIdxIdx] * theVertStride;
- theVertOffset += thePosAttrOffset;
- QT3DSVec3 thePos = *(
- reinterpret_cast<const QT3DSVec3 *>(theVertData + theVertOffset));
- theBase.m_Bounds.include(thePos);
- }
- theOuterSubset.m_SubSubsets.push_back(theBase);
- }
- } else {
- SRenderSubsetBase theBase;
- theBase.m_Bounds = theOuterSubset.m_Bounds;
- theBase.m_Count = theOuterSubset.m_Count;
- theBase.m_Offset = theOuterSubset.m_Offset;
- theOuterSubset.m_SubSubsets.push_back(theBase);
- }
- }
-#endif
- if (posData.size())
- m_Context->GetAllocator().deallocate((void *)posData.begin());
-
+ theMesh.first->second = createRenderMesh(theResult);
m_Context->GetAllocator().deallocate(theResult.m_Mesh);
}
}
diff --git a/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h
index d595c902..070ab675 100644
--- a/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h
+++ b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h
@@ -37,6 +37,10 @@
#include "Qt3DSRenderImageTextureData.h"
#include "foundation/Qt3DSBounds3.h"
+namespace qt3dsimp {
+ struct Mesh;
+}
+
namespace qt3ds {
namespace render {
@@ -93,6 +97,7 @@ namespace render {
virtual void loadSet(const QSet<QString> &imageSet) = 0;
virtual void unloadSet(const QSet<QString> &imageSet) = 0;
+ virtual void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) = 0;
virtual SRenderMesh *LoadMesh(CRegisteredString inSourcePath) = 0;
virtual SRenderMesh *CreateMesh(const char *inSourcePath, QT3DSU8 *inVertData,
diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
index ac4aa7a3..b58849aa 100644
--- a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
+++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
@@ -38,6 +38,7 @@
#include "foundation/Qt3DSTime.h"
#include "Qt3DSFNDTimer.h"
#include "Qt3DSAudioPlayer.h"
+#include "Qt3DSImportMesh.h"
#include <QList>
#include <QFileInfo>
@@ -252,6 +253,7 @@ public:
QVector<QMouseEvent *> m_pendingEvents;
QString m_error;
+ qt3dsimp::MeshBuilder *m_meshBuilder = nullptr;
void queueMouseEvent(int button, int pressed, int x, int y)
{
@@ -261,6 +263,94 @@ public:
e->setTimestamp(static_cast<ulong>(GetTimeUST()));
m_pendingEvents.append(e);
}
+
+ qt3dsimp::Mesh *buildMesh(const Q3DSViewer::MeshData &meshData, QString &error)
+ {
+ using namespace qt3dsimp;
+ using namespace qt3ds::render;
+
+ // Do some basic validation of the meshData
+ if (meshData.m_vertexBuffer.size() == 0) {
+ error = QObject::tr("Vertex buffer empty");
+ return nullptr;
+ }
+ if (meshData.m_attributeCount == 0) {
+ error = QObject::tr("No attributes defined");
+ return nullptr;
+ }
+
+ if (!m_meshBuilder)
+ m_meshBuilder = &MeshBuilder::CreateMeshBuilder();
+ m_meshBuilder->Reset();
+ m_meshBuilder->SetDrawParameters(
+ static_cast<NVRenderDrawMode::Enum>(meshData.m_primitiveType),
+ NVRenderWinding::CounterClockwise);
+
+ // The expectation is that the vertex buffer included in meshData is already properly
+ // formatted and doesn't need further processing.
+
+ // Validate attributes
+ QVector<NVRenderVertexBufferEntry> vBufEntries;
+ NVRenderComponentTypes::Enum indexBufferComponentType = NVRenderComponentTypes::Unknown;
+ int indexBufferTypeSize = 0;
+ for (int i = 0; i < meshData.m_attributeCount; ++i) {
+ const Q3DSViewer::MeshData::Attribute &att = meshData.m_attributes[i];
+ auto componentType = static_cast<NVRenderComponentTypes::Enum>(att.componentType);
+ if (att.semantic == Q3DSViewer::MeshData::Attribute::IndexSemantic) {
+ indexBufferComponentType = componentType;
+ indexBufferTypeSize = att.typeSize();
+ } else {
+ const char *name = nullptr;
+ switch (att.semantic) {
+ case MeshData::Attribute::PositionSemantic:
+ name = Mesh::GetPositionAttrName();
+ break;
+ case MeshData::Attribute::NormalSemantic:
+ name = Mesh::GetNormalAttrName();
+ break;
+ case MeshData::Attribute::TexCoordSemantic:
+ name = Mesh::GetUVAttrName();
+ break;
+ case MeshData::Attribute::TangentSemantic:
+ name = Mesh::GetTexTanAttrName();
+ break;
+ case MeshData::Attribute::BinormalSemantic:
+ name = Mesh::GetTexBinormalAttrName();
+ break;
+ default:
+ error = QObject::tr("Warning: Invalid attribute semantic: %1")
+ .arg(att.semantic);
+ return nullptr;
+ }
+ vBufEntries << NVRenderVertexBufferEntry(name, componentType,
+ QT3DSU32(att.componentCount()),
+ QT3DSU32(att.offset));
+ }
+ }
+ m_meshBuilder->SetVertexBuffer(
+ toConstDataRef(reinterpret_cast<const NVRenderVertexBufferEntry *>(
+ vBufEntries.constData()), QT3DSU32(vBufEntries.count())),
+ QT3DSU32(meshData.m_stride),
+ toConstDataRef(const_cast<QT3DSU8 *>(reinterpret_cast<const QT3DSU8 *>(
+ meshData.m_vertexBuffer.constData())),
+ QT3DSU32(meshData.m_vertexBuffer.size())));
+
+ int vertexCount = 0;
+ if (indexBufferComponentType != NVRenderComponentTypes::Unknown) {
+ m_meshBuilder->SetIndexBuffer(
+ toConstDataRef(const_cast<QT3DSU8 *>(reinterpret_cast<const QT3DSU8 *>(
+ meshData.m_indexBuffer.constData())),
+ QT3DSU32(meshData.m_indexBuffer.size())),
+ indexBufferComponentType);
+ vertexCount = meshData.m_indexBuffer.size() / indexBufferTypeSize;
+ } else {
+ vertexCount = meshData.m_vertexBuffer.size() / meshData.m_stride;
+ }
+
+ m_meshBuilder->AddMeshSubset(Mesh::s_DefaultName, QT3DSU32(vertexCount));
+
+ return &m_meshBuilder->GetMesh();
+ }
};
///< @brief contructor
@@ -278,6 +368,9 @@ Q3DSViewerApp::~Q3DSViewerApp()
{
qDeleteAll(m_Impl.m_pendingEvents);
+ if (m_Impl.m_meshBuilder)
+ m_Impl.m_meshBuilder->Release();
+
delete m_Impl.m_AudioPlayer;
if (m_Impl.m_view) {
@@ -843,6 +936,32 @@ void Q3DSViewerApp::createMaterials(const QString &elementPath,
m_Impl.m_view->createMaterials(elementPath, materialDefinitions);
}
+void Q3DSViewerApp::createMeshes(const QHash<QString, Q3DSViewer::MeshData> &meshData)
+{
+ if (!m_Impl.m_view)
+ return;
+
+ QString error;
+ QHashIterator<QString, Q3DSViewer::MeshData> dataIter(meshData);
+ while (dataIter.hasNext()) {
+ dataIter.next();
+ // Mesh creation needs to be done one by one, as Mesh pointers are not valid
+ // after builder is reused
+ qt3dsimp::Mesh *mesh = m_Impl.buildMesh(dataIter.value(), error);
+ if (mesh) {
+ m_Impl.m_view->createMesh(dataIter.key(), mesh);
+ } else {
+ QString errorMsg = QObject::tr("Creating mesh '%1' failed: %2")
+ .arg(dataIter.key()).arg(error);
+ qWarning() << __FUNCTION__ << errorMsg;
+ SigMeshesCreated(meshData.keys(), error);
+ return;
+ }
+ }
+ m_Impl.m_meshBuilder->Reset();
+ SigMeshesCreated(meshData.keys(), error);
+}
+
Q3DSViewerApp &Q3DSViewerApp::Create(void *glContext, Q3DStudio::IAudioPlayer *inAudioPlayer,
QElapsedTimer *startupTimer)
{
diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.h b/src/Runtime/Source/viewer/Qt3DSViewerApp.h
index 8022246c..f20244f3 100644
--- a/src/Runtime/Source/viewer/Qt3DSViewerApp.h
+++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.h
@@ -95,6 +95,106 @@ struct ViewerShadeModes
};
};
+// Intermediate struct to transfer mesh data between studio3d module and runtime
+struct MeshData
+{
+ // All enums must match the ones defined by Q3DSGeometry class in studio3d module
+ enum PrimitiveType { // Must match also internal NVRenderDrawMode
+ UnknownType = 0,
+ Points,
+ LineStrip,
+ LineLoop,
+ Lines,
+ TriangleStrip,
+ TriangleFan,
+ Triangles, // Default primitive type
+ Patches
+ };
+
+ struct Attribute {
+ enum Semantic {
+ UnknownSemantic = 0,
+ IndexSemantic,
+ PositionSemantic, // attr_pos
+ NormalSemantic, // attr_norm
+ TexCoordSemantic, // attr_uv0
+ TangentSemantic, // attr_textan
+ BinormalSemantic // attr_binormal
+ };
+ enum ComponentType { // Must match also internal NVRenderComponentTypes
+ DefaultType = 0,
+ U8Type,
+ I8Type,
+ U16Type,
+ I16Type,
+ U32Type, // Default for IndexSemantic
+ I32Type,
+ U64Type,
+ I64Type,
+ F16Type,
+ F32Type, // Default for other semantics
+ F64Type
+ };
+
+ int typeSize() const
+ {
+ switch (componentType) {
+ case U8Type: return 1;
+ case I8Type: return 1;
+ case U16Type: return 2;
+ case I16Type: return 2;
+ case U32Type: return 4;
+ case I32Type: return 4;
+ case U64Type: return 8;
+ case I64Type: return 8;
+ case F16Type: return 2;
+ case F32Type: return 4;
+ case F64Type: return 8;
+ default:
+ Q_ASSERT(false);
+ return 0;
+ }
+ }
+
+ int componentCount() const
+ {
+ switch (semantic) {
+ case IndexSemantic: return 1;
+ case PositionSemantic: return 3;
+ case NormalSemantic: return 3;
+ case TexCoordSemantic: return 2;
+ case TangentSemantic: return 3;
+ case BinormalSemantic: return 3;
+ default:
+ Q_ASSERT(false);
+ return 0;
+ }
+ }
+
+ Semantic semantic = PositionSemantic;
+ ComponentType componentType = F32Type;
+ int offset = 0;
+ };
+
+ static const int MAX_ATTRIBUTES = 6;
+
+ void clear()
+ {
+ m_vertexBuffer.clear();
+ m_indexBuffer.clear();
+ m_attributeCount = 0;
+ m_primitiveType = Triangles;
+ }
+
+ QByteArray m_vertexBuffer;
+ QByteArray m_indexBuffer;
+
+ Attribute m_attributes[MAX_ATTRIBUTES];
+ int m_attributeCount = 0;
+ PrimitiveType m_primitiveType = Triangles;
+ int m_stride = 0;
+};
+
class Q3DSViewerAppImpl;
class QT3DS_RUNTIME_API Q3DSViewerApp : public QObject
{
@@ -366,6 +466,7 @@ public:
const QVector<QHash<QString, QVariant>> &properties);
void deleteElements(const QStringList &elementPaths);
void createMaterials(const QString &elementPath, const QStringList &materialDefinitions);
+ void createMeshes(const QHash<QString, Q3DSViewer::MeshData> &meshData);
QString error();
@@ -410,6 +511,7 @@ Q_SIGNALS:
void SigCustomSignal(const QString &elementPath, const QString &name);
void SigElementsCreated(const QStringList &elementPaths, const QString &error);
void SigMaterialsCreated(const QStringList &materialNames, const QString &error);
+ void SigMeshesCreated(const QStringList &meshNames, const QString &error);
void SigDataOutputValueUpdated(const QString &name, const QVariant &newValue);
void SigPresentationReady();
void SigPresentationLoaded();
diff --git a/src/Runtime/api/studio3d/q3dscommandqueue.cpp b/src/Runtime/api/studio3d/q3dscommandqueue.cpp
index 01f15125..e7a02073 100644
--- a/src/Runtime/api/studio3d/q3dscommandqueue.cpp
+++ b/src/Runtime/api/studio3d/q3dscommandqueue.cpp
@@ -29,6 +29,7 @@
#include "q3dscommandqueue_p.h"
#include "q3dspresentation.h"
+#include "Qt3DSViewerApp.h"
#include <QtCore/qstringlist.h>
@@ -267,6 +268,7 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue)
fromQueue.commandAt(i).m_data = nullptr; // This queue takes ownership of data
break;
case CommandType_DeleteElements:
+ case CommandType_CreateMeshes:
queueCommand(source.m_commandType, source.m_data);
fromQueue.commandAt(i).m_data = nullptr; // This queue takes ownership of data
break;
@@ -310,11 +312,13 @@ void CommandQueue::clear(bool deleteCommandData)
delete static_cast<QVector<QHash<QString, QVariant>> *>(cmd.m_data);
break;
case CommandType_DeleteElements:
- delete static_cast<QStringList *>(cmd.m_data);
- break;
case CommandType_CreateMaterials:
delete static_cast<QStringList *>(cmd.m_data);
break;
+ case CommandType_CreateMeshes: {
+ delete static_cast<QHash<QString, Q3DSViewer::MeshData> *>(cmd.m_data);
+ break;
+ }
default:
Q_ASSERT(false); // Should never come here
break;
diff --git a/src/Runtime/api/studio3d/q3dscommandqueue_p.h b/src/Runtime/api/studio3d/q3dscommandqueue_p.h
index 72a88750..b27f5ae7 100644
--- a/src/Runtime/api/studio3d/q3dscommandqueue_p.h
+++ b/src/Runtime/api/studio3d/q3dscommandqueue_p.h
@@ -71,6 +71,7 @@ enum CommandType {
CommandType_CreateElements,
CommandType_DeleteElements,
CommandType_CreateMaterials,
+ CommandType_CreateMeshes,
// Requests
CommandType_RequestSlideInfo,
diff --git a/src/Runtime/api/studio3d/q3dsgeometry.cpp b/src/Runtime/api/studio3d/q3dsgeometry.cpp
new file mode 100644
index 00000000..bfc66a96
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsgeometry.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dsgeometry_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q3DSGeometry::Q3DSGeometry()
+ : d_ptr(new Q3DSGeometryPrivate(this))
+{
+}
+
+Q3DSGeometry::~Q3DSGeometry()
+{
+ delete d_ptr;
+}
+
+void Q3DSGeometry::setVertexData(const QByteArray &data)
+{
+ d_ptr->m_meshData.m_vertexBuffer = data;
+}
+
+void Q3DSGeometry::setIndexData(const QByteArray &data)
+{
+ d_ptr->m_meshData.m_indexBuffer = data;
+}
+
+const QByteArray &Q3DSGeometry::vertexBuffer() const
+{
+ return d_ptr->m_meshData.m_vertexBuffer;
+}
+
+QByteArray &Q3DSGeometry::vertexBuffer()
+{
+ return d_ptr->m_meshData.m_vertexBuffer;
+}
+
+const QByteArray &Q3DSGeometry::indexBuffer() const
+{
+ return d_ptr->m_meshData.m_indexBuffer;
+}
+
+QByteArray &Q3DSGeometry::indexBuffer()
+{
+ return d_ptr->m_meshData.m_indexBuffer;
+}
+
+int Q3DSGeometry::attributeCount() const
+{
+ return d_ptr->m_meshData.m_attributeCount;
+}
+
+void Q3DSGeometry::addAttribute(Q3DSGeometry::Attribute::Semantic semantic,
+ Q3DSGeometry::Attribute::ComponentType componentType)
+{
+ Q_ASSERT(d_ptr->m_meshData.m_attributeCount < Q3DSViewer::MeshData::MAX_ATTRIBUTES);
+
+ Q3DSViewer::MeshData::Attribute &att
+ = d_ptr->m_meshData.m_attributes[d_ptr->m_meshData.m_attributeCount];
+
+ Q3DSGeometry::Attribute::ComponentType theCompType = componentType;
+ if (theCompType == Attribute::DefaultType) {
+ theCompType = semantic == Attribute::IndexSemantic ? Attribute::U32Type
+ : Attribute::F32Type;
+ }
+ att.semantic = static_cast<Q3DSViewer::MeshData::Attribute::Semantic>(semantic);
+ att.componentType = static_cast<Q3DSViewer::MeshData::Attribute::ComponentType>(theCompType);
+ if (semantic != Q3DSGeometry::Attribute::IndexSemantic)
+ att.offset = d_ptr->getNextAttributeOffset();
+
+ d_ptr->m_meshData.m_attributeCount++;
+ d_ptr->m_meshData.m_stride = d_ptr->getNextAttributeOffset();
+}
+
+void Q3DSGeometry::addAttribute(const Q3DSGeometry::Attribute &att)
+{
+ addAttribute(att.semantic, att.componentType);
+}
+
+Q3DSGeometry::Attribute Q3DSGeometry::attribute(int idx) const
+{
+ Attribute att;
+ att.semantic = static_cast<Q3DSGeometry::Attribute::Semantic>(
+ d_ptr->m_meshData.m_attributes[idx].semantic);
+ att.componentType = static_cast<Q3DSGeometry::Attribute::ComponentType>(
+ d_ptr->m_meshData.m_attributes[idx].componentType);
+ return att;
+}
+
+Q3DSGeometry::PrimitiveType Q3DSGeometry::primitiveType() const
+{
+ return static_cast<Q3DSGeometry::PrimitiveType>(d_ptr->m_meshData.m_primitiveType);
+}
+
+void Q3DSGeometry::setPrimitiveType(Q3DSGeometry::PrimitiveType type)
+{
+ d_ptr->m_meshData.m_primitiveType = static_cast<Q3DSViewer::MeshData::PrimitiveType>(type);
+}
+
+void Q3DSGeometry::clear()
+{
+ d_ptr->m_meshData.clear();
+}
+
+Q3DSGeometryPrivate::Q3DSGeometryPrivate(Q3DSGeometry *parent)
+ : q_ptr(parent)
+{
+}
+
+Q3DSGeometryPrivate::~Q3DSGeometryPrivate()
+{
+}
+
+int Q3DSGeometryPrivate::getNextAttributeOffset() const
+{
+ int retval = 0;
+ for (int i = 0; i < m_meshData.m_attributeCount; ++i) {
+ if (m_meshData.m_attributes[i].semantic != Q3DSViewer::MeshData::Attribute::IndexSemantic) {
+ retval += m_meshData.m_attributes[i].typeSize()
+ * m_meshData.m_attributes[i].componentCount();
+ }
+ }
+ return retval;
+}
+
+QT_END_NAMESPACE
diff --git a/src/Runtime/api/studio3d/q3dsgeometry.h b/src/Runtime/api/studio3d/q3dsgeometry.h
new file mode 100644
index 00000000..b9b66e98
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsgeometry.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSGEOMETRY_H
+#define Q3DSGEOMETRY_H
+
+#include <QtStudio3D/qstudio3dglobal.h>
+#include <QtCore/qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSGeometryPrivate;
+
+class Q_STUDIO3D_EXPORT Q3DSGeometry
+{
+public:
+ Q_DECLARE_PRIVATE(Q3DSGeometry)
+
+ // All enums must match the ones defined by MeshData::Attribute struct in Qt3DSViewerApp.h
+ enum PrimitiveType {
+ UnknownType = 0,
+ Points,
+ LineStrip,
+ LineLoop,
+ Lines,
+ TriangleStrip,
+ TriangleFan,
+ Triangles, // Default primitive type
+ Patches
+ };
+
+ struct Attribute {
+ enum Semantic {
+ UnknownSemantic = 0,
+ IndexSemantic,
+ PositionSemantic, // attr_pos
+ NormalSemantic, // attr_norm
+ TexCoordSemantic, // attr_uv0
+ TangentSemantic, // attr_textan
+ BinormalSemantic // attr_binormal
+ };
+ enum ComponentType {
+ DefaultType = 0,
+ U8Type,
+ I8Type,
+ U16Type,
+ I16Type,
+ U32Type, // Default for IndexSemantic
+ I32Type,
+ U64Type,
+ I64Type,
+ F16Type,
+ F32Type, // Default for other semantics
+ F64Type
+ };
+ Semantic semantic = PositionSemantic;
+ ComponentType componentType = DefaultType;
+ };
+
+ explicit Q3DSGeometry();
+ virtual ~Q3DSGeometry();
+
+ void setVertexData(const QByteArray &data);
+ void setIndexData(const QByteArray &data);
+
+ const QByteArray &vertexBuffer() const;
+ QByteArray &vertexBuffer();
+ const QByteArray &indexBuffer() const;
+ QByteArray &indexBuffer();
+
+ int attributeCount() const;
+ void addAttribute(Attribute::Semantic semantic,
+ Attribute::ComponentType componentType = Attribute::DefaultType);
+ void addAttribute(const Attribute &att);
+ Attribute attribute(int idx) const;
+
+ PrimitiveType primitiveType() const;
+ void setPrimitiveType(PrimitiveType type);
+
+ void clear();
+
+protected:
+ Q_DISABLE_COPY(Q3DSGeometry)
+ Q3DSGeometryPrivate *d_ptr;
+
+ friend class Q3DSPresentation;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSGEOMETRY_H
diff --git a/src/Runtime/api/studio3d/q3dsgeometry_p.h b/src/Runtime/api/studio3d/q3dsgeometry_p.h
new file mode 100644
index 00000000..586b0d7b
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsgeometry_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSGEOMETRY_P_H
+#define Q3DSGEOMETRY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtStudio3D API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "q3dsgeometry.h"
+#include "Qt3DSViewerApp.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_STUDIO3D_EXPORT Q3DSGeometryPrivate
+{
+ Q_DECLARE_PUBLIC(Q3DSGeometry)
+public:
+ explicit Q3DSGeometryPrivate(Q3DSGeometry *parent);
+ virtual ~Q3DSGeometryPrivate();
+
+ Q3DSViewer::MeshData &meshData() { return m_meshData; }
+
+private:
+ int getNextAttributeOffset() const;
+
+ Q3DSGeometry *q_ptr;
+ Q3DSViewer::MeshData m_meshData;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSGEOMETRY_P_H
diff --git a/src/Runtime/api/studio3d/q3dspresentation.cpp b/src/Runtime/api/studio3d/q3dspresentation.cpp
index bc7c7e2b..0633050e 100644
--- a/src/Runtime/api/studio3d/q3dspresentation.cpp
+++ b/src/Runtime/api/studio3d/q3dspresentation.cpp
@@ -33,6 +33,7 @@
#include "viewerqmlstreamproxy_p.h"
#include "q3dsdatainput_p.h"
#include "q3dsdataoutput_p.h"
+#include "q3dsgeometry_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qsettings.h>
@@ -388,6 +389,37 @@ void Q3DSPresentation::createMaterials(const QString &elementPath,
}
}
+/**
+ Creates a mesh specified by given geometry. The given meshName can be used as sourcepath
+ property value for model elements created with future createElement calls.
+*/
+void Q3DSPresentation::createMesh(const QString &meshName, const Q3DSGeometry &geometry)
+{
+ QHash<QString, const Q3DSGeometry *> meshData;
+ meshData.insert(meshName, &geometry);
+ createMeshes(meshData);
+}
+
+// The ownership of supplied geometries stays with the caller
+void Q3DSPresentation::createMeshes(const QHash<QString, const Q3DSGeometry *> &meshData)
+{
+ // We can't refer to API class Q3DSGeometry on the runtime side, so let's grab the meshdata
+ // from Q3DSGeometryPrivate that is in runtime approved format and pass that on instead
+ auto theMeshData = new QHash<QString, Q3DSViewer::MeshData>;
+ QHashIterator<QString, const Q3DSGeometry *> it(meshData);
+ while (it.hasNext()) {
+ it.next();
+ theMeshData->insert(it.key(), it.value()->d_ptr->meshData());
+ }
+
+ if (d_ptr->m_viewerApp) {
+ d_ptr->m_viewerApp->createMeshes(*theMeshData);
+ delete theMeshData;
+ } else if (d_ptr->m_commandQueue) {
+ d_ptr->m_commandQueue->queueCommand(CommandType_CreateMeshes, theMeshData);
+ }
+}
+
void Q3DSPresentation::mousePressEvent(QMouseEvent *e)
{
if (d_ptr->m_viewerApp) {
@@ -537,6 +569,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool
q_ptr, &Q3DSPresentation::elementsCreated);
connect(app, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
q_ptr, &Q3DSPresentation::materialsCreated);
+ connect(app, &Q3DSViewer::Q3DSViewerApp::SigMeshesCreated,
+ q_ptr, &Q3DSPresentation::meshesCreated);
}
if (oldApp) {
disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered,
@@ -551,6 +585,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool
q_ptr, &Q3DSPresentation::elementsCreated);
disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
q_ptr, &Q3DSPresentation::materialsCreated);
+ disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMeshesCreated,
+ q_ptr, &Q3DSPresentation::meshesCreated);
}
}
}
@@ -621,6 +657,7 @@ void Q3DSPresentationPrivate::requestResponseHandler(CommandType commandType, vo
break;
}
}
+
// Doc note: The ownership of the registered scenes remains with the caller, who needs to
// ensure that registered scenes are alive as long as the presentation is alive.
void Q3DSPresentationPrivate::registerElement(Q3DSElement *element)
diff --git a/src/Runtime/api/studio3d/q3dspresentation.h b/src/Runtime/api/studio3d/q3dspresentation.h
index 6aa3b71f..4b02ffe5 100644
--- a/src/Runtime/api/studio3d/q3dspresentation.h
+++ b/src/Runtime/api/studio3d/q3dspresentation.h
@@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE
class Q3DSPresentationPrivate;
class Q3DSElement;
+class Q3DSGeometry;
class QMouseEvent;
class QWheelEvent;
class QKeyEvent;
@@ -100,6 +101,8 @@ public:
void deleteElements(const QStringList &elementPaths);
void createMaterial(const QString &elementPath, const QString &materialDefinition);
void createMaterials(const QString &elementPath, const QStringList &materialDefinitions);
+ void createMesh(const QString &meshName, const Q3DSGeometry &geometry);
+ void createMeshes(const QHash<QString, const Q3DSGeometry *> &meshData);
public Q_SLOTS:
void setSource(const QUrl &source);
@@ -130,6 +133,7 @@ Q_SIGNALS:
void delayedLoadingChanged(bool enable);
void elementsCreated(const QStringList &elementPaths, const QString &error);
void materialsCreated(const QStringList &materialNames, const QString &error);
+ void meshesCreated(const QStringList &meshNames, const QString &error);
private:
Q_DISABLE_COPY(Q3DSPresentation)
diff --git a/src/Runtime/api/studio3d/studio3d.pro b/src/Runtime/api/studio3d/studio3d.pro
index 0cc066b5..e2592426 100644
--- a/src/Runtime/api/studio3d/studio3d.pro
+++ b/src/Runtime/api/studio3d/studio3d.pro
@@ -35,7 +35,9 @@ HEADERS += \
q3dsimagesequencegenerator_p.h \
q3dsimagesequencegeneratorthread_p.h \
q3dsdatainput.h \
- q3dsdatainput_p.h
+ q3dsdatainput_p.h \
+ q3dsgeometry.h \
+ q3dsgeometry_p.h
SOURCES += q3dswidget.cpp \
q3dsdataoutput.cpp \
@@ -49,7 +51,8 @@ SOURCES += q3dswidget.cpp \
q3dscommandqueue.cpp \
q3dsimagesequencegenerator.cpp \
q3dsimagesequencegeneratorthread.cpp \
- q3dsdatainput.cpp
+ q3dsdatainput.cpp \
+ q3dsgeometry.cpp
load(qt_module)
diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
index 2ebe88f9..9ecf5c33 100644
--- a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
+++ b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
@@ -215,6 +215,8 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
this, &Q3DSRenderer::elementsCreated);
connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
this, &Q3DSRenderer::materialsCreated);
+ connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMeshesCreated,
+ this, &Q3DSRenderer::meshesCreated);
connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigDataOutputValueUpdated,
this, &Q3DSRenderer::dataOutputValueUpdated);
@@ -365,6 +367,17 @@ void Q3DSRenderer::processCommands()
command.m_data = nullptr;
break;
}
+ case CommandType_CreateMeshes: {
+ m_runtime->createMeshes(*static_cast<QHash<QString, Q3DSViewer::MeshData> *>(
+ cmd.m_data));
+ // Runtime makes copy of the data, so we can delete it now
+ auto &command = m_commands.commandAt(i);
+ auto meshData = reinterpret_cast<QHash<QString, Q3DSViewer::MeshData> *>(
+ command.m_data);
+ delete meshData;
+ command.m_data = nullptr;
+ break;
+ }
case CommandType_RequestSlideInfo: {
int current = 0;
int previous = 0;
diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.h b/src/Runtime/api/studio3dqml/q3dsrenderer.h
index 333edcda..230980ba 100644
--- a/src/Runtime/api/studio3dqml/q3dsrenderer.h
+++ b/src/Runtime/api/studio3dqml/q3dsrenderer.h
@@ -65,6 +65,7 @@ Q_SIGNALS:
void customSignalEmitted(const QString &elNmentPath, const QString &name);
void elementsCreated(const QStringList &elementPaths, const QString &error);
void materialsCreated(const QStringList &materialNames, const QString &error);
+ void meshesCreated(const QStringList &meshNames, const QString &error);
void dataOutputValueUpdated(const QString &name, const QVariant &newValue);
protected:
diff --git a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
index 769861fe..5770e176 100644
--- a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
+++ b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
@@ -223,6 +223,8 @@ QQuickFramebufferObject::Renderer *Q3DSStudio3D::createRenderer() const
m_presentation, &Q3DSPresentation::elementsCreated);
connect(renderer, &Q3DSRenderer::materialsCreated,
m_presentation, &Q3DSPresentation::materialsCreated);
+ connect(renderer, &Q3DSRenderer::meshesCreated,
+ m_presentation, &Q3DSPresentation::meshesCreated);
connect(renderer, &Q3DSRenderer::requestResponse,
this, &Q3DSStudio3D::requestResponseHandler);
connect(renderer, &Q3DSRenderer::presentationLoaded,
diff --git a/tests/auto/viewer/tst_qt3dsviewer.cpp b/tests/auto/viewer/tst_qt3dsviewer.cpp
index a20657af..4f4b58de 100644
--- a/tests/auto/viewer/tst_qt3dsviewer.cpp
+++ b/tests/auto/viewer/tst_qt3dsviewer.cpp
@@ -33,6 +33,7 @@
#include <QtStudio3D/Q3DSPresentation>
#include <QtStudio3D/Q3DSElement>
#include <QtStudio3D/Q3DSViewerSettings>
+#include <QtStudio3D/Q3DSGeometry>
#include <QtCore/QRandomGenerator>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
@@ -487,6 +488,79 @@ void tst_qt3dsviewer::testCreateMaterial()
QTest::qWait(200); // Extra wait to verify slide change visually
}
+void tst_qt3dsviewer::testCreateMesh()
+{
+ m_viewer->show();
+
+ m_settings->setShowRenderStats(true);
+ m_settings->setScaleMode(Q3DSViewerSettings::ScaleModeFill);
+
+ QSignalSpy spyExited(m_presentation,
+ SIGNAL(slideExited(const QString &, unsigned int, const QString &)));
+ QSignalSpy spyMeshCreated(m_presentation, SIGNAL(meshesCreated(const QStringList &,
+ const QString &)));
+ QSignalSpy spyElemCreated(m_presentation, SIGNAL(elementsCreated(const QStringList &,
+ const QString &)));
+ Q3DSGeometry pyramid;
+ Q3DSGeometry star;
+ createGeometries(pyramid, star);
+
+ Q3DSElement pyramidElem(m_presentation, QStringLiteral("Scene.Layer.Pyramid"));
+ Q3DSElement starElem(m_presentation, QStringLiteral("Scene.Layer.Star"));
+
+ int animValue = 0;
+ QTimer animationTimer;
+ animationTimer.setInterval(10);
+ QObject::connect(&animationTimer, &QTimer::timeout, [&]() {
+ animValue++;
+ pyramidElem.setAttribute(QStringLiteral("rotation.x"), animValue * 2);
+ pyramidElem.setAttribute(QStringLiteral("rotation.y"), animValue);
+ starElem.setAttribute(QStringLiteral("rotation.x"), -animValue * 2);
+ starElem.setAttribute(QStringLiteral("rotation.y"), -animValue);
+ });
+
+ m_presentation->createMaterial(
+ QStringLiteral("Scene"),
+ QStringLiteral(":/scenes/simple_cube_animation/materials/Basic Texture.materialdef"));
+ m_presentation->createMesh(QStringLiteral("Pyramid"), pyramid);
+
+ QObject::connect(m_presentation, &Q3DSPresentation::meshesCreated,
+ [&](const QStringList &meshNames, const QString &error) {
+ QVERIFY(error.isEmpty());
+ for (auto &name : meshNames) {
+ QHash<QString, QVariant> data;
+ if (name == QLatin1String("Pyramid")) {
+ data.insert(QStringLiteral("name"), QStringLiteral("Pyramid"));
+ data.insert(QStringLiteral("sourcepath"), QStringLiteral("Pyramid"));
+ data.insert(QStringLiteral("material"), QStringLiteral("Basic Texture"));
+ data.insert(QStringLiteral("position"),
+ QVariant::fromValue<QVector3D>(QVector3D(100, 150, 500)));
+ createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data);
+ animationTimer.start();
+ } else if (name == QLatin1String("Star")) {
+ data.insert(QStringLiteral("name"), QStringLiteral("Star"));
+ data.insert(QStringLiteral("sourcepath"), QStringLiteral("Star"));
+ data.insert(QStringLiteral("material"), QStringLiteral("Basic Texture"));
+ data.insert(QStringLiteral("position"),
+ QVariant::fromValue<QVector3D>(QVector3D(100, -150, 500)));
+ createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data);
+ } else {
+ QVERIFY(false);
+ }
+ }
+ });
+
+ // Create mesh after start
+ QTimer::singleShot(1000, [&]() {
+ m_presentation->createMesh(QStringLiteral("Star"), star);
+ });
+
+ QVERIFY(spyExited.wait(20000));
+ QCOMPARE(spyMeshCreated.count(), 2);
+ QCOMPARE(spyElemCreated.count(), 2);
+ QTest::qWait(200); // Extra wait to verify slide change visually
+}
+
void tst_qt3dsviewer::deleteCreatedElements()
{
m_presentation->deleteElements(m_createdElements);
@@ -501,4 +575,105 @@ void tst_qt3dsviewer::createElement(const QString &parentElementPath, const QStr
m_presentation->createElement(parentElementPath, slideName, properties);
}
+void tst_qt3dsviewer::createGeometries(Q3DSGeometry &pyramid, Q3DSGeometry &star)
+{
+ struct Vertex {
+ QVector3D position;
+ QVector3D normal;
+ QVector2D uv;
+ };
+
+ QVector<Vertex> vertices;
+
+ auto createVertex = [&](const QVector3D &xyz, const QVector3D &n, const QVector2D &uv) {
+ Vertex newVertex;
+ newVertex.position = xyz;
+ if (n.isNull())
+ newVertex.normal = xyz; // This is almost never the correct normal
+ else
+ newVertex.normal = n.normalized();
+ newVertex.uv = uv;
+ vertices.append(newVertex);
+ };
+
+ auto createTriangle = [&](const QVector3D &xyz1, const QVector2D &uv1,
+ const QVector3D &xyz2, const QVector2D &uv2,
+ const QVector3D &xyz3, const QVector2D &uv3) {
+ QVector3D n;
+ n = QVector3D::crossProduct(xyz2 - xyz1, xyz3 - xyz1).normalized();
+
+ createVertex(xyz1, n, uv1);
+ createVertex(xyz2, n, uv2);
+ createVertex(xyz3, n, uv3);
+ };
+
+ // Pyramid (no index buffer)
+ {
+ QVector3D xyz[5] = {{0, 0, 50}, {50, 50, -50}, {50, -50, -50}, {-50, -50, -50},
+ {-50, 50, -50}};
+ QVector2D uv[4] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
+ createTriangle(xyz[0], uv[0], xyz[1], uv[1], xyz[2], uv[2]);
+ createTriangle(xyz[0], uv[0], xyz[2], uv[1], xyz[3], uv[2]);
+ createTriangle(xyz[0], uv[0], xyz[3], uv[1], xyz[4], uv[2]);
+ createTriangle(xyz[0], uv[0], xyz[4], uv[1], xyz[1], uv[2]);
+ createTriangle(xyz[1], uv[0], xyz[4], uv[2], xyz[3], uv[1]);
+ createTriangle(xyz[1], uv[0], xyz[3], uv[3], xyz[2], uv[2]);
+
+ QByteArray vertexBuffer(reinterpret_cast<const char *>(vertices.constData()),
+ vertices.size() * int(sizeof(Vertex)));
+
+ pyramid.clear();
+ pyramid.setVertexData(vertexBuffer);
+ pyramid.addAttribute(Q3DSGeometry::Attribute::PositionSemantic);
+ pyramid.addAttribute(Q3DSGeometry::Attribute::NormalSemantic);
+ pyramid.addAttribute(Q3DSGeometry::Attribute::TexCoordSemantic);
+ }
+
+ vertices.clear();
+
+ // Star (using index buffer)
+ {
+ // Note: Since faces share vertices, the normals on the vertices are not correct
+ // for any face, leading to weird lighting behavior
+ createVertex({0, 150, 0}, {}, {0.5f, 1});
+ createVertex({50, 50, -50}, {}, {0.66f, 0.66f});
+ createVertex({150, 0, 0}, {}, {1, 0.5f});
+ createVertex({50, -50, -50}, {}, {0.66f, 0.33f});
+ createVertex({0, -150, 0}, {}, {0.5f, 0});
+ createVertex({-50, -50, -50}, {}, {0.33f, 0.33f});
+ createVertex({-150, 0, 0}, {}, {0, 0.5f});
+ createVertex({-50, 50, -50}, {}, {0.33f, 0.66f});
+ createVertex({50, 50, 50}, {}, {0.66f, 0.66f});
+ createVertex({50, -50, 50}, {}, {0.66f, 0.33f});
+ createVertex({-50, -50, 50}, {}, {0.33f, 0.33f});
+ createVertex({-50, 50, 50}, {}, {0.33f, 0.66f});
+
+ QVector<quint16> indices = {
+ 0, 1, 8, 0, 7, 1, 0, 11, 7, 0, 8, 11, // Top pyramid
+ 2, 1, 3, 2, 3, 9, 2, 9, 8, 2, 8, 1, // Right pyramid
+ 4, 3, 5, 4, 5, 10, 4, 10, 9, 4, 9, 3, // Bottom pyramid
+ 6, 5, 7, 6, 7, 11, 6, 11, 10, 6, 10, 5, // Left pyramid
+ 1, 7, 5, 1, 5, 3, // Front center rect
+ 8, 10, 11, 8, 9, 10 // Back center rect
+ };
+
+ QByteArray vertexBuffer(reinterpret_cast<const char *>(vertices.constData()),
+ vertices.size() * int(sizeof(Vertex)));
+ QByteArray indexBuffer(reinterpret_cast<const char *>(indices.constData()),
+ indices.size() * int(sizeof(quint16)));
+
+ Q3DSGeometry::Attribute indexAtt;
+ indexAtt.semantic = Q3DSGeometry::Attribute::IndexSemantic;
+ indexAtt.componentType = Q3DSGeometry::Attribute::ComponentType::U16Type;
+
+ star.clear();
+ star.setVertexData(vertexBuffer);
+ star.setIndexData(indexBuffer);
+ star.addAttribute(Q3DSGeometry::Attribute::PositionSemantic);
+ star.addAttribute(Q3DSGeometry::Attribute::NormalSemantic);
+ star.addAttribute(Q3DSGeometry::Attribute::TexCoordSemantic);
+ star.addAttribute(indexAtt);
+ }
+}
+
QTEST_MAIN(tst_qt3dsviewer)
diff --git a/tests/auto/viewer/tst_qt3dsviewer.h b/tests/auto/viewer/tst_qt3dsviewer.h
index f6281881..4cb8063c 100644
--- a/tests/auto/viewer/tst_qt3dsviewer.h
+++ b/tests/auto/viewer/tst_qt3dsviewer.h
@@ -57,11 +57,13 @@ private Q_SLOTS:
void testSettings();
void testCreateElement();
void testCreateMaterial();
+ void testCreateMesh();
private:
void deleteCreatedElements();
void createElement(const QString &parentElementPath, const QString &slideName,
const QHash<QString, QVariant> &properties);
+ void createGeometries(Q3DSGeometry &pyramid, Q3DSGeometry &star);
QQuickView *m_viewer = nullptr;
QObject *m_studio3DItem = nullptr;
diff --git a/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip b/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip
index 7578fe88..fc273121 100644
--- a/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip
+++ b/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip
@@ -8,7 +8,11 @@
<Scene id="Scene" >
<Layer id="Layer" variants="" >
<Camera id="Camera" />
- <Light id="Light" />
+ <Light id="Light" >
+ <Model id="Sphere" variants="" >
+ <Material id="Default_animatable" />
+ </Model>
+ </Light>
<Model id="Cube" variants="" >
<Material id="Basic Green_animatable" />
</Model>
@@ -22,7 +26,7 @@
<State name="Master Slide" component="#Scene" >
<Add ref="#Layer" background="SolidColor" backgroundcolor="0.584314 0.0941176 0.34902 1" />
<Add ref="#Camera" />
- <Add ref="#Light" lighttype="Point" >
+ <Add ref="#Light" brightness="400" lighttype="Point" >
<AnimationTrack property="position.x" type="EaseInOut" >0 -341.785 100 100 5 269.972 100 100</AnimationTrack>
<AnimationTrack property="position.y" type="EaseInOut" >0 0 100 100 5 0 100 100</AnimationTrack>
<AnimationTrack property="position.z" type="EaseInOut" >0 0 100 100 5 0 100 100</AnimationTrack>
@@ -31,6 +35,8 @@
<Set ref="#Layer" endtime="5000" />
<Set ref="#Camera" endtime="5000" />
<Set ref="#Light" endtime="5000" />
+ <Add ref="#Sphere" name="Sphere" endtime="5000" scale="0.1 0.1 0.1" sourcepath="#Sphere" />
+ <Add ref="#Default_animatable" name="Default_animatable" blendmode="Normal" bumpamount="0.5" diffuse="1 1 0 1" diffuselightwrap="0" displaceamount="20" emissivecolor="1 1 1 1" emissivepower="0" fresnelPower="0" importfile="" importid="" ior="1.5" opacity="100" shaderlighting="None" sourcepath="" specularamount="0" specularmodel="Default" specularroughness="0" speculartint="1 1 1 1" translucentfalloff="1" type="Material" vertexcolors="False" />
<Add ref="#Cube" name="Cube" endtime="5000" position="-458.877 -70.3589 600" sourcepath="#Cube" >
<AnimationTrack property="rotation.x" type="EaseInOut" >0 0 100 100 1 0 100 100 4 0 100 100 5 90 100 100 7 90 100 100 10 0 100 100</AnimationTrack>
<AnimationTrack property="rotation.y" type="EaseInOut" >0 0 100 100 1 0 100 100 4 90 100 100 5 0 100 100 7 0 100 100 10 0 100 100</AnimationTrack>