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