summaryrefslogtreecommitdiffstats
path: root/src/importlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/importlib')
-rw-r--r--src/importlib/Qt3DSImportContainers.h88
-rw-r--r--src/importlib/Qt3DSImportLibPrecompile.h56
-rw-r--r--src/importlib/Qt3DSImportMesh.cpp829
-rw-r--r--src/importlib/Qt3DSImportMesh.h530
-rw-r--r--src/importlib/Qt3DSImportMeshBuilder.cpp598
-rw-r--r--src/importlib/Qt3DSImportPath.cpp152
-rw-r--r--src/importlib/Qt3DSImportPath.h87
7 files changed, 2340 insertions, 0 deletions
diff --git a/src/importlib/Qt3DSImportContainers.h b/src/importlib/Qt3DSImportContainers.h
new file mode 100644
index 0000000..2f2a72e
--- /dev/null
+++ b/src/importlib/Qt3DSImportContainers.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_IMPORT_CONTAINERS_H
+#define QT3DS_IMPORT_CONTAINERS_H
+
+#include "EASTL/hash_map.h"
+#include "EASTL/hash_set.h"
+#include "EASTL/vector.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "Qt3DSDMWStrOps.h"
+
+namespace qt3dsimp {
+
+#define QT3DSIMP_FOREACH(idxnm, val) \
+ for (QT3DSU32 idxnm = 0, __numItems = (QT3DSU32)val; idxnm < __numItems; ++idxnm)
+
+template <typename TKey, typename TValue, typename THash = eastl::hash<TKey>,
+ typename TPredicate = eastl::equal_to<TKey>>
+struct ImportHashMap : public eastl::hash_map<TKey, TValue, THash, TPredicate>
+{
+ using base_type = eastl::hash_map<TKey, TValue, THash, TPredicate>;
+
+ ImportHashMap() {}
+
+
+
+ bool contains(const TKey &key) const { return find(key) != base_type::end(); }
+};
+template <typename TKey>
+struct ImportHashSet : public eastl::hash_set<TKey, eastl::hash<TKey>, eastl::equal_to<TKey>>
+{
+ using base_type = eastl::hash_set<TKey, eastl::hash<TKey>, eastl::equal_to<TKey>>;
+ ImportHashSet() {}
+ bool contains(const TKey &key) const { return base_type::find(key) != base_type::end(); }
+};
+template <typename TValue>
+struct ImportArray : public eastl::vector<TValue>
+{
+ using base_type = eastl::vector<TValue>;
+
+ ImportArray() {}
+ operator NVConstDataRef<TValue>() const
+ {
+ return NVConstDataRef<TValue>(base_type::data(), (QT3DSU32) base_type::size());
+ }
+
+ operator NVDataRef<TValue>() {
+ return NVDataRef<TValue>(base_type::data(), (QT3DSU32) base_type::size());
+ }
+};
+
+inline NVConstDataRef<wchar_t> toRef(const wchar_t *data, QT3DSU32 len = 0)
+{
+ if (IsTrivial(data))
+ return NVConstDataRef<wchar_t>(L"", 0);
+ len = len ? len : (QT3DSU32)wcslen(data);
+ return NVConstDataRef<wchar_t>(data, len);
+}
+}
+
+#endif
diff --git a/src/importlib/Qt3DSImportLibPrecompile.h b/src/importlib/Qt3DSImportLibPrecompile.h
new file mode 100644
index 0000000..34da79d
--- /dev/null
+++ b/src/importlib/Qt3DSImportLibPrecompile.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "foundation/Qt3DS.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSOption.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include <map>
+#include <vector>
+#include <string>
+#include <exception>
+#include <set>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include "Qt3DSDMDataTypes.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "foundation/Qt3DSBasicTemplates.h"
+
+namespace qt3dsimp {
+using namespace qt3ds;
+using namespace qt3ds::foundation;
+using namespace qt3ds::render;
+using namespace qt3dsdm;
+using namespace std;
+}
+
+// TODO: reference additional headers your program requires here
diff --git a/src/importlib/Qt3DSImportMesh.cpp b/src/importlib/Qt3DSImportMesh.cpp
new file mode 100644
index 0000000..1535b91
--- /dev/null
+++ b/src/importlib/Qt3DSImportMesh.cpp
@@ -0,0 +1,829 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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$
+**
+****************************************************************************/
+#ifdef WIN32
+#pragma warning(disable : 4100)
+#endif
+#include "Qt3DSImportLibPrecompile.h"
+#include "Qt3DSImportMesh.h"
+
+using namespace qt3dsimp;
+
+#ifdef QT3DS_X86
+
+// Ensure our objects are of expected sizes. This keeps us honest
+// And ensures that we can load the datastructures we expect to by simply
+// mapping the memory.
+QT3DS_COMPILE_TIME_ASSERT(sizeof(NVRenderComponentTypes::Enum) == 4);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(NVRenderDrawMode::Enum) == 4);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(NVRenderVertexBufferEntry) == 20);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(VertexBuffer) == 20);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(IndexBuffer) == 12);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(MeshSubset) == 40);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(MeshDataHeader) == 12);
+QT3DS_COMPILE_TIME_ASSERT(sizeof(Mesh) == 56);
+
+#endif
+
+#define QT3DSIMP_FOREACH(idxnm, val) \
+ for (QT3DSU32 idxnm = 0, __numItems = (QT3DSU32)val; idxnm < __numItems; ++idxnm)
+
+namespace {
+
+struct MeshSubsetV1
+{
+ // See description of a logical vertex buffer below
+ QT3DSU32 m_LogicalVbufIndex;
+ // QT3DS_MAX_U32 means use all available items
+ QT3DSU32 m_Count;
+ // Offset is in item size, not bytes.
+ QT3DSU32 m_Offset;
+ // Bounds of this subset. This is filled in by the builder
+ // see AddMeshSubset
+ NVBounds3 m_Bounds;
+};
+
+struct LogicalVertexBuffer
+{
+ QT3DSU32 m_ByteOffset;
+ QT3DSU32 m_ByteSize;
+ LogicalVertexBuffer(QT3DSU32 byteOff, QT3DSU32 byteSize)
+ : m_ByteOffset(byteOff)
+ , m_ByteSize(byteSize)
+ {
+ }
+ LogicalVertexBuffer()
+ : m_ByteOffset(0)
+ , m_ByteSize(0)
+ {
+ }
+};
+
+struct MeshV1
+{
+ VertexBuffer m_VertexBuffer;
+ IndexBuffer m_IndexBuffer;
+ SOffsetDataRef<LogicalVertexBuffer> m_LogicalVertexBuffers; // may be empty
+ SOffsetDataRef<MeshSubsetV1> m_Subsets;
+ NVRenderDrawMode::Enum m_DrawMode;
+ NVRenderWinding::Enum m_Winding;
+ typedef MeshSubsetV1 TSubsetType;
+};
+
+template <typename TSerializer>
+void Serialize(TSerializer &serializer, MeshV1 &mesh)
+{
+ QT3DSU8 *baseAddr = reinterpret_cast<QT3DSU8 *>(&mesh);
+ serializer.streamify(mesh.m_VertexBuffer.m_Entries);
+ serializer.align();
+ QT3DSIMP_FOREACH(entry, mesh.m_VertexBuffer.m_Entries.size())
+ {
+ MeshVertexBufferEntry &entryData = const_cast<MeshVertexBufferEntry &>(
+ mesh.m_VertexBuffer.m_Entries.index(baseAddr, entry));
+ serializer.streamifyCharPointerOffset(entryData.m_NameOffset);
+ serializer.align();
+ }
+ serializer.streamify(mesh.m_VertexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_IndexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_LogicalVertexBuffers);
+ serializer.align();
+ serializer.streamify(mesh.m_Subsets);
+ serializer.align();
+}
+
+struct MeshSubsetV2
+{
+ QT3DSU32 m_LogicalVbufIndex;
+ QT3DSU32 m_Count;
+ QT3DSU32 m_Offset;
+ NVBounds3 m_Bounds;
+ SOffsetDataRef<char16_t> m_Name;
+};
+
+struct MeshV2
+{
+ static const char16_t *s_DefaultName;
+
+ VertexBuffer m_VertexBuffer;
+ IndexBuffer m_IndexBuffer;
+ SOffsetDataRef<LogicalVertexBuffer> m_LogicalVertexBuffers; // may be empty
+ SOffsetDataRef<MeshSubsetV2> m_Subsets;
+ NVRenderDrawMode::Enum m_DrawMode;
+ NVRenderWinding::Enum m_Winding;
+ typedef MeshSubsetV2 TSubsetType;
+};
+
+template <typename TSerializer>
+void Serialize(TSerializer &serializer, MeshV2 &mesh)
+{
+ QT3DSU8 *baseAddr = reinterpret_cast<QT3DSU8 *>(&mesh);
+ serializer.streamify(mesh.m_VertexBuffer.m_Entries);
+ serializer.align();
+ QT3DSIMP_FOREACH(entry, mesh.m_VertexBuffer.m_Entries.size())
+ {
+ MeshVertexBufferEntry &entryData = const_cast<MeshVertexBufferEntry &>(
+ mesh.m_VertexBuffer.m_Entries.index(baseAddr, entry));
+ serializer.streamifyCharPointerOffset(entryData.m_NameOffset);
+ serializer.align();
+ }
+ serializer.streamify(mesh.m_VertexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_IndexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_LogicalVertexBuffers);
+ serializer.align();
+ serializer.streamify(mesh.m_Subsets);
+ serializer.align();
+ QT3DSIMP_FOREACH(entry, mesh.m_Subsets.size())
+ {
+ MeshSubsetV2 &theSubset = const_cast<MeshSubsetV2 &>(mesh.m_Subsets.index(baseAddr, entry));
+ serializer.streamify(theSubset.m_Name);
+ serializer.align();
+ }
+}
+
+// Localize the knowledge required to read/write a mesh into one function
+// written in such a way that you can both read and write by passing
+// in one serializer type or another.
+// This function needs to be careful to request alignment after every write of a
+// buffer that may leave us unaligned. The easiest way to be correct is to request
+// alignment a lot. The hardest way is to use knowledge of the datatypes and
+// only request alignment when necessary.
+template <typename TSerializer>
+void Serialize(TSerializer &serializer, Mesh &mesh)
+{
+ QT3DSU8 *baseAddr = reinterpret_cast<QT3DSU8 *>(&mesh);
+ serializer.streamify(mesh.m_VertexBuffer.m_Entries);
+ serializer.align();
+ QT3DSIMP_FOREACH(entry, mesh.m_VertexBuffer.m_Entries.size())
+ {
+ MeshVertexBufferEntry &entryData = mesh.m_VertexBuffer.m_Entries.index(baseAddr, entry);
+ serializer.streamifyCharPointerOffset(entryData.m_NameOffset);
+ serializer.align();
+ }
+ serializer.streamify(mesh.m_VertexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_IndexBuffer.m_Data);
+ serializer.align();
+ serializer.streamify(mesh.m_Subsets);
+ serializer.align();
+ QT3DSIMP_FOREACH(entry, mesh.m_Subsets.size())
+ {
+ MeshSubset &theSubset = const_cast<MeshSubset &>(mesh.m_Subsets.index(baseAddr, entry));
+ serializer.streamify(theSubset.m_Name);
+ serializer.align();
+ }
+ serializer.streamify(mesh.m_Joints);
+ serializer.align();
+}
+
+struct TotallingSerializer
+{
+ QT3DSU32 m_NumBytes;
+ QT3DSU8 *m_BaseAddress;
+ TotallingSerializer(QT3DSU8 *inBaseAddr)
+ : m_NumBytes(0)
+ , m_BaseAddress(inBaseAddr)
+ {
+ }
+ template <typename TDataType>
+ void streamify(const SOffsetDataRef<TDataType> &data)
+ {
+ m_NumBytes += data.size() * sizeof(TDataType);
+ }
+ void streamify(const char *data)
+ {
+ if (data == NULL)
+ data = "";
+ QT3DSU32 len = (QT3DSU32)strlen(data) + 1;
+ m_NumBytes += 4;
+ m_NumBytes += len;
+ }
+ void streamifyCharPointerOffset(QT3DSU32 inOffset)
+ {
+ if (inOffset) {
+ const char *dataPtr = (const char *)(inOffset + m_BaseAddress);
+ streamify(dataPtr);
+ } else
+ streamify("");
+ }
+ bool needsAlignment() const { return getAlignmentAmount() > 0; }
+ QT3DSU32 getAlignmentAmount() const { return 4 - (m_NumBytes % 4); }
+ void align()
+ {
+ if (needsAlignment())
+ m_NumBytes += getAlignmentAmount();
+ }
+};
+
+struct ByteWritingSerializer
+{
+ IOutStream &m_Stream;
+ TotallingSerializer m_ByteCounter;
+ QT3DSU8 *m_BaseAddress;
+ ByteWritingSerializer(IOutStream &str, QT3DSU8 *inBaseAddress)
+ : m_Stream(str)
+ , m_ByteCounter(inBaseAddress)
+ , m_BaseAddress(inBaseAddress)
+ {
+ }
+
+ template <typename TDataType>
+ void streamify(const SOffsetDataRef<TDataType> &data)
+ {
+ m_ByteCounter.streamify(data);
+ m_Stream.Write(data.begin(m_BaseAddress), data.size());
+ }
+ void streamify(const char *data)
+ {
+ m_ByteCounter.streamify(data);
+ if (data == NULL)
+ data = "";
+ QT3DSU32 len = (QT3DSU32)strlen(data) + 1;
+ m_Stream.Write(len);
+ m_Stream.Write(data, len);
+ }
+ void streamifyCharPointerOffset(QT3DSU32 inOffset)
+ {
+ const char *dataPtr = (const char *)(inOffset + m_BaseAddress);
+ streamify(dataPtr);
+ }
+
+ void align()
+ {
+ if (m_ByteCounter.needsAlignment()) {
+ QT3DSU8 buffer[] = { 0, 0, 0, 0 };
+ m_Stream.Write(buffer, m_ByteCounter.getAlignmentAmount());
+ m_ByteCounter.align();
+ }
+ }
+};
+
+struct MemoryAssigningSerializer
+{
+ QT3DSU8 *m_Memory;
+ QT3DSU8 *m_BaseAddress;
+ QT3DSU32 m_Size;
+ TotallingSerializer m_ByteCounter;
+ bool m_Failure;
+ MemoryAssigningSerializer(QT3DSU8 *data, QT3DSU32 size, QT3DSU32 startOffset)
+ : m_Memory(data + startOffset)
+ , m_BaseAddress(data)
+ , m_Size(size)
+ , m_ByteCounter(data)
+ , m_Failure(false)
+ {
+ // We expect 4 byte aligned memory to begin with
+ QT3DS_ASSERT((((size_t)m_Memory) % 4) == 0);
+ }
+
+ template <typename TDataType>
+ void streamify(const SOffsetDataRef<TDataType> &_data)
+ {
+ SOffsetDataRef<TDataType> &data = const_cast<SOffsetDataRef<TDataType> &>(_data);
+ if (m_Failure) {
+ data.m_Size = 0;
+ data.m_Offset = 0;
+ return;
+ }
+ QT3DSU32 current = m_ByteCounter.m_NumBytes;
+ m_ByteCounter.streamify(_data);
+ if (m_ByteCounter.m_NumBytes > m_Size) {
+ data.m_Size = 0;
+ data.m_Offset = 0;
+ m_Failure = true;
+ return;
+ }
+ QT3DSU32 numBytes = m_ByteCounter.m_NumBytes - current;
+ if (numBytes) {
+ data.m_Offset = (QT3DSU32)(m_Memory - m_BaseAddress);
+ updateMemoryBuffer(numBytes);
+ } else {
+ data.m_Offset = 0;
+ data.m_Size = 0;
+ }
+ }
+ void streamify(const char *&_data)
+ {
+ QT3DSU32 len;
+ m_ByteCounter.m_NumBytes += 4;
+ if (m_ByteCounter.m_NumBytes > m_Size) {
+ _data = "";
+ m_Failure = true;
+ return;
+ }
+ qt3ds::intrinsics::memCopy(&len, m_Memory, 4);
+ updateMemoryBuffer(4);
+ m_ByteCounter.m_NumBytes += len;
+ if (m_ByteCounter.m_NumBytes > m_Size) {
+ _data = "";
+ m_Failure = true;
+ return;
+ }
+ _data = (const char *)m_Memory;
+ updateMemoryBuffer(len);
+ }
+ void streamifyCharPointerOffset(QT3DSU32 &inOffset)
+ {
+ const char *dataPtr;
+ streamify(dataPtr);
+ inOffset = (QT3DSU32)(dataPtr - (const char *)m_BaseAddress);
+ }
+ void align()
+ {
+ if (m_ByteCounter.needsAlignment()) {
+ QT3DSU32 numBytes = m_ByteCounter.getAlignmentAmount();
+ m_ByteCounter.align();
+ updateMemoryBuffer(numBytes);
+ }
+ }
+ void updateMemoryBuffer(QT3DSU32 numBytes) { m_Memory += numBytes; }
+};
+
+inline QT3DSU32 GetMeshDataSize(Mesh &mesh)
+{
+ TotallingSerializer s(reinterpret_cast<QT3DSU8 *>(&mesh));
+ Serialize(s, mesh);
+ return s.m_NumBytes;
+}
+
+template <typename TDataType>
+QT3DSU32 NextIndex(const QT3DSU8 *inBaseAddress, const SOffsetDataRef<QT3DSU8> data, QT3DSU32 idx)
+{
+ QT3DSU32 numItems = data.size() / sizeof(TDataType);
+ if (idx < numItems) {
+ const TDataType *dataPtr(reinterpret_cast<const TDataType *>(data.begin(inBaseAddress)));
+ return dataPtr[idx];
+ } else {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+}
+
+template <typename TDataType>
+QT3DSU32 NextIndex(NVConstDataRef<QT3DSU8> data, QT3DSU32 idx)
+{
+ QT3DSU32 numItems = data.size() / sizeof(TDataType);
+ if (idx < numItems) {
+ const TDataType *dataPtr(reinterpret_cast<const TDataType *>(data.begin()));
+ return dataPtr[idx];
+ } else {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+}
+
+inline QT3DSU32 NextIndex(NVConstDataRef<QT3DSU8> inData,
+ qt3ds::render::NVRenderComponentTypes::Enum inCompType, QT3DSU32 idx)
+{
+ if (inData.size() == 0)
+ return idx;
+ switch (inCompType) {
+ case NVRenderComponentTypes::QT3DSU8:
+ return NextIndex<QT3DSU8>(inData, idx);
+ case NVRenderComponentTypes::QT3DSI8:
+ return NextIndex<QT3DSI8>(inData, idx);
+ case NVRenderComponentTypes::QT3DSU16:
+ return NextIndex<QT3DSU16>(inData, idx);
+ case NVRenderComponentTypes::QT3DSI16:
+ return NextIndex<QT3DSI16>(inData, idx);
+ case NVRenderComponentTypes::QT3DSU32:
+ return NextIndex<QT3DSU32>(inData, idx);
+ case NVRenderComponentTypes::QT3DSI32:
+ return NextIndex<QT3DSI32>(inData, idx);
+ default:
+ break;
+ }
+
+ // Invalid index buffer index type.
+ QT3DS_ASSERT(false);
+ return 0;
+}
+
+template <typename TMeshType>
+// Not exposed to the outside world
+TMeshType *DoInitialize(MeshBufHeaderFlags /*meshFlags*/, NVDataRef<QT3DSU8> data)
+{
+ QT3DSU8 *newMem = data.begin();
+ QT3DSU32 amountLeft = data.size() - sizeof(TMeshType);
+ MemoryAssigningSerializer s(newMem, amountLeft, sizeof(TMeshType));
+ TMeshType *retval = (TMeshType *)newMem;
+ Serialize(s, *retval);
+ if (s.m_Failure)
+ return NULL;
+ return retval;
+}
+}
+
+NVBounds3 Mesh::CalculateSubsetBounds(const NVRenderVertexBufferEntry &inEntry,
+ NVConstDataRef<QT3DSU8> inVertxData, QT3DSU32 inStride,
+ NVConstDataRef<QT3DSU8> inIndexData,
+ qt3ds::render::NVRenderComponentTypes::Enum inIndexCompType,
+ QT3DSU32 inSubsetCount, QT3DSU32 inSubsetOffset)
+{
+ NVBounds3 retval(NVBounds3::empty());
+ const NVRenderVertexBufferEntry &entry(inEntry);
+ if (entry.m_ComponentType != NVRenderComponentTypes::QT3DSF32 || entry.m_NumComponents != 3) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+
+ const QT3DSU8 *beginPtr = inVertxData.begin();
+ QT3DSU32 numBytes = inVertxData.size();
+ QT3DSU32 dataStride = inStride;
+ QT3DSU32 posOffset = entry.m_FirstItemOffset;
+ // The loop below could be template specialized *if* we wanted to do this.
+ // and the perf of the existing loop was determined to be a problem.
+ // Else I would rather stay way from the template specialization.
+ QT3DSIMP_FOREACH(idx, inSubsetCount)
+ {
+ QT3DSU32 dataIdx = NextIndex(inIndexData, inIndexCompType, idx + inSubsetOffset);
+ QT3DSU32 finalOffset = (dataIdx * dataStride) + posOffset;
+ if (finalOffset + sizeof(QT3DSVec3) <= numBytes) {
+ const QT3DSU8 *dataPtr = beginPtr + finalOffset;
+ retval.include(*reinterpret_cast<const QT3DSVec3 *>(dataPtr));
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ return retval;
+}
+
+void Mesh::Save(IOutStream &outStream) const
+{
+ Mesh &mesh(const_cast<Mesh &>(*this));
+ QT3DSU8 *baseAddress = reinterpret_cast<QT3DSU8 *>(&mesh);
+ QT3DSU32 numBytes = sizeof(Mesh) + GetMeshDataSize(mesh);
+ MeshDataHeader header(numBytes);
+ outStream.Write(header);
+ outStream.Write(*this);
+ ByteWritingSerializer writer(outStream, baseAddress);
+ Serialize(writer, mesh);
+}
+
+wchar_t g_DefaultName[] = { 0 };
+
+const wchar_t *Mesh::s_DefaultName = g_DefaultName;
+
+template <typename TMeshType>
+struct SubsetNameHandler
+{
+};
+
+template <>
+struct SubsetNameHandler<MeshV1>
+{
+ void AssignName(const QT3DSU8 * /*v1BaseAddress*/, const MeshSubsetV1 & /*mesh*/,
+ QT3DSU8 * /*baseAddress*/, QT3DSU8 *& /*nameBuffer*/, MeshSubset &outDest)
+ {
+ outDest.m_Name = SOffsetDataRef<char16_t>();
+ }
+ QT3DSU32 NameLength(const MeshSubsetV1 &) { return 0; }
+};
+
+using qt3ds::intrinsics::memCopy;
+
+template <>
+struct SubsetNameHandler<MeshV2>
+{
+ void AssignName(const QT3DSU8 *v2BaseAddress, const MeshSubsetV2 &mesh, QT3DSU8 *baseAddress,
+ QT3DSU8 *&nameBuffer, MeshSubset &outDest)
+ {
+ outDest.m_Name.m_Size = mesh.m_Name.m_Size;
+ outDest.m_Name.m_Offset = (QT3DSU32)(nameBuffer - baseAddress);
+ QT3DSU32 dtypeSize = mesh.m_Name.m_Size * 2;
+ memCopy(nameBuffer, mesh.m_Name.begin(v2BaseAddress), dtypeSize);
+ nameBuffer += dtypeSize;
+ }
+ QT3DSU32 NameLength(const MeshSubsetV2 &mesh) { return (mesh.m_Name.size() + 1) * 2; }
+};
+
+QT3DSU32 GetAlignedOffset(QT3DSU32 offset, QT3DSU32 align)
+{
+ QT3DSU32 leftover = offset % align;
+ if (leftover)
+ return offset + (align - leftover);
+ return offset;
+}
+
+template <typename TPreviousMeshType>
+Mesh *CreateMeshFromPreviousMesh(TPreviousMeshType *temp, NVAllocatorCallback &alloc)
+{
+ QT3DSU32 newMeshSize = sizeof(Mesh);
+ QT3DSU8 *tempBaseAddress = reinterpret_cast<QT3DSU8 *>(temp);
+ QT3DSU32 alignment = sizeof(void *);
+
+ QT3DSU32 vertBufferSize = GetAlignedOffset(temp->m_VertexBuffer.m_Data.size(), alignment);
+ newMeshSize += vertBufferSize;
+ QT3DSU32 entryDataSize = temp->m_VertexBuffer.m_Entries.size() * sizeof(MeshVertexBufferEntry);
+ newMeshSize += entryDataSize;
+ QT3DSU32 indexBufferSize = GetAlignedOffset(temp->m_IndexBuffer.m_Data.size(), alignment);
+ newMeshSize += indexBufferSize;
+ QT3DSU32 entryNameSize = 0;
+ for (QT3DSU32 entryIdx = 0, entryEnd = temp->m_VertexBuffer.m_Entries.size(); entryIdx < entryEnd;
+ ++entryIdx) {
+ const qt3ds::render::NVRenderVertexBufferEntry theEntry =
+ temp->m_VertexBuffer.m_Entries.index(tempBaseAddress, entryIdx)
+ .ToVertexBufferEntry(tempBaseAddress);
+ const char *namePtr = theEntry.m_Name;
+ if (namePtr == NULL)
+ namePtr = "";
+
+ entryNameSize += (QT3DSU32)strlen(theEntry.m_Name) + 1;
+ }
+ entryNameSize = GetAlignedOffset(entryNameSize, alignment);
+
+ newMeshSize += entryNameSize;
+ QT3DSU32 subsetBufferSize = temp->m_Subsets.size() * sizeof(MeshSubset);
+ newMeshSize += subsetBufferSize;
+ QT3DSU32 nameLength = 0;
+ for (QT3DSU32 subsetIdx = 0, subsetEnd = temp->m_Subsets.size(); subsetIdx < subsetEnd;
+ ++subsetIdx) {
+ nameLength += SubsetNameHandler<TPreviousMeshType>().NameLength(
+ temp->m_Subsets.index(tempBaseAddress, subsetIdx));
+ }
+ nameLength = GetAlignedOffset(nameLength, alignment);
+
+ newMeshSize += nameLength;
+
+ Mesh *retval = (Mesh *)alloc.allocate(newMeshSize, "TempData", __FILE__, __LINE__);
+ new (retval) Mesh();
+ QT3DSU8 *baseOffset = reinterpret_cast<QT3DSU8 *>(retval);
+ QT3DSU8 *vertBufferData = baseOffset + sizeof(Mesh);
+ QT3DSU8 *entryBufferData = vertBufferData + vertBufferSize;
+ QT3DSU8 *entryNameBuffer = entryBufferData + entryDataSize;
+ QT3DSU8 *indexBufferData = entryNameBuffer + entryNameSize;
+ QT3DSU8 *subsetBufferData = indexBufferData + indexBufferSize;
+ QT3DSU8 *nameData = subsetBufferData + subsetBufferSize;
+
+ retval->m_DrawMode = temp->m_DrawMode;
+ retval->m_Winding = temp->m_Winding;
+ retval->m_VertexBuffer = temp->m_VertexBuffer;
+ retval->m_VertexBuffer.m_Data.m_Offset = (QT3DSU32)(vertBufferData - baseOffset);
+ retval->m_VertexBuffer.m_Entries.m_Offset = (QT3DSU32)(entryBufferData - baseOffset);
+ memCopy(vertBufferData, temp->m_VertexBuffer.m_Data.begin(tempBaseAddress),
+ temp->m_VertexBuffer.m_Data.size());
+ memCopy(entryBufferData, temp->m_VertexBuffer.m_Entries.begin(tempBaseAddress), entryDataSize);
+ QT3DSIMP_FOREACH(idx, temp->m_VertexBuffer.m_Entries.size())
+ {
+ const MeshVertexBufferEntry &src =
+ temp->m_VertexBuffer.m_Entries.index(tempBaseAddress, idx);
+ MeshVertexBufferEntry &dest = retval->m_VertexBuffer.m_Entries.index(baseOffset, idx);
+
+ const char *targetName = reinterpret_cast<const char *>(src.m_NameOffset + tempBaseAddress);
+ if (src.m_NameOffset == 0)
+ targetName = "";
+ QT3DSU32 nameLen = (QT3DSU32)strlen(targetName) + 1;
+ dest.m_NameOffset = (QT3DSU32)(entryNameBuffer - baseOffset);
+ memCopy(entryNameBuffer, targetName, nameLen);
+ entryNameBuffer += nameLen;
+ }
+
+ retval->m_IndexBuffer = temp->m_IndexBuffer;
+ retval->m_IndexBuffer.m_Data.m_Offset = (QT3DSU32)(indexBufferData - baseOffset);
+ memCopy(indexBufferData, temp->m_IndexBuffer.m_Data.begin(tempBaseAddress),
+ temp->m_IndexBuffer.m_Data.size());
+
+ retval->m_Subsets.m_Size = temp->m_Subsets.m_Size;
+ retval->m_Subsets.m_Offset = (QT3DSU32)(subsetBufferData - baseOffset);
+ QT3DSIMP_FOREACH(idx, temp->m_Subsets.size())
+ {
+ MeshSubset &dest = const_cast<MeshSubset &>(retval->m_Subsets.index(baseOffset, idx));
+ const typename TPreviousMeshType::TSubsetType &src =
+ temp->m_Subsets.index(tempBaseAddress, idx);
+ dest.m_Count = src.m_Count;
+ dest.m_Offset = src.m_Offset;
+ dest.m_Bounds = src.m_Bounds;
+ SubsetNameHandler<TPreviousMeshType>().AssignName(tempBaseAddress, src, baseOffset,
+ nameData, dest);
+ }
+ alloc.deallocate(temp);
+ return retval;
+}
+
+Mesh *Mesh::Load(NVAllocatorCallback &alloc, IInStream &inStream)
+{
+ MeshDataHeader header;
+ inStream.Read(header);
+ QT3DS_ASSERT(header.m_FileId == MeshDataHeader::GetFileId());
+ if (header.m_FileId != MeshDataHeader::GetFileId())
+ return NULL;
+ if (header.m_FileVersion < 1 || header.m_FileVersion > MeshDataHeader::GetCurrentFileVersion())
+ return NULL;
+ if (header.m_SizeInBytes < sizeof(Mesh))
+ return NULL;
+ QT3DSU8 *newMem = (QT3DSU8 *)alloc.allocate(header.m_SizeInBytes, "Mesh", __FILE__, __LINE__);
+ QT3DSU32 amountRead = inStream.Read(NVDataRef<QT3DSU8>(newMem, header.m_SizeInBytes));
+ if (amountRead != header.m_SizeInBytes)
+ goto failure;
+
+ if (header.m_FileVersion == 1) {
+ MeshV1 *temp = DoInitialize<MeshV1>(header.m_HeaderFlags,
+ NVDataRef<QT3DSU8>(newMem, header.m_SizeInBytes));
+ if (temp == NULL)
+ goto failure;
+ return CreateMeshFromPreviousMesh(temp, alloc);
+
+ } else if (header.m_FileVersion == 2) {
+ MeshV2 *temp = DoInitialize<MeshV2>(header.m_HeaderFlags,
+ NVDataRef<QT3DSU8>(newMem, header.m_SizeInBytes));
+ if (temp == NULL)
+ goto failure;
+ return CreateMeshFromPreviousMesh(temp, alloc);
+ } else {
+ Mesh *retval = Initialize(header.m_FileVersion, header.m_HeaderFlags,
+ NVDataRef<QT3DSU8>(newMem, header.m_SizeInBytes));
+ if (retval == NULL)
+ goto failure;
+ return retval;
+ }
+
+failure:
+ QT3DS_ASSERT(false);
+ alloc.deallocate(newMem);
+ return NULL;
+}
+
+Mesh *Mesh::Initialize(QT3DSU16 meshVersion, MeshBufHeaderFlags meshFlags, NVDataRef<QT3DSU8> data)
+{
+ if (meshVersion != MeshDataHeader::GetCurrentFileVersion())
+ return NULL;
+ return DoInitialize<Mesh>(meshFlags, data);
+}
+
+// Multimesh support where you have multiple meshes in a single file.
+// Save multi where you have overridden the allocator.
+QT3DSU32 Mesh::SaveMulti(NVAllocatorCallback &alloc, ISeekableIOStream &inStream, QT3DSU32 inId) const
+{
+ QT3DSU32 nextId = 1;
+ MeshMultiHeader tempHeader;
+ MeshMultiHeader *theHeader = NULL;
+ MeshMultiHeader *theWriteHeader = NULL;
+
+ QT3DSI64 newMeshStartPos = 0;
+ if (inStream.GetLength() != 0) {
+ theHeader = LoadMultiHeader(alloc, inStream);
+ if (theHeader == NULL) {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+ QT3DSU8 *headerBaseAddr = reinterpret_cast<QT3DSU8 *>(theHeader);
+ for (QT3DSU32 idx = 0, end = theHeader->m_Entries.size(); idx < end; ++idx) {
+ if (inId != 0) {
+ QT3DS_ASSERT(inId != theHeader->m_Entries.index(headerBaseAddr, idx).m_MeshId);
+ }
+ nextId = qMax(nextId, theHeader->m_Entries.index(headerBaseAddr, idx).m_MeshId + 1);
+ }
+ newMeshStartPos =
+ sizeof(MeshMultiHeader) + theHeader->m_Entries.size() * sizeof(MeshMultiEntry);
+ theWriteHeader = theHeader;
+ } else
+ theWriteHeader = &tempHeader;
+
+ inStream.SetPosition(-newMeshStartPos, SeekPosition::End);
+ QT3DSI64 meshOffset = inStream.GetPosition();
+
+ Save(inStream);
+
+ if (inId != 0)
+ nextId = inId;
+ QT3DSU8 *theWriteBaseAddr = reinterpret_cast<QT3DSU8 *>(theWriteHeader);
+ // Now write a new header out.
+ inStream.Write(theWriteHeader->m_Entries.begin(theWriteBaseAddr),
+ theWriteHeader->m_Entries.size());
+ MeshMultiEntry newEntry(static_cast<QT3DSI64>(meshOffset), nextId);
+ inStream.Write(newEntry);
+ theWriteHeader->m_Entries.m_Size++;
+ inStream.Write(*theWriteHeader);
+
+ if (theHeader != NULL) {
+ alloc.deallocate(theHeader);
+ }
+ return static_cast<QT3DSU32>(nextId);
+}
+
+// Load a single mesh directly from a multi file with the provided overridden items
+SMultiLoadResult Mesh::LoadMulti(NVAllocatorCallback &alloc, ISeekableIOStream &inStream,
+ QT3DSU32 inId)
+{
+ MeshMultiHeader *theHeader(LoadMultiHeader(alloc, inStream));
+ if (theHeader == NULL) {
+ return SMultiLoadResult();
+ }
+ QT3DSU64 fileOffset = (QT3DSU64)-1;
+ QT3DSU32 theId = inId;
+ QT3DSU8 *theHeaderBaseAddr = reinterpret_cast<QT3DSU8 *>(theHeader);
+ bool foundMesh = false;
+ for (QT3DSU32 idx = 0, end = theHeader->m_Entries.size(); idx < end && !foundMesh; ++idx) {
+ const MeshMultiEntry &theEntry(theHeader->m_Entries.index(theHeaderBaseAddr, idx));
+ if (theEntry.m_MeshId == inId || (inId == 0 && theEntry.m_MeshId > theId)) {
+ if (theEntry.m_MeshId == inId)
+ foundMesh = true;
+ theId = qMax(theId, (QT3DSU32)theEntry.m_MeshId);
+ fileOffset = theEntry.m_MeshOffset;
+ }
+ }
+ Mesh *retval = NULL;
+ if (fileOffset == (QT3DSU64)-1) {
+ goto endFunction;
+ }
+
+ inStream.SetPosition(static_cast<QT3DSI64>(fileOffset), SeekPosition::Begin);
+ retval = Load(alloc, inStream);
+endFunction:
+ if (theHeader != NULL)
+ alloc.deallocate(theHeader);
+ return SMultiLoadResult(retval, theId);
+}
+
+// Returns true if this is a multimesh (several meshes in one file).
+bool Mesh::IsMulti(ISeekableIOStream &inStream)
+{
+ MeshMultiHeader theHeader;
+ inStream.SetPosition(-((QT3DSI64)(sizeof(MeshMultiHeader))), SeekPosition::End);
+ QT3DSU32 numBytes = inStream.Read(theHeader);
+ if (numBytes != sizeof(MeshMultiHeader))
+ return false;
+ return theHeader.m_Version == MeshMultiHeader::GetMultiStaticFileId();
+}
+// Load a multi header from a stream.
+MeshMultiHeader *Mesh::LoadMultiHeader(NVAllocatorCallback &alloc, ISeekableIOStream &inStream)
+{
+ MeshMultiHeader theHeader;
+ inStream.SetPosition(-((QT3DSI64)sizeof(MeshMultiHeader)), SeekPosition::End);
+ QT3DSU32 numBytes = inStream.Read(theHeader);
+ if (numBytes != sizeof(MeshMultiHeader)
+ || theHeader.m_FileId != MeshMultiHeader::GetMultiStaticFileId()
+ || theHeader.m_Version > MeshMultiHeader::GetMultiStaticVersion()) {
+ return NULL;
+ }
+ size_t allocSize =
+ sizeof(MeshMultiHeader) + theHeader.m_Entries.m_Size * sizeof(MeshMultiEntry);
+ MeshMultiHeader *retval =
+ (MeshMultiHeader *)alloc.allocate(allocSize, "MeshMultiHeader", __FILE__, __LINE__);
+ if (retval == NULL) {
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+ QT3DSU8 *baseAddr = reinterpret_cast<QT3DSU8 *>(retval);
+ QT3DSU8 *entryData = baseAddr + sizeof(MeshMultiHeader);
+ *retval = theHeader;
+ retval->m_Entries.m_Offset = (QT3DSU32)(entryData - baseAddr);
+ inStream.SetPosition(-((QT3DSI64)allocSize), SeekPosition::End);
+
+ numBytes =
+ inStream.Read(reinterpret_cast<MeshMultiEntry *>(entryData), retval->m_Entries.m_Size);
+ if (numBytes != retval->m_Entries.m_Size * sizeof(MeshMultiEntry)) {
+ QT3DS_ASSERT(false);
+ alloc.deallocate(retval);
+ retval = NULL;
+ }
+ return retval;
+}
+
+QT3DSU32 GetHighestId(NVAllocatorCallback &inAlloc, MeshMultiHeader *inHeader)
+{
+ if (inHeader == NULL) {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+ QT3DSU8 *baseHeaderAddr = reinterpret_cast<QT3DSU8 *>(inHeader);
+ QT3DSU32 highestId = 0;
+ for (QT3DSU32 idx = 0, end = inHeader->m_Entries.size(); idx < end; ++idx)
+ highestId = qMax(highestId, inHeader->m_Entries.index(baseHeaderAddr, idx).m_MeshId);
+ inAlloc.deallocate(inHeader);
+ return highestId;
+}
+
+QT3DSU32 Mesh::GetHighestMultiVersion(NVAllocatorCallback &alloc, ISeekableIOStream &inStream)
+{
+ return GetHighestId(alloc, LoadMultiHeader(alloc, inStream));
+}
diff --git a/src/importlib/Qt3DSImportMesh.h b/src/importlib/Qt3DSImportMesh.h
new file mode 100644
index 0000000..36b7a84
--- /dev/null
+++ b/src/importlib/Qt3DSImportMesh.h
@@ -0,0 +1,530 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_IMPORT_MESH_H
+#define QT3DS_IMPORT_MESH_H
+#include "foundation/Qt3DS.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "foundation/Qt3DSBounds3.h"
+#include "foundation/Qt3DSAllocatorCallback.h"
+#include "foundation/IOStreams.h"
+
+namespace qt3dsimp {
+using namespace qt3ds;
+using namespace qt3ds::foundation;
+using namespace qt3ds::render;
+template <typename TDataType>
+struct SOffsetDataRef
+{
+ QT3DSU32 m_Offset;
+ QT3DSU32 m_Size;
+ SOffsetDataRef()
+ : m_Offset(0)
+ , m_Size(0)
+ {
+ }
+ TDataType *begin(QT3DSU8 *inBase) { return reinterpret_cast<TDataType *>(inBase + m_Offset); }
+ TDataType *end(QT3DSU8 *inBase) { return begin(inBase) + m_Size; }
+ const TDataType *begin(const QT3DSU8 *inBase) const
+ {
+ return reinterpret_cast<const TDataType *>(inBase + m_Offset);
+ }
+ const TDataType *end(const QT3DSU8 *inBase) const { return begin(inBase) + m_Size; }
+ QT3DSU32 size() const { return m_Size; }
+ bool empty() const { return m_Size == 0; }
+ TDataType &index(QT3DSU8 *inBase, QT3DSU32 idx)
+ {
+ QT3DS_ASSERT(idx < m_Size);
+ return begin(inBase)[idx];
+ }
+ const TDataType &index(const QT3DSU8 *inBase, QT3DSU32 idx) const
+ {
+ QT3DS_ASSERT(idx < m_Size);
+ return begin(inBase)[idx];
+ }
+};
+
+struct MeshVertexBufferEntry
+{
+ QT3DSU32 m_NameOffset;
+ /** Datatype of the this entry points to in the buffer */
+ NVRenderComponentTypes::Enum m_ComponentType;
+ /** Number of components of each data member. 1,2,3, or 4. Don't be stupid.*/
+ QT3DSU32 m_NumComponents;
+ /** Offset from the beginning of the buffer of the first item */
+ QT3DSU32 m_FirstItemOffset;
+ MeshVertexBufferEntry()
+ : m_NameOffset(0)
+ , m_ComponentType(NVRenderComponentTypes::QT3DSF32)
+ , m_NumComponents(3)
+ , m_FirstItemOffset(0)
+ {
+ }
+ NVRenderVertexBufferEntry ToVertexBufferEntry(QT3DSU8 *inBaseAddress)
+ {
+ const char *nameBuffer = "";
+ if (m_NameOffset)
+ nameBuffer = reinterpret_cast<const char *>(inBaseAddress + m_NameOffset);
+ return NVRenderVertexBufferEntry(nameBuffer, m_ComponentType, m_NumComponents,
+ m_FirstItemOffset);
+ }
+};
+
+struct VertexBuffer
+{
+ SOffsetDataRef<MeshVertexBufferEntry> m_Entries;
+ QT3DSU32 m_Stride;
+ SOffsetDataRef<QT3DSU8> m_Data;
+ VertexBuffer(SOffsetDataRef<MeshVertexBufferEntry> entries, QT3DSU32 stride,
+ SOffsetDataRef<QT3DSU8> data)
+ : m_Entries(entries)
+ , m_Stride(stride)
+ , m_Data(data)
+ {
+ }
+ VertexBuffer()
+ : m_Stride(0)
+ {
+ }
+};
+
+struct IndexBuffer
+{
+ // Component types must be either QT3DSU16 or QT3DSU8 in order for the
+ // graphics hardware to deal with the buffer correctly.
+ NVRenderComponentTypes::Enum m_ComponentType;
+ SOffsetDataRef<QT3DSU8> m_Data;
+ // Either QT3DSU8 or QT3DSU16 component types are allowed by the underlying rendering
+ // system, so you would be wise to stick with those.
+ IndexBuffer(NVRenderComponentTypes::Enum compType, SOffsetDataRef<QT3DSU8> data)
+ : m_ComponentType(compType)
+ , m_Data(data)
+ {
+ }
+ IndexBuffer()
+ : m_ComponentType(NVRenderComponentTypes::Unknown)
+ {
+ }
+};
+
+template <QT3DSU32 TNumBytes>
+struct MeshPadding
+{
+ QT3DSU8 m_Padding[TNumBytes];
+ MeshPadding() { memZero(m_Padding, TNumBytes); }
+};
+
+struct MeshSubset
+{
+ // QT3DS_MAX_U32 means use all available items
+ QT3DSU32 m_Count;
+ // Offset is in item size, not bytes.
+ QT3DSU32 m_Offset;
+ // Bounds of this subset. This is filled in by the builder
+ // see AddMeshSubset
+ NVBounds3 m_Bounds;
+
+ // Subsets have to be named else artists will be unable to use
+ // a mesh with multiple subsets as they won't have any idea
+ // while part of the model a given mesh actually maps to.
+ SOffsetDataRef<char16_t> m_Name;
+
+ MeshSubset(QT3DSU32 count, QT3DSU32 off, const NVBounds3 &bounds, SOffsetDataRef<char16_t> inName)
+ : m_Count(count)
+ , m_Offset(off)
+ , m_Bounds(bounds)
+ , m_Name(inName)
+ {
+ }
+ MeshSubset()
+ : m_Count((QT3DSU32)-1)
+ , m_Offset(0)
+ , m_Bounds(NVBounds3::empty())
+ {
+ }
+ bool HasCount() const { return m_Count != QT3DS_MAX_U32; }
+};
+
+// Placeholder for bitflags in the mesh header.
+struct MeshBufHeaderFlagValues
+{
+ enum Enum {};
+};
+
+struct MeshBufHeaderFlags : NVFlags<MeshBufHeaderFlagValues::Enum, QT3DSU16>
+{
+ MeshBufHeaderFlags() {}
+ MeshBufHeaderFlags(QT3DSU16 value)
+ : NVFlags<MeshBufHeaderFlagValues::Enum, QT3DSU16>(value)
+ {
+ }
+ // Binary or creates an integer.
+ MeshBufHeaderFlags(int value)
+ : NVFlags<MeshBufHeaderFlagValues::Enum, QT3DSU16>((QT3DSU16)value)
+ {
+ }
+};
+
+struct MeshDataHeader
+{
+ static QT3DSU32 GetFileId() { return (QT3DSU32)-929005747; }
+ static QT3DSU16 GetCurrentFileVersion() { return 3; }
+ QT3DSU32 m_FileId;
+ QT3DSU16 m_FileVersion;
+ MeshBufHeaderFlags m_HeaderFlags;
+ QT3DSU32 m_SizeInBytes;
+ MeshDataHeader(QT3DSU32 size = 0)
+ : m_FileId(GetFileId())
+ , m_FileVersion(GetCurrentFileVersion())
+ , m_SizeInBytes(size)
+ {
+ }
+};
+
+struct Joint
+{
+ QT3DSI32 m_JointID;
+ QT3DSI32 m_ParentID;
+ QT3DSF32 m_invBindPose[16];
+ QT3DSF32 m_localToGlobalBoneSpace[16];
+
+ Joint(QT3DSI32 jointID, QT3DSI32 parentID, const QT3DSF32 *invBindPose,
+ const QT3DSF32 *localToGlobalBoneSpace)
+ : m_JointID(jointID)
+ , m_ParentID(parentID)
+ {
+ ::memcpy(m_invBindPose, invBindPose, sizeof(float) * 16);
+ ::memcpy(m_localToGlobalBoneSpace, localToGlobalBoneSpace, sizeof(float) * 16);
+ }
+ Joint()
+ : m_JointID(-1)
+ , m_ParentID(-1)
+ {
+ ::memset(m_invBindPose, 0, sizeof(float) * 16);
+ ::memset(m_localToGlobalBoneSpace, 0, sizeof(float) * 16);
+ }
+};
+
+// Tells us what offset a mesh with this ID starts.
+struct MeshMultiEntry
+{
+ QT3DSU64 m_MeshOffset;
+ QT3DSU32 m_MeshId;
+ QT3DSU32 m_Padding;
+ MeshMultiEntry()
+ : m_MeshOffset(0)
+ , m_MeshId(0)
+ , m_Padding(0)
+ {
+ }
+ MeshMultiEntry(QT3DSU64 mo, QT3DSU32 meshId)
+ : m_MeshOffset(mo)
+ , m_MeshId(meshId)
+ , m_Padding(0)
+ {
+ }
+};
+
+// The multi headers are actually saved at the end of the file.
+// Thus when you append to the file we overwrite the last header
+// then write out a new header structure.
+// The last 8 bytes of the file contain the multi header.
+// The previous N*8 bytes contain the mesh entries.
+struct MeshMultiHeader
+{
+ QT3DSU32 m_FileId;
+ QT3DSU32 m_Version;
+ SOffsetDataRef<MeshMultiEntry> m_Entries;
+ static QT3DSU32 GetMultiStaticFileId() { return (QT3DSU32)555777497; }
+ static QT3DSU32 GetMultiStaticVersion() { return 1; }
+
+ MeshMultiHeader()
+ : m_FileId(GetMultiStaticFileId())
+ , m_Version(GetMultiStaticVersion())
+ {
+ }
+};
+
+struct Mesh;
+
+// Result of a multi-load operation. This returns both the mesh
+// and the id of the mesh that was loaded.
+struct SMultiLoadResult
+{
+ Mesh *m_Mesh;
+ QT3DSU32 m_Id;
+ SMultiLoadResult(Mesh *inMesh, QT3DSU32 inId)
+ : m_Mesh(inMesh)
+ , m_Id(inId)
+ {
+ }
+ SMultiLoadResult()
+ : m_Mesh(NULL)
+ , m_Id(0)
+ {
+ }
+ operator Mesh *() { return m_Mesh; }
+};
+
+/**
+* A Mesh defines one vertex buffer layout, one or more logical vertex buffers
+* one index buffer, and a set of defined draw calls (subsets).
+*
+* The vertex buffer data is held together continguously and the layout cannot
+* change. There can be several actual vertex buffers on the card, however, in order
+* to facilitate using index buffer components of smaller component sizes than there
+* are actual vertex buffer data entries. For instance, you may have a very large vertex
+* buffer, larger than 64 K but openglES has a restriction on the size of the index buffer
+*component
+* that it cannot be larger than two bytes per entry. So you would need to split the vertex
+*buffer
+* into multiple logical vertex buffers and adjust your indexes such that you indexed into
+* only one logical vertex buffer per draw call.
+*
+* No logical vertex buffers means that the vertex buffer index on a mesh subset will be
+*ignored.
+*/
+struct Mesh
+{
+ static const wchar_t *s_DefaultName;
+
+ VertexBuffer m_VertexBuffer;
+ IndexBuffer m_IndexBuffer;
+ SOffsetDataRef<MeshSubset> m_Subsets;
+ SOffsetDataRef<Joint> m_Joints;
+ NVRenderDrawMode::Enum m_DrawMode;
+ NVRenderWinding::Enum m_Winding;
+
+ Mesh()
+ : m_DrawMode(NVRenderDrawMode::Triangles)
+ , m_Winding(NVRenderWinding::CounterClockwise)
+ {
+ }
+ Mesh(VertexBuffer vbuf, IndexBuffer ibuf, const SOffsetDataRef<MeshSubset> &insts,
+ const SOffsetDataRef<Joint> &joints,
+ NVRenderDrawMode::Enum drawMode = NVRenderDrawMode::Triangles,
+ NVRenderWinding::Enum winding = NVRenderWinding::CounterClockwise)
+ : m_VertexBuffer(vbuf)
+ , m_IndexBuffer(ibuf)
+ , m_Subsets(insts)
+ , m_Joints(joints)
+ , m_DrawMode(drawMode)
+ , m_Winding(winding)
+ {
+ }
+
+ QT3DSU8 *GetBaseAddress() { return reinterpret_cast<QT3DSU8 *>(this); }
+ const QT3DSU8 *GetBaseAddress() const { return reinterpret_cast<const QT3DSU8 *>(this); }
+
+ static const char *GetPositionAttrName() { return "attr_pos"; }
+ static const char *GetNormalAttrName() { return "attr_norm"; }
+ static const char *GetUVAttrName() { return "attr_uv0"; }
+ static const char *GetUV2AttrName() { return "attr_uv1"; }
+ static const char *GetTexTanAttrName() { return "attr_textan"; }
+ static const char *GetTexBinormalAttrName() { return "attr_binormal"; }
+ static const char *GetWeightAttrName() { return "attr_weight"; }
+ static const char *GetBoneIndexAttrName() { return "attr_boneid"; }
+ static const char *GetColorAttrName() { return "attr_color"; }
+
+ // Run through the vertex buffer items indicated by subset
+ // Assume vbuf entry[posEntryIndex] is the position entry
+ // This entry has to be QT3DSF32 and 3 components.
+ // Using this entry and the (possibly empty) index buffer
+ // along with the (possibly emtpy) logical vbuf data
+ // return a bounds of the given vertex buffer.
+ static NVBounds3 CalculateSubsetBounds(const NVRenderVertexBufferEntry &inEntry,
+ NVConstDataRef<QT3DSU8> inVertxData, QT3DSU32 inStride,
+ NVConstDataRef<QT3DSU8> inIndexData,
+ qt3ds::render::NVRenderComponentTypes::Enum inIndexCompType,
+ QT3DSU32 inSubsetCount, QT3DSU32 inSubsetOffset);
+
+ // Format is:
+ // MeshDataHeader
+ // mesh data.
+ void Save(IOutStream &outStream) const;
+
+ // Save a mesh using fopen and fwrite
+ bool Save(const char *inFilePath) const;
+
+ // read the header, then read the object.
+ // Object data is written in LE format for now.
+ // Free the new mesh by calling:
+ // alloc.deallocate( mesh );
+ // All the memory is allocated once and then pointers are back
+ // filled, so this will work.
+ static Mesh *Load(NVAllocatorCallback &alloc, IInStream &inStream);
+
+ // Load a mesh using fopen and fread
+ // Mesh needs to be freed by the caller using free
+ static Mesh *Load(const char *inFilePath);
+
+ // Create a mesh given this header, and that data. data.size() must match
+ // header.SizeInBytes. The mesh returned starts a data[0], so however data
+ // was allocated is how the mesh should be deallocated.
+ static Mesh *Initialize(QT3DSU16 meshVersion, MeshBufHeaderFlags meshFlags, NVDataRef<QT3DSU8> data);
+
+ // Multimesh support where you have multiple meshes in a single file.
+ // Save multi where you have overridden the allocator.
+ QT3DSU32 SaveMulti(NVAllocatorCallback &alloc, ISeekableIOStream &inStream, QT3DSU32 inId = 0) const;
+ // You can save multiple meshes in a file. Each mesh returns an incrementing
+ // integer for the multi file. The original meshes aren't changed, and the file
+ // is appended to.
+ QT3DSU32 SaveMulti(const char *inFilePath) const;
+
+ // Load a single mesh directly from a multi file with the provided overridden items
+ // Loading a multimesh with id == 0 indicates to just load the mesh with the highest id.
+ static SMultiLoadResult LoadMulti(NVAllocatorCallback &alloc, ISeekableIOStream &inStream,
+ QT3DSU32 inId = 0);
+ // Load a single mesh using c file API and malloc/free.
+ static SMultiLoadResult LoadMulti(const char *inFilePath, QT3DSU32 inId);
+ // Returns true if this is a multimesh (several meshes in one file).
+ static bool IsMulti(ISeekableIOStream &inStream);
+ // Load a multi header from a stream.
+ static MeshMultiHeader *LoadMultiHeader(NVAllocatorCallback &alloc,
+ ISeekableIOStream &inStream);
+ // Load a multi header from a file using malloc. Header needs to be freed using free.
+ static MeshMultiHeader *LoadMultiHeader(const char *inFilePath);
+
+ // Get the highest mesh version from a stream.
+ static QT3DSU32 GetHighestMultiVersion(NVAllocatorCallback &alloc, ISeekableIOStream &inStream);
+ // Get the highest mesh version from a file.
+ static QT3DSU32 GetHighestMultiVersion(const char *inFilePath);
+};
+
+struct ScopedMesh
+{
+ Mesh *m_Mesh;
+ NVAllocatorCallback *m_Callback;
+ ScopedMesh(Mesh *m, NVAllocatorCallback *cback = NULL)
+ : m_Mesh(m)
+ , m_Callback(cback)
+ {
+ }
+ ~ScopedMesh()
+ {
+ if (m_Mesh) {
+ if (m_Callback)
+ m_Callback->deallocate(m_Mesh);
+ else
+ free(m_Mesh);
+ }
+ }
+ Mesh *operator->() { return m_Mesh; }
+ operator Mesh *() { return m_Mesh; }
+};
+
+struct MeshBuilderVBufEntry
+{
+ const char *m_Name;
+ NVConstDataRef<QT3DSU8> m_Data;
+ NVRenderComponentTypes::Enum m_ComponentType;
+ QT3DSU32 m_NumComponents;
+ MeshBuilderVBufEntry()
+ : m_Name(NULL)
+ , m_ComponentType(NVRenderComponentTypes::Unknown)
+ , m_NumComponents(0)
+ {
+ }
+ MeshBuilderVBufEntry(const char *name, NVConstDataRef<QT3DSU8> data,
+ NVRenderComponentTypes::Enum componentType, QT3DSU32 numComponents)
+ : m_Name(name)
+ , m_Data(data)
+ , m_ComponentType(componentType)
+ , m_NumComponents(numComponents)
+ {
+ }
+};
+
+// Useful class to build up a mesh. Necessary since meshes don't include that
+// sort of utility.
+class MeshBuilder
+{
+protected:
+ virtual ~MeshBuilder() {}
+public:
+ virtual void Release() = 0;
+ virtual void Reset() = 0;
+ // Set the draw parameters for any subsets. Defaults to triangles and counter clockwise
+ virtual void SetDrawParameters(NVRenderDrawMode::Enum drawMode,
+ NVRenderWinding::Enum winding) = 0;
+ // Set the vertex buffer and have the mesh builder interleave the data for you
+ virtual bool SetVertexBuffer(NVConstDataRef<MeshBuilderVBufEntry> entries) = 0;
+ // Set the vertex buffer from interleaved data.
+ virtual void SetVertexBuffer(NVConstDataRef<NVRenderVertexBufferEntry> entries, QT3DSU32 stride,
+ NVConstDataRef<QT3DSU8> data) = 0;
+ // The builder (and the majority of the rest of the product) only supports unsigned 16 bit
+ // indexes
+ virtual void SetIndexBuffer(NVConstDataRef<QT3DSU8> data, NVRenderComponentTypes::Enum comp) = 0;
+ // Assets if the supplied parameters are out of range.
+ virtual void AddJoint(QT3DSI32 jointID, QT3DSI32 parentID, const QT3DSF32 *invBindPose,
+ const QT3DSF32 *localToGlobalBoneSpace) = 0;
+ /**
+ * Add a subset, which equates roughly to a draw call.
+ * A logical vertex buffer allows you to have more that 64K vertexes but still
+ * use u16 index buffers. In any case, if the mesh has an index buffer then this subset
+ * refers to that index buffer, else it is assumed to index into the vertex buffer.
+ * count and offset do exactly what they seem to do, while boundsPositionEntryIndex, if set to
+ * something other than QT3DS_MAX_U32, drives the calculation of the aa-bounds of the subset
+ * using mesh::CalculateSubsetBounds
+ */
+ virtual void AddMeshSubset(const wchar_t *inSubsetName = Mesh::s_DefaultName,
+ QT3DSU32 count = QT3DS_MAX_U32, QT3DSU32 offset = 0,
+ QT3DSU32 boundsPositionEntryIndex = QT3DS_MAX_U32) = 0;
+
+ virtual void AddMeshSubset(const wchar_t *inSubsetName, QT3DSU32 count, QT3DSU32 offset,
+ const NVBounds3 &inBounds) = 0;
+
+ // Call to optimize the index and vertex buffers. This doesn't change the subset information,
+ // each triangle is rendered precisely the same.
+ // It just orders the vertex data so we iterate through it as linearly as possible.
+ // This *only* works if the *entire* builder is using triangles as the draw mode. This will be
+ // a disaster if that
+ // condition is not met.
+ virtual void OptimizeMesh() = 0;
+
+ /**
+ * @brief This functions stitches together sub-meshes with the same material.
+ * This re-writes the index buffer
+ *
+ * @return no return.
+ */
+ virtual void ConnectSubMeshes() = 0;
+
+ // Return the current mesh. This is only good for this function call, item may change or be
+ // released
+ // due to any further function calls.
+ virtual Mesh &GetMesh() = 0;
+
+ // Uses new/delete.
+ static MeshBuilder &CreateMeshBuilder();
+};
+}
+
+#endif
diff --git a/src/importlib/Qt3DSImportMeshBuilder.cpp b/src/importlib/Qt3DSImportMeshBuilder.cpp
new file mode 100644
index 0000000..e8c6d92
--- /dev/null
+++ b/src/importlib/Qt3DSImportMeshBuilder.cpp
@@ -0,0 +1,598 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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 "Qt3DSImportLibPrecompile.h"
+#include "Qt3DSImportMesh.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+#include "Qt3DSImportContainers.h"
+
+using namespace qt3dsimp;
+
+// Disable mesh optimization. TODO: After removing NvTriStrip need
+// to implement mesh optimization.
+//#define DISABLE_MESH_OPTIMIZATION 1
+
+// It turns out we can't enable vertex remapping because it would break
+// mesh morphing.
+#define DISABLE_VERTEX_REMAP 1
+
+namespace {
+template <typename TDataType>
+NVConstDataRef<TDataType> toRefBuf(QT3DSU8 *bufData, QT3DSU32 off, QT3DSU32 size)
+{
+ QT3DS_ASSERT(size % sizeof(TDataType) == 0);
+ if (size)
+ return NVConstDataRef<TDataType>((TDataType *)(bufData + off), size / sizeof(TDataType));
+ return NVConstDataRef<TDataType>();
+}
+struct DynamicVBuf
+{
+ QT3DSU32 m_Stride;
+ ImportArray<NVRenderVertexBufferEntry> m_VertexBufferEntries;
+ MemoryBuffer<RawAllocator> m_VertexData;
+
+ void clear()
+ {
+ m_Stride = 0;
+ m_VertexBufferEntries.clear();
+ m_VertexData.clear();
+ }
+};
+struct DynamicIndexBuf
+{
+ NVRenderComponentTypes::Enum m_CompType;
+ MemoryBuffer<RawAllocator> m_IndexData;
+ DynamicIndexBuf() {}
+
+ void clear() { m_IndexData.clear(); }
+};
+
+struct SubsetDesc
+{
+ QT3DSU32 m_Count;
+ QT3DSU32 m_Offset;
+
+ NVBounds3 m_Bounds;
+ QString m_Name;
+ SubsetDesc(QT3DSU32 c, QT3DSU32 off)
+ : m_Count(c)
+ , m_Offset(off)
+ {
+ }
+ SubsetDesc()
+ : m_Count(0)
+ , m_Offset(0)
+ {
+ }
+};
+
+QT3DSU32 GetAlignedOffset(QT3DSU32 offset, QT3DSU32 align)
+{
+ QT3DSU32 leftover = offset % align;
+ if (leftover)
+ return offset + (align - leftover);
+ return offset;
+}
+
+class MeshBuilderImpl : public MeshBuilder
+{
+ DynamicVBuf m_VertexBuffer;
+ DynamicIndexBuf m_IndexBuffer;
+ ImportArray<Joint> m_Joints;
+ ImportArray<SubsetDesc> m_MeshSubsetDescs;
+ NVRenderDrawMode::Enum m_DrawMode;
+ NVRenderWinding::Enum m_Winding;
+ MemoryBuffer<RawAllocator> m_RemappedVertexData;
+ MemoryBuffer<RawAllocator> m_NewIndexBuffer;
+ ImportArray<QT3DSU8> m_MeshBuffer;
+
+public:
+ MeshBuilderImpl() { Reset(); }
+ virtual ~MeshBuilderImpl() { Reset(); }
+ void Release() override { delete this; }
+ void Reset() override
+ {
+ m_VertexBuffer.clear();
+ m_IndexBuffer.clear();
+ m_Joints.clear();
+ m_MeshSubsetDescs.clear();
+ m_DrawMode = NVRenderDrawMode::Triangles;
+ m_Winding = NVRenderWinding::CounterClockwise;
+ m_MeshBuffer.clear();
+ }
+
+ void SetDrawParameters(NVRenderDrawMode::Enum drawMode, NVRenderWinding::Enum winding) override
+ {
+ m_DrawMode = drawMode;
+ m_Winding = winding;
+ }
+
+ // Somewhat burly method to interleave the data as tightly as possible
+ // while taking alignment into account.
+ bool SetVertexBuffer(NVConstDataRef<MeshBuilderVBufEntry> entries) override
+ {
+ QT3DSU32 currentOffset = 0;
+ QT3DSU32 bufferAlignment = 0;
+ QT3DSU32 numItems = 0;
+ bool retval = true;
+ QT3DSIMP_FOREACH(idx, entries.size())
+ {
+ const MeshBuilderVBufEntry &entry(entries[idx]);
+ // Ignore entries with no data.
+ if (entry.m_Data.begin() == NULL || entry.m_Data.size() == 0)
+ continue;
+
+ QT3DSU32 alignment = NVRenderComponentTypes::getSizeofType(entry.m_ComponentType);
+ bufferAlignment = qMax(bufferAlignment, alignment);
+ QT3DSU32 byteSize = alignment * entry.m_NumComponents;
+
+ if (entry.m_Data.size() % alignment != 0) {
+ QT3DS_ASSERT(false);
+ retval = false;
+ }
+
+ QT3DSU32 localNumItems = entry.m_Data.size() / byteSize;
+ if (numItems == 0) {
+ numItems = localNumItems;
+ } else if (numItems != localNumItems) {
+ QT3DS_ASSERT(false);
+ retval = false;
+ numItems = qMin(numItems, localNumItems);
+ }
+ // Lots of platforms can't handle non-aligned data.
+ // so ensure we are aligned.
+ currentOffset = GetAlignedOffset(currentOffset, alignment);
+ NVRenderVertexBufferEntry vbufEntry(entry.m_Name, entry.m_ComponentType,
+ entry.m_NumComponents, currentOffset);
+ m_VertexBuffer.m_VertexBufferEntries.push_back(vbufEntry);
+ currentOffset += byteSize;
+ }
+ m_VertexBuffer.m_Stride = GetAlignedOffset(currentOffset, bufferAlignment);
+
+ // Packed interleave the data
+ QT3DSIMP_FOREACH(idx, numItems)
+ {
+ QT3DSU32 dataOffset = 0;
+ QT3DSIMP_FOREACH(entryIdx, entries.size())
+ {
+ const MeshBuilderVBufEntry &entry(entries[entryIdx]);
+ // Ignore entries with no data.
+ if (entry.m_Data.begin() == NULL || entry.m_Data.size() == 0)
+ continue;
+
+ QT3DSU32 alignment = NVRenderComponentTypes::getSizeofType(entry.m_ComponentType);
+ QT3DSU32 byteSize = alignment * entry.m_NumComponents;
+ QT3DSU32 offset = byteSize * idx;
+ QT3DSU32 newOffset = GetAlignedOffset(dataOffset, alignment);
+ if (newOffset != dataOffset)
+ m_VertexBuffer.m_VertexData.writeZeros(newOffset - dataOffset);
+ m_VertexBuffer.m_VertexData.write(entry.m_Data.begin() + offset, byteSize);
+ dataOffset = newOffset + byteSize;
+ }
+ QT3DS_ASSERT(dataOffset == m_VertexBuffer.m_Stride);
+ }
+ return retval;
+ }
+
+ void SetVertexBuffer(NVConstDataRef<NVRenderVertexBufferEntry> entries, QT3DSU32 stride,
+ NVConstDataRef<QT3DSU8> data) override
+ {
+ QT3DSIMP_FOREACH(idx, (QT3DSU32)entries.size())
+ {
+ m_VertexBuffer.m_VertexBufferEntries.push_back(entries[idx]);
+ }
+ m_VertexBuffer.m_VertexData.write(data.begin(), data.size());
+ if (stride == 0) {
+ // Calculate the stride of the buffer using the vbuf entries
+ QT3DSIMP_FOREACH(idx, entries.size())
+ {
+ const NVRenderVertexBufferEntry &entry(entries[idx]);
+ stride = qMax(stride, entry.m_FirstItemOffset
+ + (entry.m_NumComponents * NVRenderComponentTypes::getSizeofType(
+ entry.m_ComponentType)));
+ }
+ }
+ m_VertexBuffer.m_Stride = stride;
+ }
+
+ void SetIndexBuffer(NVConstDataRef<QT3DSU8> data, NVRenderComponentTypes::Enum comp) override
+ {
+ m_IndexBuffer.m_CompType = comp;
+ m_IndexBuffer.m_IndexData.write(data.begin(), data.size());
+ }
+
+ void AddJoint(QT3DSI32 jointID, QT3DSI32 parentID, const QT3DSF32 *invBindPose,
+ const QT3DSF32 *localToGlobalBoneSpace) override
+ {
+ m_Joints.push_back(Joint(jointID, parentID, invBindPose, localToGlobalBoneSpace));
+ }
+
+ SubsetDesc CreateSubset(const wchar_t *inName, QT3DSU32 count, QT3DSU32 offset)
+ {
+ if (inName == NULL)
+ inName = L"";
+ SubsetDesc retval(count, offset);
+ retval.m_Name = QString::fromWCharArray(inName);
+ return retval;
+ }
+
+ // indexBuffer QT3DS_MAX_U32 means no index buffer.
+ // count of QT3DS_MAX_U32 means use all available items
+ // offset means exactly what you would think. Offset is in item size, not bytes.
+ void AddMeshSubset(const wchar_t *inName, QT3DSU32 count, QT3DSU32 offset,
+ QT3DSU32 boundsPositionEntryIndex) override
+ {
+ SubsetDesc retval = CreateSubset(inName, count, offset);
+ if (boundsPositionEntryIndex != QT3DS_MAX_U32) {
+ retval.m_Bounds = Mesh::CalculateSubsetBounds(
+ m_VertexBuffer.m_VertexBufferEntries[boundsPositionEntryIndex],
+ m_VertexBuffer.m_VertexData, m_VertexBuffer.m_Stride, m_IndexBuffer.m_IndexData,
+ m_IndexBuffer.m_CompType, count, offset);
+ }
+ m_MeshSubsetDescs.push_back(retval);
+ }
+
+ void AddMeshSubset(const wchar_t *inName, QT3DSU32 count, QT3DSU32 offset,
+ const NVBounds3 &inBounds) override
+ {
+ SubsetDesc retval = CreateSubset(inName, count, offset);
+ retval.m_Bounds = inBounds;
+ m_MeshSubsetDescs.push_back(retval);
+ }
+#ifndef DISABLE_MESH_OPTIMIZATION
+ void DeletePrimitiveGroup(PrimitiveGroup *&inGroup)
+ {
+ if (inGroup)
+ delete[] inGroup;
+ inGroup = NULL;
+ }
+#endif
+ // We connect sub meshes which habe the same material
+ void ConnectSubMeshes() override
+ {
+ if (m_MeshSubsetDescs.size() < 2) {
+ // nothing to do
+ return;
+ }
+
+ QT3DSU32 matDuplicates = 0;
+
+ // as a pre-step we check if we have duplicate material at all
+ for (QT3DSU32 i = 0, subsetEnd = m_MeshSubsetDescs.size(); i < subsetEnd && !matDuplicates;
+ ++i) {
+ SubsetDesc &currentSubset = m_MeshSubsetDescs[i];
+
+ for (QT3DSU32 j = 0, subsetEnd = m_MeshSubsetDescs.size(); j < subsetEnd; ++j) {
+ SubsetDesc &theSubset = m_MeshSubsetDescs[j];
+
+ if (i == j)
+ continue;
+
+ if (currentSubset.m_Name == theSubset.m_Name) {
+ matDuplicates++;
+ break; // found a duplicate bail out
+ }
+ }
+ }
+
+ // did we find some duplicates?
+ if (matDuplicates) {
+ ImportArray<SubsetDesc> newMeshSubsetDescs;
+ ImportArray<SubsetDesc>::iterator theIter;
+ QString curMatName;
+ m_NewIndexBuffer.clear();
+
+ for (theIter = m_MeshSubsetDescs.begin(); theIter != m_MeshSubsetDescs.end();
+ ++theIter) {
+ bool bProcessed = false;
+
+ for (ImportArray<SubsetDesc>::iterator iter = newMeshSubsetDescs.begin();
+ iter != newMeshSubsetDescs.end(); ++iter) {
+ if (theIter->m_Name == iter->m_Name) {
+ bProcessed = true;
+ break;
+ }
+ }
+
+ if (bProcessed)
+ continue;
+
+ curMatName = theIter->m_Name;
+
+ QT3DSU32 theIndexCompSize =
+ NVRenderComponentTypes::getSizeofType(m_IndexBuffer.m_CompType);
+ // get pointer to indices
+ QT3DSU8 *theIndices =
+ (m_IndexBuffer.m_IndexData.begin()) + (theIter->m_Offset * theIndexCompSize);
+ // write new offset
+ theIter->m_Offset = m_NewIndexBuffer.size() / theIndexCompSize;
+ // store indices
+ m_NewIndexBuffer.write(theIndices, theIter->m_Count * theIndexCompSize);
+
+ for (QT3DSU32 j = 0, subsetEnd = m_MeshSubsetDescs.size(); j < subsetEnd; ++j) {
+ if (theIter == &m_MeshSubsetDescs[j])
+ continue;
+
+ SubsetDesc &theSubset = m_MeshSubsetDescs[j];
+
+ if (curMatName == theSubset.m_Name) {
+ // get pointer to indices
+ QT3DSU8 *theIndices = (m_IndexBuffer.m_IndexData.begin())
+ + (theSubset.m_Offset * theIndexCompSize);
+ // store indices
+ m_NewIndexBuffer.write(theIndices, theSubset.m_Count * theIndexCompSize);
+ // increment indices count
+ theIter->m_Count += theSubset.m_Count;
+ }
+ }
+
+ newMeshSubsetDescs.push_back(*theIter);
+ }
+
+ m_MeshSubsetDescs.clear();
+ m_MeshSubsetDescs = newMeshSubsetDescs;
+ m_IndexBuffer.m_IndexData.clear();
+ m_IndexBuffer.m_IndexData.write(m_NewIndexBuffer.begin(), m_NewIndexBuffer.size());
+
+ // compute new bounding box
+ for (theIter = m_MeshSubsetDescs.begin(); theIter != m_MeshSubsetDescs.end();
+ ++theIter) {
+ theIter->m_Bounds = Mesh::CalculateSubsetBounds(
+ m_VertexBuffer.m_VertexBufferEntries[0], m_VertexBuffer.m_VertexData,
+ m_VertexBuffer.m_Stride, m_IndexBuffer.m_IndexData, m_IndexBuffer.m_CompType,
+ theIter->m_Count, theIter->m_Offset);
+ }
+ }
+ }
+
+ // Here is the NVTriStrip magic.
+ void OptimizeMesh() override
+ {
+ if (NVRenderComponentTypes::getSizeofType(m_IndexBuffer.m_CompType) != 2) {
+ // we currently re-arrange unsigned int indices.
+ // this is because NvTriStrip only supports short indices
+ QT3DS_ASSERT(NVRenderComponentTypes::getSizeofType(m_IndexBuffer.m_CompType) == 4);
+ return;
+ }
+#ifndef DISABLE_MESH_OPTIMIZATION
+ SetCacheSize(CACHESIZE_GEFORCE3);
+ SetStitchStrips(true);
+ SetMinStripSize(0);
+ // Create the optimized list indices
+ SetListsOnly(true);
+ // Optimize the indices using NvTriStrip
+
+ // First, nv-tri-strip all of the indexes. They shouldn't be interleaved, meaning
+ // there is an assumption here that mesh subset1 doesn't uses indexes from mesh subset 2.
+ // They may share vertexes, however, which means that we need to be careful when remapping
+ // them.
+ // Have to go subset by subset in order for the tri-strip to avoid stepping on subsets.
+ m_NewIndexBuffer.clear();
+ for (QT3DSU32 subsetIdx = 0, subsetEnd = m_MeshSubsetDescs.size(); subsetIdx < subsetEnd;
+ ++subsetIdx) {
+ SubsetDesc &theSubset = m_MeshSubsetDescs[subsetIdx];
+ QT3DSU16 *theIndices =
+ reinterpret_cast<QT3DSU16 *>(m_IndexBuffer.m_IndexData.begin()) + theSubset.m_Offset;
+ QT3DSU32 theIndexCount = theSubset.m_Count;
+ QT3DSU16 theNumGroups = 0;
+ PrimitiveGroup *thePrimitiveGroupsList = NULL;
+ theSubset.m_Offset = m_NewIndexBuffer.size() / sizeof(QT3DSU16);
+ theSubset.m_Count = 0;
+
+ // We don't support larger vertex buffers. That requires splitting the buffer,
+ // an operation we haven't implemented (yet).
+ if (GenerateStrips(theIndices, theIndexCount, &thePrimitiveGroupsList, &theNumGroups)) {
+ if (theNumGroups) {
+ QT3DS_ASSERT(theNumGroups == 1);
+ PrimitiveGroup &srcGroup(thePrimitiveGroupsList[0]);
+ QT3DS_ASSERT(srcGroup.type == PT_LIST);
+ m_NewIndexBuffer.write(srcGroup.indices, srcGroup.numIndices);
+ theSubset.m_Count = srcGroup.numIndices;
+ }
+ }
+
+ DeletePrimitiveGroup(thePrimitiveGroupsList);
+ }
+ m_IndexBuffer.m_IndexData.clear();
+ m_IndexBuffer.m_IndexData.write(m_NewIndexBuffer.begin(), m_NewIndexBuffer.size());
+
+#ifndef DISABLE_VERTEX_REMAP
+ // This operation does not go subset by subset
+ // by rather remaps the entire vertex buffer in one shot
+ // once all of the index buffers are setup.
+ QT3DSU16 *theIndices = reinterpret_cast<QT3DSU16 *>(m_IndexBuffer.m_IndexData.begin());
+ QT3DSU32 theIndexCount = m_IndexBuffer.m_IndexData.size() / sizeof(QT3DSU16);
+
+ // Setup input to the remap system
+ QT3DSU16 theNumGroups = 1;
+ PrimitiveGroup thePrimitiveGroup;
+ thePrimitiveGroup.type = PT_LIST;
+ thePrimitiveGroup.numIndices = theIndexCount;
+ thePrimitiveGroup.indices = new QT3DSU16[theIndexCount];
+ memCopy(thePrimitiveGroup.indices, theIndices, theIndexCount * sizeof(QT3DSU16));
+
+ PrimitiveGroup *theRemappedGroup = NULL;
+ QT3DSU32 vertBufByteSize = m_VertexBuffer.m_VertexData.size();
+ QT3DSU32 numVertexIndices = vertBufByteSize / m_VertexBuffer.m_Stride;
+ QT3DS_ASSERT(numVertexIndices < QT3DS_MAX_U16);
+ QT3DSU32 vertBufStride = m_VertexBuffer.m_Stride;
+ // This remaps the vertexes
+ RemapIndices(&thePrimitiveGroup, theNumGroups, static_cast<QT3DSU16>(numVertexIndices),
+ &theRemappedGroup);
+ m_RemappedVertexData.reserve(vertBufByteSize);
+ const QT3DSU8 *srcVertexPtr(m_VertexBuffer.m_VertexData.begin());
+ QT3DSU8 *dstVertexPtr(m_RemappedVertexData.begin());
+ QT3DS_ASSERT(theNumGroups == 1);
+ for (QT3DSU32 theGroupIdx = 0; theGroupIdx < 1; ++theGroupIdx) {
+ PrimitiveGroup &srcGroup(thePrimitiveGroup);
+ PrimitiveGroup &dstGroup(theRemappedGroup[theGroupIdx]);
+ QT3DS_ASSERT(srcGroup.type == PT_LIST);
+
+ for (QT3DSU32 theIndexIdx = 0; theIndexIdx < srcGroup.numIndices; ++theIndexIdx) {
+ QT3DSU16 srcIndex = srcGroup.indices[theIndexIdx];
+ QT3DSU16 dstIndex = dstGroup.indices[theIndexIdx];
+ QT3DS_ASSERT(dstIndex * m_VertexBuffer.m_Stride < vertBufByteSize);
+ QT3DS_ASSERT(srcIndex * m_VertexBuffer.m_Stride < vertBufByteSize);
+ // Maybe add in a check to see if we possibly already copied this information
+ // That would of course be only an optimization.
+ memCopy(dstVertexPtr + dstIndex * vertBufStride,
+ srcVertexPtr + srcIndex * vertBufStride, vertBufStride);
+ theIndices[theIndexIdx] = dstIndex;
+ }
+ memCopy(m_VertexBuffer.m_VertexData.begin(), m_RemappedVertexData.begin(),
+ vertBufByteSize);
+ }
+
+ DeletePrimitiveGroup(theRemappedGroup);
+#endif // DISABLE_VERTEX_REMAP
+#endif // DISABLE_MESH_OPTIMIZATION
+ }
+
+ template <typename TDataType>
+ static void Assign(QT3DSU8 *inBaseAddress, QT3DSU8 *inDataAddress,
+ SOffsetDataRef<TDataType> &inBuffer, const TDataType *inDestData,
+ QT3DSU32 inDestSize)
+ {
+ inBuffer.m_Offset = (QT3DSU32)(inDataAddress - inBaseAddress);
+ inBuffer.m_Size = inDestSize;
+ memCopy(inDataAddress, inDestData, inDestSize * sizeof(TDataType));
+ }
+ template <typename TDataType>
+ static void Assign(QT3DSU8 *inBaseAddress, QT3DSU8 *inDataAddress,
+ SOffsetDataRef<TDataType> &inBuffer, QT3DSU32 inDestSize)
+ {
+ inBuffer.m_Offset = (QT3DSU32)(inDataAddress - inBaseAddress);
+ inBuffer.m_Size = inDestSize;
+ }
+ // Return the current mesh. This is only good for this function call, item may change or be
+ // released
+ // due to any further function calls.
+ Mesh &GetMesh() override
+ {
+ QT3DSU32 meshSize = sizeof(Mesh);
+ QT3DSU32 alignment = sizeof(void *);
+ QT3DSU32 vertDataSize = GetAlignedOffset(m_VertexBuffer.m_VertexData.size(), alignment);
+ meshSize += vertDataSize;
+ QT3DSU32 entrySize = m_VertexBuffer.m_VertexBufferEntries.size()
+ * sizeof(qt3ds::render::NVRenderVertexBufferEntry);
+ meshSize += entrySize;
+ QT3DSU32 entryNameSize = 0;
+ for (QT3DSU32 idx = 0, end = m_VertexBuffer.m_VertexBufferEntries.size(); idx < end; ++idx) {
+ const qt3ds::render::NVRenderVertexBufferEntry &theEntry(
+ m_VertexBuffer.m_VertexBufferEntries[idx]);
+ const char *entryName = theEntry.m_Name;
+ if (entryName == NULL)
+ entryName = "";
+ entryNameSize += (QT3DSU32)(strlen(theEntry.m_Name)) + 1;
+ }
+ entryNameSize = GetAlignedOffset(entryNameSize, alignment);
+ meshSize += entryNameSize;
+ QT3DSU32 indexBufferSize = GetAlignedOffset(m_IndexBuffer.m_IndexData.size(), alignment);
+ meshSize += indexBufferSize;
+ QT3DSU32 subsetSize = m_MeshSubsetDescs.size() * sizeof(MeshSubset);
+ QT3DSU32 nameSize = 0;
+ for (QT3DSU32 idx = 0, end = m_MeshSubsetDescs.size(); idx < end; ++idx) {
+ if (!m_MeshSubsetDescs[idx].m_Name.isEmpty())
+ nameSize += m_MeshSubsetDescs[idx].m_Name.size() + 1;
+ }
+ nameSize *= sizeof(char16_t);
+ nameSize = GetAlignedOffset(nameSize, alignment);
+
+ meshSize += subsetSize + nameSize;
+ QT3DSU32 jointsSize = m_Joints.size() * sizeof(qt3dsimp::Joint);
+ meshSize += jointsSize;
+ m_MeshBuffer.resize(meshSize);
+ QT3DSU8 *baseAddress = m_MeshBuffer.data();
+ Mesh *retval = reinterpret_cast<Mesh *>(baseAddress);
+ retval->m_DrawMode = m_DrawMode;
+ retval->m_Winding = m_Winding;
+ QT3DSU8 *vertBufferData = baseAddress + sizeof(Mesh);
+ QT3DSU8 *vertEntryData = vertBufferData + vertDataSize;
+ QT3DSU8 *vertEntryNameData = vertEntryData + entrySize;
+ QT3DSU8 *indexBufferData = vertEntryNameData + entryNameSize;
+ QT3DSU8 *subsetBufferData = indexBufferData + indexBufferSize;
+ QT3DSU8 *nameBufferData = subsetBufferData + subsetSize;
+ QT3DSU8 *jointBufferData = nameBufferData + nameSize;
+
+ retval->m_VertexBuffer.m_Stride = m_VertexBuffer.m_Stride;
+ Assign(baseAddress, vertBufferData, retval->m_VertexBuffer.m_Data,
+ m_VertexBuffer.m_VertexData.begin(), m_VertexBuffer.m_VertexData.size());
+ retval->m_VertexBuffer.m_Entries.m_Size = m_VertexBuffer.m_VertexBufferEntries.size();
+ retval->m_VertexBuffer.m_Entries.m_Offset = (QT3DSU32)(vertEntryData - baseAddress);
+ for (QT3DSU32 idx = 0, end = m_VertexBuffer.m_VertexBufferEntries.size(); idx < end; ++idx) {
+ const qt3ds::render::NVRenderVertexBufferEntry &theEntry(
+ m_VertexBuffer.m_VertexBufferEntries[idx]);
+ MeshVertexBufferEntry &theDestEntry(
+ retval->m_VertexBuffer.m_Entries.index(baseAddress, idx));
+ theDestEntry.m_ComponentType = theEntry.m_ComponentType;
+ theDestEntry.m_FirstItemOffset = theEntry.m_FirstItemOffset;
+ theDestEntry.m_NumComponents = theEntry.m_NumComponents;
+ const char *targetName = theEntry.m_Name;
+ if (targetName == NULL)
+ targetName = "";
+
+ QT3DSU32 entryNameLen = (QT3DSU32)(strlen(targetName)) + 1;
+ theDestEntry.m_NameOffset = (QT3DSU32)(vertEntryNameData - baseAddress);
+ memCopy(vertEntryNameData, theEntry.m_Name, entryNameLen);
+ vertEntryNameData += entryNameLen;
+ }
+
+ retval->m_IndexBuffer.m_ComponentType = m_IndexBuffer.m_CompType;
+ Assign(baseAddress, indexBufferData, retval->m_IndexBuffer.m_Data,
+ m_IndexBuffer.m_IndexData.begin(), m_IndexBuffer.m_IndexData.size());
+ Assign(baseAddress, subsetBufferData, retval->m_Subsets, m_MeshSubsetDescs.size());
+ for (QT3DSU32 idx = 0, end = m_MeshSubsetDescs.size(); idx < end; ++idx) {
+ SubsetDesc &theDesc = m_MeshSubsetDescs[idx];
+ MeshSubset &theSubset = reinterpret_cast<MeshSubset *>(subsetBufferData)[idx];
+ theSubset.m_Bounds = theDesc.m_Bounds;
+ theSubset.m_Count = theDesc.m_Count;
+ theSubset.m_Offset = theDesc.m_Offset;
+ if (!theDesc.m_Name.isEmpty()) {
+ theSubset.m_Name.m_Size = theDesc.m_Name.size() + 1;
+ theSubset.m_Name.m_Offset = (QT3DSU32)(nameBufferData - baseAddress);
+ std::transform(theDesc.m_Name.begin(), theDesc.m_Name.end(),
+ reinterpret_cast<char16_t *>(nameBufferData),
+ [](QChar c) { return static_cast<char16_t>(c.unicode()); });
+ reinterpret_cast<char16_t *>(nameBufferData)[theDesc.m_Name.size()] = 0;
+ nameBufferData += (theDesc.m_Name.size() + 1) * sizeof(char16_t);
+ } else {
+ theSubset.m_Name.m_Size = 0;
+ theSubset.m_Name.m_Offset = 0;
+ }
+ }
+ Assign(baseAddress, jointBufferData, retval->m_Joints, m_Joints.data(), m_Joints.size());
+ return *retval;
+ }
+};
+}
+
+// Uses new/delete.
+MeshBuilder &MeshBuilder::CreateMeshBuilder()
+{
+ return *(new MeshBuilderImpl());
+}
diff --git a/src/importlib/Qt3DSImportPath.cpp b/src/importlib/Qt3DSImportPath.cpp
new file mode 100644
index 0000000..3523ef5
--- /dev/null
+++ b/src/importlib/Qt3DSImportPath.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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 "Qt3DSImportLibPrecompile.h"
+#include "Qt3DSImportPath.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+
+using namespace qt3dsimp;
+
+void SPathBuffer::Save(IOutStream &outStream) const
+{
+ outStream.Write(GetFileTag());
+ outStream.Write(GetFileVersion());
+ outStream.Write((QT3DSU32)m_Commands.size());
+ outStream.Write((QT3DSU32)m_Data.size());
+ outStream.Write(toU8ConstDataRef((PathCommand::Enum *)m_Commands.begin(), m_Commands.size()));
+ outStream.Write(toU8ConstDataRef((QT3DSF32 *)m_Data.begin(), m_Data.size()));
+}
+
+SPathBuffer *SPathBuffer::Load(IInStream &inStream, NVFoundationBase &inFoundation)
+{
+ QT3DSU64 fileTag;
+ QT3DSU32 version, numCommands, numData;
+ inStream.Read(fileTag);
+ inStream.Read(version);
+ inStream.Read(numCommands);
+ inStream.Read(numData);
+ if (fileTag != GetFileTag()) {
+ qCCritical(INVALID_OPERATION, "Invalid file, not a path file");
+ return NULL;
+ }
+ if (version > GetFileVersion()) {
+ qCCritical(INVALID_OPERATION, "Version number out of range.");
+ return NULL;
+ }
+ QT3DSU32 commandSize = numCommands * sizeof(QT3DSU32);
+ QT3DSU32 dataSize = numData * sizeof(QT3DSF32);
+ QT3DSU32 objectSize = sizeof(SPathBuffer);
+ QT3DSU32 allocSize = objectSize + commandSize + dataSize;
+ QT3DSU8 *rawData =
+ (QT3DSU8 *)inFoundation.getAllocator().allocate(allocSize, "SPathBuffer", __FILE__, __LINE__);
+ SPathBuffer *retval = new (rawData) SPathBuffer();
+ QT3DSU8 *commandBuffer = rawData + sizeof(SPathBuffer);
+ QT3DSU8 *dataBuffer = commandBuffer + commandSize;
+ inStream.Read(commandBuffer, commandSize);
+ inStream.Read(dataBuffer, dataSize);
+ retval->m_Commands = toDataRef((PathCommand::Enum *)commandBuffer, numCommands);
+ retval->m_Data = toDataRef((QT3DSF32 *)dataBuffer, numData);
+ return retval;
+}
+
+void SPathBuffer::Free(NVAllocatorCallback &inAllocator)
+{
+ inAllocator.deallocate(this);
+}
+
+namespace {
+struct SBuilder : public IPathBufferBuilder
+{
+ NVFoundationBase &m_Foundation;
+ nvvector<PathCommand::Enum> m_Commands;
+ nvvector<QT3DSF32> m_Data;
+ QT3DSI32 m_RefCount;
+
+ SBuilder(NVFoundationBase &inFoundation)
+ : m_Foundation(inFoundation)
+ , m_Commands(inFoundation.getAllocator(), "m_Commands")
+ , m_Data(inFoundation.getAllocator(), "m_Data")
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &theAlloc(m_Foundation.getAllocator());
+ NVDelete(theAlloc, this);
+ }
+ }
+
+ void Clear() override
+ {
+ m_Commands.clear();
+ m_Data.clear();
+ }
+
+ void Push(const QT3DSVec2 inPos)
+ {
+ m_Data.push_back(inPos.x);
+ m_Data.push_back(inPos.y);
+ }
+
+ void MoveTo(const QT3DSVec2 &inPos) override
+ {
+ m_Commands.push_back(PathCommand::MoveTo);
+ Push(inPos);
+ }
+
+ void CubicCurveTo(const QT3DSVec2 &inC1, const QT3DSVec2 &inC2, const QT3DSVec2 &inP2) override
+ {
+ m_Commands.push_back(PathCommand::CubicCurveTo);
+ Push(inC1);
+ Push(inC2);
+ Push(inP2);
+ }
+
+ void Close() override { m_Commands.push_back(PathCommand::Close); }
+
+ // Points back to internal data structures, must use or copy.
+ SPathBuffer GetPathBuffer() override
+ {
+ SPathBuffer retval;
+ retval.m_Data = m_Data;
+ retval.m_Commands = m_Commands;
+ return retval;
+ }
+};
+}
+
+IPathBufferBuilder &IPathBufferBuilder::CreateBuilder(NVFoundationBase &inFoundation)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SBuilder)(inFoundation);
+}
diff --git a/src/importlib/Qt3DSImportPath.h b/src/importlib/Qt3DSImportPath.h
new file mode 100644
index 0000000..3b52c8c
--- /dev/null
+++ b/src/importlib/Qt3DSImportPath.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_IMPORT_PATH_H
+#define QT3DS_IMPORT_PATH_H
+#include "foundation/Qt3DS.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "foundation/Qt3DSBounds3.h"
+#include "foundation/Qt3DSAllocatorCallback.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSVec2.h"
+
+namespace qt3dsimp {
+using namespace qt3ds;
+using namespace qt3ds::foundation;
+using namespace qt3ds::render;
+struct PathCommand
+{
+ enum Enum {
+ Noner = 0,
+ MoveTo, // 2 floats
+ CubicCurveTo, // 6 floats, c1, c2, p2. p1 is existing location
+ Close, // 0 floats
+ };
+};
+
+struct SPathBuffer
+{
+ // 64 bit random number to uniquely identify this file type.
+ static QT3DSU64 GetFileTag() { return 0x7b1a41633c43a6afULL; }
+ static QT3DSU32 GetFileVersion() { return 1; }
+ NVConstDataRef<PathCommand::Enum> m_Commands;
+ NVConstDataRef<QT3DSF32> m_Data;
+ SPathBuffer() {}
+ void Save(IOutStream &outStream) const;
+ static SPathBuffer *Load(IInStream &inStream, NVFoundationBase &inFoundation);
+
+ // Object is unused after this call. Anything created with Load must use this function.
+ void Free(NVAllocatorCallback &inAllocator);
+ SPathBuffer *Copy(NVAllocatorCallback &inAllocator) const;
+};
+
+class IPathBufferBuilder : public NVRefCounted
+{
+public:
+ virtual void Clear() = 0;
+
+ virtual void MoveTo(const QT3DSVec2 &inPos) = 0;
+ virtual void CubicCurveTo(const QT3DSVec2 &inC1, const QT3DSVec2 &inC2, const QT3DSVec2 &inP2) = 0;
+ virtual void Close() = 0;
+ // Points back to internal data structures, must use or copy.
+ virtual SPathBuffer GetPathBuffer() = 0;
+
+ static IPathBufferBuilder &CreateBuilder(NVFoundationBase &inFoundation);
+};
+}
+
+#endif \ No newline at end of file