summaryrefslogtreecommitdiffstats
path: root/src/importlib/Qt3DSImportMesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/importlib/Qt3DSImportMesh.cpp')
-rw-r--r--src/importlib/Qt3DSImportMesh.cpp829
1 files changed, 829 insertions, 0 deletions
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));
+}