refactor: Move TOR support code into a separate library
This commit is contained in:
parent
a0fa2be9a3
commit
b90c1d9f4a
24 changed files with 261 additions and 481 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in a new issue