refactor: Move TOR support code into a separate library

This commit is contained in:
Vsevolod Kremianskii 2021-02-07 20:45:10 +07:00
parent a0fa2be9a3
commit b90c1d9f4a
24 changed files with 261 additions and 481 deletions

View file

@ -163,8 +163,6 @@ set(RENDER_HEADERS
src/render/mesh/mesh.h
src/render/mesh/modelmesh.h
src/render/model/animation.h
src/render/model/gr2file.h
src/render/model/jbafile.h
src/render/model/mdlfile.h
src/render/model/model.h
src/render/model/modelnode.h
@ -200,8 +198,6 @@ set(RENDER_SOURCES
src/render/mesh/mesh.cpp
src/render/mesh/modelmesh.cpp
src/render/model/animation.cpp
src/render/model/gr2file.cpp
src/render/model/jbafile.cpp
src/render/model/mdlfile.cpp
src/render/model/model.cpp
src/render/model/modelnode.cpp
@ -221,6 +217,21 @@ set_target_properties(librender PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINA
## END librender static library
## libtor static library
set(TOR_HEADERS
src/tor/gr2file.h
src/tor/jbafile.h)
set(TOR_SOURCES
src/tor/gr2file.cpp
src/tor/jbafile.cpp)
add_library(libtor STATIC ${TOR_HEADERS} ${TOR_SOURCES})
set_target_properties(libtor PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
## END libtor static library
## libaudio static library
set(AUDIO_HEADERS
@ -665,7 +676,7 @@ add_executable(reone ${REONE_HEADERS} ${REONE_SOURCES})
set_target_properties(reone PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
target_link_libraries(reone PRIVATE
libmp libnet libgame libscript libgui libscene libvideo librender libaudio libresource libcommon
libmp libnet libgame libscript libgui libscene libvideo libaudio libtor librender libresource libcommon
libs3tc
${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY}
GLEW::GLEW

View file

@ -23,8 +23,6 @@
#include <boost/format.hpp>
#include "../../common/streamutil.h"
#include "../../render/model/gr2file.h"
#include "../../render/model/jbafile.h"
#include "../../render/models.h"
#include "../../render/textures.h"
#include "../../resource/resources.h"
@ -42,11 +40,6 @@ namespace reone {
namespace game {
// This is a hook to replace vanilla models with SWTOR models
static const unordered_map<string, string> g_modelOverrides {
// { "p_bastilabb", "bfn_satele_a02" }
};
static const string g_headHookNode("headhook");
static const string g_maskHookNode("gogglehook");
@ -60,21 +53,12 @@ shared_ptr<ModelSceneNode> CreatureModelBuilder::build() {
string modelName(getBodyModelName());
if (modelName.empty()) return nullptr;
bool gr2Model = false;
auto maybeOverride = g_modelOverrides.find(modelName);
if (maybeOverride != g_modelOverrides.end()) {
modelName = maybeOverride->second;
gr2Model = true;
}
shared_ptr<Model> model(Models::instance().get(modelName, gr2Model));
shared_ptr<Model> model(Models::instance().get(modelName));
if (!model) return nullptr;
auto modelSceneNode = make_unique<ModelSceneNode>(&_creature->sceneGraph(), model);
modelSceneNode->setLightingEnabled(true);
if (gr2Model) return move(modelSceneNode);
// Body texture
string bodyTextureName(getBodyTextureName());

View file

@ -56,11 +56,7 @@ AABBMesh &AABBMesh::instance() {
return mesh;
}
AABBMesh::AABBMesh() {
_vertexCount = 8;
_vertices = g_vertices;
_indices = g_indices;
_offsets = g_offsets;
AABBMesh::AABBMesh() : Mesh(8, move(g_vertices), move(g_indices), move(g_offsets)) {
}
void AABBMesh::render(const AABB &aabb, const glm::mat4 &transform) const {

View file

@ -41,11 +41,7 @@ BillboardMesh &BillboardMesh::instance() {
return mesh;
}
BillboardMesh::BillboardMesh() {
_vertexCount = 4;
_vertices = move(g_vertices);
_indices = move(g_indices);
_offsets = move(g_offsets);
BillboardMesh::BillboardMesh() : Mesh(4, move(g_vertices), move(g_indices), move(g_offsets)) {
}
} // namespace render

View file

@ -54,11 +54,7 @@ CubeMesh &CubeMesh::instance() {
return mesh;
}
CubeMesh::CubeMesh() {
_vertexCount = 8;
_vertices = move(g_vertices);
_indices = move(g_indices);
_offsets = move(g_offsets);
CubeMesh::CubeMesh() : Mesh(8, move(g_vertices), move(g_indices), move(g_offsets)) {
}
void CubeMesh::render(const glm::mat4 &transform) const {

View file

@ -53,6 +53,8 @@ public:
void initGL();
void deinitGL();
void computeAABB();
void renderLines() const;
void renderTriangles() const;
@ -67,9 +69,6 @@ protected:
uint32_t _indexBufferId { 0 };
uint32_t _vertexArrayId { 0 };
Mesh() = default;
void computeAABB();
void render(uint32_t mode, int count, int offset) const;
private:

View file

@ -17,6 +17,8 @@
#include "modelmesh.h"
#include <stdexcept>
#include "GL/glew.h"
#include "SDL2/SDL_opengl.h"
@ -29,10 +31,23 @@ namespace reone {
namespace render {
ModelMesh::ModelMesh(bool render, int transparency, bool shadow) :
ModelMesh::ModelMesh(const shared_ptr<Mesh> &mesh, bool render, int transparency, bool shadow) :
_mesh(mesh),
_render(render),
_transparency(transparency),
_shadow(shadow) {
if (!mesh) {
throw invalid_argument("mesh must not be null");
}
}
void ModelMesh::initGL() {
_mesh->initGL();
}
void ModelMesh::deinitGL() {
_mesh->deinitGL();
}
void ModelMesh::render(const shared_ptr<Texture> &diffuseOverride) const {
@ -62,9 +77,9 @@ void ModelMesh::render(const shared_ptr<Texture> &diffuseOverride) const {
}
if (additive) {
withAdditiveBlending([this]() { Mesh::renderTriangles(); });
withAdditiveBlending([this]() { _mesh->renderTriangles(); });
} else {
Mesh::renderTriangles();
_mesh->renderTriangles();
}
}
@ -82,6 +97,23 @@ bool ModelMesh::isTransparent() const {
return true;
}
void ModelMesh::setDiffuseTexture(const shared_ptr<Texture> &texture) {
_diffuse = texture;
}
void ModelMesh::setBumpmapTexture(const shared_ptr<Texture> &texture, bool swizzled) {
_bumpmap = texture;
_bumpmapSwizzled = swizzled;
}
void ModelMesh::setDiffuseColor(glm::vec3 color) {
_diffuseColor = move(color);
}
void ModelMesh::setAmbientColor(glm::vec3 color) {
_ambientColor = move(color);
}
} // namespace render
} // namespace reone

View file

@ -27,7 +27,6 @@ namespace reone {
namespace render {
class Gr2File;
class MdlFile;
/**
@ -36,7 +35,7 @@ class MdlFile;
* @see reone::render::ModelNode
* @see reone::render::Texture
*/
class ModelMesh : public Mesh {
class ModelMesh {
public:
struct UVAnimation {
bool animated { false };
@ -46,7 +45,10 @@ public:
float jitterSpeed { 0.0f };
};
ModelMesh(bool render, int transparency, bool shadow);
ModelMesh(const std::shared_ptr<Mesh> &mesh, bool render, int transparency, bool shadow);
void initGL();
void deinitGL();
void render(const std::shared_ptr<Texture> &diffuseOverride = nullptr) const;
@ -55,7 +57,7 @@ public:
bool isTransparent() const;
bool isBackgroundGeometry() const { return _backgroundGeometry; }
bool isBumpmapFromTOR() const { return _bumpmapFromTOR; }
bool isBumpmapSwizzled() const { return _bumpmapSwizzled; }
bool hasDiffuseTexture() const { return static_cast<bool>(_diffuse); }
bool hasEnvmapTexture() const { return static_cast<bool>(_envmap); }
@ -63,6 +65,7 @@ public:
bool hasBumpyShinyTexture() const { return static_cast<bool>(_bumpyShiny); }
bool hasBumpmapTexture() const { return static_cast<bool>(_bumpmap); }
std::shared_ptr<Mesh> mesh() const { return _mesh; }
int transparency() const { return _transparency; }
const glm::vec3 &diffuseColor() const { return _diffuseColor; }
const glm::vec3 &ambientColor() const { return _ambientColor; }
@ -71,7 +74,13 @@ public:
const std::shared_ptr<Texture> &diffuseTexture() const { return _diffuse; }
const std::shared_ptr<Texture> &bumpmapTexture() const { return _bumpmap; }
void setDiffuseTexture(const std::shared_ptr<Texture> &texture);
void setBumpmapTexture(const std::shared_ptr<Texture> &texture, bool swizzled = false);
void setDiffuseColor(glm::vec3 color);
void setAmbientColor(glm::vec3 color);
private:
std::shared_ptr<Mesh> _mesh;
bool _render;
int _transparency;
bool _shadow;
@ -80,7 +89,7 @@ private:
glm::vec3 _diffuseColor { 1.0f };
glm::vec3 _ambientColor { 0.0f };
UVAnimation _uvAnimation;
bool _bumpmapFromTOR { false }; /**< SWTOR normal maps require special handling by the shader */
bool _bumpmapSwizzled { false };
// Textures
@ -93,7 +102,6 @@ private:
// END Textures
friend class MdlFile;
friend class Gr2File;
};
} // namespace render

View file

@ -76,11 +76,7 @@ Quad &Quad::getXYFlipped() {
return quad;
}
Quad::Quad(vector<float> &&vertices) {
_vertexCount = 4;
_vertices = move(vertices);
_indices = g_indices;
_offsets = g_offsets;
Quad::Quad(vector<float> &&vertices) : Mesh(4, move(vertices), g_indices, g_offsets) {
}
} // namespace render

View file

@ -148,8 +148,7 @@ void MdlFile::doLoad() {
superModel = Models::instance().get(superModelName);
}
_model = make_unique<Model>(_name, move(rootNode), anims, superModel);
_model->setClassification(getClassification(classification));
_model = make_unique<Model>(_name, getClassification(classification), move(rootNode), anims, superModel);
_model->setAnimationScale(scale);
}
@ -793,43 +792,41 @@ unique_ptr<ModelMesh> MdlFile::readMesh(const string &nodeName, int nodeFlags) {
seek(endPos);
auto mesh = make_unique<ModelMesh>(render, transparency, shadow);
mesh->_vertexCount = vertexCount;
mesh->_vertices = move(vertices);
mesh->_offsets = move(offsets);
mesh->_indices = move(indices);
mesh->_backgroundGeometry = backgroundGeometry != 0;
mesh->_diffuseColor = glm::make_vec3(&diffuseColor[0]);
mesh->_ambientColor = glm::make_vec3(&ambientColor[0]);
auto mesh = make_unique<Mesh>(vertexCount, move(vertices), move(indices), move(offsets));
mesh->computeAABB();
auto modelMesh = make_unique<ModelMesh>(move(mesh), render, transparency, shadow);
modelMesh->_backgroundGeometry = backgroundGeometry != 0;
modelMesh->_diffuseColor = glm::make_vec3(&diffuseColor[0]);
modelMesh->_ambientColor = glm::make_vec3(&ambientColor[0]);
if (!diffuse.empty() && diffuse != "null") {
mesh->_diffuse = Textures::instance().get(diffuse, TextureType::Diffuse);
if (mesh->_diffuse) {
const TextureFeatures &features = mesh->_diffuse->features();
modelMesh->_diffuse = Textures::instance().get(diffuse, TextureType::Diffuse);
if (modelMesh->_diffuse) {
const TextureFeatures &features = modelMesh->_diffuse->features();
if (!features.envMapTexture.empty()) {
mesh->_envmap = Textures::instance().get(features.envMapTexture, TextureType::EnvironmentMap);
modelMesh->_envmap = Textures::instance().get(features.envMapTexture, TextureType::EnvironmentMap);
}
if (!features.bumpyShinyTexture.empty()) {
mesh->_bumpyShiny = Textures::instance().get(features.bumpyShinyTexture, TextureType::EnvironmentMap);
modelMesh->_bumpyShiny = Textures::instance().get(features.bumpyShinyTexture, TextureType::EnvironmentMap);
}
if (!features.bumpMapTexture.empty()) {
mesh->_bumpmap = Textures::instance().get(features.bumpMapTexture, TextureType::Bumpmap);
modelMesh->_bumpmap = Textures::instance().get(features.bumpMapTexture, TextureType::Bumpmap);
}
}
}
if (!lightmap.empty()) {
mesh->_lightmap = Textures::instance().get(lightmap, TextureType::Lightmap);
modelMesh->_lightmap = Textures::instance().get(lightmap, TextureType::Lightmap);
}
if (animateUv) {
mesh->_uvAnimation.animated = true;
mesh->_uvAnimation.directionX = uvDirectionX;
mesh->_uvAnimation.directionY = uvDirectionY;
mesh->_uvAnimation.jitter = uvJitter;
mesh->_uvAnimation.jitterSpeed = uvJitterSpeed;
modelMesh->_uvAnimation.animated = true;
modelMesh->_uvAnimation.directionX = uvDirectionX;
modelMesh->_uvAnimation.directionY = uvDirectionY;
modelMesh->_uvAnimation.jitter = uvJitter;
modelMesh->_uvAnimation.jitterSpeed = uvJitterSpeed;
}
return move(mesh);
return move(modelMesh);
}
void MdlFile::readSkin(ModelNode &node) {
@ -840,8 +837,8 @@ void MdlFile::readSkin(ModelNode &node) {
uint32_t bonesOffset = readUint32();
uint32_t boneCount = readUint32();
node._mesh->_offsets.boneWeights = boneWeightsOffset;
node._mesh->_offsets.boneIndices = boneIndicesOffset;
node._mesh->_mesh->_offsets.boneWeights = boneWeightsOffset;
node._mesh->_mesh->_offsets.boneIndices = boneIndicesOffset;
unordered_map<uint16_t, uint16_t> nodeIdxByBoneIdx;
seek(kMdlDataOffset + bonesOffset);

View file

@ -17,6 +17,8 @@
#include "model.h"
#include <stdexcept>
#include "../../common/log.h"
using namespace std;
@ -27,14 +29,19 @@ namespace render {
Model::Model(
const string &name,
Classification classification,
const shared_ptr<ModelNode> &rootNode,
vector<shared_ptr<Animation>> &anims,
const shared_ptr<Model> &superModel
) :
_name(name),
_classification(classification),
_rootNode(rootNode),
_superModel(superModel) {
if (!rootNode) {
throw invalid_argument("rootNode must not be null");
}
for (auto &anim : anims) {
_animations.insert(make_pair(anim->name(), move(anim)));
}
@ -49,7 +56,7 @@ void Model::init(const shared_ptr<ModelNode> &node) {
shared_ptr<ModelMesh> mesh(node->mesh());
if (mesh) {
_aabb.expand(mesh->aabb() * node->absoluteTransform());
_aabb.expand(mesh->mesh()->aabb() * node->absoluteTransform());
}
for (auto &child : node->children()) {
@ -100,10 +107,6 @@ shared_ptr<ModelNode> Model::findNodeByName(const string &name) const {
return it != _nodeByName.end() ? it->second : nullptr;
}
void Model::setClassification(Classification classification) {
_classification = classification;
}
void Model::setAnimationScale(float scale) {
_animationScale = scale;
}

View file

@ -51,6 +51,7 @@ public:
Model(
const std::string &name,
Classification classification,
const std::shared_ptr<ModelNode> &rootNode,
std::vector<std::shared_ptr<Animation>> &anims,
const std::shared_ptr<Model> &superModel = nullptr);
@ -69,15 +70,15 @@ public:
std::shared_ptr<Model> superModel() const { return _superModel; }
const AABB &aabb() const { return _aabb; }
void setClassification(Classification classification);
void setAnimationScale(float scale);
private:
Classification _classification { Classification::Other };
std::string _name;
Classification _classification;
std::shared_ptr<ModelNode> _rootNode;
std::unordered_map<std::string, std::shared_ptr<Animation>> _animations;
std::shared_ptr<Model> _superModel;
std::unordered_map<uint16_t, std::shared_ptr<ModelNode>> _nodeByNumber;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
AABB _aabb;
@ -90,7 +91,6 @@ private:
void init(const std::shared_ptr<ModelNode> &node);
friend class MdlFile;
friend class Gr2File;
};
} // namespace render

View file

@ -150,7 +150,11 @@ bool ModelNode::getScale(float time, float &scale) const {
}
const glm::vec3 &ModelNode::getCenterOfAABB() const {
return _mesh->aabb().center();
return _mesh->mesh()->aabb().center();
}
void ModelNode::setName(string name) {
_name = move(name);
}
} // namespace render

View file

@ -28,7 +28,6 @@ namespace reone {
namespace render {
class JbaFile;
class Model;
/**
@ -41,6 +40,21 @@ class Model;
*/
class ModelNode {
public:
struct PositionKeyframe {
float time { 0.0f };
glm::vec3 position { 0.0f };
};
struct OrientationKeyframe {
float time { 0.0f };
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
};
struct ScaleKeyframe {
float time { 0.0f };
float scale { 0.0f };
};
struct Light {
int priority { 1 };
bool ambientOnly { false };
@ -97,22 +111,9 @@ public:
std::shared_ptr<Emitter> emitter() const { return _emitter; }
const std::vector<std::shared_ptr<ModelNode>> &children() const { return _children; }
void setName(std::string name);
private:
struct PositionKeyframe {
float time { 0.0f };
glm::vec3 position { 0.0f };
};
struct OrientationKeyframe {
float time { 0.0f };
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
};
struct ScaleKeyframe {
float time { 0.0f };
float scale { 0.0f };
};
int _index { 0 };
const ModelNode *_parent { nullptr };
uint16_t _flags { 0 };
@ -142,8 +143,6 @@ private:
ModelNode &operator=(const ModelNode &) = delete;
friend class MdlFile;
friend class Gr2File;
friend class JbaFile;
};
} // namespace render

View file

@ -20,7 +20,6 @@
#include "../common/streamutil.h"
#include "../resource/resources.h"
#include "model/gr2file.h"
#include "model/mdlfile.h"
using namespace std;
@ -44,34 +43,25 @@ void Models::invalidateCache() {
_cache.clear();
}
shared_ptr<Model> Models::get(const string &resRef, bool gr2) {
shared_ptr<Model> Models::get(const string &resRef) {
auto maybeModel = _cache.find(resRef);
if (maybeModel != _cache.end()) {
return maybeModel->second;
}
auto inserted = _cache.insert(make_pair(resRef, doGet(resRef, gr2)));
auto inserted = _cache.insert(make_pair(resRef, doGet(resRef)));
return inserted.first->second;
}
shared_ptr<Model> Models::doGet(const string &resRef, bool gr2) {
shared_ptr<Model> Models::doGet(const string &resRef) {
shared_ptr<ByteArray> mdlData(Resources::instance().get(resRef, ResourceType::Mdl));
shared_ptr<ByteArray> mdxData(Resources::instance().get(resRef, ResourceType::Mdx));
shared_ptr<Model> model;
if (gr2) {
shared_ptr<ByteArray> gr2Data(Resources::instance().get(resRef, ResourceType::Gr2));
if (gr2Data) {
Gr2File gr2(resRef);
gr2.load(wrap(gr2Data));
model = gr2.model();
}
} else {
shared_ptr<ByteArray> mdlData(Resources::instance().get(resRef, ResourceType::Mdl));
shared_ptr<ByteArray> mdxData(Resources::instance().get(resRef, ResourceType::Mdx));
if (mdlData && mdxData) {
MdlFile mdl(_gameId);
mdl.load(wrap(mdlData), wrap(mdxData));
model = mdl.model();
}
if (mdlData && mdxData) {
MdlFile mdl(_gameId);
mdl.load(wrap(mdlData), wrap(mdxData));
model = mdl.model();
}
if (model) {
model->initGL();

View file

@ -38,11 +38,7 @@ public:
void init(resource::GameID gameId);
void invalidateCache();
/**
* @param resRef ResRef of the model
* @param gr2 true if model is from SWTOR, false otherwise
*/
std::shared_ptr<Model> get(const std::string &resRef, bool gr2 = false);
std::shared_ptr<Model> get(const std::string &resRef);
private:
resource::GameID _gameId { resource::GameID::KotOR };
@ -52,7 +48,7 @@ private:
Models(const Models &) = delete;
Models &operator=(const Models &) = delete;
std::shared_ptr<Model> doGet(const std::string &resRef, bool gr2);
std::shared_ptr<Model> doGet(const std::string &resRef);
};
} // namespace render

View file

@ -108,7 +108,7 @@ layout(std140) uniform Bumpmap {
uniform float uBumpmapScaling;
uniform vec2 uBumpmapGridSize;
uniform int uBumpmapFrame;
uniform bool uBumpmapFromTOR;
uniform bool uBumpmapSwizzled;
};
)END";
@ -371,7 +371,7 @@ void applyBumpmapToNormal(inout vec3 normal, vec2 uv) {
} else {
vec4 bumpmapSample = texture(uBumpmap, uv);
if (uBumpmapFromTOR) {
if (uBumpmapSwizzled) {
normal = vec3(bumpmapSample.a, bumpmapSample.g, 1.0);
} else {
normal = vec3(bumpmapSample.r, bumpmapSample.g, bumpmapSample.b);

View file

@ -130,7 +130,7 @@ struct BumpmapUniforms {
float scaling { 0.0f };
glm::vec2 gridSize { 1.0f };
int frame { 0 };
int fromTOR { 0 };
int swizzled { 0 };
char padding[8];
};

View file

@ -129,7 +129,7 @@ void ModelNodeSceneNode::renderSingle(bool shadowPass) const {
locals.bumpmap.scaling = bumpmapTexture->features().bumpMapScaling;
locals.bumpmap.gridSize = glm::vec2(bumpmapTexture->features().numX, bumpmapTexture->features().numY);
locals.bumpmap.frame = _bumpmapFrame;
locals.bumpmap.fromTOR = mesh->isBumpmapFromTOR();
locals.bumpmap.swizzled = mesh->isBumpmapSwizzled();
}
bool receivesShadows =

View file

@ -348,9 +348,9 @@ void ModelSceneNode::refreshAABB() {
auto modelNodeSceneNode = dynamic_cast<ModelNodeSceneNode *>(node);
if (modelNodeSceneNode) {
shared_ptr<Mesh> mesh(modelNodeSceneNode->modelNode()->mesh());
shared_ptr<ModelMesh> mesh(modelNodeSceneNode->modelNode()->mesh());
if (mesh) {
_aabb.expand(mesh->aabb() * node->localTransform());
_aabb.expand(mesh->mesh()->aabb() * node->localTransform());
}
}

View file

@ -24,27 +24,28 @@
#include "glm/gtc/type_ptr.hpp"
#include "../../common/log.h"
#include "../../common/streamutil.h"
#include "../../render/model/jbafile.h"
#include "../../render/models.h"
#include "../../render/textures.h"
#include "../../resource/resources.h"
#include "../common/log.h"
#include "../common/streamutil.h"
#include "../render/models.h"
#include "../render/textures.h"
#include "../resource/resources.h"
#include "jbafile.h"
using namespace std;
using namespace reone::render;
using namespace reone::resource;
namespace pt = boost::property_tree;
namespace reone {
namespace render {
namespace tor {
Gr2File::Gr2File(string resRef, shared_ptr<Gr2File> skeleton) :
Gr2File::Gr2File(string resRef) :
BinaryFile(4, "GAWB"),
_resRef(move(resRef)),
_skeleton(move(skeleton)) {
_resRef(move(resRef)) {
}
void Gr2File::doLoad() {
@ -75,12 +76,6 @@ void Gr2File::doLoad() {
loadMeshes();
loadSkeletonBones();
loadAttachments();
if (_fileType == FileType::Skeleton) {
loadSkeletonModel();
} else {
loadGeometryModel();
}
}
void Gr2File::loadMeshes() {
@ -258,14 +253,12 @@ unique_ptr<ModelMesh> Gr2File::readModelMesh(const Gr2Mesh &mesh) {
indices.insert(indices.end(), pieceIndices.begin(), pieceIndices.end());
}
auto modelMesh = make_unique<ModelMesh>(true, 0, true);
modelMesh->_vertexCount = mesh.header.numVertices;
modelMesh->_vertices = move(vertices);
modelMesh->_offsets = move(offsets);
modelMesh->_indices = move(indices);
modelMesh->_diffuseColor = glm::vec3(0.8f);
modelMesh->_ambientColor = glm::vec3(0.2f);
modelMesh->computeAABB();
auto simpleMesh = make_unique<Mesh>(mesh.header.numVertices, move(vertices), move(indices), move(offsets));
simpleMesh->computeAABB();
auto modelMesh = make_unique<ModelMesh>(move(simpleMesh), true, 0, true);
modelMesh->setDiffuseColor(glm::vec3(0.8f));
modelMesh->setAmbientColor(glm::vec3(0.2f));
// Fill mesh textures from materials
if (!_materials.empty()) {
@ -293,10 +286,9 @@ unique_ptr<ModelMesh> Gr2File::readModelMesh(const Gr2Mesh &mesh) {
value = value.substr(lastSlashIdx + 1ll);
}
if (semantic == "DiffuseMap") {
modelMesh->_diffuse = Textures::instance().get(value, TextureType::Diffuse);
modelMesh->setDiffuseTexture(Textures::instance().get(value, TextureType::Diffuse));
} else if (semantic == "RotationMap1") {
modelMesh->_bumpmap = Textures::instance().get(value, TextureType::Bumpmap);
modelMesh->_bumpmapFromTOR = true;
modelMesh->setBumpmapTexture(Textures::instance().get(value, TextureType::Bumpmap), true);
}
}
}
@ -376,172 +368,6 @@ unique_ptr<Gr2File::Attachment> Gr2File::readAttachment() {
return move(attachment);
}
void Gr2File::loadSkeletonModel() {
vector<float> vertices {
-0.001f, -0.001f, -0.001f,
-0.001f, -0.001f, 0.001f,
-0.001f, 0.001f, 0.001f,
-0.001f, 0.001f, -0.001f,
0.001f, 0.001f, 0.001f,
0.001f, 0.001f, -0.001f,
0.001f, -0.001f, -0.001f,
0.001f, -0.001f, 0.001f
};
vector<uint16_t> indices {
0, 1, 2, 2, 3, 0,
2, 4, 5, 5, 3, 2,
1, 7, 4, 4, 2, 1,
0, 6, 7, 7, 1, 0,
7, 6, 5, 5, 4, 7,
6, 0, 3, 3, 5, 6
};
Mesh::VertexOffsets offsets { 0, -1, -1, -1, -1, -1, -1, -1, 3 * sizeof(float) };
auto mesh = make_shared<ModelMesh>(true, 0, false);
mesh->_vertexCount = 8;
mesh->_vertices = move(vertices);
mesh->_offsets = move(offsets);
mesh->_indices = move(indices);
mesh->_diffuse = Textures::instance().get("redfill", TextureType::GUI);
mesh->_diffuseColor = glm::vec3(0.0f);
mesh->_ambientColor = glm::vec3(1.0f);
mesh->computeAABB();
glm::mat4 transform(1.0f);
transform *= glm::mat4_cast(glm::angleAxis(glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
transform *= glm::mat4_cast(glm::angleAxis(glm::half_pi<float>(), glm::vec3(1.0f, 0.0, 0.0f)));
transform = glm::scale(transform, glm::vec3(10.0f));
int index = 0;
auto rootNode = make_shared<ModelNode>(index++);
rootNode->_absTransform = transform;
rootNode->_absTransformInv = glm::inverse(rootNode->_absTransform);
vector<shared_ptr<ModelNode>> nodes;
// Convert bones into model nodes
for (uint16_t i = 0; i < _numBones; ++i) {
auto node = make_shared<ModelNode>(index);
node->_nodeNumber = index;
node->_name = _bones[i]->name;
node->_mesh = mesh;
node->_absTransform = rootNode->_absTransform * glm::inverse(_bones[i]->rootToBone);
node->_absTransformInv = glm::inverse(node->_absTransform);
nodes.push_back(move(node));
++index;
}
// Reparent model nodes
for (uint16_t i = 0; i < _numBones; ++i) {
int parentIdx = _bones[i]->parentIndex;
if (parentIdx == 0xffffffff) {
nodes[i]->_parent = rootNode.get();
rootNode->_children.push_back(nodes[i]);
} else {
nodes[i]->_parent = nodes[parentIdx].get();
nodes[parentIdx]->_children.push_back(nodes[i]);
}
}
rootNode->computeLocalTransforms();
vector<shared_ptr<Animation>> anims;
_model = make_shared<Model>("", rootNode, anims);
_model->_classification = Model::Classification::Character;
_model->initGL();
}
static string getSkeletonResRef(const string &modelResRef) {
return boost::starts_with(modelResRef, "bfn_") ? "bfnnew_skeleton" : "";
}
static vector<string> getSkeletonAnimations(const string &skeletonResRef) {
vector<string> result;
if (skeletonResRef == "bfnnew_skeleton") {
result.push_back("mv_run_forward");
}
return move(result);
}
void Gr2File::loadGeometryModel() {
if (_meshes.empty()) return;
// In SWTOR, geometry, skeletons and animations are all separate files
shared_ptr<Model> skeletonModel;
string skeletonResRef(getSkeletonResRef(_resRef));
if (!skeletonResRef.empty()) {
skeletonModel = Models::instance().get(skeletonResRef, true);
}
shared_ptr<ModelNode> rootNode;
int index = 0;
if (skeletonModel) {
rootNode = skeletonModel->_rootNode;
index = skeletonModel->_maxNodeIndex + 1;
} else {
// Create our own root node, if skeleton model is not found
glm::mat4 transform(1.0f);
transform *= glm::mat4_cast(glm::angleAxis(glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
transform *= glm::mat4_cast(glm::angleAxis(glm::radians(90.0f), glm::vec3(1.0f, 0.0, 0.0f)));
transform = glm::scale(transform, glm::vec3(10.0f));
rootNode = make_shared<ModelNode>(index++);
rootNode->_localTransform = transform;
rootNode->_absTransform = rootNode->_localTransform;
rootNode->_absTransformInv = glm::inverse(rootNode->_absTransform);
}
for (uint16_t i = 0; i < _numMeshes; ++i) {
if (boost::contains(_meshes[i]->header.name, "collision")) continue; // do not add collision meshes to the model
auto node = make_shared<ModelNode>(index);
node->_nodeNumber = index;
node->_name = _meshes[i]->header.name;
node->_mesh = _meshes[i]->mesh;
node->_absTransform = rootNode->_absTransform;
node->_absTransformInv = glm::inverse(node->_absTransform);
if (skeletonModel) {
node->_skin = make_shared<ModelNode::Skin>();
for (uint16_t j = 0; j < _meshes[i]->header.numUsedBones; ++j) {
shared_ptr<ModelNode> boneNode(skeletonModel->findNodeByName(_meshes[i]->bones[j]->name));
if (boneNode) {
node->_skin->nodeIdxByBoneIdx.insert(make_pair(j, boneNode->index()));
}
}
}
rootNode->_children.push_back(move(node));
++index;
}
vector<shared_ptr<Animation>> anims;
if (skeletonModel) {
vector<string> animResRefs(getSkeletonAnimations(skeletonResRef));
for (auto &resRef : animResRefs) {
shared_ptr<Animation> animation;
shared_ptr<ByteArray> jbaData(Resources::instance().get(resRef, ResourceType::Jba));
if (jbaData) {
JbaFile jbaFile(resRef, skeletonModel);
jbaFile.load(wrap(jbaData));
animation = jbaFile.animation();
}
if (animation) {
anims.push_back(move(animation));
}
}
}
_model = make_shared<Model>("", rootNode, anims);
_model->_classification = Model::Classification::Character; // this prevents self-shadowing
_model->initGL();
}
} // namespace render
} // namespace tor
} // namespace reone

View file

@ -17,16 +17,15 @@
#pragma once
#include "../../render/model/model.h"
#include "../../resource/format/binfile.h"
#include "../render/model/model.h"
#include "../resource/format/binfile.h"
namespace reone {
namespace render {
namespace tor {
/**
* Encapsulates reading GR2 model files, used by Star Wars: The Old Republic.
* This is highly experimental.
* Encapsulates loading GR2 model files, used by Star Wars: The Old Republic.
*/
class Gr2File : public resource::BinaryFile {
public:
@ -36,13 +35,7 @@ public:
Skeleton = 2
};
/**
* @param resRef ResRef of the model
* @param skeleton skeleton file to use when constructing this model (optional)
*/
Gr2File(std::string resRef, std::shared_ptr<Gr2File> skeleton = nullptr);
std::shared_ptr<render::Model> model() const { return _model; }
Gr2File(std::string resRef);
private:
struct MeshHeader {
@ -74,7 +67,7 @@ private:
struct Gr2Mesh {
MeshHeader header;
std::vector<std::shared_ptr<MeshPiece>> pieces;
std::shared_ptr<ModelMesh> mesh;
std::shared_ptr<render::ModelMesh> mesh;
std::vector<std::shared_ptr<MeshBone>> bones;
};
@ -91,7 +84,6 @@ private:
};
std::string _resRef;
std::shared_ptr<Gr2File> _skeleton;
FileType _fileType { FileType::Geometry };
uint16_t _numMeshes { 0 };
@ -107,7 +99,6 @@ private:
std::vector<std::shared_ptr<SkeletonBone>> _bones;
std::vector<std::string> _materials;
std::vector<std::shared_ptr<Attachment>> _attachments;
std::shared_ptr<render::Model> _model;
void doLoad() override;
@ -115,17 +106,15 @@ private:
void loadMaterials();
void loadSkeletonBones();
void loadAttachments();
void loadSkeletonModel();
void loadGeometryModel();
std::unique_ptr<Gr2Mesh> readMesh();
std::unique_ptr<MeshPiece> readMeshPiece();
std::unique_ptr<ModelMesh> readModelMesh(const Gr2Mesh &mesh);
std::unique_ptr<render::ModelMesh> readModelMesh(const Gr2Mesh &mesh);
std::unique_ptr<MeshBone> readMeshBone();
std::unique_ptr<SkeletonBone> readSkeletonBone();
std::unique_ptr<Attachment> readAttachment();
};
} // namespace render
} // namespace tor
} // namespace reone

View file

@ -22,21 +22,23 @@
#include "glm/gtc/type_ptr.hpp"
#include "../../common/log.h"
#include "../../render/model/model.h"
#include "../common/log.h"
#include "../render/model/model.h"
using namespace std;
using namespace reone::render;
using namespace reone::resource;
namespace reone {
namespace render {
namespace tor {
JbaFile::JbaFile(const string &resRef, const shared_ptr<Model> &skeleton) :
static const glm::vec3 g_translationModifier { -0.001f, 0.001f, -0.001f };
JbaFile::JbaFile(const string &resRef) :
BinaryFile(4, "\0\0\0\0"),
_resRef(resRef),
_skeleton(skeleton) {
_resRef(resRef) {
}
void JbaFile::doLoad() {
@ -48,7 +50,6 @@ void JbaFile::doLoad() {
loadPartData();
loadKeyframes();
loadBones();
loadAnimation();
}
void JbaFile::loadHeader() {
@ -69,8 +70,8 @@ void JbaFile::loadPartHeaders() {
for (uint32_t i = 0; i < _numParts; ++i) {
JbaPart part;
part.keyframeIdx = readUint32();
part.dataSize = readUint32();
debug(boost::format("JBA: part %d header: %u %u") % i % part.keyframeIdx % part.dataSize);
part.keyframesSize = readUint32();
debug(boost::format("JBA: part %d header: %u %u") % i % part.keyframeIdx % part.keyframesSize);
_parts.push_back(move(part));
}
ignore(4 * _numParts);
@ -88,6 +89,15 @@ static string describeVector(const glm::vec3 &vec) {
return str(boost::format("(%.06f, %.06f, %.06f)") % vec.x % vec.y % vec.z);
}
static string describeQuaternion(const glm::quat &q) {
return str(boost::format("(%.06f, %.06f, %.06f, %.06f)") % q.x % q.y % q.z % q.w);
}
static glm::quat getUnitQuaternion(const glm::vec3 &axis) {
float w = glm::sqrt(1.0f - glm::dot(axis, axis));
return glm::normalize(glm::quat(w, axis));
}
void JbaFile::loadBoneData() {
seek(alignAt80(tell()));
vector<uint8_t> data(readArray<uint8_t>(48ll * _numBones));
@ -95,15 +105,15 @@ void JbaFile::loadBoneData() {
for (uint32_t i = 0; i < _numBones; ++i) {
JbaBone bone;
bone.minTranslation = glm::make_vec3(dataPtr + 0);
bone.maxTranslation = glm::make_vec3(dataPtr + 3);
bone.minOrientation = glm::make_vec3(dataPtr + 6);
bone.maxOrientation = glm::make_vec3(dataPtr + 9);
bone.minTranslation = glm::make_vec3(dataPtr + 0) * g_translationModifier;
bone.maxTranslation = glm::make_vec3(dataPtr + 3) * g_translationModifier;
bone.minOrientation = getUnitQuaternion(glm::make_vec3(dataPtr + 6));
bone.maxOrientation = getUnitQuaternion(glm::make_vec3(dataPtr + 9));
debug(boost::format("JBA: bone %d data: %s, %s, %s, %s")
% i
% describeVector(bone.minTranslation) % describeVector(bone.maxTranslation)
% describeVector(bone.minOrientation) % describeVector(bone.maxOrientation), 2);
% describeQuaternion(bone.minOrientation) % describeQuaternion(bone.maxOrientation), 2);
_bones.push_back(move(bone));
@ -116,34 +126,52 @@ void JbaFile::loadPartData() {
uint32_t start = alignAt80(tell());
seek(start);
_parts[i].data = readPartData();
_parts[i].keyframes = readPartKeyframes();
seek(start + _parts[i].dataSize);
seek(start + _parts[i].keyframesSize);
}
}
static glm::vec3 decompressPosition(uint32_t value, const glm::vec3 &min, const glm::vec3 &max) {
float nx = (value & 0x3ff) / 1023.0f;
float ny = ((value >> 10) & 0x7ff) / 2047.0f;
float nz = ((value >> 21) & 0x7ff) / 2047.0f;
return min + max * glm::vec3(nx, ny, nz);
uint32_t z = (value & 0x3ff);
uint32_t y = ((value >> 10) & 0x7ff);
uint32_t x = ((value >> 21) & 0x7ff);
float nx = (x & 0x1ff) / 511.0f;
float ny = (y & 0x3ff) / 1023.0f;
float nz = (z & 0x3ff) / 1023.0f;
bool sx = (x & !0x3ff) != 0;
bool sy = (y & !0x3ff) != 0;
bool sz = (z & !0x1ff) != 0;
glm::vec3 result(min);
result.x += (sx ? -1.0f : 1.0f) * nx * max.x;
result.y += (sy ? -1.0f : 1.0f) * ny * max.y;
result.z += (sz ? -1.0f : 1.0f) * nz * max.z;
return move(result);
}
static glm::quat decompressOrientation(const uint16_t *values, const glm::vec3 &min, const glm::vec3 &max) {
static glm::quat decompressOrientation(const uint16_t *values, const glm::quat &min, const glm::quat &max) {
float nx = (values[0] & 0x7fff) / 32767.0f;
float ny = (values[1] & 0xffff) / 65535.0f;
float nz = (values[2] & 0xffff) / 65535.0f;
glm::vec3 v(min + max * glm::vec3(nx, ny, nz));
float w = glm::sqrt(1.0f - glm::dot(v, v));
return glm::quat(w, v);
float ny = (values[1] & 0x7fff) / 32767.0f;
float nz = (values[2] & 0x7fff) / 32767.0f;
bool sx = (values[0] & 0x8000) != 0;
bool sy = (values[1] & 0x8000) != 0;
bool sz = (values[2] & 0x8000) != 0;
glm::vec3 axis(min.x, min.y, min.z);
axis.x += (sx ? -1.0f : 1.0f) * nx * max.x;
axis.y += (sy ? -1.0f : 1.0f) * ny * max.y;
axis.z += (sz ? -1.0f : 1.0f) * nz * max.z;
return getUnitQuaternion(axis);
}
static string describeQuaternion(const glm::quat &q) {
return str(boost::format("(%.06f, %.06f, %.06f, %.06f)") % q.x % q.y % q.z % q.w);
}
unique_ptr<JbaFile::PartData> JbaFile::readPartData() {
auto partData = make_unique<JbaFile::PartData>();
vector<vector<JbaFile::JbaKeyframe>> JbaFile::readPartKeyframes() {
vector<vector<JbaKeyframe>> keyframes;
ignore(8); // number of bones again
@ -160,28 +188,28 @@ unique_ptr<JbaFile::PartData> JbaFile::readPartData() {
const uint32_t *keyframeLayoutPtr = &keyframeLayout[0];
for (uint32_t i = 0; i < _numBones; ++i) {
vector<JbaKeyframe> keyframes;
keyframes.resize(keyframeLayoutPtr[0]);
vector<JbaKeyframe> boneKeyframes;
boneKeyframes.resize(keyframeLayoutPtr[0]);
// Decompress 48-bit orientation poses
for (uint32_t j = 0; j < keyframeLayoutPtr[0]; ++j) {
vector<uint16_t> values(readArray<uint16_t>(3));
keyframes[j].orientation = decompressOrientation(&values[0], _bones[i].minOrientation, _bones[i].maxOrientation);
debug(boost::format("JBA: bone %u: keyframe %u: orientation: %04X %04X %04X -> %s") % i % j % values[0] % values[1] % values[2] % describeQuaternion(keyframes[j].orientation), 2);
boneKeyframes[j].orientation = decompressOrientation(&values[0], _bones[i].minOrientation, _bones[i].maxOrientation);
debug(boost::format("JBA: bone %u: keyframe %u: orientation: %04X %04X %04X -> %s") % i % j % values[0] % values[1] % values[2] % describeQuaternion(boneKeyframes[j].orientation), 2);
}
// Decompress 32-bit translation poses, if any
for (uint32_t j = 0; j < keyframeLayoutPtr[2]; ++j) {
uint32_t value = readUint32();
keyframes[j].translation = decompressPosition(value, _bones[i].minTranslation, _bones[i].maxTranslation);
debug(boost::format("JBA: bone %u: keyframe %u: translation: %08X -> %s") % i % j % value % describeVector(keyframes[j].translation), 2);
boneKeyframes[j].translation = decompressPosition(value, _bones[i].minTranslation, _bones[i].maxTranslation);
debug(boost::format("JBA: bone %u: keyframe %u: translation: %08X -> %s") % i % j % value % describeVector(boneKeyframes[j].translation), 2);
}
partData->keyframes.push_back(move(keyframes));
keyframes.push_back(move(boneKeyframes));
keyframeLayoutPtr += 4;
}
return move(partData);
return move(keyframes);
}
void JbaFile::loadKeyframes() {
@ -191,16 +219,17 @@ void JbaFile::loadKeyframes() {
ignore(8);
vector<float> valuesAt08(readArray<float>(12));
_maxTranslation = glm::make_vec3(&valuesAt08[0]);
_minTranslation = glm::make_vec3(&valuesAt08[3]);
_maxOrientation = glm::make_vec3(&valuesAt08[6]);
_minOrientation = glm::make_vec3(&valuesAt08[9]);
_maxTranslation = glm::make_vec3(&valuesAt08[0]) * g_translationModifier;
_minTranslation = glm::make_vec3(&valuesAt08[3]) * g_translationModifier;
_maxOrientation = getUnitQuaternion(glm::make_vec3(&valuesAt08[6]));
_minOrientation = getUnitQuaternion(glm::make_vec3(&valuesAt08[9]));
debug(boost::format("JBA: maxTranslation=%s minTranslation=%s maxOrientation=%s minOrientation=%s") %
describeVector(_maxTranslation) % describeVector(_minTranslation) %
describeVector(_maxOrientation) % describeVector(_minOrientation));
describeQuaternion(_maxOrientation) % describeQuaternion(_minOrientation));
_numKeyframes = readUint32();
debug("JBA: numKeyframes=" + to_string(_numKeyframes));
_keyframes.resize(_numKeyframes);
ignore(3 * sizeof(uint32_t));
@ -212,6 +241,7 @@ void JbaFile::loadKeyframes() {
valuesAt48Ptr[0] % valuesAt48Ptr[1] % valuesAt48Ptr[2] %
describeQuaternion(orientation), 2);
_keyframes[i].orientation = move(orientation);
valuesAt48Ptr += 3;
}
if (_numKeyframes % 2 != 0) {
@ -222,6 +252,7 @@ void JbaFile::loadKeyframes() {
for (uint32_t i = 0; i < _numKeyframes; ++i) {
glm::vec3 translation(decompressPosition(keyframeValues[i], _minTranslation, _maxTranslation));
debug(boost::format("JBA: keyframe %d translation: %08X -> %s") % i % keyframeValues[i] % describeVector(translation), 2);
_keyframes[i].translation = move(translation);
}
}
@ -241,68 +272,6 @@ void JbaFile::loadBones() {
}
}
void JbaFile::loadAnimation() {
if (_numParts == 0) return;
int index = 0;
auto rootNode = make_shared<ModelNode>(index++);
stack<ModelNode *> modelNodes;
modelNodes.push(&_skeleton->rootNode());
while (!modelNodes.empty()) {
ModelNode *modelNode = modelNodes.top();
modelNodes.pop();
// Match a skeleton model node to a bone
auto maybeBone = find_if(_bones.begin(), _bones.end(), [this, &modelNode](auto &bone) {
return bone.name == modelNode->_name;
});
if (maybeBone != _bones.end()) {
// Convert a skeleton model node to an animation model node
auto animNode = make_shared<ModelNode>(index++);
animNode->_name = modelNode->_name;
uint32_t boneIdx = maybeBone->index;
for (size_t i = 0; i < _numKeyframes; ++i) {
int partIdx = getPartByKeyframe(i);
if (partIdx == -1) break;
const vector<JbaKeyframe> &keyframes = _parts[partIdx].data->keyframes[boneIdx];
const JbaKeyframe &keyframe = keyframes[i - _parts[partIdx].keyframeIdx];
float step = _length / static_cast<float>(keyframes.size() - 1);
float time = i * step;
ModelNode::PositionKeyframe pos;
pos.time = time;
pos.position = keyframe.translation;
animNode->_positionFrames.push_back(move(pos));
//ModelNode::OrientationKeyframe orient;
//orient.time = time;
//orient.orientation = modelNode->_orientation * keyframe.orientation;
//animNode->_orientationFrames.push_back(move(orient));
}
rootNode->_children.push_back(move(animNode));
}
for (auto &child : modelNode->_children) {
modelNodes.push(child.get());
}
}
_animation = make_shared<Animation>("pause1" /* _resRef */, _length, 0.5f * _length, vector<Animation::Event>(), rootNode);
}
int JbaFile::getPartByKeyframe(int keyframeIdx) const {
for (uint32_t i = 0; i < _numParts; ++i) {
if (keyframeIdx >= _parts[i].keyframeIdx) return i;
}
return -1;
}
} // namespace render
} // namespace tor
} // namespace reone

View file

@ -20,18 +20,15 @@
#include "glm/gtc/quaternion.hpp"
#include "glm/vec3.hpp"
#include "../../render/model/animation.h"
#include "../../resource/format/binfile.h"
#include "../resource/format/binfile.h"
namespace reone {
namespace render {
namespace tor {
class JbaFile : public resource::BinaryFile {
public:
JbaFile(const std::string &resRef, const std::shared_ptr<Model> &skeleton);
std::shared_ptr<Animation> animation() const { return _animation; }
JbaFile(const std::string &resRef);
private:
struct JbaKeyframe {
@ -39,27 +36,22 @@ private:
glm::quat orientation { 1.0f, 0.0f, 0.0, 0.0f };
};
struct PartData {
std::vector<std::vector<JbaKeyframe>> keyframes;
};
struct JbaPart {
uint32_t keyframeIdx { 0 };
uint32_t dataSize;
std::unique_ptr<PartData> data;
uint32_t keyframesSize;
std::vector<std::vector<JbaKeyframe>> keyframes;
};
struct JbaBone {
glm::vec3 minTranslation { 0.0f };
glm::vec3 maxTranslation { 0.0f };
glm::vec3 minOrientation { 0.0f };
glm::vec3 maxOrientation { 0.0f };
glm::quat minOrientation { 1.0f, 0.0f, 0.0f, 0.0f };
glm::quat maxOrientation { 1.0f, 0.0f, 0.0f, 0.0f };
uint32_t index { 0 };
std::string name;
};
std::string _resRef;
std::shared_ptr<render::Model> _skeleton;
float _length { 0.0f };
float _fps { 0 };
@ -69,14 +61,12 @@ private:
glm::vec3 _maxTranslation { 0.0f };
glm::vec3 _minTranslation { 0.0f };
glm::vec3 _maxOrientation { 0.0f };
glm::vec3 _minOrientation { 0.0f };
glm::quat _maxOrientation { 1.0f, 0.0f, 0.0f, 0.0f };
glm::quat _minOrientation { 1.0f, 0.0f, 0.0f, 0.0f };
std::vector<JbaPart> _parts;
std::vector<glm::quat> _orientationFrames;
std::vector<glm::vec3> _positionFrames;
std::vector<JbaKeyframe> _keyframes;
std::vector<JbaBone> _bones;
std::shared_ptr<Animation> _animation;
void doLoad();
@ -86,13 +76,12 @@ private:
void loadPartData();
void loadKeyframes();
void loadBones();
void loadAnimation();
std::unique_ptr<PartData> readPartData();
std::vector<std::vector<JbaKeyframe>> readPartKeyframes();
int getPartByKeyframe(int keyframeIdx) const;
};
} // namespace render
} // namespace tor
} // namespace reone