Add spm loader

This commit is contained in:
Benau 2017-08-16 13:48:27 +08:00
parent 196fe378b4
commit 3031fabcf9
8 changed files with 1100 additions and 25 deletions

View File

@ -3,11 +3,11 @@
<config>
<!-- Minimum and maximum kart versions that can be used by this binary.
Older version will be ignored. -->
<kart-version min="2" max="2"/>
<kart-version min="2" max="3"/>
<!-- Minimum and maxium track versions that be be read by this binary.
Older versions will be ignored. -->
<track-version min="6" max="6"/>
<track-version min="6" max="7"/>
<!-- Maximum number of karts to be used at the same time. This limit
can easily be increased, but some tracks might not have valid start

View File

@ -21,7 +21,7 @@ namespace scene
CSkinnedMesh::CSkinnedMesh()
: SkinningBuffers(0), AnimationFrames(0.f), FramesPerSecond(25.f),
LastAnimatedFrame(-1), SkinnedLastFrame(false),
InterpolationMode(EIM_LINEAR),
InterpolationMode(EIM_LINEAR), TransposedMatrix(false),
HasAnimation(false), PreparedForSkinning(false),
AnimateNormals(true), HardwareSkinning(false), m_total_joints(0),
m_current_joint(0)
@ -92,6 +92,13 @@ IMesh* CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32
}
//! For loader usage.
void CSkinnedMesh::setTransposedMatrix(bool val)
{
TransposedMatrix = val;
}
//--------------------------------------------------------------------------
// Keyframe Animation
//--------------------------------------------------------------------------
@ -183,7 +190,14 @@ void CSkinnedMesh::buildAllLocalAnimatedMatrices()
// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility.
// Not tested so far if this was correct or wrong before quaternion fix!
if (TransposedMatrix)
{
joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix);
}
else
{
joint->LocalAnimatedMatrix=joint->Animatedrotation.getMatrix();
}
// --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() ---
f32 *m1 = joint->LocalAnimatedMatrix.pointer();

View File

@ -155,6 +155,8 @@ namespace scene
virtual void updateBoundingBox(void);
void setTransposedMatrix(bool);
//! Recovers the joints from the mesh
void recoverJointsFromMesh(core::array<IBoneSceneNode*> &jointChildSceneNodes);
@ -223,6 +225,7 @@ private:
E_INTERPOLATION_MODE InterpolationMode:8;
bool TransposedMatrix;
bool HasAnimation;
bool PreparedForSkinning;
bool AnimateNormals;

View File

@ -37,6 +37,7 @@
#include "graphics/stk_animated_mesh.hpp"
#include "graphics/stk_billboard.hpp"
#include "graphics/stk_mesh_loader.hpp"
#include "graphics/sp_mesh_loader.hpp"
#include "graphics/stk_mesh_scene_node.hpp"
#include "graphics/stk_tex_manager.hpp"
#include "graphics/stk_texture.hpp"
@ -605,6 +606,9 @@ void IrrDriver::initDevice()
STKMeshLoader* sml = new STKMeshLoader(m_scene_manager);
m_scene_manager->addExternalMeshLoader(sml);
sml->drop();
SPMeshLoader* spml = new SPMeshLoader(m_scene_manager);
m_scene_manager->addExternalMeshLoader(spml);
spml->drop();
m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize();

View File

@ -0,0 +1,637 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// 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 3
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "graphics/sp_mesh_loader.hpp"
#include "graphics/stk_tex_manager.hpp"
#include "utils/constants.hpp"
const uint8_t VERSION_NOW = 1;
#include <algorithm>
#include <cmath>
#include <IVideoDriver.h>
#include <IFileSystem.h>
// ----------------------------------------------------------------------------
bool SPMeshLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension(filename, "spm");
} // isALoadableFileExtension
// ----------------------------------------------------------------------------
scene::IAnimatedMesh* SPMeshLoader::createMesh(io::IReadFile* f)
{
if (!IS_LITTLE_ENDIAN)
{
Log::error("SPMeshLoader", "Not little endian machine.");
return NULL;
}
if (f == NULL)
{
return NULL;
}
m_bind_frame = 0;
m_joint_count = 0;
//m_frame_count = 0;
m_mesh = NULL;
m_mesh = new scene::CSkinnedMesh();
io::IFileSystem* fs = m_scene_manager->getFileSystem();
std::string base_path = fs->getFileDir(f->getFileName()).c_str();
std::string header;
header.resize(2);
f->read(&header.front(), 2);
if (header != "SP")
{
Log::error("SPMeshLoader", "Not a spm file.");
m_mesh->drop();
return NULL;
}
uint8_t byte = 0;
f->read(&byte, 1);
uint8_t version = byte >> 3;
if (version != VERSION_NOW)
{
Log::error("SPMeshLoader", "Version mismatch, file %d SP %d", version,
VERSION_NOW);
m_mesh->drop();
return NULL;
}
byte &= ~0x08;
header = byte == 0 ? "SPMS" : byte == 1 ? "SPMA" : "SPMN";
if (header == "SPMS")
{
Log::error("SPMeshLoader", "Space partitioned mesh not supported.");
m_mesh->drop();
return NULL;
}
f->read(&byte, 1);
bool read_normal = byte & 0x01;
bool read_vcolor = byte >> 1 & 0x01;
bool read_tangent = byte >> 2 & 0x01;
const bool is_skinned = header == "SPMA";
const SPVertexType vt = is_skinned ? SPVT_SKINNED : SPVT_NORMAL;
float bbox[6];
f->read(bbox, 24);
uint16_t size_num = 0;
f->read(&size_num, 2);
unsigned id = 0;
std::unordered_map<unsigned, std::tuple<video::SMaterial, bool,
bool> > mat_map;
while (size_num != 0)
{
uint8_t tex_size;
std::string tex_name_1, tex_name_2;
f->read(&tex_size, 1);
if (tex_size > 0)
{
tex_name_1.resize(tex_size);
f->read(&tex_name_1.front(), tex_size);
}
f->read(&tex_size, 1);
if (tex_size > 0)
{
tex_name_2.resize(tex_size);
f->read(&tex_name_2.front(), tex_size);
}
TexConfig mtc(true/*srgb*/, false/*premul_alpha*/, true/*mesh_tex*/,
true/*set_material*/);
video::ITexture* textures[2] = { NULL, NULL };
if (!tex_name_1.empty())
{
std::string full_path = base_path + "/" + tex_name_1;
if (fs->existFile(full_path.c_str()))
{
tex_name_1 = full_path;
}
video::ITexture* tex = STKTexManager::getInstance()
->getTexture(tex_name_1, &mtc);
if (tex != NULL)
{
textures[0] = tex;
}
}
if (!tex_name_2.empty())
{
std::string full_path = base_path + "/" + tex_name_2;
if (fs->existFile(full_path.c_str()))
{
tex_name_2 = full_path;
}
textures[1] = STKTexManager::getInstance()->getTexture(tex_name_2,
&mtc);
}
video::SMaterial m;
m.MaterialType = video::EMT_SOLID;
if (textures[0] != NULL)
{
m.setTexture(0, textures[0]);
}
if (textures[1] != NULL)
{
m.setTexture(1, textures[1]);
}
mat_map[id] =
std::make_tuple(m, !tex_name_1.empty(), !tex_name_2.empty());
size_num--;
id++;
}
f->read(&size_num, 2);
while (size_num != 0)
{
uint16_t mat_size;
f->read(&mat_size, 2);
while (mat_size != 0)
{
uint32_t vertices_count, indices_count;
uint16_t mat_id;
f->read(&vertices_count, 4);
if (vertices_count > 65535)
{
Log::error("SPMeshLoader", "32bit index not supported.");
m_mesh->drop();
return NULL;
}
f->read(&indices_count, 4);
f->read(&mat_id, 2);
assert(mat_id < mat_map.size());
decompress(f, vertices_count, indices_count, read_normal,
read_vcolor, read_tangent, std::get<1>(mat_map[mat_id]),
std::get<2>(mat_map[mat_id]), vt,
std::get<0>(mat_map[mat_id]));
mat_size--;
}
if (header == "SPMS")
{
// Reserved, never used
assert(false);
f->read(bbox, 24);
}
size_num--;
}
if (header == "SPMA")
{
createAnimationData(f);
convertIrrlicht();
}
else if (header == "SPMS")
{
// Reserved, never used
assert(false);
uint16_t pre_computed_size = 0;
f->read(&pre_computed_size, 2);
}
m_mesh->finalize();
m_all_armatures.clear();
m_to_bind_pose_matrices.clear();
m_joints.clear();
return m_mesh;
} // createMesh
// ----------------------------------------------------------------------------
void SPMeshLoader::decompress(irr::io::IReadFile* spm, unsigned vertices_count,
unsigned indices_count, bool read_normal,
bool read_vcolor, bool read_tangent, bool uv_one,
bool uv_two, SPVertexType vt,
const video::SMaterial& m)
{
assert(vertices_count != 0);
assert(indices_count != 0);
scene::SSkinMeshBuffer* mb = m_mesh->addMeshBuffer();
if (uv_two)
{
mb->convertTo2TCoords();
}
using namespace MiniGLM;
const unsigned idx_size = vertices_count > 255 ? 2 : 1;
char tmp[8] = {};
std::vector<std::pair<std::array<short, 4>, std::array<float, 4> > >
cur_joints;
for (unsigned i = 0; i < vertices_count; i++)
{
video::S3DVertex2TCoords vertex;
// 3 * float position
spm->read(&vertex.Pos, 12);
if (read_normal)
{
// 3 10 + 2 bits normal
uint32_t packed;
spm->read(&packed, 4);
vertex.Normal = extract3Int10Bit(packed);
}
if (read_vcolor)
{
// Color identifier
uint8_t ci;
spm->read(&ci, 1);
if (ci == 128)
{
// All white
vertex.Color = video::SColor(255, 255, 255, 255);
}
else
{
uint8_t r, g, b;
spm->read(&r, 1);
spm->read(&g, 1);
spm->read(&b, 1);
vertex.Color = video::SColor(255, r, g, b);
}
}
else
{
vertex.Color = video::SColor(255, 255, 255, 255);
}
if (uv_one)
{
short hf[2];
spm->read(hf, 4);
vertex.TCoords.X = toFloat32(hf[0]);
vertex.TCoords.Y = toFloat32(hf[1]);
assert(!std::isnan(vertex.TCoords.X));
assert(!std::isnan(vertex.TCoords.Y));
if (uv_two)
{
spm->read(hf, 4);
vertex.TCoords2.X = toFloat32(hf[0]);
vertex.TCoords2.Y = toFloat32(hf[1]);
assert(!std::isnan(vertex.TCoords2.X));
assert(!std::isnan(vertex.TCoords2.Y));
}
if (read_tangent)
{
// Unused, tangents are re-calculated anyway
spm->read(tmp, 8);
}
}
if (vt == SPVT_SKINNED)
{
std::array<short, 4> joint_idx;
spm->read(joint_idx.data(), 8);
spm->read(tmp, 8);
std::array<float, 4> joint_weight = {};
for (int j = 0; j < 8; j += 2)
{
short hf;
memcpy(&hf, tmp + j, 2);
const unsigned idx = j >> 1;
joint_weight[idx] = toFloat32(hf);
assert(!std::isnan(joint_weight[idx]));
}
cur_joints.emplace_back(joint_idx, joint_weight);
}
if (uv_two)
{
mb->Vertices_2TCoords.push_back(vertex);
}
else
{
mb->Vertices_Standard.push_back(vertex);
}
}
if (vt == SPVT_SKINNED)
{
m_joints.emplace_back(std::move(cur_joints));
}
if (m.TextureLayer[0].Texture != NULL)
{
mb->Material = m;
}
mb->Indices.set_used(indices_count);
if (idx_size == 2)
{
spm->read(mb->Indices.pointer(), indices_count * 2);
}
else
{
std::vector<uint8_t> tmp_idx;
tmp_idx.resize(indices_count);
spm->read(tmp_idx.data(), indices_count);
for (unsigned i = 0; i < indices_count; i++)
{
mb->Indices[i] = tmp_idx[i];
}
}
} // decompress
// ----------------------------------------------------------------------------
void SPMeshLoader::createAnimationData(irr::io::IReadFile* spm)
{
if (m_joints.empty())
{
Log::error("SPMeshLoader", "No joints are added.");
return;
}
assert(m_joints.size() == m_mesh->getMeshBufferCount());
uint8_t armature_size = 0;
spm->read(&armature_size, 1);
assert(armature_size > 0);
m_bind_frame = 0;
spm->read(&m_bind_frame, 2);
m_all_armatures.resize(armature_size);
for (unsigned i = 0; i < armature_size; i++)
{
m_all_armatures[i].read(spm);
}
for (unsigned i = 0; i < armature_size; i++)
{
//m_frame_count = std::max(m_frame_count,
// (unsigned)m_all_armatures[i].m_frame_pose_matrices.back().first);
m_joint_count += m_all_armatures[i].m_joint_used;
}
m_to_bind_pose_matrices.resize(m_joint_count);
unsigned accumulated_joints = 0;
for (unsigned i = 0; i < armature_size; i++)
{
m_all_armatures[i].getPose((float)m_bind_frame,
&m_to_bind_pose_matrices[accumulated_joints]);
accumulated_joints += m_all_armatures[i].m_joint_used;
}
for (unsigned i = 0; i < m_to_bind_pose_matrices.size(); i++)
{
m_to_bind_pose_matrices[i].makeInverse();
}
for (unsigned i = 0; i < m_mesh->getMeshBufferCount(); i++)
{
for (unsigned j = 0; j < m_joints[i].size(); j++)
{
if (!(m_joints[i][j].first[0] == -1 ||
m_joints[i][j].second[0] == 0.0f))
{
core::vector3df bind_pos, bind_nor;
for (unsigned k = 0; k < 4; k++)
{
if (m_joints[i][j].second[k] == 0.0f)
{
break;
}
core::vector3df cur_pos, cur_nor;
m_to_bind_pose_matrices[m_joints[i][j].first[k]]
.transformVect(cur_pos,
m_mesh->getMeshBuffers()[i]->getVertex(j)->Pos);
bind_pos += cur_pos * m_joints[i][j].second[k];
m_to_bind_pose_matrices[m_joints[i][j].first[k]]
.rotateVect(cur_nor,
m_mesh->getMeshBuffers()[i]->getVertex(j)->Normal);
bind_nor += cur_nor * m_joints[i][j].second[k];
}
m_mesh->getMeshBuffers()[i]->getVertex(j)->Pos = bind_pos;
m_mesh->getMeshBuffers()[i]->getVertex(j)->Normal = bind_nor;
}
}
}
} // createAnimationData
// ----------------------------------------------------------------------------
void SPMeshLoader::convertIrrlicht()
{
unsigned total_joints = 0;
for (unsigned i = 0; i < m_all_armatures.size(); i++)
{
total_joints += (unsigned)m_all_armatures[i].m_joint_names.size();
}
for (unsigned i = 0; i < total_joints; i++)
{
m_mesh->addJoint(NULL);
}
core::array<scene::CSkinnedMesh::SJoint*>& joints = m_mesh->getAllJoints();
std::vector<int> used_joints_map;
used_joints_map.resize(m_joint_count);
total_joints = 0;
unsigned used_joints = 0;
for (unsigned i = 0; i < m_all_armatures.size(); i++)
{
for (unsigned j = 0; j < m_all_armatures[i].m_joint_names.size(); j++)
{
if (m_all_armatures[i].m_joint_used > j)
{
used_joints_map[used_joints + j] = total_joints + j;
}
joints[total_joints + j]->Name =
m_all_armatures[i].m_joint_names[j].c_str();
const int p_id = m_all_armatures[i].m_parent_infos[j];
core::matrix4 tmp;
if (p_id != -1)
{
joints[total_joints + p_id]->Children.push_back
(joints[total_joints + j]);
m_all_armatures[i].m_joint_matrices[j].getInverse(tmp);
tmp = m_all_armatures[i].m_joint_matrices[p_id] * tmp;
}
else
{
m_all_armatures[i].m_joint_matrices[j].getInverse(tmp);
}
joints[total_joints + j]->LocalMatrix = tmp;
for (unsigned k = 0; k <
m_all_armatures[i].m_frame_pose_matrices.size(); k++)
{
float frame = (float)
m_all_armatures[i].m_frame_pose_matrices[k].first;
core::vector3df pos = m_all_armatures[i]
.m_frame_pose_matrices[k].second[j].m_loc;
core::quaternion q = m_all_armatures[i]
.m_frame_pose_matrices[k].second[j].m_rot;
core::vector3df scl = m_all_armatures[i]
.m_frame_pose_matrices[k].second[j].m_scale;
joints[total_joints + j]->PositionKeys.push_back({frame, pos});
joints[total_joints + j]->RotationKeys.push_back({frame, q});
joints[total_joints + j]->ScaleKeys.push_back({frame, scl});
}
}
total_joints += (unsigned)m_all_armatures[i].m_joint_names.size();
used_joints += m_all_armatures[i].m_joint_used;
}
for (unsigned i = 0; i < m_joints.size(); i++)
{
for (unsigned j = 0; j < m_joints[i].size(); j++)
{
for (unsigned k = 0; k < 4; k++)
{
if (m_joints[i][j].first[k] == -1 ||
m_joints[i][j].second[k] == 0.0f)
{
break;
}
scene::CSkinnedMesh::SWeight* w = m_mesh->addWeight
(joints[used_joints_map[m_joints[i][j].first[k]]]);
w->buffer_id = (uint16_t)i;
w->vertex_id = j;
w->strength = m_joints[i][j].second[k];
}
}
}
} // convertIrrlicht
// ----------------------------------------------------------------------------
void SPMeshLoader::Armature::read(irr::io::IReadFile* spm)
{
LocRotScale lrs;
spm->read(&m_joint_used, 2);
assert(m_joint_used > 0);
unsigned all_joints_size = 0;
spm->read(&all_joints_size, 2);
assert(all_joints_size > 0);
m_joint_names.resize(all_joints_size);
for (unsigned i = 0; i < all_joints_size; i++)
{
unsigned str_len = 0;
spm->read(&str_len, 1);
m_joint_names[i].resize(str_len);
spm->read(&m_joint_names[i].front(), str_len);
}
m_joint_matrices.resize(all_joints_size);
m_interpolated_matrices.resize(all_joints_size);
for (unsigned i = 0; i < all_joints_size; i++)
{
lrs.read(spm);
m_joint_matrices[i] = lrs.toMatrix();
}
m_world_matrices.resize(m_interpolated_matrices.size(),
std::make_pair(core::matrix4(), false));
m_parent_infos.resize(all_joints_size);
bool non_parent_bone = false;
for (unsigned i = 0; i < all_joints_size; i++)
{
int16_t info = 0;
spm->read(&info, 2);
if (info == -1)
{
non_parent_bone = true;
}
m_parent_infos[i] = info;
}
if (!non_parent_bone)
{
Log::fatal("SPMeshLoader::Armature", "Non-parent bone missing in"
"armature");
}
unsigned frame_size = 0;
spm->read(&frame_size, 2);
m_frame_pose_matrices.resize(frame_size);
for (unsigned i = 0; i < frame_size; i++)
{
m_frame_pose_matrices[i].second.resize(all_joints_size);
unsigned frame_index = 0;
spm->read(&frame_index, 2);
m_frame_pose_matrices[i].first = frame_index;
for (unsigned j = 0; j < m_frame_pose_matrices[i].second.size(); j++)
{
m_frame_pose_matrices[i].second[j].read(spm);
}
}
} // Armature::read
// ----------------------------------------------------------------------------
void SPMeshLoader::LocRotScale::read(irr::io::IReadFile* spm)
{
float tmp[10];
spm->read(&tmp, 40);
m_loc = core::vector3df(tmp[0], tmp[1], tmp[2]);
m_rot = core::quaternion(tmp[3], tmp[4], tmp[5], tmp[6]);
m_rot.normalize();
m_scale = core::vector3df(tmp[7], tmp[8], tmp[9]);
} // LocRotScale::read
// ----------------------------------------------------------------------------
void SPMeshLoader::Armature::getInterpolatedMatrices(float frame)
{
if (frame < float(m_frame_pose_matrices.front().first) ||
frame >= float(m_frame_pose_matrices.back().first))
{
for (unsigned i = 0; i < m_interpolated_matrices.size(); i++)
{
m_interpolated_matrices[i] =
frame >= float(m_frame_pose_matrices.back().first) ?
m_frame_pose_matrices.back().second[i].toMatrix() :
m_frame_pose_matrices.front().second[i].toMatrix();
}
return;
}
int frame_1 = -1;
int frame_2 = -1;
float interpolation = 0.0f;
for (unsigned i = 0; i < m_frame_pose_matrices.size(); i++)
{
assert(i + 1 < m_frame_pose_matrices.size());
if (frame >= float(m_frame_pose_matrices[i].first) &&
frame < float(m_frame_pose_matrices[i + 1].first))
{
frame_1 = i;
frame_2 = i + 1;
interpolation =
(frame - float(m_frame_pose_matrices[i].first)) /
float(m_frame_pose_matrices[i + 1].first -
m_frame_pose_matrices[i].first);
break;
}
}
assert(frame_1 != -1);
assert(frame_2 != -1);
for (unsigned i = 0; i < m_interpolated_matrices.size(); i++)
{
LocRotScale interpolated;
interpolated.m_loc =
m_frame_pose_matrices[frame_2].second[i].m_loc.getInterpolated
(m_frame_pose_matrices[frame_1].second[i].m_loc, interpolation);
interpolated.m_rot.slerp
(m_frame_pose_matrices[frame_1].second[i].m_rot,
m_frame_pose_matrices[frame_2].second[i].m_rot, interpolation);
interpolated.m_scale =
m_frame_pose_matrices[frame_2].second[i].m_scale.getInterpolated
(m_frame_pose_matrices[frame_1].second[i].m_scale, interpolation);
m_interpolated_matrices[i] = interpolated.toMatrix();
}
} // Armature::getInterpolatedMatrices
// ----------------------------------------------------------------------------
void SPMeshLoader::Armature::getPose(float frame, core::matrix4* dest)
{
getInterpolatedMatrices(frame);
for (auto& p : m_world_matrices)
{
p.second = false;
}
for (unsigned i = 0; i < m_joint_used; i++)
{
dest[i] = getWorldMatrix(m_interpolated_matrices, i) *
m_joint_matrices[i];
}
} // Armature::getPose
// ----------------------------------------------------------------------------
core::matrix4 SPMeshLoader::Armature::getWorldMatrix(
const std::vector<core::matrix4>& matrix, unsigned id)
{
core::matrix4 mat = matrix[id];
int parent_id = m_parent_infos[id];
if (parent_id == -1)
{
m_world_matrices[id] = std::make_pair(mat, true);
return mat;
}
if (!m_world_matrices[parent_id].second)
{
m_world_matrices[parent_id] = std::make_pair
(getWorldMatrix(matrix, parent_id), true);
}
m_world_matrices[id] =
std::make_pair(m_world_matrices[parent_id].first * mat, true);
return m_world_matrices[id].first;
} // Armature::getWorldMatrix

View File

@ -0,0 +1,403 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// 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 3
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_SP_MESH_LOADER_HPP
#define HEADER_SP_MESH_LOADER_HPP
#include "../lib/irrlicht/source/Irrlicht/CSkinnedMesh.h"
#include "utils/types.hpp"
#include <IMeshLoader.h>
#include <ISceneManager.h>
#include <IReadFile.h>
#include <array>
#include <vector>
using namespace irr;
// GLM without template
namespace MiniGLM
{
// ------------------------------------------------------------------------
inline float overflow()
{
volatile float f = 1e10;
for (int i = 0; i < 10; i++)
f *= f; // this will overflow before the for loop terminates
return f;
} // overflow
// ------------------------------------------------------------------------
inline float toFloat32(short value)
{
int s = (value >> 15) & 0x00000001;
int e = (value >> 10) & 0x0000001f;
int m = value & 0x000003ff;
if (e == 0)
{
if (m == 0)
{
//
// Plus or minus zero
//
uint32_t tmp_data = (unsigned int)(s << 31);
float ret;
memcpy(&ret, &tmp_data, 4);
return ret;
}
else
{
//
// Denormalized number -- renormalize it
//
while(!(m & 0x00000400))
{
m <<= 1;
e -= 1;
}
e += 1;
m &= ~0x00000400;
}
}
else if (e == 31)
{
if (m == 0)
{
//
// Positive or negative infinity
//
uint32_t tmp_data = (unsigned int)((s << 31) | 0x7f800000);
float ret;
memcpy(&ret, &tmp_data, 4);
return ret;
}
else
{
//
// Nan -- preserve sign and significand bits
//
uint32_t tmp_data = (unsigned int)((s << 31) | 0x7f800000 |
(m << 13));
float ret;
memcpy(&ret, &tmp_data, 4);
return ret;
}
}
//
// Normalized number
//
e = e + (127 - 15);
m = m << 13;
//
// Assemble s, e and m.
//
uint32_t tmp_data = (unsigned int)((s << 31) | (e << 23) | m);
float ret;
memcpy(&ret, &tmp_data, 4);
return ret;
} // toFloat32
// ------------------------------------------------------------------------
inline short toFloat16(float const & f)
{
int i;
memcpy(&i, &f, 4);
//
// Our floating point number, f, is represented by the bit
// pattern in integer i. Disassemble that bit pattern into
// the sign, s, the exponent, e, and the significand, m.
// Shift s into the position where it will go in in the
// resulting half number.
// Adjust e, accounting for the different exponent bias
// of float and half (127 versus 15).
//
int s = (i >> 16) & 0x00008000;
int e = ((i >> 23) & 0x000000ff) - (127 - 15);
int m = i & 0x007fffff;
//
// Now reassemble s, e and m into a half:
//
if (e <= 0)
{
if (e < -10)
{
//
// E is less than -10. The absolute value of f is
// less than half_MIN (f may be a small normalized
// float, a denormalized float or a zero).
//
// We convert f to a half zero.
//
return short(s);
}
//
// E is between -10 and 0. F is a normalized float,
// whose magnitude is less than __half_NRM_MIN.
//
// We convert f to a denormalized half.
//
m = (m | 0x00800000) >> (1 - e);
//
// Round to nearest, round "0.5" up.
//
// Rounding may cause the significand to overflow and make
// our number normalized. Because of the way a half's bits
// are laid out, we don't have to treat this case separately;
// the code below will handle it correctly.
//
if (m & 0x00001000)
m += 0x00002000;
//
// Assemble the half from s, e (zero) and m.
//
return short(s | (m >> 13));
}
else if (e == 0xff - (127 - 15))
{
if (m == 0)
{
//
// F is an infinity; convert f to a half
// infinity with the same sign as f.
//
return short(s | 0x7c00);
}
else
{
//
// F is a NAN; we produce a half NAN that preserves
// the sign bit and the 10 leftmost bits of the
// significand of f, with one exception: If the 10
// leftmost bits are all zero, the NAN would turn
// into an infinity, so we have to set at least one
// bit in the significand.
//
m >>= 13;
return short(s | 0x7c00 | m | (m == 0));
}
}
else
{
//
// E is greater than zero. F is a normalized float.
// We try to convert f to a normalized half.
//
//
// Round to nearest, round "0.5" up
//
if (m & 0x00001000)
{
m += 0x00002000;
if (m & 0x00800000)
{
m = 0; // overflow in significand,
e += 1; // adjust exponent
}
}
//
// Handle exponent overflow
//
if (e > 30)
{
overflow(); // Cause a hardware floating point overflow;
return short(s | 0x7c00);
// if this returns, the half becomes an
} // infinity with the same sign as f.
//
// Assemble the half from s, e and m.
//
return short(s | (e << 10) | (m >> 13));
}
} // toFloat16
// ------------------------------------------------------------------------
inline uint32_t vectorTo3Int10Bit(const irr::core::vector3df& vec)
{
int part;
uint32_t sum;
float v;
sum = 0;
v = fminf(1.0, fmaxf(-1.0, vec.X));
if (v > 0.0)
{
part = (int)((v * 511.0) + 0.5);
}
else
{
part = (int)((v * 512.0) - 0.5);
}
sum |= ((uint32_t)part & 1023) << 0;
v = fminf(1.0, fmaxf(-1.0, vec.Y));
if (v > 0.0)
{
part = (int)((v * 511.0) + 0.5);
}
else
{
part = (int)((v * 512.0) - 0.5);
}
sum |= ((uint32_t)part & 1023) << 10;
v = fminf(1.0, fmaxf(-1.0, vec.Z));
if (v > 0.0)
{
part = (int)((v * 511.0) + 0.5);
}
else
{
part = (int)((v * 512.0) - 0.5);
}
sum |= ((uint32_t)part & 1023) << 20;
v = 0.0f;
part = (int)((v * 2.0) - 0.5);
sum |= ((uint32_t)part & 3) << 30;
return sum;
} // vectorTo3Int10Bit
// ------------------------------------------------------------------------
inline core::vector3df extract3Int10Bit(uint32_t sum)
{
core::vector3df ret;
int part = sum & 1023;
if (part & 512)
{
ret.X = (float)(1024 - part) * (-1.0f / 512.0f);
}
else
{
ret.X = (float)part * (1.0f / 511.0f);
}
part = (sum >> 10) & 1023;
if (part & 512)
{
ret.Y = (float)(1024 - part) * (-1.0f / 512.0f);
}
else
{
ret.Y = (float)part * (1.0f / 511.0f);
}
part = (sum >> 20) & 1023;
if (part & 512)
{
ret.Z = (float)(1024 - part) * (-1.0f / 512.0f);
}
else
{
ret.Z = (float)part * (1.0f / 511.0f);
}
return ret.normalize();
} // extract3Int10Bit
}
class SPMeshLoader : public scene::IMeshLoader
{
private:
// ------------------------------------------------------------------------
struct LocRotScale
{
core::vector3df m_loc;
core::quaternion m_rot;
core::vector3df m_scale;
// --------------------------------------------------------------------
inline core::matrix4 toMatrix() const
{
core::matrix4 lm, sm, rm;
lm.setTranslation(m_loc);
sm.setScale(m_scale);
m_rot.getMatrix(rm);
return lm * rm * sm;
}
// --------------------------------------------------------------------
void read(irr::io::IReadFile* spm);
};
struct Armature
{
unsigned m_joint_used;
std::vector<std::string> m_joint_names;
std::vector<core::matrix4> m_joint_matrices;
std::vector<core::matrix4> m_interpolated_matrices;
std::vector<std::pair<core::matrix4, bool> > m_world_matrices;
std::vector<int> m_parent_infos;
std::vector<std::pair<int, std::vector<LocRotScale> > >
m_frame_pose_matrices;
// --------------------------------------------------------------------
void read(irr::io::IReadFile* spm);
// --------------------------------------------------------------------
void getPose(float frame, core::matrix4* dest);
// --------------------------------------------------------------------
void getInterpolatedMatrices(float frame);
// --------------------------------------------------------------------
core::matrix4 getWorldMatrix(const std::vector<core::matrix4>& matrix,
unsigned id);
};
// ------------------------------------------------------------------------
unsigned m_bind_frame, m_joint_count;//, m_frame_count;
// ------------------------------------------------------------------------
std::vector<Armature> m_all_armatures;
// ------------------------------------------------------------------------
std::vector<core::matrix4> m_to_bind_pose_matrices;
// ------------------------------------------------------------------------
enum SPVertexType: unsigned int
{
SPVT_NORMAL,
SPVT_SKINNED
};
// ------------------------------------------------------------------------
void decompress(irr::io::IReadFile* spm, unsigned vertices_count,
unsigned indices_count, bool read_normal, bool read_vcolor,
bool read_tangent, bool uv_one, bool uv_two,
SPVertexType vt, const video::SMaterial& m);
// ------------------------------------------------------------------------
void createAnimationData(irr::io::IReadFile* spm);
// ------------------------------------------------------------------------
void convertIrrlicht();
scene::CSkinnedMesh* m_mesh;
scene::ISceneManager* m_scene_manager;
std::vector<std::vector<
std::pair<std::array<short, 4>, std::array<float, 4> > > > m_joints;
public:
// ------------------------------------------------------------------------
SPMeshLoader(scene::ISceneManager* smgr) : m_scene_manager(smgr) {}
// ------------------------------------------------------------------------
virtual bool isALoadableFileExtension(const io::path& filename) const;
// ------------------------------------------------------------------------
virtual scene::IAnimatedMesh* createMesh(io::IReadFile* file);
};
#endif

View File

@ -41,6 +41,7 @@ scene::IAnimatedMesh* STKMeshLoader::createMesh(io::IReadFile* f)
B3DFile = f;
AnimatedMesh = new scene::CSkinnedMesh();
AnimatedMesh->setTransposedMatrix(true);
ShowWarning = true; // If true a warning is issued if too many textures are used
VerticesStart=0;

View File

@ -1111,6 +1111,10 @@ bool Track::loadMainTrack(const XMLNode &root)
track_node->getName().c_str(), model_name.c_str());
}
scene::ISceneNode* scene_node = NULL;
scene::IMesh* tangent_mesh = NULL;
if (m_version < 7)
{
// The mesh as returned does not have all mesh buffers with the same
// texture combined. This can result in a _HUGE_ overhead. E.g. instead
// of 46 different mesh buffers over 500 (for some tracks even >1000)
@ -1123,27 +1127,36 @@ bool Track::loadMainTrack(const XMLNode &root)
merged_mesh->addMesh(mesh);
merged_mesh->finalize();
#ifndef SERVER_ONLY
scene::IMesh* tangent_mesh = MeshTools::createMeshWithTangents(merged_mesh, &MeshTools::isNormalMap);
tangent_mesh = MeshTools::createMeshWithTangents(merged_mesh, &MeshTools::isNormalMap);
adjustForFog(tangent_mesh, NULL);
#else
scene::IMesh* tangent_mesh = merged_mesh;
tangent_mesh = merged_mesh;
#endif
// The reference count of the mesh is 1, since it is in irrlicht's
// cache. So we only have to remove it from the cache.
irr_driver->removeMeshFromCache(mesh);
}
else
{
// SPM does the combine for you
#ifndef SERVER_ONLY
tangent_mesh = MeshTools::createMeshWithTangents(mesh, &MeshTools::isNormalMap);
adjustForFog(tangent_mesh, NULL);
tangent_mesh->grab();
#else
tangent_mesh = mesh;
#endif
}
// The merged mesh is grabbed by the octtree, so we don't need
// to keep a reference to it.
scene::ISceneNode *scene_node = irr_driver->addMesh(tangent_mesh, "track_main");
//scene::IMeshSceneNode *scene_node = irr_driver->addOctTree(merged_mesh);
scene_node = irr_driver->addMesh(tangent_mesh, "track_main");
// We should drop the merged mesh (since it's now referred to in the
// scene node), but then we need to grab it since it's in the
// m_all_cached_meshes.
m_all_cached_meshes.push_back(tangent_mesh);
irr_driver->grabAllTextures(tangent_mesh);
// The reference count of the mesh is 1, since it is in irrlicht's
// cache. So we only have to remove it from the cache.
irr_driver->removeMeshFromCache(mesh);
#ifdef DEBUG
std::string debug_name=model_name+" (main track, octtree)";
scene_node->setName(debug_name.c_str());