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> <config>
<!-- Minimum and maximum kart versions that can be used by this binary. <!-- Minimum and maximum kart versions that can be used by this binary.
Older version will be ignored. --> 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. <!-- Minimum and maxium track versions that be be read by this binary.
Older versions will be ignored. --> 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 <!-- 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 can easily be increased, but some tracks might not have valid start

View File

@ -21,7 +21,7 @@ namespace scene
CSkinnedMesh::CSkinnedMesh() CSkinnedMesh::CSkinnedMesh()
: SkinningBuffers(0), AnimationFrames(0.f), FramesPerSecond(25.f), : SkinningBuffers(0), AnimationFrames(0.f), FramesPerSecond(25.f),
LastAnimatedFrame(-1), SkinnedLastFrame(false), LastAnimatedFrame(-1), SkinnedLastFrame(false),
InterpolationMode(EIM_LINEAR), InterpolationMode(EIM_LINEAR), TransposedMatrix(false),
HasAnimation(false), PreparedForSkinning(false), HasAnimation(false), PreparedForSkinning(false),
AnimateNormals(true), HardwareSkinning(false), m_total_joints(0), AnimateNormals(true), HardwareSkinning(false), m_total_joints(0),
m_current_joint(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 // 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. // 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! // Not tested so far if this was correct or wrong before quaternion fix!
joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix); if (TransposedMatrix)
{
joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix);
}
else
{
joint->LocalAnimatedMatrix=joint->Animatedrotation.getMatrix();
}
// --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() --- // --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() ---
f32 *m1 = joint->LocalAnimatedMatrix.pointer(); f32 *m1 = joint->LocalAnimatedMatrix.pointer();

View File

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

View File

@ -37,6 +37,7 @@
#include "graphics/stk_animated_mesh.hpp" #include "graphics/stk_animated_mesh.hpp"
#include "graphics/stk_billboard.hpp" #include "graphics/stk_billboard.hpp"
#include "graphics/stk_mesh_loader.hpp" #include "graphics/stk_mesh_loader.hpp"
#include "graphics/sp_mesh_loader.hpp"
#include "graphics/stk_mesh_scene_node.hpp" #include "graphics/stk_mesh_scene_node.hpp"
#include "graphics/stk_tex_manager.hpp" #include "graphics/stk_tex_manager.hpp"
#include "graphics/stk_texture.hpp" #include "graphics/stk_texture.hpp"
@ -605,6 +606,9 @@ void IrrDriver::initDevice()
STKMeshLoader* sml = new STKMeshLoader(m_scene_manager); STKMeshLoader* sml = new STKMeshLoader(m_scene_manager);
m_scene_manager->addExternalMeshLoader(sml); m_scene_manager->addExternalMeshLoader(sml);
sml->drop(); sml->drop();
SPMeshLoader* spml = new SPMeshLoader(m_scene_manager);
m_scene_manager->addExternalMeshLoader(spml);
spml->drop();
m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize(); 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; B3DFile = f;
AnimatedMesh = new scene::CSkinnedMesh(); AnimatedMesh = new scene::CSkinnedMesh();
AnimatedMesh->setTransposedMatrix(true);
ShowWarning = true; // If true a warning is issued if too many textures are used ShowWarning = true; // If true a warning is issued if too many textures are used
VerticesStart=0; VerticesStart=0;

View File

@ -1111,39 +1111,52 @@ bool Track::loadMainTrack(const XMLNode &root)
track_node->getName().c_str(), model_name.c_str()); track_node->getName().c_str(), model_name.c_str());
} }
// The mesh as returned does not have all mesh buffers with the same scene::ISceneNode* scene_node = NULL;
// texture combined. This can result in a _HUGE_ overhead. E.g. instead scene::IMesh* tangent_mesh = NULL;
// of 46 different mesh buffers over 500 (for some tracks even >1000) if (m_version < 7)
// were created. This means less effect from hardware support, less {
// vertices per opengl operation, more overhead on CPU, ... // The mesh as returned does not have all mesh buffers with the same
// So till we have a better b3d exporter which can combine the different // texture combined. This can result in a _HUGE_ overhead. E.g. instead
// meshes which use the same texture when exporting, the meshes are // of 46 different mesh buffers over 500 (for some tracks even >1000)
// combined using CBatchingMesh. // were created. This means less effect from hardware support, less
scene::CBatchingMesh *merged_mesh = new scene::CBatchingMesh(); // vertices per opengl operation, more overhead on CPU, ...
merged_mesh->addMesh(mesh); // So till we have a better b3d exporter which can combine the different
merged_mesh->finalize(); // meshes which use the same texture when exporting, the meshes are
// combined using CBatchingMesh.
scene::CBatchingMesh *merged_mesh = new scene::CBatchingMesh();
merged_mesh->addMesh(mesh);
merged_mesh->finalize();
#ifndef SERVER_ONLY #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); adjustForFog(tangent_mesh, NULL);
#else #else
scene::IMesh* tangent_mesh = merged_mesh; tangent_mesh = merged_mesh;
#endif #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 // The merged mesh is grabbed by the octtree, so we don't need
// to keep a reference to it. // to keep a reference to it.
scene::ISceneNode *scene_node = irr_driver->addMesh(tangent_mesh, "track_main"); scene_node = irr_driver->addMesh(tangent_mesh, "track_main");
//scene::IMeshSceneNode *scene_node = irr_driver->addOctTree(merged_mesh);
// We should drop the merged mesh (since it's now referred to in the // 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 // scene node), but then we need to grab it since it's in the
// m_all_cached_meshes. // m_all_cached_meshes.
m_all_cached_meshes.push_back(tangent_mesh); m_all_cached_meshes.push_back(tangent_mesh);
irr_driver->grabAllTextures(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 #ifdef DEBUG
std::string debug_name=model_name+" (main track, octtree)"; std::string debug_name=model_name+" (main track, octtree)";
scene_node->setName(debug_name.c_str()); scene_node->setName(debug_name.c_str());