mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
Classes under "rulesets" have been moved to "rules", and split up into futher subdirectories matching their library. As a result we can now better separate the python bindings, so that things that belongs to the simulation are separeted from things that belongs to the ai.
837 lines
28 KiB
C++
837 lines
28 KiB
C++
/*
|
|
Copyright (C) 2018 Erik Ogenvik
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
Parts of this file is taken from the OGRE3D(http://www.ogre3d.org/) project, which uses a MIT license.
|
|
|
|
Copyright (c) 2000-2014 Torus Knot Software Ltd
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
|
|
#include "OgreMeshDeserializer.h"
|
|
|
|
#include <sstream>
|
|
|
|
const std::uint16_t HEADER_STREAM_ID = 0x1000;
|
|
const std::uint16_t OTHER_ENDIAN_HEADER_STREAM_ID = 0x0010;
|
|
|
|
const char* OGRE_MESH_VERSION = "[MeshSerializer_v1.100]";
|
|
|
|
|
|
/** Definition of the OGRE .mesh file format
|
|
|
|
.mesh files are binary files (for read efficiency at runtime) and are arranged into chunks
|
|
of data, very like 3D Studio's format.
|
|
A chunk always consists of:
|
|
unsigned short CHUNK_ID : one of the following chunk ids identifying the chunk
|
|
unsigned long LENGTH : length of the chunk in bytes, including this header
|
|
void* DATA : the data, which may contain other sub-chunks (various data types)
|
|
|
|
A .mesh file can contain both the definition of the Mesh itself, and optionally the definitions
|
|
of the materials is uses (although these can be omitted, if so the Mesh assumes that at runtime the
|
|
Materials referred to by name in the Mesh are loaded/created from another source)
|
|
|
|
A .mesh file only contains a single mesh, which can itself have multiple submeshes.
|
|
|
|
*/
|
|
|
|
enum MeshChunkID {
|
|
M_HEADER = 0x1000,
|
|
// char* version : Version number check
|
|
M_MESH = 0x3000,
|
|
// bool skeletallyAnimated // important flag which affects h/w buffer policies
|
|
// Optional M_GEOMETRY chunk
|
|
M_SUBMESH = 0x4000,
|
|
// char* materialName
|
|
// bool useSharedVertices
|
|
// unsigned int indexCount
|
|
// bool indexes32Bit
|
|
// unsigned int* faceVertexIndices (indexCount)
|
|
// OR
|
|
// unsigned short* faceVertexIndices (indexCount)
|
|
// M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
|
|
M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
|
|
// unsigned short operationType
|
|
M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
|
|
// Optional bone weights (repeating section)
|
|
// unsigned int vertexIndex;
|
|
// unsigned short boneIndex;
|
|
// float weight;
|
|
// Optional chunk that matches a texture name to an alias
|
|
// a texture alias is sent to the submesh material to use this texture name
|
|
// instead of the one in the texture unit with a matching alias name
|
|
M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section
|
|
// char* aliasName;
|
|
// char* textureName;
|
|
|
|
M_GEOMETRY = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
|
|
// unsigned int vertexCount
|
|
M_GEOMETRY_VERTEX_DECLARATION = 0x5100,
|
|
M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section
|
|
// unsigned short source; // buffer bind source
|
|
// unsigned short type; // VertexElementType
|
|
// unsigned short semantic; // VertexElementSemantic
|
|
// unsigned short offset; // start offset in buffer in bytes
|
|
// unsigned short index; // index of the semantic (for colours and texture coords)
|
|
M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section
|
|
// unsigned short bindIndex; // Index to bind this buffer to
|
|
// unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index
|
|
M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210,
|
|
// raw buffer data
|
|
M_MESH_SKELETON_LINK = 0x6000,
|
|
// Optional link to skeleton
|
|
// char* skeletonName : name of .skeleton to use
|
|
M_MESH_BONE_ASSIGNMENT = 0x7000,
|
|
// Optional bone weights (repeating section)
|
|
// unsigned int vertexIndex;
|
|
// unsigned short boneIndex;
|
|
// float weight;
|
|
M_MESH_LOD_LEVEL = 0x8000,
|
|
// Optional LOD information
|
|
// string strategyName;
|
|
// unsigned short numLevels;
|
|
// bool manual; (true for manual alternate meshes, false for generated)
|
|
M_MESH_LOD_USAGE = 0x8100,
|
|
// Repeating section, ordered in increasing depth
|
|
// NB LOD 0 (full detail from 0 depth) is omitted
|
|
// LOD value - this is a distance, a pixel count etc, based on strategy
|
|
// float lodValue;
|
|
M_MESH_LOD_MANUAL = 0x8110,
|
|
// Required if M_MESH_LOD section manual = true
|
|
// String manualMeshName;
|
|
M_MESH_LOD_GENERATED = 0x8120,
|
|
// Required if M_MESH_LOD section manual = false
|
|
// Repeating section (1 per submesh)
|
|
// unsigned int indexCount;
|
|
// bool indexes32Bit
|
|
// unsigned short* faceIndexes; (indexCount)
|
|
// OR
|
|
// unsigned int* faceIndexes; (indexCount)
|
|
M_MESH_BOUNDS = 0x9000,
|
|
// float minx, miny, minz
|
|
// float maxx, maxy, maxz
|
|
// float radius
|
|
|
|
// Added By DrEvil
|
|
// optional chunk that contains a table of submesh indexes and the names of
|
|
// the sub-meshes.
|
|
M_SUBMESH_NAME_TABLE = 0xA000,
|
|
// Subchunks of the name table. Each chunk contains an index & string
|
|
M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100,
|
|
// short index
|
|
// char* name
|
|
|
|
// Optional chunk which stores precomputed edge data
|
|
M_EDGE_LISTS = 0xB000,
|
|
// Each LOD has a separate edge list
|
|
M_EDGE_LIST_LOD = 0xB100,
|
|
// unsigned short lodIndex
|
|
// bool isManual // If manual, no edge data here, loaded from manual mesh
|
|
// bool isClosed
|
|
// unsigned long numTriangles
|
|
// unsigned long numEdgeGroups
|
|
// Triangle* triangleList
|
|
// unsigned long indexSet
|
|
// unsigned long vertexSet
|
|
// unsigned long vertIndex[3]
|
|
// unsigned long sharedVertIndex[3]
|
|
// float normal[4]
|
|
|
|
M_EDGE_GROUP = 0xB110,
|
|
// unsigned long vertexSet
|
|
// unsigned long triStart
|
|
// unsigned long triCount
|
|
// unsigned long numEdges
|
|
// Edge* edgeList
|
|
// unsigned long triIndex[2]
|
|
// unsigned long vertIndex[2]
|
|
// unsigned long sharedVertIndex[2]
|
|
// bool degenerate
|
|
|
|
// Optional poses section, referred to by pose keyframes
|
|
M_POSES = 0xC000,
|
|
M_POSE = 0xC100,
|
|
// char* name (may be blank)
|
|
// unsigned short target // 0 for shared geometry,
|
|
// 1+ for submesh index + 1
|
|
// bool includesNormals [1.8+]
|
|
M_POSE_VERTEX = 0xC111,
|
|
// unsigned long vertexIndex
|
|
// float xoffset, yoffset, zoffset
|
|
// float xnormal, ynormal, znormal (optional, 1.8+)
|
|
// Optional vertex animation chunk
|
|
M_ANIMATIONS = 0xD000,
|
|
M_ANIMATION = 0xD100,
|
|
// char* name
|
|
// float length
|
|
M_ANIMATION_BASEINFO = 0xD105,
|
|
// [Optional] base keyframe information (pose animation only)
|
|
// char* baseAnimationName (blank for self)
|
|
// float baseKeyFrameTime
|
|
|
|
M_ANIMATION_TRACK = 0xD110,
|
|
// unsigned short type // 1 == morph, 2 == pose
|
|
// unsigned short target // 0 for shared geometry,
|
|
// 1+ for submesh index + 1
|
|
M_ANIMATION_MORPH_KEYFRAME = 0xD111,
|
|
// float time
|
|
// bool includesNormals [1.8+]
|
|
// float x,y,z // repeat by number of vertices in original geometry
|
|
M_ANIMATION_POSE_KEYFRAME = 0xD112,
|
|
// float time
|
|
M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses
|
|
// unsigned short poseIndex
|
|
// float influence
|
|
|
|
// Optional submesh extreme vertex list chink
|
|
M_TABLE_EXTREMES = 0xE000,
|
|
// unsigned short submesh_index;
|
|
// float extremes [n_extremes][3];
|
|
|
|
/* Version 1.2 of the .mesh format (deprecated)
|
|
enum MeshChunkID {
|
|
M_HEADER = 0x1000,
|
|
// char* version : Version number check
|
|
M_MESH = 0x3000,
|
|
// bool skeletallyAnimated // important flag which affects h/w buffer policies
|
|
// Optional M_GEOMETRY chunk
|
|
M_SUBMESH = 0x4000,
|
|
// char* materialName
|
|
// bool useSharedVertices
|
|
// unsigned int indexCount
|
|
// bool indexes32Bit
|
|
// unsigned int* faceVertexIndices (indexCount)
|
|
// OR
|
|
// unsigned short* faceVertexIndices (indexCount)
|
|
// M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
|
|
M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
|
|
// unsigned short operationType
|
|
M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
|
|
// Optional bone weights (repeating section)
|
|
// unsigned int vertexIndex;
|
|
// unsigned short boneIndex;
|
|
// float weight;
|
|
M_GEOMETRY = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
|
|
*/
|
|
// unsigned int vertexCount
|
|
// float* pVertices (x, y, z order x numVertices)
|
|
M_GEOMETRY_NORMALS = 0x5100, //(Optional)
|
|
// float* pNormals (x, y, z order x numVertices)
|
|
M_GEOMETRY_COLOURS = 0x5200, //(Optional)
|
|
// unsigned long* pColours (RGBA 8888 format x numVertices)
|
|
M_GEOMETRY_TEXCOORDS = 0x5300 //(Optional, REPEATABLE, each one adds an extra set)
|
|
// unsigned short dimensions (1 for 1D, 2 for 2D, 3 for 3D)
|
|
// float* pTexCoords (u [v] [w] order, dimensions x numVertices)
|
|
/*
|
|
M_MESH_SKELETON_LINK = 0x6000,
|
|
// Optional link to skeleton
|
|
// char* skeletonName : name of .skeleton to use
|
|
M_MESH_BONE_ASSIGNMENT = 0x7000,
|
|
// Optional bone weights (repeating section)
|
|
// unsigned int vertexIndex;
|
|
// unsigned short boneIndex;
|
|
// float weight;
|
|
M_MESH_LOD = 0x8000,
|
|
// Optional LOD information
|
|
// unsigned short numLevels;
|
|
// bool manual; (true for manual alternate meshes, false for generated)
|
|
M_MESH_LOD_USAGE = 0x8100,
|
|
// Repeating section, ordered in increasing depth
|
|
// NB LOD 0 (full detail from 0 depth) is omitted
|
|
// float fromSquaredDepth;
|
|
M_MESH_LOD_MANUAL = 0x8110,
|
|
// Required if M_MESH_LOD section manual = true
|
|
// String manualMeshName;
|
|
M_MESH_LOD_GENERATED = 0x8120,
|
|
// Required if M_MESH_LOD section manual = false
|
|
// Repeating section (1 per submesh)
|
|
// unsigned int indexCount;
|
|
// bool indexes32Bit
|
|
// unsigned short* faceIndexes; (indexCount)
|
|
// OR
|
|
// unsigned int* faceIndexes; (indexCount)
|
|
M_MESH_BOUNDS = 0x9000
|
|
// float minx, miny, minz
|
|
// float maxx, maxy, maxz
|
|
// float radius
|
|
|
|
// Added By DrEvil
|
|
// optional chunk that contains a table of submesh indexes and the names of
|
|
// the sub-meshes.
|
|
M_SUBMESH_NAME_TABLE,
|
|
// Subchunks of the name table. Each chunk contains an index & string
|
|
M_SUBMESH_NAME_TABLE_ELEMENT,
|
|
// short index
|
|
// char* name
|
|
|
|
*/
|
|
};
|
|
/** @} */
|
|
/** @} */
|
|
|
|
|
|
/** Reverses byte order of chunks in buffer, where 'size' is size of one chunk.
|
|
*/
|
|
static inline void bswapChunks(void * pData, size_t size, size_t count)
|
|
{
|
|
for(size_t c = 0; c < count; ++c)
|
|
{
|
|
char swapByte;
|
|
for(char *p0 = (char*)pData + c * size, *p1 = p0 + size - 1; p0 < p1; ++p0, --p1)
|
|
{
|
|
swapByte = *p0;
|
|
*p0 = *p1;
|
|
*p1 = swapByte;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Vertex element semantics, used to identify the meaning of vertex buffer contents
|
|
enum VertexElementSemantic
|
|
{
|
|
/// Position, 3 reals per vertex
|
|
VES_POSITION = 1,
|
|
/// Blending weights
|
|
VES_BLEND_WEIGHTS = 2,
|
|
/// Blending indices
|
|
VES_BLEND_INDICES = 3,
|
|
/// Normal, 3 reals per vertex
|
|
VES_NORMAL = 4,
|
|
/// Diffuse colours
|
|
VES_DIFFUSE = 5,
|
|
/// Specular colours
|
|
VES_SPECULAR = 6,
|
|
/// Texture coordinates
|
|
VES_TEXTURE_COORDINATES = 7,
|
|
/// Binormal (Y axis if normal is Z)
|
|
VES_BINORMAL = 8,
|
|
/// Tangent (X axis if normal is Z)
|
|
VES_TANGENT = 9,
|
|
/// The number of VertexElementSemantic elements (note - the first value VES_POSITION is 1)
|
|
VES_COUNT = 9
|
|
};
|
|
|
|
/**
|
|
* Vertex element type, used to identify the base types of the vertex contents
|
|
*
|
|
* @note VET_SHORT1, VET_SHORT3, VET_USHORT1 and VET_USHORT3 should never be used
|
|
* because they aren't supported on any known hardware - they are unaligned as their size
|
|
* is not a multiple of 4 bytes. Therefore drivers usually must add padding on upload.
|
|
*/
|
|
enum VertexElementType
|
|
{
|
|
VET_FLOAT1 = 0,
|
|
VET_FLOAT2 = 1,
|
|
VET_FLOAT3 = 2,
|
|
VET_FLOAT4 = 3,
|
|
/// alias to more specific colour type - use the current rendersystem's colour packing
|
|
VET_COLOUR = 4,
|
|
VET_SHORT1 = 5, ///< @deprecated (see #VertexElementType note)
|
|
VET_SHORT2 = 6,
|
|
VET_SHORT3 = 7, ///< @deprecated (see #VertexElementType note)
|
|
VET_SHORT4 = 8,
|
|
VET_UBYTE4 = 9,
|
|
/// D3D style compact colour
|
|
VET_COLOUR_ARGB = 10,
|
|
/// GL style compact colour
|
|
VET_COLOUR_ABGR = 11,
|
|
|
|
// the following are not universally supported on all hardware:
|
|
VET_DOUBLE1 = 12,
|
|
VET_DOUBLE2 = 13,
|
|
VET_DOUBLE3 = 14,
|
|
VET_DOUBLE4 = 15,
|
|
VET_USHORT1 = 16, ///< @deprecated (see #VertexElementType note)
|
|
VET_USHORT2 = 17,
|
|
VET_USHORT3 = 18, ///< @deprecated (see #VertexElementType note)
|
|
VET_USHORT4 = 19,
|
|
VET_INT1 = 20,
|
|
VET_INT2 = 21,
|
|
VET_INT3 = 22,
|
|
VET_INT4 = 23,
|
|
VET_UINT1 = 24,
|
|
VET_UINT2 = 25,
|
|
VET_UINT3 = 26,
|
|
VET_UINT4 = 27,
|
|
VET_BYTE4 = 28, /// signed bytes
|
|
VET_BYTE4_NORM = 29, /// signed bytes (normalized to -1..1)
|
|
VET_UBYTE4_NORM = 30, /// unsigned bytes (normalized to 0..1)
|
|
VET_SHORT2_NORM = 31, /// signed shorts (normalized to -1..1)
|
|
VET_SHORT4_NORM = 32,
|
|
VET_USHORT2_NORM = 33, /// unsigned shorts (normalized to 0..1)
|
|
VET_USHORT4_NORM = 34
|
|
};
|
|
|
|
|
|
OgreMeshDeserializer::OgreMeshDeserializer(std::fstream& stream)
|
|
: m_stream(stream),
|
|
m_flipEndian(false)
|
|
{
|
|
|
|
}
|
|
|
|
void OgreMeshDeserializer::deserialize()
|
|
{
|
|
|
|
// Determine endianness (must be the first thing we do!)
|
|
determineEndianness(m_stream);
|
|
|
|
// Check header
|
|
readFileHeader(m_stream);
|
|
|
|
unsigned short streamID = readChunk(m_stream);
|
|
|
|
while (!m_stream.eof()) {
|
|
switch (streamID) {
|
|
case M_MESH:
|
|
readMesh();
|
|
break;
|
|
default:
|
|
skipChunk(m_stream);
|
|
}
|
|
|
|
streamID = readChunk(m_stream);
|
|
}
|
|
|
|
}
|
|
|
|
void OgreMeshDeserializer::readGeometry()
|
|
{
|
|
std::vector<char> vertexBuffer;
|
|
std::vector<OgreMeshDeserializer::VertexElement> elements;
|
|
|
|
unsigned int vertexCount = 0;
|
|
readInts(m_stream, &vertexCount, 1);
|
|
// Find optional geometry streams
|
|
if (!m_stream.eof()) {
|
|
|
|
unsigned short streamID = readChunk(m_stream);
|
|
while (!m_stream.eof() &&
|
|
(streamID == M_GEOMETRY_VERTEX_DECLARATION ||
|
|
streamID == M_GEOMETRY_VERTEX_BUFFER)) {
|
|
switch (streamID) {
|
|
case M_GEOMETRY_VERTEX_DECLARATION:
|
|
elements = readGeometryVertexDeclaration();
|
|
break;
|
|
case M_GEOMETRY_VERTEX_BUFFER:
|
|
vertexBuffer = readGeometryVertexBuffer(vertexCount);
|
|
break;
|
|
default:
|
|
skipChunk(m_stream);
|
|
}
|
|
// Get next stream
|
|
if (!m_stream.eof()) {
|
|
streamID = readChunk(m_stream);
|
|
}
|
|
}
|
|
|
|
if (!elements.empty()) {
|
|
for (auto& element : elements) {
|
|
if (element.vSemantic == VertexElementSemantic::VES_POSITION) {
|
|
size_t vertexSize = vertexBuffer.size() / vertexCount;
|
|
|
|
for (size_t i = 0; i < vertexCount; ++i) {
|
|
char* vertexStart = vertexBuffer.data() + (i * vertexSize);
|
|
char* positionStart = vertexStart + element.offset;
|
|
if (element.vType == VertexElementType::VET_FLOAT3) {
|
|
float* positions = reinterpret_cast<float*>(positionStart);
|
|
m_vertices.push_back(*positions);
|
|
positions++;
|
|
m_vertices.push_back(*positions);
|
|
positions++;
|
|
m_vertices.push_back(*positions);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!m_stream.eof()) {
|
|
// Backpedal back to start of non-submesh stream
|
|
backpedalChunkHeader(m_stream);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
std::vector<OgreMeshDeserializer::VertexElement> OgreMeshDeserializer::readGeometryVertexDeclaration()
|
|
{
|
|
std::vector<OgreMeshDeserializer::VertexElement> elements;
|
|
// Find optional geometry streams
|
|
if (!m_stream.eof()) {
|
|
|
|
unsigned short streamID = readChunk(m_stream);
|
|
while (!m_stream.eof() &&
|
|
(streamID == M_GEOMETRY_VERTEX_ELEMENT)) {
|
|
switch (streamID) {
|
|
case M_GEOMETRY_VERTEX_ELEMENT:
|
|
elements.push_back(readGeometryVertexElement());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// Get next m_stream
|
|
if (!m_stream.eof()) {
|
|
streamID = readChunk(m_stream);
|
|
}
|
|
}
|
|
|
|
if (!m_stream.eof()) {
|
|
// Backpedal back to start of non-submesh m_stream
|
|
backpedalChunkHeader(m_stream);
|
|
}
|
|
|
|
}
|
|
return elements;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
OgreMeshDeserializer::VertexElement OgreMeshDeserializer::readGeometryVertexElement()
|
|
{
|
|
VertexElement element{};
|
|
// unsigned short source; // buffer bind source
|
|
readShorts(m_stream, &element.source, 1);
|
|
// unsigned short type; // VertexElementType
|
|
readShorts(m_stream, &element.vType, 1);
|
|
// unsigned short semantic; // VertexElementSemantic
|
|
readShorts(m_stream, &element.vSemantic, 1);
|
|
// unsigned short offset; // start offset in buffer in bytes
|
|
readShorts(m_stream, &element.offset, 1);
|
|
// unsigned short index; // index of the semantic
|
|
readShorts(m_stream, &element.index, 1);
|
|
return element;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
std::vector<char> OgreMeshDeserializer::readGeometryVertexBuffer(unsigned int vertexCount)
|
|
{
|
|
std::vector<char> vertices;
|
|
unsigned short bindIndex, vertexSize;
|
|
// unsigned short bindIndex; // Index to bind this buffer to
|
|
readShorts(m_stream, &bindIndex, 1);
|
|
// unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index
|
|
readShorts(m_stream, &vertexSize, 1);
|
|
|
|
{
|
|
// Check for vertex data header
|
|
unsigned short headerID;
|
|
headerID = readChunk(m_stream);
|
|
if (headerID != M_GEOMETRY_VERTEX_BUFFER_DATA) {
|
|
std::runtime_error("Can't find vertex buffer data area");
|
|
}
|
|
|
|
// Create / populate vertex buffer
|
|
vertices.resize(vertexCount * vertexSize);
|
|
m_stream.read(vertices.data(), vertexCount * vertexSize);
|
|
|
|
}
|
|
|
|
return vertices;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readMesh()
|
|
{
|
|
//First value is whether it's skeletally animated
|
|
m_stream.seekg(sizeof(bool), std::ios_base::cur);
|
|
|
|
// Find all substreams
|
|
if (!m_stream.eof()) {
|
|
|
|
unsigned short streamID = readChunk(m_stream);
|
|
while (!m_stream.eof() &&
|
|
(streamID == M_GEOMETRY ||
|
|
streamID == M_SUBMESH ||
|
|
streamID == M_MESH_SKELETON_LINK ||
|
|
streamID == M_MESH_BONE_ASSIGNMENT ||
|
|
streamID == M_MESH_LOD_LEVEL ||
|
|
streamID == M_MESH_BOUNDS ||
|
|
streamID == M_SUBMESH_NAME_TABLE ||
|
|
streamID == M_EDGE_LISTS ||
|
|
streamID == M_POSES ||
|
|
streamID == M_ANIMATIONS ||
|
|
streamID == M_TABLE_EXTREMES)) {
|
|
switch (streamID) {
|
|
case M_GEOMETRY:
|
|
readGeometry();
|
|
break;
|
|
case M_SUBMESH:
|
|
readSubMesh();
|
|
break;
|
|
case M_MESH_BOUNDS:
|
|
readBoundsInfo();
|
|
break;
|
|
default:
|
|
skipChunk(m_stream);
|
|
}
|
|
|
|
if (!m_stream.eof()) {
|
|
streamID = readChunk(m_stream);
|
|
}
|
|
|
|
}
|
|
if (!m_stream.eof()) {
|
|
// Backpedal back to start of m_stream
|
|
backpedalChunkHeader(m_stream);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readSubMesh()
|
|
{
|
|
unsigned short streamID;
|
|
|
|
std::string materialName = readString(m_stream);
|
|
|
|
bool useSharedVertices;
|
|
readBools(m_stream, &useSharedVertices, 1);
|
|
|
|
size_t offset = 0;
|
|
if (!useSharedVertices) {
|
|
offset = m_vertices.size() / 3;
|
|
}
|
|
|
|
unsigned int indexCount = 0;
|
|
readInts(m_stream, &indexCount, 1);
|
|
|
|
bool idx32bit;
|
|
readBools(m_stream, &idx32bit, 1);
|
|
if (indexCount > 0) {
|
|
if (idx32bit) {
|
|
|
|
std::vector<std::uint32_t> indices;
|
|
indices.resize(indexCount);
|
|
readInts(m_stream, indices.data(), indexCount);
|
|
|
|
for (auto& index : indices) {
|
|
m_indices.emplace_back(index + offset);
|
|
}
|
|
|
|
} else // 16-bit
|
|
{
|
|
std::vector<std::uint16_t> indices;
|
|
indices.resize(indexCount);
|
|
readShorts(m_stream, indices.data(), indexCount);
|
|
for (auto& index : indices) {
|
|
m_indices.emplace_back(index + offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
// M_GEOMETRY m_stream (Optional: present only if useSharedVertices = false)
|
|
if (!useSharedVertices) {
|
|
streamID = readChunk(m_stream);
|
|
if (streamID != M_GEOMETRY) {
|
|
std::runtime_error("Missing geometry data in mesh file");
|
|
}
|
|
readGeometry();
|
|
}
|
|
|
|
|
|
// Find all bone assignments, submesh operation, and texture aliases (if present)
|
|
if (!m_stream.eof()) {
|
|
streamID = readChunk(m_stream);
|
|
while (!m_stream.eof() &&
|
|
(streamID == M_SUBMESH_BONE_ASSIGNMENT ||
|
|
streamID == M_SUBMESH_OPERATION ||
|
|
streamID == M_SUBMESH_TEXTURE_ALIAS)) {
|
|
skipChunk(m_stream);
|
|
|
|
if (!m_stream.eof()) {
|
|
streamID = readChunk(m_stream);
|
|
}
|
|
|
|
}
|
|
if (!m_stream.eof()) {
|
|
// Backpedal back to start of m_stream
|
|
backpedalChunkHeader(m_stream);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void OgreMeshDeserializer::readBoundsInfo()
|
|
{
|
|
WFMath::Point<3> min, max;
|
|
readFloats(m_stream, &min.x(), 1);
|
|
readFloats(m_stream, &min.y(), 1);
|
|
readFloats(m_stream, &min.z(), 1);
|
|
readFloats(m_stream, &max.x(), 1);
|
|
readFloats(m_stream, &max.y(), 1);
|
|
readFloats(m_stream, &max.z(), 1);
|
|
|
|
min.setValid(true);
|
|
max.setValid(true);
|
|
m_bounds = WFMath::AxisBox<3>(min, max);
|
|
readFloats(m_stream, &m_radius, 1);
|
|
}
|
|
|
|
void OgreMeshDeserializer::determineEndianness(std::istream& stream)
|
|
{
|
|
if (stream.tellg() != 0) {
|
|
throw std::runtime_error(
|
|
"Can only determine the endianness of the input stream if it "
|
|
"is at the start");
|
|
}
|
|
|
|
std::uint16_t dest;
|
|
// read header id manually (no conversion)
|
|
stream.read(reinterpret_cast<char*>(&dest), sizeof(std::uint16_t));
|
|
auto actually_read = stream.gcount();
|
|
// skip back
|
|
stream.seekg(0);
|
|
if (actually_read != sizeof(std::uint16_t)) {
|
|
// end of file?
|
|
std::runtime_error("Couldn't read 16 bit header value from input stream.");
|
|
}
|
|
if (dest == HEADER_STREAM_ID) {
|
|
m_flipEndian = false;
|
|
} else if (dest == OTHER_ENDIAN_HEADER_STREAM_ID) {
|
|
m_flipEndian = true;
|
|
} else {
|
|
std::runtime_error("Header chunk didn't match either endian: Corrupted stream?");
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readFileHeader(std::istream& stream)
|
|
{
|
|
unsigned short headerID;
|
|
|
|
// Read header ID
|
|
readShorts(stream, &headerID, 1);
|
|
|
|
if (headerID == HEADER_STREAM_ID) {
|
|
// Read version
|
|
std::string ver = readString(stream);
|
|
if (ver != OGRE_MESH_VERSION) {
|
|
std::runtime_error("Invalid file: version incompatible, file reports " + ver +
|
|
" Deserializer is version " + OGRE_MESH_VERSION);
|
|
}
|
|
} else {
|
|
std::runtime_error("Invalid file: no header");
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
unsigned short OgreMeshDeserializer::readChunk(std::istream& stream)
|
|
{
|
|
unsigned short id;
|
|
readShorts(stream, &id, 1);
|
|
|
|
readInts(stream, &mCurrentstreamLen, 1);
|
|
|
|
return id;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readBools(std::istream& stream, bool* pDest, size_t count)
|
|
{
|
|
char pTemp[count];
|
|
stream.read(pTemp, 1 * count);
|
|
for (unsigned int i = 0; i < count; i++)
|
|
*(pDest + i) = *(pTemp + i);
|
|
|
|
//no flipping on 1-byte datatypes
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readFloats(std::istream& stream, float* pDest, size_t count)
|
|
{
|
|
stream.read(reinterpret_cast<char*>(pDest), sizeof(float) * count);
|
|
flipFromLittleEndian(pDest, sizeof(float), count);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readShorts(std::istream& stream, unsigned short* pDest, size_t count)
|
|
{
|
|
stream.read(reinterpret_cast<char*>(pDest), sizeof(unsigned short) * count);
|
|
flipFromLittleEndian(pDest, sizeof(unsigned short), count);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
void OgreMeshDeserializer::readInts(std::istream& stream, std::uint32_t* pDest, size_t count)
|
|
{
|
|
stream.read(reinterpret_cast<char*>(pDest), sizeof(std::uint32_t) * count);
|
|
flipFromLittleEndian(pDest, sizeof(std::uint32_t), count);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
std::string OgreMeshDeserializer::readString(std::istream& stream)
|
|
{
|
|
std::stringbuf sb;
|
|
stream.get(sb);
|
|
stream.seekg(1, std::ios_base::cur); //skip delimiter
|
|
return sb.str();
|
|
}
|
|
|
|
void OgreMeshDeserializer::flipFromLittleEndian(void* pData, size_t size, size_t count)
|
|
{
|
|
if (m_flipEndian) {
|
|
bswapChunks(pData, size, count);
|
|
}
|
|
}
|
|
|
|
size_t OgreMeshDeserializer::calcChunkHeaderSize()
|
|
{
|
|
return sizeof(std::uint16_t) + sizeof(std::uint32_t);
|
|
}
|
|
|
|
void OgreMeshDeserializer::skipChunk(std::istream& stream)
|
|
{
|
|
stream.seekg(mCurrentstreamLen - calcChunkHeaderSize(), std::ios_base::cur);
|
|
}
|
|
|
|
void OgreMeshDeserializer::backpedalChunkHeader(std::istream& stream)
|
|
{
|
|
if (!stream.eof()) {
|
|
stream.seekg(-(int) calcChunkHeaderSize(), std::ios_base::cur);
|
|
}
|
|
|
|
}
|
|
|