mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-01 14:21:41 +00:00
WINTERMUTE: Implement X file loader and restore original engine code
This commit is contained in:
parent
ed543e3747
commit
0c4d570bcf
@ -49,8 +49,8 @@ XMeshOpenGLShader::~XMeshOpenGLShader() {
|
||||
glDeleteBuffers(1, &_indexBuffer);
|
||||
}
|
||||
|
||||
bool XMeshOpenGLShader::loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) {
|
||||
if (XMesh::loadFromX(filename, lexer, materialReferences)) {
|
||||
bool XMeshOpenGLShader::loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
|
||||
if (XMesh::loadFromXData(filename, xobj, materialReferences)) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, 4 * kVertexComponentCount * _vertexCount, _vertexData, GL_DYNAMIC_DRAW);
|
||||
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
XMeshOpenGLShader(BaseGame *inGame, OpenGL::Shader *shader, OpenGL::Shader *flatShadowShader);
|
||||
~XMeshOpenGLShader() override;
|
||||
|
||||
bool loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) override;
|
||||
bool loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) override;
|
||||
bool render(XModel *model) override;
|
||||
bool renderFlatShadowModel() override;
|
||||
bool update(FrameNode *parentFrame) override;
|
||||
|
@ -26,11 +26,12 @@
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/base/gfx/xanimation.h"
|
||||
#include "engines/wintermute/base/gfx/xanimation_set.h"
|
||||
#include "engines/wintermute/base/gfx/xframe_node.h"
|
||||
#include "engines/wintermute/base/gfx/xmodel.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
|
||||
namespace Wintermute {
|
||||
@ -67,72 +68,197 @@ bool Animation::findBone(FrameNode *rootFrame) {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool Animation::loadFromX(XFileLexer &lexer, AnimationSet *parentAnimSet) {
|
||||
if (lexer.tokenIsIdentifier()) {
|
||||
lexer.advanceToNextToken(); // skip name
|
||||
lexer.advanceOnOpenBraces();
|
||||
} else {
|
||||
lexer.advanceOnOpenBraces();
|
||||
}
|
||||
bool Animation::load(XFileData *xobj, AnimationSet *parentAnimSet) {
|
||||
bool result;
|
||||
XClassType objectType;
|
||||
|
||||
bool ret = true;
|
||||
if (xobj->isReference()) {
|
||||
// The original data is found
|
||||
result = xobj->getType(objectType);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("AnimationKey")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken(); // skip name
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
int keyType = lexer.readInt();
|
||||
int keyCount = lexer.readInt();
|
||||
|
||||
switch (keyType) {
|
||||
case 0:
|
||||
loadRotationKeyData(lexer, keyCount);
|
||||
break;
|
||||
case 1:
|
||||
loadScaleKeyData(lexer, keyCount);
|
||||
break;
|
||||
case 2:
|
||||
loadPositionKeyData(lexer, keyCount);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
loadMatrixKeyData(lexer, keyCount);
|
||||
break;
|
||||
default:
|
||||
warning("Animation::loadFromX unknown key type encountered");
|
||||
// The object must be a frame
|
||||
if (objectType == kXClassFrame) {
|
||||
// The frame is found, get its name
|
||||
// The name will be used later by the findBone function to get
|
||||
// a pointer to the target frame
|
||||
if (_targetFrame) {
|
||||
BaseEngine::LOG(0, "Animation frame name reference duplicated");
|
||||
return false;
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
// get name
|
||||
result = XModel::loadName(_targetName, xobj);
|
||||
if (!result) {
|
||||
BaseEngine::LOG(0, "Error retrieving frame name while loading animation");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// a data object is found, get its type
|
||||
result = xobj->getType(objectType);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
} else if (lexer.tokenIsIdentifier("AnimationOptions")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
// I think we can ignore these for the moment
|
||||
lexer.readInt(); // whether animation is open or closed
|
||||
lexer.readInt(); // position quality
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
} else if (lexer.tokenIsOfType(OPEN_BRACES)) {
|
||||
// this is a reference to a frame/bone, given as an identifier
|
||||
lexer.advanceToNextToken();
|
||||
_targetName = lexer.readString();
|
||||
} else if (lexer.tokenIsIdentifier("Animation")) {
|
||||
// pass it up
|
||||
break;
|
||||
} else if (lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
break;
|
||||
} else {
|
||||
warning("Animation::loadFromX unexpected token encounterd");
|
||||
ret = false;
|
||||
break;
|
||||
if (objectType == kXClassAnimationKey) {
|
||||
// an animation key is found, load the data
|
||||
XAnimationKeyObject *animationKey = xobj->getXAnimationKeyObject();
|
||||
if (!animationKey)
|
||||
return false;
|
||||
result = loadAnimationKeyData(animationKey);
|
||||
if (!result)
|
||||
return false;
|
||||
} else if (objectType == kXClassAnimationOptions) {
|
||||
XAnimationOptionsObject *animationOptions = xobj->getXAnimationOptionsObject();
|
||||
if (!animationOptions)
|
||||
return false;
|
||||
result = loadAnimationOptionData(animationOptions, parentAnimSet);
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool Animation::loadAnimationOptionData(XAnimationOptionsObject *animationOptionData, AnimationSet *parentAnimSet) {
|
||||
if (animationOptionData->_openclosed && parentAnimSet)
|
||||
parentAnimSet->_looping = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool Animation::loadAnimationKeyData(XAnimationKeyObject *animationKey) {
|
||||
// get the type and count of the key
|
||||
uint32 keyType = animationKey->_keyType;
|
||||
uint32 numKeys = animationKey->_numKeys;
|
||||
|
||||
if (keyType == 0) { // rotation key
|
||||
if (_rotKeys.size() != 0) {
|
||||
BaseEngine::LOG(0, "Rotation key duplicated");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 key = 0; key < numKeys; key++) {
|
||||
const XTimedFloatKeys *fileRotKey = &animationKey->_keys[key];
|
||||
assert(fileRotKey->_numTfkeys == 4);
|
||||
|
||||
BoneRotationKey *rotKey = new BoneRotationKey;
|
||||
rotKey->_time = fileRotKey->_time;
|
||||
// NOTE x files are w x y z and QUATERNIONS are x y z w
|
||||
rotKey->_rotation.w() = fileRotKey->_tfkeys[0];
|
||||
rotKey->_rotation.x() = fileRotKey->_tfkeys[1];
|
||||
rotKey->_rotation.y() = fileRotKey->_tfkeys[2];
|
||||
// mirror z component
|
||||
rotKey->_rotation.z() = -fileRotKey->_tfkeys[3];
|
||||
|
||||
_rotKeys.push_back(rotKey);
|
||||
}
|
||||
} else if (keyType == 1) { // scale key
|
||||
if (_scaleKeys.size() != 0) {
|
||||
BaseEngine::LOG(0, "Scale key duplicated");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 key = 0; key < numKeys; key++) {
|
||||
const XTimedFloatKeys *fileScaleKey = &animationKey->_keys[key];
|
||||
assert(fileScaleKey->_numTfkeys == 3);
|
||||
|
||||
BoneScaleKey *scaleKey = new BoneScaleKey;
|
||||
scaleKey->_time = fileScaleKey->_time;
|
||||
for (uint i = 0; i < fileScaleKey->_numTfkeys; ++i) {
|
||||
scaleKey->_scale.getData()[i] = fileScaleKey->_tfkeys[i];
|
||||
}
|
||||
|
||||
_scaleKeys.push_back(scaleKey);
|
||||
}
|
||||
} else if (keyType == 2) { // position key
|
||||
if (_posKeys.size() != 0) {
|
||||
BaseEngine::LOG(0, "Position key duplicated");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 key = 0; key < numKeys; key++) {
|
||||
const XTimedFloatKeys *filePosKey = &animationKey->_keys[key];
|
||||
assert(filePosKey->_numTfkeys == 3);
|
||||
|
||||
BonePositionKey *posKey = new BonePositionKey;
|
||||
posKey->_time = filePosKey->_time;
|
||||
for (uint i = 0; i < filePosKey->_numTfkeys; ++i) {
|
||||
posKey->_pos.getData()[i] = filePosKey->_tfkeys[i];
|
||||
}
|
||||
|
||||
// mirror Z
|
||||
posKey->_pos.getData()[2] *= -1.0f;
|
||||
|
||||
_posKeys.push_back(posKey);
|
||||
}
|
||||
} else if (keyType == 4) { // matrix key
|
||||
if (_rotKeys.size() != 0 || _scaleKeys.size() != 0 || _posKeys.size() != 0) {
|
||||
BaseEngine::LOG(0, "Matrix key duplicated");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 key = 0; key < numKeys; key++) {
|
||||
const XTimedFloatKeys *fileMatrixKey = &animationKey->_keys[key];
|
||||
uint32 time = fileMatrixKey->_time;
|
||||
assert(fileMatrixKey->_numTfkeys == 16);
|
||||
|
||||
Math::Matrix4 keyData;
|
||||
for (int r = 0; r < 4; ++r) {
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
keyData(c, r) = fileMatrixKey->_tfkeys[r * 4 + c];
|
||||
}
|
||||
}
|
||||
|
||||
// mirror at orign
|
||||
keyData(2, 3) *= -1.0f;
|
||||
|
||||
// mirror base vectors
|
||||
keyData(2, 0) *= -1.0f;
|
||||
keyData(2, 1) *= -1.0f;
|
||||
|
||||
// change handedness
|
||||
keyData(0, 2) *= -1.0f;
|
||||
keyData(1, 2) *= -1.0f;
|
||||
|
||||
Math::Vector3d translation = keyData.getPosition();
|
||||
|
||||
Math::Vector3d scale;
|
||||
scale.x() = keyData(0, 0) * keyData(0, 0) + keyData(1, 0) * keyData(1, 0) + keyData(2, 0) * keyData(2, 0);
|
||||
scale.x() = sqrtf(scale.x());
|
||||
scale.y() = keyData(0, 1) * keyData(0, 1) + keyData(1, 1) * keyData(1, 1) + keyData(2, 1) * keyData(2, 1);
|
||||
scale.y() = sqrtf(scale.y());
|
||||
scale.z() = keyData(0, 2) * keyData(0, 2) + keyData(1, 2) * keyData(1, 2) + keyData(2, 2) * keyData(2, 2);
|
||||
scale.z() = sqrtf(scale.z());
|
||||
|
||||
Math::Quaternion rotation;
|
||||
rotation.fromMatrix(keyData.getRotation());
|
||||
|
||||
BonePositionKey *positionKey = new BonePositionKey;
|
||||
BoneScaleKey *scaleKey = new BoneScaleKey;
|
||||
BoneRotationKey *rotationKey = new BoneRotationKey;
|
||||
|
||||
positionKey->_time = time;
|
||||
scaleKey->_time = time;
|
||||
rotationKey->_time = time;
|
||||
|
||||
positionKey->_pos = translation;
|
||||
scaleKey->_scale = scale;
|
||||
rotationKey->_rotation = rotation;
|
||||
|
||||
_posKeys.push_back(positionKey);
|
||||
_scaleKeys.push_back(scaleKey);
|
||||
_rotKeys.push_back(rotationKey);
|
||||
}
|
||||
} else {
|
||||
// the type is unknown, report the error
|
||||
BaseEngine::LOG(0, "Unexpected animation key type (%d)", keyType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -307,146 +433,4 @@ uint32 Animation::getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
// try to put these functions into a template?
|
||||
bool Animation::loadRotationKeyData(XFileLexer &lexer, int count) {
|
||||
for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
|
||||
BoneRotationKey *key = new BoneRotationKey;
|
||||
key->_time = lexer.readInt();
|
||||
|
||||
int floatCount = lexer.readInt();
|
||||
assert(floatCount == 4);
|
||||
|
||||
// .X file format puts the w coordinate first
|
||||
key->_rotation.w() = lexer.readFloat();
|
||||
key->_rotation.x() = lexer.readFloat();
|
||||
key->_rotation.y() = lexer.readFloat();
|
||||
// mirror z component
|
||||
key->_rotation.z() = -lexer.readFloat();
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
}
|
||||
|
||||
_rotKeys.push_back(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::loadScaleKeyData(XFileLexer &lexer, int count) {
|
||||
for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
|
||||
BoneScaleKey *key = new BoneScaleKey;
|
||||
key->_time = lexer.readInt();
|
||||
|
||||
int floatCount = lexer.readInt();
|
||||
assert(floatCount == 3);
|
||||
|
||||
for (int i = 0; i < floatCount; ++i) {
|
||||
key->_scale.getData()[i] = lexer.readFloat();
|
||||
}
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
}
|
||||
|
||||
_scaleKeys.push_back(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::loadPositionKeyData(XFileLexer &lexer, int count) {
|
||||
for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
|
||||
BonePositionKey *key = new BonePositionKey;
|
||||
key->_time = lexer.readInt();
|
||||
|
||||
int floatCount = lexer.readInt();
|
||||
assert(floatCount == 3);
|
||||
|
||||
for (int i = 0; i < floatCount; ++i) {
|
||||
key->_pos.getData()[i] = lexer.readFloat();
|
||||
}
|
||||
|
||||
key->_pos.getData()[2] *= -1.0f;
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
}
|
||||
|
||||
_posKeys.push_back(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::loadMatrixKeyData(XFileLexer &lexer, int count) {
|
||||
for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
|
||||
uint32 time = lexer.readInt();
|
||||
int floatCount = lexer.readInt();
|
||||
assert(floatCount == 16);
|
||||
|
||||
Math::Matrix4 keyData;
|
||||
|
||||
for (int r = 0; r < 4; ++r) {
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
keyData(c, r) = lexer.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
// mirror at orign
|
||||
keyData(2, 3) *= -1.0f;
|
||||
|
||||
// mirror base vectors
|
||||
keyData(2, 0) *= -1.0f;
|
||||
keyData(2, 1) *= -1.0f;
|
||||
|
||||
// change handedness
|
||||
keyData(0, 2) *= -1.0f;
|
||||
keyData(1, 2) *= -1.0f;
|
||||
|
||||
Math::Vector3d translation = keyData.getPosition();
|
||||
|
||||
Math::Vector3d scale;
|
||||
scale.x() = keyData(0, 0) * keyData(0, 0) + keyData(1, 0) * keyData(1, 0) + keyData(2, 0) * keyData(2, 0);
|
||||
scale.x() = sqrtf(scale.x());
|
||||
scale.y() = keyData(0, 1) * keyData(0, 1) + keyData(1, 1) * keyData(1, 1) + keyData(2, 1) * keyData(2, 1);
|
||||
scale.y() = sqrtf(scale.y());
|
||||
scale.z() = keyData(0, 2) * keyData(0, 2) + keyData(1, 2) * keyData(1, 2) + keyData(2, 2) * keyData(2, 2);
|
||||
scale.z() = sqrtf(scale.z());
|
||||
|
||||
Math::Quaternion rotation;
|
||||
rotation.fromMatrix(keyData.getRotation());
|
||||
|
||||
BonePositionKey *positionKey = new BonePositionKey;
|
||||
BoneScaleKey *scaleKey = new BoneScaleKey;
|
||||
BoneRotationKey *rotationKey = new BoneRotationKey;
|
||||
|
||||
positionKey->_time = time;
|
||||
scaleKey->_time = time;
|
||||
rotationKey->_time = time;
|
||||
|
||||
positionKey->_pos = translation;
|
||||
scaleKey->_scale = scale;
|
||||
rotationKey->_rotation = rotation;
|
||||
|
||||
_posKeys.push_back(positionKey);
|
||||
_scaleKeys.push_back(scaleKey);
|
||||
_rotKeys.push_back(rotationKey);
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
||||
|
@ -38,14 +38,16 @@ namespace Wintermute {
|
||||
|
||||
class FrameNode;
|
||||
class AnimationSet;
|
||||
class XFileLexer;
|
||||
class XFileData;
|
||||
struct XAnimationKeyObject;
|
||||
struct XAnimationOptionsObject;
|
||||
|
||||
class Animation : public BaseClass {
|
||||
public:
|
||||
Animation(BaseGame *inGame);
|
||||
virtual ~Animation();
|
||||
|
||||
bool loadFromX(XFileLexer &lexer, AnimationSet *parentAnimationSet);
|
||||
bool load(XFileData *xobj, AnimationSet *parentAnimSet);
|
||||
|
||||
bool findBone(FrameNode *rootFrame);
|
||||
bool update(int slot, uint32 localTime, float animLerpValue);
|
||||
@ -79,10 +81,8 @@ protected:
|
||||
BaseArray<BoneScaleKey *> _scaleKeys;
|
||||
|
||||
private:
|
||||
bool loadRotationKeyData(XFileLexer &lexer, int count);
|
||||
bool loadScaleKeyData(XFileLexer &lexer, int count);
|
||||
bool loadPositionKeyData(XFileLexer &lexer, int count);
|
||||
bool loadMatrixKeyData(XFileLexer &lexer, int count);
|
||||
bool loadAnimationKeyData(XAnimationKeyObject *animationKey);
|
||||
bool loadAnimationOptionData(XAnimationOptionsObject *animationSet, AnimationSet *parentAnimSet);
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/gfx/xanimation_set.h"
|
||||
#include "engines/wintermute/base/gfx/xmodel.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
|
||||
namespace Wintermute {
|
||||
@ -56,39 +55,6 @@ AnimationSet::~AnimationSet() {
|
||||
_events.clear();
|
||||
}
|
||||
|
||||
bool AnimationSet::loadFromX(XFileLexer &lexer, const Common::String &filename) {
|
||||
if (lexer.tokenIsIdentifier()) {
|
||||
setName(lexer.tokenToString().c_str());
|
||||
lexer.advanceToNextToken();
|
||||
} else {
|
||||
Common::String name = filename + "_animation";
|
||||
setName(name.c_str());
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken();
|
||||
|
||||
bool ret = true;
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("Animation")) {
|
||||
lexer.advanceToNextToken();
|
||||
|
||||
Animation *animation = new Animation(_gameRef);
|
||||
animation->loadFromX(lexer, this);
|
||||
_animations.add(animation);
|
||||
} else if (lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
break;
|
||||
} else {
|
||||
warning("AnimationSet::loadFromX unexpected token");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool AnimationSet::findBones(FrameNode *rootFrame) {
|
||||
for (uint32 i = 0; i < _animations.size(); i++) {
|
||||
|
@ -38,7 +38,6 @@
|
||||
namespace Wintermute {
|
||||
|
||||
class XModel;
|
||||
class XFileLexer;
|
||||
|
||||
class AnimationSet : public BaseNamedObject {
|
||||
public:
|
||||
@ -75,7 +74,6 @@ public:
|
||||
AnimationSet(BaseGame *inGame, XModel *model);
|
||||
virtual ~AnimationSet();
|
||||
|
||||
bool loadFromX(XFileLexer &lexer, const Common::String &filename);
|
||||
bool findBones(FrameNode *rootFrame);
|
||||
bool addAnimation(Animation *anim);
|
||||
bool addEvent(AnimationEvent *event);
|
||||
|
89
engines/wintermute/base/gfx/xfile.cpp
Normal file
89
engines/wintermute/base/gfx/xfile.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_file_manager.h"
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/base/gfx/xfile.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XFile::XFile(BaseGame *inGame) : BaseClass(inGame) {
|
||||
_xfile = nullptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XFile::~XFile() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XFile::closeFile() {
|
||||
delete _xfile;
|
||||
_xfile = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XFile::openFile(const Common::String &filename) {
|
||||
closeFile();
|
||||
|
||||
// load file
|
||||
uint32 size;
|
||||
byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size);
|
||||
if (!buffer) {
|
||||
closeFile();
|
||||
return false;
|
||||
}
|
||||
|
||||
_xfile = new XFileLoader();
|
||||
if (!_xfile) {
|
||||
delete[] buffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = _xfile->load(buffer, size);
|
||||
delete[] buffer;
|
||||
if (!res) {
|
||||
BaseEngine::LOG(0, "Error loading X file '%s'", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// create enum object
|
||||
if (!res || !_xfile->createEnumObject(_xenum)) {
|
||||
BaseEngine::LOG(res, "Error creating XFile enum object for '%s'", filename.c_str());
|
||||
closeFile();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
56
engines/wintermute/base/gfx/xfile.h
Normal file
56
engines/wintermute/base/gfx/xfile.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_XFILE_H
|
||||
#define WINTERMUTE_XFILE_H
|
||||
|
||||
#include "engines/wintermute/base/base.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
class XFile : public BaseClass {
|
||||
public:
|
||||
XFile(BaseGame *inGame);
|
||||
virtual ~XFile();
|
||||
|
||||
bool openFile(const Common::String &filename);
|
||||
bool closeFile();
|
||||
|
||||
XFileEnumObject getEnum() {
|
||||
return _xenum;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
XFileLoader *_xfile;
|
||||
XFileEnumObject _xenum;
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
||||
#endif
|
1510
engines/wintermute/base/gfx/xfile_loader.cpp
Normal file
1510
engines/wintermute/base/gfx/xfile_loader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
566
engines/wintermute/base/gfx/xfile_loader.h
Normal file
566
engines/wintermute/base/gfx/xfile_loader.h
Normal file
@ -0,0 +1,566 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Partially based on XFile parser code from Wine sources.
|
||||
* Copyright 2008 Christian Costa
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_XFILE_LOADER_H
|
||||
#define WINTERMUTE_XFILE_LOADER_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stack.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
enum XTokenType : uint16 {
|
||||
XTOKEN_ERROR = 0xffff,
|
||||
XTOKEN_NONE = 0,
|
||||
XTOKEN_NAME = 1,
|
||||
XTOKEN_STRING = 2,
|
||||
XTOKEN_INTEGER = 3,
|
||||
XTOKEN_GUID = 5,
|
||||
XTOKEN_INTEGER_LIST = 6,
|
||||
XTOKEN_FLOAT_LIST = 7,
|
||||
XTOKEN_OBRACE = 10,
|
||||
XTOKEN_CBRACE = 11,
|
||||
XTOKEN_OPAREN = 12,
|
||||
XTOKEN_CPAREN = 13,
|
||||
XTOKEN_OBRACKET = 14,
|
||||
XTOKEN_CBRACKET = 15,
|
||||
XTOKEN_OANGLE = 16,
|
||||
XTOKEN_CANGLE = 17,
|
||||
XTOKEN_DOT = 18,
|
||||
XTOKEN_COMMA = 19,
|
||||
XTOKEN_SEMICOLON = 20,
|
||||
XTOKEN_TEMPLATE = 31,
|
||||
XTOKEN_WORD = 40,
|
||||
XTOKEN_DWORD = 41,
|
||||
XTOKEN_FLOAT = 42,
|
||||
XTOKEN_DOUBLE = 43,
|
||||
XTOKEN_CHAR = 44,
|
||||
XTOKEN_UCHAR = 45,
|
||||
XTOKEN_SWORD = 46,
|
||||
XTOKEN_SDWORD = 47,
|
||||
XTOKEN_VOID = 48,
|
||||
XTOKEN_LPSTR = 49,
|
||||
XTOKEN_UNICODE = 50,
|
||||
XTOKEN_CSTRING = 51,
|
||||
XTOKEN_ARRAY = 52
|
||||
};
|
||||
|
||||
#define XMAX_NAME_LEN 120
|
||||
#define XMAX_STRING_LEN 500
|
||||
|
||||
struct XToken {
|
||||
XTokenType _type;
|
||||
char _textVal[XMAX_STRING_LEN];
|
||||
uint32 _integerVal;
|
||||
float _floatVal;
|
||||
};
|
||||
|
||||
struct XVector {
|
||||
float _x;
|
||||
float _y;
|
||||
float _z;
|
||||
};
|
||||
|
||||
struct XCoords2d {
|
||||
float _u;
|
||||
float _v;
|
||||
};
|
||||
|
||||
struct XMeshFace {
|
||||
uint32 _numFaceVertexIndices;
|
||||
uint32 _faceVertexIndices[4];
|
||||
};
|
||||
|
||||
struct XTimedFloatKeys {
|
||||
float _time;
|
||||
uint32 _numTfkeys;
|
||||
float _tfkeys[16];
|
||||
};
|
||||
|
||||
struct XIndexedColor {
|
||||
uint32 _index;
|
||||
float _indexColorR;
|
||||
float _indexColorG;
|
||||
float _indexColorB;
|
||||
float _indexColorA;
|
||||
};
|
||||
|
||||
struct XVertexElement {
|
||||
uint32 _type;
|
||||
uint32 _method;
|
||||
uint32 _usage;
|
||||
uint32 _usageIndex;
|
||||
};
|
||||
|
||||
struct XMeshMaterialListObject {
|
||||
uint32 _nMaterials;
|
||||
uint32 _numFaceIndexes;
|
||||
uint32 *_faceIndexes{};
|
||||
|
||||
~XMeshMaterialListObject() {
|
||||
delete[] _faceIndexes;
|
||||
}
|
||||
};
|
||||
|
||||
struct XVertexDuplicationIndicesObject {
|
||||
uint32 _nOriginalVertices;
|
||||
uint32 _numIndices;
|
||||
uint32 *_indices{};
|
||||
|
||||
~XVertexDuplicationIndicesObject() {
|
||||
delete[] _indices;
|
||||
}
|
||||
};
|
||||
|
||||
struct XSkinMeshHeaderObject{
|
||||
uint32 _nMaxSkinWeightsPerVertex;
|
||||
uint32 _nMaxSkinWeightsPerFace;
|
||||
uint32 _nBones;
|
||||
};
|
||||
|
||||
struct XSkinWeightsObject {
|
||||
char _transformNodeName[XMAX_NAME_LEN];
|
||||
uint32 _numVertexIndices;
|
||||
uint32 *_vertexIndices{};
|
||||
uint32 _numWeights;
|
||||
float *_weights{};
|
||||
float _matrixOffset[16];
|
||||
|
||||
~XSkinWeightsObject() {
|
||||
delete[] _vertexIndices;
|
||||
delete[] _weights;
|
||||
}
|
||||
};
|
||||
|
||||
struct XMeshObject {
|
||||
uint32 _numVertices;
|
||||
XVector *_vertices{};
|
||||
uint32 _numFaces;
|
||||
XMeshFace *_faces{};
|
||||
|
||||
~XMeshObject() {
|
||||
delete[] _vertices;
|
||||
delete[] _faces;
|
||||
}
|
||||
};
|
||||
|
||||
struct XMeshNormalsObject {
|
||||
uint32 _numNormals;
|
||||
XVector *_normals{};
|
||||
uint32 _numFaceNormals;
|
||||
XMeshFace *_faceNormals{};
|
||||
|
||||
~XMeshNormalsObject() {
|
||||
delete[] _normals;
|
||||
delete[] _faceNormals;
|
||||
}
|
||||
};
|
||||
|
||||
struct XMeshVertexColorsObject {
|
||||
uint32 _numVertexColors;
|
||||
XIndexedColor *_vertexColors{};
|
||||
|
||||
~XMeshVertexColorsObject() {
|
||||
delete[] _vertexColors;
|
||||
}
|
||||
};
|
||||
|
||||
struct XMeshTextureCoordsObject {
|
||||
uint32 _numTextureCoords;
|
||||
XCoords2d *_textureCoords{};
|
||||
|
||||
~XMeshTextureCoordsObject() {
|
||||
delete[] _textureCoords;
|
||||
}
|
||||
};
|
||||
|
||||
struct XMaterialObject {
|
||||
float _colorR;
|
||||
float _colorG;
|
||||
float _colorB;
|
||||
float _colorA;
|
||||
float _power;
|
||||
float _specularR;
|
||||
float _specularG;
|
||||
float _specularB;
|
||||
float _emissiveR;
|
||||
float _emissiveG;
|
||||
float _emissiveB;
|
||||
};
|
||||
|
||||
struct XTextureFilenameObject {
|
||||
char _filename[XMAX_NAME_LEN];
|
||||
};
|
||||
|
||||
struct XAnimTicksPerSecondObject {
|
||||
uint32 _animTicksPerSecond;
|
||||
};
|
||||
|
||||
struct XAnimationSetObject{
|
||||
};
|
||||
|
||||
struct XAnimationObject{
|
||||
};
|
||||
|
||||
struct XAnimationKeyObject {
|
||||
uint32 _keyType;
|
||||
uint32 _numKeys;
|
||||
XTimedFloatKeys *_keys{};
|
||||
|
||||
~XAnimationKeyObject() {
|
||||
delete[] _keys;
|
||||
}
|
||||
};
|
||||
|
||||
struct XAnimationOptionsObject {
|
||||
uint32 _openclosed;
|
||||
uint32 _positionquality;
|
||||
};
|
||||
|
||||
struct XFrameObject {
|
||||
};
|
||||
|
||||
struct XFrameTransformMatrixObject {
|
||||
float _frameMatrix[16];
|
||||
};
|
||||
|
||||
struct XDeclDataObject {
|
||||
uint32 _numElements;
|
||||
XVertexElement *_elements{};
|
||||
uint32 _numData;
|
||||
uint32 *_data{};
|
||||
|
||||
~XDeclDataObject() {
|
||||
delete[] _elements;
|
||||
delete[] _data;
|
||||
}
|
||||
};
|
||||
|
||||
struct XFVFDataObject {
|
||||
uint32 _dwFVF;
|
||||
uint32 _numData;
|
||||
uint32 *_data{};
|
||||
|
||||
~XFVFDataObject() {
|
||||
delete[] _data;
|
||||
}
|
||||
};
|
||||
|
||||
enum XClassType {
|
||||
kXClassUnknown = 0,
|
||||
kXClassAnimTicksPerSecond,
|
||||
kXClassFrameTransformMatrix,
|
||||
kXClassFrame,
|
||||
kXClassMesh,
|
||||
kXClassMeshNormals,
|
||||
kXClassMeshVertexColors,
|
||||
kXClassMeshTextureCoords,
|
||||
kXClassMeshMaterialList,
|
||||
kXClassVertexDuplicationIndices,
|
||||
kXClassMaterial,
|
||||
kXClassTextureFilename,
|
||||
kXClassSkinMeshHeader,
|
||||
kXClassSkinWeights,
|
||||
kXClassAnimationSet,
|
||||
kXClassAnimation,
|
||||
kXClassAnimationKey,
|
||||
kXClassAnimationOptions,
|
||||
kXClassDeclData,
|
||||
kXClassFVFData,
|
||||
};
|
||||
|
||||
class XFileEnumObject;
|
||||
|
||||
class XObject {
|
||||
friend class XFileLoader;
|
||||
friend class XFileData;
|
||||
friend class XFileEnumObject;
|
||||
|
||||
private:
|
||||
|
||||
Common::String _name;
|
||||
XClassType _classType{};
|
||||
void *_object{};
|
||||
XObject *_targetObject{};
|
||||
Common::Stack<XObject *> _children;
|
||||
|
||||
public:
|
||||
|
||||
void deinit() {
|
||||
switch (_classType) {
|
||||
case kXClassAnimTicksPerSecond:
|
||||
delete (XAnimTicksPerSecondObject *)_object;
|
||||
break;
|
||||
case kXClassAnimationKey:
|
||||
delete (XAnimationKeyObject *)_object;
|
||||
break;
|
||||
case kXClassAnimation:
|
||||
delete (XAnimationObject *)_object;
|
||||
break;
|
||||
case kXClassAnimationOptions:
|
||||
delete (XAnimationOptionsObject *)_object;
|
||||
break;
|
||||
case kXClassAnimationSet:
|
||||
delete (XAnimationSetObject *)_object;
|
||||
break;
|
||||
case kXClassDeclData:
|
||||
delete (XDeclDataObject *)_object;
|
||||
break;
|
||||
case kXClassFrame:
|
||||
delete (XFrameObject *)_object;
|
||||
break;
|
||||
case kXClassFrameTransformMatrix:
|
||||
delete (XFrameTransformMatrixObject *)_object;
|
||||
break;
|
||||
case kXClassFVFData:
|
||||
delete (XFVFDataObject *)_object;
|
||||
break;
|
||||
case kXClassMaterial:
|
||||
delete (XMaterialObject *)_object;
|
||||
break;
|
||||
case kXClassMesh:
|
||||
delete (XMeshObject *)_object;
|
||||
break;
|
||||
case kXClassMeshMaterialList:
|
||||
delete (XMeshMaterialListObject *)_object;
|
||||
break;
|
||||
case kXClassMeshNormals:
|
||||
delete (XMeshNormalsObject *)_object;
|
||||
break;
|
||||
case kXClassMeshVertexColors:
|
||||
delete (XMeshVertexColorsObject *)_object;
|
||||
break;
|
||||
case kXClassMeshTextureCoords:
|
||||
delete (XMeshTextureCoordsObject *)_object;
|
||||
break;
|
||||
case kXClassSkinMeshHeader:
|
||||
delete (XSkinMeshHeaderObject *)_object;
|
||||
break;
|
||||
case kXClassSkinWeights:
|
||||
delete (XSkinWeightsObject *)_object;
|
||||
break;
|
||||
case kXClassVertexDuplicationIndices:
|
||||
delete (XVertexDuplicationIndicesObject *)_object;
|
||||
break;
|
||||
case kXClassTextureFilename:
|
||||
delete (XTextureFilenameObject *)_object;
|
||||
break;
|
||||
case kXClassUnknown:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class XFileLoader {
|
||||
friend class XFileEnumObject;
|
||||
|
||||
private:
|
||||
|
||||
const int kCabBlockSize = 0x8000;
|
||||
const int kCabInputmax = kCabBlockSize + 12;
|
||||
|
||||
bool _initialised{};
|
||||
XToken _currentToken;
|
||||
byte *_decompBuffer{};
|
||||
byte *_buffer;
|
||||
uint32 _bufferLeft;
|
||||
bool _isText;
|
||||
uint32 _listNbElements;
|
||||
bool _listTypeFloat;
|
||||
bool _listSeparator;
|
||||
bool _tokenPresent;
|
||||
|
||||
Common::Stack<XObject *> _xobjects;
|
||||
|
||||
public:
|
||||
|
||||
XFileLoader();
|
||||
~XFileLoader();
|
||||
bool load(byte *buffer, uint32 bufferSize);
|
||||
bool createEnumObject(XFileEnumObject &xobj);
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
FORCEINLINE bool readChar(char &c);
|
||||
FORCEINLINE void rewindBytes(uint32 size);
|
||||
bool readBytes(void *data, uint32 size);
|
||||
bool readLE16(uint16 *data);
|
||||
bool readLE32(uint32 *data);
|
||||
bool readBE32(uint32 *data);
|
||||
|
||||
FORCEINLINE bool getInteger(uint32 &value);
|
||||
FORCEINLINE bool getFloat(float &value);
|
||||
FORCEINLINE bool getString(char *str, uint maxLen);
|
||||
FORCEINLINE bool skipSemicolonComma();
|
||||
|
||||
FORCEINLINE bool isSpace(char c);
|
||||
FORCEINLINE bool isOperator(char c);
|
||||
FORCEINLINE bool isSeparator(char c);
|
||||
FORCEINLINE bool isPrimitiveType(XTokenType token);
|
||||
FORCEINLINE bool isGuid();
|
||||
FORCEINLINE bool isName();
|
||||
FORCEINLINE bool isFloat();
|
||||
FORCEINLINE bool isInteger();
|
||||
FORCEINLINE bool isString();
|
||||
FORCEINLINE bool isKeyword(const char *keyword, uint len);
|
||||
FORCEINLINE XTokenType getKeywordToken();
|
||||
FORCEINLINE XTokenType checkToken();
|
||||
XTokenType getToken();
|
||||
void parseToken();
|
||||
|
||||
bool decompressMsZipData();
|
||||
|
||||
bool parseHeader();
|
||||
|
||||
bool parseTemplate();
|
||||
bool parseTemplateParts();
|
||||
bool parseTemplateOptionInfo();
|
||||
bool parseTemplateMembersList();
|
||||
XObject *resolveChildObject(XObject *object, const Common::String &referenceName);
|
||||
bool resolveObject(XObject *referenceObject, const Common::String &referenceName);
|
||||
bool parseObject(XObject *object);
|
||||
bool parseChildObjects(XObject *object);
|
||||
bool parseObjectParts(XObject *object);
|
||||
};
|
||||
|
||||
class XFileData {
|
||||
friend class XFileEnumObject;
|
||||
|
||||
private:
|
||||
|
||||
XObject *_xobject{};
|
||||
bool _reference{};
|
||||
|
||||
public:
|
||||
|
||||
bool getChild(uint id, XFileData &child) {
|
||||
if (_xobject) {
|
||||
if (id < _xobject->_children.size()) {
|
||||
child._xobject = _xobject->_children[id];
|
||||
if (child._xobject->_targetObject) {
|
||||
child._xobject = child._xobject->_targetObject;
|
||||
child._reference = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getChildren(uint &num) {
|
||||
if (_xobject) {
|
||||
num = _xobject->_children.size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getName(Common::String &name) {
|
||||
if (_xobject) {
|
||||
name = _xobject->_name;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getType(XClassType &classType) {
|
||||
if (_xobject) {
|
||||
classType = _xobject->_classType;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isReference() {
|
||||
if (_xobject) {
|
||||
return _reference;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define GET_OBJECT_FUNC(objectName) \
|
||||
objectName *get ## objectName() { \
|
||||
if (_xobject) \
|
||||
return static_cast<objectName *>(_xobject->_object); \
|
||||
else \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
GET_OBJECT_FUNC(XAnimTicksPerSecondObject)
|
||||
GET_OBJECT_FUNC(XAnimationKeyObject)
|
||||
GET_OBJECT_FUNC(XAnimationObject)
|
||||
GET_OBJECT_FUNC(XAnimationOptionsObject)
|
||||
GET_OBJECT_FUNC(XAnimationSetObject)
|
||||
GET_OBJECT_FUNC(XDeclDataObject)
|
||||
GET_OBJECT_FUNC(XFrameObject)
|
||||
GET_OBJECT_FUNC(XFrameTransformMatrixObject)
|
||||
GET_OBJECT_FUNC(XFVFDataObject)
|
||||
GET_OBJECT_FUNC(XMaterialObject)
|
||||
GET_OBJECT_FUNC(XMeshObject)
|
||||
GET_OBJECT_FUNC(XMeshMaterialListObject)
|
||||
GET_OBJECT_FUNC(XMeshNormalsObject)
|
||||
GET_OBJECT_FUNC(XMeshVertexColorsObject)
|
||||
GET_OBJECT_FUNC(XMeshTextureCoordsObject)
|
||||
GET_OBJECT_FUNC(XSkinMeshHeaderObject)
|
||||
GET_OBJECT_FUNC(XSkinWeightsObject)
|
||||
GET_OBJECT_FUNC(XVertexDuplicationIndicesObject)
|
||||
GET_OBJECT_FUNC(XTextureFilenameObject)
|
||||
};
|
||||
|
||||
class XFileEnumObject {
|
||||
friend class XFileLoader;
|
||||
|
||||
private:
|
||||
|
||||
XFileLoader *_file{};
|
||||
|
||||
public:
|
||||
|
||||
bool getChild(uint id, XFileData &child) {
|
||||
if (_file) {
|
||||
if (id < _file->_xobjects.size()) {
|
||||
child._xobject = _file->_xobjects[id];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getChildren(uint &num) {
|
||||
if (_file) {
|
||||
num = _file->_xobjects.size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
||||
#endif
|
@ -26,11 +26,12 @@
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/base/gfx/base_renderer3d.h"
|
||||
#include "engines/wintermute/base/gfx/xmaterial.h"
|
||||
#include "engines/wintermute/base/gfx/xframe_node.h"
|
||||
#include "engines/wintermute/base/gfx/xmodel.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
|
||||
namespace Wintermute {
|
||||
@ -97,42 +98,35 @@ void FrameNode::setTransformation(int slot, Math::Vector3d pos, Math::Vector3d s
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool FrameNode::loadFromX(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences) {
|
||||
bool FrameNode::loadFromXData(const Common::String &filename, XModel *model, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
|
||||
_gameRef->miniUpdate();
|
||||
|
||||
bool ret = true;
|
||||
bool res = true;
|
||||
|
||||
setName(lexer.tokenToString().c_str());
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("Frame")) {
|
||||
lexer.advanceToNextToken();
|
||||
FrameNode *child = new FrameNode(_gameRef);
|
||||
if (child->loadFromX(filename, lexer, model, materialReferences)) {
|
||||
_frames.add(child);
|
||||
} else {
|
||||
delete child;
|
||||
}
|
||||
} else if (lexer.tokenIsIdentifier("Mesh")) {
|
||||
lexer.advanceToNextToken();
|
||||
XMesh *mesh = _gameRef->_renderer3D->createXMesh();
|
||||
|
||||
if (mesh->loadFromX(filename, lexer, materialReferences)) {
|
||||
_meshes.add(mesh);
|
||||
} else {
|
||||
delete mesh;
|
||||
}
|
||||
} else if (lexer.tokenIsIdentifier("FrameTransformMatrix")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken(); // skip optional name
|
||||
lexer.advanceOnOpenBraces();
|
||||
// get the type of the object
|
||||
XClassType objectType;
|
||||
res = xobj->getType(objectType);
|
||||
|
||||
if (objectType == kXClassMesh) { // load a child mesh
|
||||
XMesh *mesh = _gameRef->_renderer3D->createXMesh();
|
||||
res = mesh->loadFromXData(filename, xobj, materialReferences);
|
||||
if (res) {
|
||||
_meshes.add(mesh);
|
||||
return true;
|
||||
} else {
|
||||
delete mesh;
|
||||
return false;
|
||||
}
|
||||
} else if (objectType == kXClassFrameTransformMatrix) { // load the transformation matrix
|
||||
XFrameTransformMatrixObject *frameTransformMatrix = xobj->getXFrameTransformMatrixObject();
|
||||
if (!frameTransformMatrix) {
|
||||
BaseEngine::LOG(0, "Error loading transformation matrix");
|
||||
return false;
|
||||
} else {
|
||||
// TODO: check if this is the right format
|
||||
for (int r = 0; r < 4; ++r) {
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
_transformationMatrix(c, r) = lexer.readFloat();
|
||||
_transformationMatrix(c, r) = frameTransformMatrix->_frameMatrix[r * 4 + c];
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,84 +142,85 @@ bool FrameNode::loadFromX(const Common::String &filename, XFileLexer &lexer, XMo
|
||||
_transformationMatrix(1, 2) *= -1.0f;
|
||||
|
||||
_originalMatrix = _transformationMatrix;
|
||||
|
||||
lexer.skipTerminator();
|
||||
lexer.advanceToNextToken();
|
||||
|
||||
} else if (lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken();
|
||||
break;
|
||||
} else {
|
||||
warning("FrameNode::loadFromX unexpected %i token excountered", lexer.getTypeOfToken());
|
||||
ret = false;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
} else if (objectType == kXClassAnimationSet) { // load animation set
|
||||
return model->loadAnimationSet(filename, xobj);
|
||||
} else if (objectType == kXClassAnimation) { // load a single animation (shouldn't happen here)
|
||||
return model->loadAnimation(filename, xobj);
|
||||
} else if (objectType == kXClassFrame) { // create a new child frame
|
||||
FrameNode *childFrame = new FrameNode(_gameRef);
|
||||
|
||||
// get the name of the child frame
|
||||
res = XModel::loadName(childFrame, xobj);
|
||||
if (!res) {
|
||||
BaseEngine::LOG(0, "Error loading frame name");
|
||||
delete childFrame;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Enumerate child objects.
|
||||
res = false;
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
res = xobj->getChild(i, xchildData);
|
||||
if (res)
|
||||
res = childFrame->loadFromXData(filename, model, &xchildData, materialReferences);
|
||||
}
|
||||
if (res)
|
||||
_frames.add(childFrame);
|
||||
else
|
||||
delete childFrame;
|
||||
return res;
|
||||
} else if (objectType == kXClassAnimTicksPerSecond) {
|
||||
if (!xobj->getXAnimTicksPerSecondObject()) {
|
||||
BaseEngine::LOG(0, "Error loading ticks per seconds info");
|
||||
return res;
|
||||
} else {
|
||||
model->_ticksPerSecond = xobj->getXAnimTicksPerSecondObject()->_animTicksPerSecond;
|
||||
return true;
|
||||
}
|
||||
} else if (objectType == kXClassMaterial) {
|
||||
MaterialReference materialReference;
|
||||
xobj->getName(materialReference._name);
|
||||
materialReference._material = new Material(_gameRef);
|
||||
materialReference._material->loadFromX(xobj, filename);
|
||||
materialReferences.push_back(materialReference);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrameNode::loadFromXAsRoot(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences) {
|
||||
// technically, there is no root node in a .X file
|
||||
// so we just start parsing it here
|
||||
lexer.advanceToNextToken();
|
||||
bool FrameNode::mergeFromXData(const Common::String &filename, XModel *model, XFileData *xobj) {
|
||||
bool res = true;
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("Frame")) {
|
||||
lexer.advanceToNextToken();
|
||||
FrameNode *child = new FrameNode(_gameRef);
|
||||
if (child->loadFromX(filename, lexer, model, materialReferences)) {
|
||||
_frames.add(child);
|
||||
} else {
|
||||
delete child;
|
||||
}
|
||||
} else if (lexer.tokenIsIdentifier("Mesh")) {
|
||||
lexer.advanceToNextToken();
|
||||
XMesh *mesh = _gameRef->_renderer3D->createXMesh();
|
||||
// get the type of the object
|
||||
XClassType objectType;
|
||||
res = xobj->getType(objectType);
|
||||
if (!res) {
|
||||
BaseEngine::LOG(0, "Error getting object type");
|
||||
return res;
|
||||
} else if (objectType == kXClassAnimationSet) { // load animation set
|
||||
return model->loadAnimationSet(filename, xobj);
|
||||
} else if (objectType == kXClassAnimation) { // load a single animation (shouldn't happen here)
|
||||
return model->loadAnimation(filename, xobj);
|
||||
} else if (objectType == kXClassFrame) { // scan child frames
|
||||
// Enumerate child objects.
|
||||
res = false;
|
||||
|
||||
if (mesh->loadFromX(filename, lexer, materialReferences)) {
|
||||
_meshes.add(mesh);
|
||||
} else {
|
||||
delete mesh;
|
||||
}
|
||||
} else if (lexer.tokenIsIdentifier("AnimTicksPerSecond")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
model->_ticksPerSecond = lexer.readInt();
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
} else if (lexer.tokenIsIdentifier("AnimationSet")) {
|
||||
lexer.advanceToNextToken();
|
||||
model->loadAnimationSet(lexer, filename);
|
||||
} else if (lexer.tokenIsIdentifier("template")) {
|
||||
// we can ignore templates
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.reachedClosedBraces()) {
|
||||
break;
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken();
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken();
|
||||
} else if(lexer.tokenIsIdentifier("Material")) {
|
||||
lexer.advanceToNextToken();
|
||||
MaterialReference materialReference;
|
||||
|
||||
materialReference._name = lexer.tokenToString();
|
||||
materialReference._material = new Material(_gameRef);
|
||||
materialReference._material->loadFromX(lexer, filename);
|
||||
|
||||
materialReferences.push_back(materialReference);
|
||||
} else if (lexer.tokenIsOfType(NULL_CHAR)) {
|
||||
// prevents some unnecessary warnings
|
||||
lexer.advanceToNextToken();
|
||||
} else {
|
||||
warning("FrameNode::loadFromXAsRoot unknown token %i encountered", lexer.getTypeOfToken());
|
||||
lexer.advanceToNextToken(); // just ignore it for the moment
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
res = xobj->getChild(i, xchildData);
|
||||
if (res)
|
||||
res = mergeFromXData(filename, model, &xchildData);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,8 @@
|
||||
namespace Wintermute {
|
||||
|
||||
class XModel;
|
||||
class XFileData;
|
||||
class BaseSprite;
|
||||
class XFileLexer;
|
||||
|
||||
class FrameNode : public BaseNamedObject {
|
||||
public:
|
||||
@ -55,8 +55,8 @@ public:
|
||||
bool renderFlatShadowModel();
|
||||
bool updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const Math::Vector3d &light, float extrusionDepth);
|
||||
|
||||
bool loadFromX(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences);
|
||||
bool loadFromXAsRoot(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences);
|
||||
bool loadFromXData(const Common::String &filename, XModel *model, XFileData *xobj, Common::Array<MaterialReference> &materialReferences);
|
||||
bool mergeFromXData(const Common::String &filename, XModel *model, XFileData *xobj);
|
||||
bool findBones(FrameNode *rootFrame);
|
||||
FrameNode *findFrame(const char *frameName);
|
||||
Math::Matrix4 *getCombinedMatrix();
|
||||
|
@ -1,483 +0,0 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
void nextTokenText(Common::MemoryReadStream &buffer, int &lineCount, Token &tok) {
|
||||
char current = buffer.readSByte();
|
||||
|
||||
while (true) {
|
||||
if (Common::isSpace(current)) {
|
||||
if (current == '\n') {
|
||||
++lineCount;
|
||||
}
|
||||
|
||||
current = buffer.readSByte();
|
||||
} else if (current == '/' || current == '#') {
|
||||
// single slashes do not seem to be used in .X files,
|
||||
// so checking for one should be enough
|
||||
|
||||
while (current != '\n') {
|
||||
current = buffer.readSByte();
|
||||
}
|
||||
|
||||
current = buffer.readSByte();
|
||||
++lineCount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Common::isAlpha(current) || current == '_') {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
|
||||
while (Common::isAlnum(current) || current == '_' || current == '-') {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
}
|
||||
|
||||
buffer.seek(-1, SEEK_CUR);
|
||||
tok._type = IDENTIFIER;
|
||||
return;
|
||||
} else if (Common::isDigit(current) || current == '-') {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
|
||||
while (Common::isDigit(current)) {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
}
|
||||
|
||||
if (current == '.') {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
|
||||
while (Common::isDigit(current)) {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
}
|
||||
|
||||
buffer.seek(-1, SEEK_CUR);
|
||||
tok._type = FLOAT;
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.seek(-1, SEEK_CUR);
|
||||
tok._type = INT;
|
||||
return;
|
||||
} else if (current == '<') {
|
||||
// a uuid consists of 36 characters, 32 alphanumeric characters
|
||||
// and four "-". We add space for a null character at the
|
||||
// end of the tempoary buffer
|
||||
const int uuidSize = 37;
|
||||
char uuid[uuidSize];
|
||||
buffer.read(uuid, 36);
|
||||
uuid[uuidSize - 1] = 0;
|
||||
|
||||
current = buffer.readSByte();
|
||||
|
||||
if (current != '>') {
|
||||
warning("Wrong UUID format at line %i", lineCount);
|
||||
} else {
|
||||
tok._textVal = uuid;
|
||||
tok._type = UUID;
|
||||
return;
|
||||
}
|
||||
} else if (current == '"') {
|
||||
current = buffer.readSByte();
|
||||
|
||||
while (current != '"') {
|
||||
tok.pushChar(current);
|
||||
current = buffer.readSByte();
|
||||
}
|
||||
|
||||
tok._type = STRING;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (current) {
|
||||
case '(':
|
||||
tok._type = OPEN_PAREN;
|
||||
break;
|
||||
case ')':
|
||||
tok._type = CLOSE_PAREN;
|
||||
break;
|
||||
case '{':
|
||||
tok._type = OPEN_BRACES;
|
||||
break;
|
||||
case '}':
|
||||
tok._type = CLOSE_BRACES;
|
||||
break;
|
||||
case ']':
|
||||
tok._type = OPEN_BRACKET;
|
||||
break;
|
||||
case '[':
|
||||
tok._type = CLOSE_BRACKET;
|
||||
break;
|
||||
case ',':
|
||||
tok._type = COMMA;
|
||||
break;
|
||||
case ';':
|
||||
tok._type = SEMICOLON;
|
||||
break;
|
||||
case '.':
|
||||
tok._type = DOT;
|
||||
break;
|
||||
case '\0':
|
||||
tok._type = NULL_CHAR;
|
||||
break;
|
||||
default:
|
||||
tok._type = UNKNOWN_TOKEN;
|
||||
warning("Unknown token %c at line %i", current, lineCount);
|
||||
}
|
||||
}
|
||||
|
||||
// based on MSDN .X file format documentation
|
||||
const uint16 XBIN_TOKEN_NAME = 1;
|
||||
const uint16 XBIN_TOKEN_STRING = 2;
|
||||
const uint16 XBIN_TOKEN_INTEGER = 3;
|
||||
const uint16 XBIN_TOKEN_GUID = 5;
|
||||
const uint16 XBIN_TOKEN_INTEGER_LIST = 6;
|
||||
const uint16 XBIN_TOKEN_FLOAT_LIST = 7;
|
||||
|
||||
const uint16 XBIN_TOKEN_OBRACE = 10;
|
||||
const uint16 XBIN_TOKEN_CBRACE = 11;
|
||||
const uint16 XBIN_TOKEN_OPAREN = 12;
|
||||
const uint16 XBIN_TOKEN_CPAREN = 13;
|
||||
const uint16 XBIN_TOKEN_OBRACKET = 14;
|
||||
const uint16 XBIN_TOKEN_CBRACKET = 15;
|
||||
const uint16 XBIN_TOKEN_OANGLE = 16;
|
||||
const uint16 XBIN_TOKEN_CANGLE = 17;
|
||||
const uint16 XBIN_TOKEN_DOT = 18;
|
||||
const uint16 XBIN_TOKEN_COMMA = 19;
|
||||
const uint16 XBIN_TOKEN_SEMICOLON = 20;
|
||||
const uint16 XBIN_TOKEN_TEMPLATE = 31;
|
||||
const uint16 XBIN_TOKEN_WORD = 40;
|
||||
const uint16 XBIN_TOKEN_DWORD = 41;
|
||||
const uint16 XBIN_TOKEN_FLOAT = 42;
|
||||
const uint16 XBIN_TOKEN_DOUBLE = 43;
|
||||
const uint16 XBIN_TOKEN_CHAR = 44;
|
||||
const uint16 XBIN_TOKEN_UCHAR = 45;
|
||||
const uint16 XBIN_TOKEN_SWORD = 46;
|
||||
const uint16 XBIN_TOKEN_SDWORD = 47;
|
||||
const uint16 XBIN_TOKEN_VOID = 48;
|
||||
const uint16 XBIN_TOKEN_LPSTR = 49;
|
||||
const uint16 XBIN_TOKEN_UNICODE = 50;
|
||||
const uint16 XBIN_TOKEN_CSTRING = 51;
|
||||
const uint16 XBIN_TOKEN_ARRAY = 52;
|
||||
|
||||
void XFileLexer::nextTokenBinary() {
|
||||
uint16 current = _buffer.readUint16LE();
|
||||
uint32 length = -1;
|
||||
|
||||
switch (current) {
|
||||
case XBIN_TOKEN_NAME:
|
||||
length = _buffer.readUint32LE();
|
||||
|
||||
for (uint32 i = 0; i < length; ++i) {
|
||||
_tok.pushChar(_buffer.readByte());
|
||||
}
|
||||
|
||||
_tok._type = IDENTIFIER;
|
||||
break;
|
||||
case XBIN_TOKEN_STRING:
|
||||
length = _buffer.readUint32LE();
|
||||
|
||||
for (uint32 i = 0; i < length; ++i) {
|
||||
_tok.pushChar(_buffer.readByte());
|
||||
}
|
||||
|
||||
_tok._type = STRING;
|
||||
break;
|
||||
case XBIN_TOKEN_INTEGER:
|
||||
_tok._integerVal = _buffer.readUint32LE();
|
||||
_tok._type = INT;
|
||||
break;
|
||||
case XBIN_TOKEN_GUID:
|
||||
// ignore the UUID value
|
||||
_buffer.readUint32LE();
|
||||
_buffer.readUint16LE();
|
||||
_buffer.readUint16LE();
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_buffer.readByte();
|
||||
}
|
||||
|
||||
_tok._type = UUID;
|
||||
break;
|
||||
case XBIN_TOKEN_INTEGER_LIST:
|
||||
_integersToRead = _buffer.readUint32LE();
|
||||
_tok._type = INT;
|
||||
_expectsTerminator = false;
|
||||
break;
|
||||
case XBIN_TOKEN_FLOAT_LIST:
|
||||
_floatsToRead = _buffer.readUint32LE();
|
||||
_tok._type = FLOAT;
|
||||
_expectsTerminator = false;
|
||||
break;
|
||||
case XBIN_TOKEN_OBRACE:
|
||||
_tok._type = OPEN_BRACES;
|
||||
break;
|
||||
case XBIN_TOKEN_CBRACE:
|
||||
_tok._type = CLOSE_BRACES;
|
||||
break;
|
||||
case XBIN_TOKEN_OPAREN:
|
||||
_tok._type = OPEN_PAREN;
|
||||
break;
|
||||
case XBIN_TOKEN_CPAREN:
|
||||
_tok._type = CLOSE_PAREN;
|
||||
break;
|
||||
case XBIN_TOKEN_OBRACKET:
|
||||
_tok._type = OPEN_BRACKET;
|
||||
break;
|
||||
case XBIN_TOKEN_CBRACKET:
|
||||
_tok._type = CLOSE_BRACKET;
|
||||
break;
|
||||
case XBIN_TOKEN_OANGLE:
|
||||
_tok._type = OPEN_ANGLE;
|
||||
break;
|
||||
case XBIN_TOKEN_CANGLE:
|
||||
_tok._type = CLOSE_ANGLE;
|
||||
break;
|
||||
case XBIN_TOKEN_DOT:
|
||||
_tok._type = DOT;
|
||||
break;
|
||||
case XBIN_TOKEN_COMMA:
|
||||
_tok._type = COMMA;
|
||||
break;
|
||||
case XBIN_TOKEN_SEMICOLON:
|
||||
_tok._type = SEMICOLON;
|
||||
break;
|
||||
case XBIN_TOKEN_TEMPLATE:
|
||||
_tok._textVal = "template";
|
||||
_tok._type = IDENTIFIER;
|
||||
break;
|
||||
case XBIN_TOKEN_WORD:
|
||||
break;
|
||||
case XBIN_TOKEN_DWORD:
|
||||
break;
|
||||
case XBIN_TOKEN_FLOAT:
|
||||
break;
|
||||
case XBIN_TOKEN_DOUBLE:
|
||||
break;
|
||||
case XBIN_TOKEN_CHAR:
|
||||
break;
|
||||
case XBIN_TOKEN_UCHAR:
|
||||
break;
|
||||
case XBIN_TOKEN_SWORD:
|
||||
break;
|
||||
case XBIN_TOKEN_SDWORD:
|
||||
break;
|
||||
case XBIN_TOKEN_VOID:
|
||||
break;
|
||||
case XBIN_TOKEN_LPSTR:
|
||||
break;
|
||||
case XBIN_TOKEN_UNICODE:
|
||||
break;
|
||||
case XBIN_TOKEN_CSTRING:
|
||||
break;
|
||||
case XBIN_TOKEN_ARRAY:
|
||||
break;
|
||||
case 0:
|
||||
_tok._type = NULL_CHAR;
|
||||
break;
|
||||
default:
|
||||
_tok._type = UNKNOWN_TOKEN;
|
||||
warning("XFileLexer::nextBinaryToken: Unknown token encountered");
|
||||
}
|
||||
}
|
||||
|
||||
XFileLexer::XFileLexer(byte *buffer, uint32 fileSize, bool isText)
|
||||
: _buffer(buffer, fileSize), _lineCount(1), _isText(isText), _integersToRead(0), _floatsToRead(0), _expectsTerminator(true) {
|
||||
}
|
||||
|
||||
void XFileLexer::advanceToNextToken() {
|
||||
_tok._textVal.clear();
|
||||
|
||||
if (_isText) {
|
||||
nextTokenText(_buffer, _lineCount, _tok);
|
||||
} else {
|
||||
nextTokenBinary();
|
||||
}
|
||||
}
|
||||
|
||||
void XFileLexer::skipTerminator() {
|
||||
if (_expectsTerminator) {
|
||||
advanceToNextToken();
|
||||
}
|
||||
|
||||
_expectsTerminator = (_floatsToRead == 0) && (_integersToRead == 0);
|
||||
}
|
||||
|
||||
bool XFileLexer::eof() {
|
||||
return _buffer.pos() == _buffer.size();
|
||||
}
|
||||
|
||||
bool XFileLexer::tokenIsIdentifier() {
|
||||
return _tok._type == IDENTIFIER;
|
||||
}
|
||||
|
||||
bool XFileLexer::tokenIsIdentifier(const char *val) {
|
||||
return _tok._type == IDENTIFIER && _tok._textVal == val;
|
||||
}
|
||||
|
||||
void XFileLexer::advanceOnOpenBraces() {
|
||||
if (_tok._type == OPEN_BRACES) {
|
||||
advanceToNextToken();
|
||||
}
|
||||
}
|
||||
|
||||
bool XFileLexer::reachedClosedBraces() {
|
||||
return _tok._type == CLOSE_BRACES;
|
||||
}
|
||||
|
||||
TokenType XFileLexer::getTypeOfToken() {
|
||||
return _tok._type;
|
||||
}
|
||||
|
||||
bool XFileLexer::tokenIsOfType(TokenType type) {
|
||||
return _tok._type == type;
|
||||
}
|
||||
|
||||
int XFileLexer::tokenToInt() {
|
||||
return atoi(_tok._textVal.c_str());
|
||||
}
|
||||
|
||||
double XFileLexer::tokenToFloat() {
|
||||
return atof(_tok._textVal.c_str());
|
||||
}
|
||||
|
||||
Common::String XFileLexer::tokenToString() {
|
||||
return _tok._textVal;
|
||||
}
|
||||
|
||||
uint32 XFileLexer::tokenToUint32() {
|
||||
// All integer values in a .X file are unsigned and at most 32 bit
|
||||
// so parsing to unsigned long and then converting down should be fine
|
||||
return strtoul(_tok._textVal.c_str(), nullptr, 10);
|
||||
}
|
||||
|
||||
void XFileLexer::skipObject() {
|
||||
advanceToNextToken(); // optional name
|
||||
advanceToNextToken();
|
||||
advanceOnOpenBraces();
|
||||
|
||||
// we have one open braces right now, once the counter reaches zero, we're done
|
||||
int closedBracesCount = 1;
|
||||
|
||||
while (closedBracesCount > 0) {
|
||||
while (_integersToRead > 0 || _floatsToRead > 0) {
|
||||
if (_integersToRead > 0) {
|
||||
readInt();
|
||||
}
|
||||
|
||||
if (_floatsToRead > 0) {
|
||||
readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
if (_tok._type == OPEN_BRACES) {
|
||||
++closedBracesCount;
|
||||
}
|
||||
|
||||
if (_tok._type == CLOSE_BRACES) {
|
||||
--closedBracesCount;
|
||||
}
|
||||
|
||||
advanceToNextToken();
|
||||
}
|
||||
}
|
||||
|
||||
void Token::pushChar(char c) {
|
||||
_textVal.insertChar(c, _textVal.size());
|
||||
}
|
||||
|
||||
float XFileLexer::readFloat() {
|
||||
if (_floatsToRead > 0) {
|
||||
--_floatsToRead;
|
||||
float tmp = _buffer.readFloatLE();
|
||||
|
||||
if (_floatsToRead == 0) {
|
||||
advanceToNextToken();
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
float tmp = tokenToFloat();
|
||||
advanceToNextToken();
|
||||
advanceToNextToken(); // skip comma or semicolon
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int XFileLexer::readInt() {
|
||||
if (_integersToRead > 0) {
|
||||
--_integersToRead;
|
||||
int tmp = _buffer.readUint32LE();
|
||||
|
||||
if (_integersToRead == 0) {
|
||||
advanceToNextToken();
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int tmp = tokenToInt();
|
||||
advanceToNextToken();
|
||||
advanceToNextToken(); // skip comma or semicolon
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Common::String XFileLexer::readString() {
|
||||
Common::String tmp = tokenToString();
|
||||
advanceToNextToken();
|
||||
advanceToNextToken(); // skip comma or semicolon
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint32 XFileLexer::readUint32() {
|
||||
if (_integersToRead > 0) {
|
||||
--_integersToRead;
|
||||
uint32 tmp = _buffer.readUint32LE();
|
||||
|
||||
if (_integersToRead == 0) {
|
||||
advanceToNextToken();
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint32 tmp = tokenToUint32();
|
||||
advanceToNextToken();
|
||||
advanceToNextToken(); // skip comma or semicolon
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
@ -1,100 +0,0 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_X_FILE_LEXER_H
|
||||
#define WINTERMUTE_X_FILE_LEXER_H
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
enum TokenType {
|
||||
IDENTIFIER = 0,
|
||||
STRING,
|
||||
UUID,
|
||||
INT,
|
||||
FLOAT,
|
||||
OPEN_BRACES,
|
||||
CLOSE_BRACES,
|
||||
OPEN_PAREN,
|
||||
CLOSE_PAREN,
|
||||
OPEN_ANGLE,
|
||||
CLOSE_ANGLE,
|
||||
OPEN_BRACKET,
|
||||
CLOSE_BRACKET,
|
||||
SEMICOLON,
|
||||
COMMA,
|
||||
DOT,
|
||||
NULL_CHAR,
|
||||
UNKNOWN_TOKEN
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType _type;
|
||||
Common::String _textVal;
|
||||
int _integerVal;
|
||||
float _floatVal;
|
||||
|
||||
void pushChar(char c);
|
||||
};
|
||||
|
||||
class XFileLexer {
|
||||
public:
|
||||
XFileLexer(byte *buffer, uint32 fileSize, bool isText);
|
||||
|
||||
void advanceToNextToken();
|
||||
void skipTerminator();
|
||||
bool eof();
|
||||
bool tokenIsIdentifier();
|
||||
bool tokenIsIdentifier(const char *val);
|
||||
void advanceOnOpenBraces();
|
||||
bool reachedClosedBraces();
|
||||
TokenType getTypeOfToken();
|
||||
bool tokenIsOfType(TokenType type);
|
||||
int tokenToInt();
|
||||
double tokenToFloat();
|
||||
Common::String tokenToString();
|
||||
uint32 tokenToUint32();
|
||||
|
||||
void skipObject();
|
||||
|
||||
float readFloat();
|
||||
int readInt();
|
||||
Common::String readString();
|
||||
uint32 readUint32();
|
||||
|
||||
private:
|
||||
void nextTokenBinary();
|
||||
|
||||
Token _tok;
|
||||
Common::MemoryReadStream _buffer;
|
||||
int _lineCount;
|
||||
bool _isText;
|
||||
int _integersToRead;
|
||||
int _floatsToRead;
|
||||
bool _expectsTerminator;
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
||||
#endif
|
@ -30,7 +30,7 @@
|
||||
#include "engines/wintermute/base/base_surface_storage.h"
|
||||
#include "engines/wintermute/base/gfx/base_surface.h"
|
||||
#include "engines/wintermute/base/gfx/xmaterial.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
#include "engines/wintermute/utils/path_util.h"
|
||||
#include "engines/wintermute/video/video_theora_player.h"
|
||||
@ -141,51 +141,44 @@ BaseSurface *Material::getSurface() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Material::loadFromX(XFileLexer &lexer, const Common::String &filename) {
|
||||
lexer.advanceToNextToken(); // skip optional name
|
||||
lexer.advanceOnOpenBraces();
|
||||
bool Material::loadFromX(XFileData *xobj, const Common::String &filename) {
|
||||
XMaterialObject *material = xobj->getXMaterialObject();
|
||||
if (!material)
|
||||
return false;
|
||||
|
||||
_diffuse.r() = lexer.readFloat();
|
||||
_diffuse.g() = lexer.readFloat();
|
||||
_diffuse.b() = lexer.readFloat();
|
||||
_diffuse.a() = lexer.readFloat();
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
_diffuse.r() = material->_colorR;
|
||||
_diffuse.g() = material->_colorG;
|
||||
_diffuse.b() = material->_colorB;
|
||||
_diffuse.a() = material->_colorA;
|
||||
|
||||
_shininess = lexer.readFloat();
|
||||
_shininess = material->_power;
|
||||
|
||||
_specular.r() = lexer.readFloat();
|
||||
_specular.g() = lexer.readFloat();
|
||||
_specular.b() = lexer.readFloat();
|
||||
_specular.r() = material->_specularR;
|
||||
_specular.g() = material->_specularG;
|
||||
_specular.b() = material->_specularB;
|
||||
_specular.a() = 1.0f;
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
_emissive.r() = lexer.readFloat();
|
||||
_emissive.g() = lexer.readFloat();
|
||||
_emissive.b() = lexer.readFloat();
|
||||
_emissive.r() = material->_emissiveR;
|
||||
_emissive.g() = material->_emissiveG;
|
||||
_emissive.b() = material->_emissiveB;
|
||||
_emissive.a() = 1.0f;
|
||||
lexer.skipTerminator();
|
||||
|
||||
while (!lexer.eof()) {
|
||||
// according to assimp sources, we got both possibilities
|
||||
// wine also seems to support this
|
||||
// MSDN only names the first option
|
||||
if (lexer.tokenIsIdentifier("TextureFilename") || lexer.tokenIsIdentifier("TextureFileName")) {
|
||||
lexer.advanceToNextToken(); // skip optional name
|
||||
lexer.advanceOnOpenBraces();
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
|
||||
Common::String textureFilename = lexer.readString();
|
||||
PathUtil::getDirectoryName(filename);
|
||||
setTexture(PathUtil::getDirectoryName(filename) + textureFilename);
|
||||
lexer.advanceToNextToken(); // skip semicolon
|
||||
} else if (lexer.tokenIsIdentifier()) {
|
||||
warning("Material::loadFromX unexpected token %i", lexer.getTypeOfToken());
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
XClassType objectType;
|
||||
bool res = xobj->getChild(i, xchildData);
|
||||
if (res) {
|
||||
res = xchildData.getType(objectType);
|
||||
if (res && objectType == kXClassTextureFilename) {
|
||||
Common::String textureFilename = xchildData.getXTextureFilenameObject()->_filename;
|
||||
setTexture(PathUtil::getDirectoryName(filename) + textureFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip semicolon
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace Wintermute {
|
||||
class BaseSprite;
|
||||
class BaseSurface;
|
||||
class VideoTheoraPlayer;
|
||||
class XFileLexer;
|
||||
class XFileData;
|
||||
|
||||
struct ColorValue {
|
||||
float &r() {
|
||||
@ -89,7 +89,7 @@ public:
|
||||
bool setTheora(VideoTheoraPlayer *theora, bool adoptName = false);
|
||||
BaseSurface *getSurface();
|
||||
|
||||
bool loadFromX(XFileLexer &lexer, const Common::String &filename);
|
||||
bool loadFromX(XFileData *xobj, const Common::String &filename);
|
||||
|
||||
bool invalidateDeviceObjects();
|
||||
bool restoreDeviceObjects();
|
||||
|
@ -29,8 +29,9 @@
|
||||
#include "engines/wintermute/base/gfx/xmaterial.h"
|
||||
#include "engines/wintermute/base/gfx/xmesh.h"
|
||||
#include "engines/wintermute/base/gfx/xframe_node.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
#include "engines/wintermute/base/gfx/xmodel.h"
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/math/math_util.h"
|
||||
|
||||
namespace Wintermute {
|
||||
@ -60,10 +61,20 @@ XMesh::~XMesh() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XMesh::loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) {
|
||||
lexer.advanceToNextToken(); // skip the name
|
||||
lexer.advanceOnOpenBraces();
|
||||
_vertexCount = lexer.readInt();
|
||||
bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
|
||||
// get name
|
||||
if (!XModel::loadName(this, xobj)) {
|
||||
BaseEngine::LOG(0, "Error loading mesh name");
|
||||
return false;
|
||||
}
|
||||
|
||||
XMeshObject *mesh = xobj->getXMeshObject();
|
||||
if (!mesh) {
|
||||
BaseEngine::LOG(0, "Error loading skin mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
_vertexCount = mesh->_numVertices;
|
||||
|
||||
// vertex format for .X meshes will be position + normals + textures
|
||||
_vertexData = new float[kVertexComponentCount * _vertexCount]();
|
||||
@ -72,76 +83,47 @@ bool XMesh::loadFromX(const Common::String &filename, XFileLexer &lexer, Common:
|
||||
// TODO: might have to generate normals if file does not contain any
|
||||
_vertexNormalData = new float[3 * _vertexCount]();
|
||||
|
||||
parsePositionCoords(lexer);
|
||||
parsePositionCoords(mesh);
|
||||
|
||||
int faceCount = lexer.readInt();
|
||||
int faceCount = mesh->_numFaces;
|
||||
|
||||
Common::Array<int> indexCountPerFace;
|
||||
|
||||
parseFaces(lexer, faceCount, indexCountPerFace);
|
||||
parseFaces(mesh, faceCount, indexCountPerFace);
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("MeshTextureCoords")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
|
||||
parseTextureCoords(lexer);
|
||||
} else if (lexer.tokenIsIdentifier("MeshNormals")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
XClassType objectType;
|
||||
if (xobj->getChild(i, xchildData)) {
|
||||
if (xchildData.getType(objectType)) {
|
||||
if (objectType == kXClassMeshTextureCoords) {
|
||||
parseTextureCoords(&xchildData);
|
||||
} else if (objectType == kXClassMeshNormals) {
|
||||
parseNormalCoords(&xchildData);
|
||||
} else if (objectType == kXClassMeshMaterialList) {
|
||||
parseMaterials(&xchildData, faceCount, filename, materialReferences, indexCountPerFace);
|
||||
} else if (objectType == kXClassMaterial) {
|
||||
Material *mat = new Material(_gameRef);
|
||||
mat->loadFromX(&xchildData, filename);
|
||||
_materials.add(mat);
|
||||
|
||||
parseNormalCoords(lexer);
|
||||
} else if (lexer.tokenIsIdentifier("MeshMaterialList")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
parseMaterials(lexer, faceCount, filename, materialReferences, indexCountPerFace);
|
||||
} else if (lexer.tokenIsIdentifier("Material")) {
|
||||
lexer.advanceToNextToken();
|
||||
Material *mat = new Material(_gameRef);
|
||||
mat->loadFromX(lexer, filename);
|
||||
_materials.add(mat);
|
||||
|
||||
// one material = one index range
|
||||
_numAttrs = 1;
|
||||
_indexRanges.push_back(0);
|
||||
_indexRanges.push_back(_indexData.size());
|
||||
} else if (lexer.tokenIsIdentifier("XSkinMeshHeader")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
// if any of this is zero, we should have an unskinned mesh
|
||||
lexer.readInt(); // max skin weights per vertex
|
||||
lexer.readInt(); // max skin weights per face
|
||||
int boneCount = lexer.readInt();
|
||||
|
||||
_skinnedMesh = boneCount > 0;
|
||||
|
||||
lexer.advanceToNextToken(); // skip semicolon
|
||||
} else if (lexer.tokenIsIdentifier("SkinWeights")) {
|
||||
// but now we certainly should have a skinned mesh
|
||||
_skinnedMesh = true;
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
parseSkinWeights(lexer);
|
||||
} else if (lexer.tokenIsIdentifier("DeclData")) {
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceOnOpenBraces();
|
||||
|
||||
parseVertexDeclaration(lexer);
|
||||
} else if (lexer.tokenIsIdentifier()) {
|
||||
lexer.skipObject();
|
||||
} else if (lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
break;
|
||||
} else {
|
||||
warning("XMesh::loadFromX unknown token %i encountered", lexer.getTypeOfToken());
|
||||
lexer.advanceToNextToken();
|
||||
// one material = one index range
|
||||
_numAttrs = 1;
|
||||
_indexRanges.push_back(0);
|
||||
_indexRanges.push_back(_indexData.size());
|
||||
} else if (objectType == kXClassSkinMeshHeader) {
|
||||
int boneCount = xchildData.getXSkinMeshHeaderObject()->_nBones;
|
||||
_skinnedMesh = boneCount > 0;
|
||||
} else if (objectType == kXClassSkinWeights) {
|
||||
_skinnedMesh = true;
|
||||
parseSkinWeights(&xchildData);
|
||||
} else if (objectType == kXClassDeclData) {
|
||||
parseVertexDeclaration(&xchildData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,43 +450,41 @@ bool XMesh::restoreDeviceObjects() {
|
||||
}
|
||||
}
|
||||
|
||||
bool XMesh::parsePositionCoords(XFileLexer &lexer) {
|
||||
bool XMesh::parsePositionCoords(XMeshObject *mesh) {
|
||||
for (uint i = 0; i < _vertexCount; ++i) {
|
||||
_vertexPositionData[i * 3 + 0] = mesh->_vertices[i]._x;
|
||||
_vertexPositionData[i * 3 + 1] = mesh->_vertices[i]._y;
|
||||
_vertexPositionData[i * 3 + 2] = mesh->_vertices[i]._z;
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
_vertexPositionData[i * 3 + j] = lexer.readFloat();
|
||||
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
|
||||
}
|
||||
|
||||
_vertexPositionData[i * 3 + 2] *= -1.0f;
|
||||
_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& indexCountPerFace) {
|
||||
bool XMesh::parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace) {
|
||||
for (int i = 0; i < faceCount; ++i) {
|
||||
int indexCount = lexer.readInt();
|
||||
|
||||
XMeshFace *face = &mesh->_faces[i];
|
||||
int indexCount = face->_numFaceVertexIndices;
|
||||
if (indexCount == 3) {
|
||||
uint16 index1 = lexer.readInt();
|
||||
uint16 index2 = lexer.readInt();
|
||||
uint16 index3 = lexer.readInt();
|
||||
uint16 index1 = face->_faceVertexIndices[0];
|
||||
uint16 index2 = face->_faceVertexIndices[1];
|
||||
uint16 index3 = face->_faceVertexIndices[2];
|
||||
|
||||
_indexData.push_back(index3);
|
||||
_indexData.push_back(index2);
|
||||
_indexData.push_back(index1);
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
indexCountPerFace.push_back(3);
|
||||
} else if (indexCount == 4) {
|
||||
uint16 index1 = lexer.readInt();
|
||||
uint16 index2 = lexer.readInt();
|
||||
uint16 index3 = lexer.readInt();
|
||||
uint16 index4 = lexer.readInt();
|
||||
uint16 index1 = face->_faceVertexIndices[0];
|
||||
uint16 index2 = face->_faceVertexIndices[1];
|
||||
uint16 index3 = face->_faceVertexIndices[2];
|
||||
uint16 index4 = face->_faceVertexIndices[3];
|
||||
|
||||
_indexData.push_back(index3);
|
||||
_indexData.push_back(index2);
|
||||
@ -514,8 +494,6 @@ bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& ind
|
||||
_indexData.push_back(index3);
|
||||
_indexData.push_back(index1);
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
|
||||
indexCountPerFace.push_back(6);
|
||||
} else {
|
||||
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
|
||||
@ -526,62 +504,59 @@ bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& ind
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseTextureCoords(XFileLexer &lexer) {
|
||||
bool XMesh::parseTextureCoords(XFileData *xobj) {
|
||||
XMeshTextureCoordsObject *texCoords = xobj->getXMeshTextureCoordsObject();
|
||||
if (!texCoords)
|
||||
return false;
|
||||
// should be the same as _vertexCount
|
||||
int textureCoordCount = lexer.readInt();
|
||||
int textureCoordCount = texCoords->_numTextureCoords;
|
||||
|
||||
for (int i = 0; i < textureCoordCount; ++i) {
|
||||
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = lexer.readFloat();
|
||||
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = lexer.readFloat();
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
}
|
||||
|
||||
if (lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken();
|
||||
} else {
|
||||
warning("Missing } in mesh object");
|
||||
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = texCoords->_textureCoords[i]._u;
|
||||
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = texCoords->_textureCoords[i]._v;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseNormalCoords(XFileLexer &lexer) {
|
||||
bool XMesh::parseNormalCoords(XFileData *xobj) {
|
||||
XMeshNormalsObject *normals = xobj->getXMeshNormalsObject();
|
||||
if (!normals)
|
||||
return false;
|
||||
// should be the same as _vertex count
|
||||
uint vertexNormalCount = lexer.readInt();
|
||||
// assert(vertexNormalCount == _vertexCount);
|
||||
uint vertexNormalCount = normals->_numNormals;
|
||||
//assert(vertexNormalCount == _vertexCount);
|
||||
|
||||
Common::Array<float> vertexNormalData;
|
||||
vertexNormalData.resize(3 * vertexNormalCount);
|
||||
|
||||
for (uint i = 0; i < vertexNormalCount; ++i) {
|
||||
vertexNormalData[i * 3 + 0] = lexer.readFloat();
|
||||
vertexNormalData[i * 3 + 1] = lexer.readFloat();
|
||||
vertexNormalData[i * 3 + 0] = normals->_normals[i]._x;
|
||||
vertexNormalData[i * 3 + 1] = normals->_normals[i]._y;
|
||||
// mirror z coordinate to change to OpenGL coordinate system
|
||||
vertexNormalData[i * 3 + 2] = -lexer.readFloat();
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
vertexNormalData[i * 3 + 2] = -normals->_normals[i]._z;
|
||||
}
|
||||
|
||||
uint faceNormalCount = lexer.readInt();
|
||||
uint faceNormalCount = normals->_numFaceNormals;
|
||||
Common::Array<int> faceNormals;
|
||||
|
||||
for (uint i = 0; i < faceNormalCount; ++i) {
|
||||
int indexCount = lexer.readInt();
|
||||
XMeshFace *normalFace = &normals->_faceNormals[i];
|
||||
int indexCount = normalFace->_numFaceVertexIndices;
|
||||
|
||||
if (indexCount == 3) {
|
||||
uint16 index1 = lexer.readInt();
|
||||
uint16 index2 = lexer.readInt();
|
||||
uint16 index3 = lexer.readInt();
|
||||
uint16 index1 = normalFace->_faceVertexIndices[0];
|
||||
uint16 index2 = normalFace->_faceVertexIndices[1];
|
||||
uint16 index3 = normalFace->_faceVertexIndices[2];
|
||||
|
||||
faceNormals.push_back(index3);
|
||||
faceNormals.push_back(index2);
|
||||
faceNormals.push_back(index1);
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
} else if (indexCount == 4) {
|
||||
uint16 index1 = lexer.readInt();
|
||||
uint16 index2 = lexer.readInt();
|
||||
uint16 index3 = lexer.readInt();
|
||||
uint16 index4 = lexer.readInt();
|
||||
uint16 index1 = normalFace->_faceVertexIndices[0];
|
||||
uint16 index2 = normalFace->_faceVertexIndices[1];
|
||||
uint16 index3 = normalFace->_faceVertexIndices[2];
|
||||
uint16 index4 = normalFace->_faceVertexIndices[3];
|
||||
|
||||
faceNormals.push_back(index3);
|
||||
faceNormals.push_back(index2);
|
||||
@ -590,8 +565,6 @@ bool XMesh::parseNormalCoords(XFileLexer &lexer) {
|
||||
faceNormals.push_back(index4);
|
||||
faceNormals.push_back(index3);
|
||||
faceNormals.push_back(index1);
|
||||
|
||||
lexer.skipTerminator(); // skip semicolon
|
||||
} else {
|
||||
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
|
||||
return false;
|
||||
@ -610,27 +583,28 @@ bool XMesh::parseNormalCoords(XFileLexer &lexer) {
|
||||
}
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseMaterials(XFileLexer &lexer, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace) {
|
||||
bool XMesh::parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace) {
|
||||
XMeshMaterialListObject *materialList = xobj->getXMeshMaterialListObject();
|
||||
if (!materialList)
|
||||
return false;
|
||||
|
||||
// there can be unused materials inside a .X file
|
||||
// so this piece of information is probably useless
|
||||
lexer.readInt(); // material count
|
||||
// should be the same as faceCount
|
||||
int faceMaterialCount = lexer.readInt();
|
||||
int faceMaterialCount = materialList->_numFaceIndexes;
|
||||
assert(faceMaterialCount == faceCount);
|
||||
|
||||
_indexRanges.push_back(0);
|
||||
int currentMaterialIndex = lexer.readInt();
|
||||
int currentMaterialIndex = materialList->_faceIndexes[0];
|
||||
_materialIndices.push_back(currentMaterialIndex);
|
||||
|
||||
int currentIndex = indexCountPerFace[0];
|
||||
|
||||
for (int i = 1; i < faceMaterialCount; ++i) {
|
||||
int currentMaterialIndexTmp = lexer.readInt();
|
||||
int currentMaterialIndexTmp = materialList->_faceIndexes[i];
|
||||
|
||||
if (currentMaterialIndex != currentMaterialIndexTmp) {
|
||||
currentMaterialIndex = currentMaterialIndexTmp;
|
||||
@ -644,82 +618,65 @@ bool XMesh::parseMaterials(XFileLexer &lexer, int faceCount, const Common::Strin
|
||||
_indexRanges.push_back(currentIndex);
|
||||
_numAttrs = _indexRanges.size() - 1;
|
||||
|
||||
// Strange Change has an extra semicolon here
|
||||
if (lexer.tokenIsOfType(SEMICOLON)) {
|
||||
lexer.advanceToNextToken();
|
||||
}
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("Material")) {
|
||||
lexer.advanceToNextToken();
|
||||
Material *mat = new Material(_gameRef);
|
||||
mat->loadFromX(lexer, filename);
|
||||
_materials.add(mat);
|
||||
|
||||
MaterialReference materialReference;
|
||||
materialReference._material = mat;
|
||||
materialReferences.push_back(materialReference);
|
||||
} else if (lexer.tokenIsIdentifier()) {
|
||||
while (!lexer.reachedClosedBraces()) {
|
||||
lexer.advanceToNextToken();
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
} else if (lexer.reachedClosedBraces()) {
|
||||
break;
|
||||
} else if (lexer.tokenIsOfType(OPEN_BRACES)) {
|
||||
lexer.advanceToNextToken();
|
||||
Common::String materialReference = lexer.tokenToString();
|
||||
|
||||
for (uint i = 0; i < materialReferences.size(); ++i) {
|
||||
if (materialReferences[i]._name == materialReference) {
|
||||
_materials.add(materialReferences[i]._material);
|
||||
break;
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
XClassType objectType;
|
||||
bool res = xobj->getChild(i, xchildData);
|
||||
if (res) {
|
||||
res = xchildData.getType(objectType);
|
||||
if (res) {
|
||||
if (xchildData.isReference()) {
|
||||
Common::String materialReference;
|
||||
xchildData.getName(materialReference);
|
||||
for (uint32 j = 0; j < materialReferences.size(); j++) {
|
||||
if (materialReferences[j]._name == materialReference) {
|
||||
_materials.add(materialReferences[j]._material);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (objectType == kXClassMaterial) {
|
||||
Material *mat = new Material(_gameRef);
|
||||
mat->loadFromX(&xchildData, filename);
|
||||
_materials.add(mat);
|
||||
MaterialReference materialReference;
|
||||
materialReference._material = mat;
|
||||
materialReferences.push_back(materialReference);
|
||||
}
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken();
|
||||
lexer.advanceToNextToken();
|
||||
} else {
|
||||
warning("XMeshOpenGL::loadFromX unknown token %i encountered while loading materials", lexer.getTypeOfToken());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseSkinWeights(XFileLexer &lexer) {
|
||||
bool XMesh::parseSkinWeights(XFileData *xobj) {
|
||||
XSkinWeightsObject *skinWeights = xobj->getXSkinWeightsObject();
|
||||
if (!skinWeights)
|
||||
return false;
|
||||
|
||||
skinWeightsList.resize(skinWeightsList.size() + 1);
|
||||
SkinWeights &currSkinWeights = skinWeightsList.back();
|
||||
|
||||
currSkinWeights._boneName = lexer.readString();
|
||||
currSkinWeights._boneName = skinWeights->_transformNodeName;
|
||||
|
||||
int weightCount = lexer.readInt();
|
||||
int weightCount = skinWeights->_numWeights;
|
||||
currSkinWeights._vertexIndices.resize(weightCount);
|
||||
currSkinWeights._vertexWeights.resize(weightCount);
|
||||
|
||||
for (int i = 0; i < weightCount; ++i) {
|
||||
currSkinWeights._vertexIndices[i] = lexer.readInt();
|
||||
}
|
||||
|
||||
if (weightCount == 0 && lexer.tokenIsOfType(SEMICOLON)) {
|
||||
lexer.advanceToNextToken();
|
||||
currSkinWeights._vertexIndices[i] = skinWeights->_vertexIndices[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < weightCount; ++i) {
|
||||
currSkinWeights._vertexWeights[i] = lexer.readFloat();
|
||||
}
|
||||
|
||||
if (weightCount == 0 && lexer.tokenIsOfType(SEMICOLON)) {
|
||||
lexer.advanceToNextToken();
|
||||
currSkinWeights._vertexWeights[i] = skinWeights->_weights[i];
|
||||
}
|
||||
|
||||
for (int r = 0; r < 4; ++r) {
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
currSkinWeights._offsetMatrix(c, r) = lexer.readFloat();
|
||||
currSkinWeights._offsetMatrix(c, r) = skinWeights->_matrixOffset[r * 4 + c];
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,14 +691,15 @@ bool XMesh::parseSkinWeights(XFileLexer &lexer) {
|
||||
currSkinWeights._offsetMatrix(0, 2) *= -1.0f;
|
||||
currSkinWeights._offsetMatrix(1, 2) *= -1.0f;
|
||||
|
||||
lexer.skipTerminator(); // semicolon of matrix
|
||||
lexer.advanceToNextToken(); // closed braces of skin weights object
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
|
||||
int vertexElementCount = lexer.readInt();
|
||||
bool XMesh::parseVertexDeclaration(XFileData *xobj) {
|
||||
XDeclDataObject *declData = xobj->getXDeclDataObject();
|
||||
if (!declData)
|
||||
return false;
|
||||
|
||||
int vertexElementCount = declData->_numElements;
|
||||
|
||||
// size of a vertex measured in four byte blocks
|
||||
int vertexSize = 0;
|
||||
@ -749,11 +707,10 @@ bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
|
||||
int textureOffset = -1;
|
||||
|
||||
for (int i = 0; i < vertexElementCount; ++i) {
|
||||
int type = lexer.readInt();
|
||||
int method = lexer.readInt();
|
||||
int usage = lexer.readInt();
|
||||
int usageIndex = lexer.readInt();
|
||||
lexer.skipTerminator();
|
||||
int type = declData->_elements[i]._type;
|
||||
int method = declData->_elements[i]._method;
|
||||
int usage = declData->_elements[i]._usage;
|
||||
int usageIndex = declData->_elements[i]._usageIndex;
|
||||
|
||||
debug("Vertex Element: Type: %i, Method: %i, Usage: %i, Usage index: %i", type, method, usage, usageIndex);
|
||||
|
||||
@ -842,24 +799,14 @@ bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
|
||||
}
|
||||
}
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON)) {
|
||||
lexer.advanceToNextToken();
|
||||
}
|
||||
|
||||
int dataSize = lexer.readInt();
|
||||
int dataSize = declData->_numData;
|
||||
Common::Array<uint32> data;
|
||||
data.reserve(dataSize);
|
||||
|
||||
for (int i = 0; i < dataSize; ++i) {
|
||||
data.push_back(lexer.readUint32());
|
||||
data.push_back(declData->_data[i]);
|
||||
}
|
||||
|
||||
if (lexer.tokenIsOfType(SEMICOLON)) {
|
||||
lexer.advanceToNextToken();
|
||||
}
|
||||
|
||||
lexer.advanceToNextToken(); // skip closed braces
|
||||
|
||||
assert(dataSize % vertexSize == 0);
|
||||
assert(dataSize / vertexSize == static_cast<int>(_vertexCount));
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Material;
|
||||
class XModel;
|
||||
class ShadowVolume;
|
||||
class VideoTheoraPlayer;
|
||||
class XFileLexer;
|
||||
struct XMeshObject;
|
||||
|
||||
struct SkinWeights {
|
||||
Common::String _boneName;
|
||||
@ -57,7 +57,7 @@ public:
|
||||
XMesh(BaseGame *inGame);
|
||||
virtual ~XMesh();
|
||||
|
||||
virtual bool loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences);
|
||||
virtual bool loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences);
|
||||
bool findBones(FrameNode *rootFrame);
|
||||
virtual bool update(FrameNode *parentFrame);
|
||||
virtual bool render(XModel *model) = 0;
|
||||
@ -84,13 +84,13 @@ protected:
|
||||
// anything which does not fit into 16 bits would we fine
|
||||
static const uint32 kNullIndex = 0xFFFFFFFF;
|
||||
|
||||
bool parsePositionCoords(XFileLexer &lexer);
|
||||
bool parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int> &indexCountPerFace);
|
||||
bool parseTextureCoords(XFileLexer &lexer);
|
||||
bool parseNormalCoords(XFileLexer &lexer);
|
||||
bool parseMaterials(XFileLexer &lexer, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
|
||||
bool parseSkinWeights(XFileLexer &lexer);
|
||||
bool parseVertexDeclaration(XFileLexer &lexer);
|
||||
bool parsePositionCoords(XMeshObject *mesh);
|
||||
bool parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace);
|
||||
bool parseTextureCoords(XFileData *xobj);
|
||||
bool parseNormalCoords(XFileData *xobj);
|
||||
bool parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
|
||||
bool parseSkinWeights(XFileData *xobj);
|
||||
bool parseVertexDeclaration(XFileData *xobj);
|
||||
|
||||
void updateBoundingBox();
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "engines/wintermute/base/base_file_manager.h"
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/base/base_parser.h"
|
||||
#include "engines/wintermute/base/gfx/base_renderer.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
|
||||
@ -38,7 +39,8 @@
|
||||
#include "engines/wintermute/base/gfx/xframe_node.h"
|
||||
#include "engines/wintermute/base/gfx/xmaterial.h"
|
||||
#include "engines/wintermute/base/gfx/xmodel.h"
|
||||
#include "engines/wintermute/base/gfx/xloader.h"
|
||||
#include "engines/wintermute/base/gfx/xfile.h"
|
||||
#include "engines/wintermute/base/gfx/xfile_loader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
#include "engines/wintermute/utils/path_util.h"
|
||||
#include "engines/wintermute/utils/utils.h"
|
||||
@ -46,106 +48,6 @@
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
static const int kCabBlockSize = 0x8000;
|
||||
static const int kCabInputmax = kCabBlockSize + 12;
|
||||
|
||||
static byte *DecompressMsZipData(byte *buffer, uint32 inputSize, uint32 &decompressedSize) {
|
||||
#ifdef USE_ZLIB
|
||||
bool error = false;
|
||||
|
||||
Common::MemoryReadStream data(buffer, inputSize);
|
||||
byte *compressedBlock = new byte[kCabInputmax];
|
||||
byte *decompressedBlock = new byte[kCabBlockSize];
|
||||
|
||||
// Read decompressed size of compressed data and minus 16 bytes of xof header
|
||||
decompressedSize = data.readUint32LE() - 16;
|
||||
|
||||
uint32 remainingData = inputSize;
|
||||
uint32 decompressedPos = 0;
|
||||
byte *decompressedData = new byte[decompressedSize];
|
||||
if (!decompressedData)
|
||||
error = true;
|
||||
|
||||
while (!error && remainingData) {
|
||||
uint16 uncompressedLen = data.readUint16LE();
|
||||
uint16 compressedLen = data.readUint16LE();
|
||||
remainingData -= 4;
|
||||
|
||||
if (data.err()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (remainingData == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (compressedLen > kCabInputmax || uncompressedLen > kCabBlockSize) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read the compressed block
|
||||
if (data.read(compressedBlock, compressedLen) != compressedLen) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
remainingData -= compressedLen;
|
||||
|
||||
// Check the CK header
|
||||
if (compressedBlock[0] != 'C' || compressedBlock[1] != 'K') {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Decompress the block. If it isn't the first, provide the previous block as dictonary
|
||||
byte *dict = decompressedPos ? decompressedBlock : nullptr;
|
||||
bool decRes = Common::inflateZlibHeaderless(decompressedBlock, uncompressedLen, compressedBlock + 2, compressedLen - 2, dict, kCabBlockSize);
|
||||
if (!decRes) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy the decompressed data
|
||||
memcpy(decompressedData + decompressedPos, decompressedBlock, uncompressedLen);
|
||||
decompressedPos += uncompressedLen;
|
||||
}
|
||||
if (decompressedSize != decompressedPos)
|
||||
error = true;
|
||||
|
||||
delete[] compressedBlock;
|
||||
delete[] decompressedBlock;
|
||||
|
||||
if (!error)
|
||||
return decompressedData;
|
||||
#endif
|
||||
warning("DecompressMsZipData: Error decompressing data!");
|
||||
decompressedSize = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static XFileLexer createXFileLexer(byte *&buffer, uint32 fileSize) {
|
||||
// xof header of an .X file consists of 16 bytes
|
||||
// bytes 9 to 12 contain a string which can be 'txt ', 'bin ', 'bzip, 'tzip', depending on the format
|
||||
byte dataFormatBlock[5];
|
||||
Common::copy(buffer + 8, buffer + 12, dataFormatBlock);
|
||||
dataFormatBlock[4] = '\0';
|
||||
|
||||
bool textMode = (strcmp((char *)dataFormatBlock, "txt ") == 0 || strcmp((char *)dataFormatBlock, "tzip") == 0);
|
||||
|
||||
if (strcmp((char *)dataFormatBlock, "bzip") == 0 || strcmp((char *)dataFormatBlock, "tzip") == 0) {
|
||||
uint32 decompressedSize;
|
||||
// we skip the 16 bytes xof header of the file
|
||||
byte *buf = DecompressMsZipData(buffer + 16, fileSize - 16, decompressedSize);
|
||||
delete[] buffer;
|
||||
buffer = buf;
|
||||
return XFileLexer(buffer, decompressedSize, textMode);
|
||||
} else {
|
||||
// we skip the 16 bytes xof header of the file
|
||||
return XFileLexer(buffer + 16, fileSize - 16, textMode);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_PERSISTENT(XModel, false)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -221,98 +123,197 @@ void XModel::cleanup(bool complete) {
|
||||
bool XModel::loadFromFile(const Common::String &filename, XModel *parentModel) {
|
||||
cleanup(false);
|
||||
|
||||
XFile *xfile = new XFile(_gameRef);
|
||||
if (!xfile)
|
||||
return false;
|
||||
|
||||
XFileData xobj;
|
||||
bool resLoop = false;
|
||||
|
||||
_parentModel = parentModel;
|
||||
|
||||
uint32 fileSize = 0;
|
||||
byte *buffer = BaseFileManager::getEngineInstance()->getEngineInstance()->readWholeFile(filename, &fileSize);
|
||||
XFileLexer lexer = createXFileLexer(buffer, fileSize);
|
||||
bool res = xfile->openFile(filename);
|
||||
if (!res) {
|
||||
delete xfile;
|
||||
//return false;
|
||||
error("XModel: Error loading X file: %s", filename.c_str());
|
||||
}
|
||||
|
||||
_rootFrame = new FrameNode(_gameRef);
|
||||
|
||||
bool res = _rootFrame->loadFromXAsRoot(filename, lexer, this, _materialReferences);
|
||||
if (res) {
|
||||
findBones(false, parentModel);
|
||||
uint32 numChildren = 0;
|
||||
xfile->getEnum().getChildren(numChildren);
|
||||
for (uint i = 0; i < numChildren; i++) {
|
||||
resLoop = xfile->getEnum().getChild(i, xobj);
|
||||
if (!resLoop)
|
||||
break;
|
||||
|
||||
bool res = _rootFrame->loadFromXData(filename, this, &xobj, _materialReferences);
|
||||
if (!res) {
|
||||
BaseEngine::LOG(0, "Error loading top level object from '%s'", filename.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_rootFrame->hasChildren()) {
|
||||
BaseEngine::LOG(0, "Error getting any top level objects in '%s'", filename.c_str());
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (res) {
|
||||
res = findBones(false, parentModel);
|
||||
}
|
||||
|
||||
// setup animation channels
|
||||
for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; ++i) {
|
||||
_channels[i] = new AnimationChannel(_gameRef, this);
|
||||
}
|
||||
|
||||
setFilename(filename.c_str());
|
||||
|
||||
delete[] buffer;
|
||||
delete xfile;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::mergeFromFile(const Common::String &filename) {
|
||||
if (!_rootFrame) {
|
||||
BaseEngine::LOG(0, "Error: XModel::mergeFromFile called on an empty model");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 fileSize = 0;
|
||||
byte *buffer = BaseFileManager::getEngineInstance()->getEngineInstance()->readWholeFile(filename, &fileSize);
|
||||
XFileLexer lexer = createXFileLexer(buffer, fileSize);
|
||||
XFile *xfile = new XFile(_gameRef);
|
||||
if (!xfile)
|
||||
return false;
|
||||
|
||||
lexer.advanceToNextToken();
|
||||
parseFrameDuringMerge(lexer, filename);
|
||||
bool res = xfile->openFile(filename);
|
||||
if (!res) {
|
||||
delete xfile;
|
||||
return false;
|
||||
}
|
||||
|
||||
findBones(false, nullptr);
|
||||
XFileData xobj;
|
||||
bool resLoop = false;
|
||||
|
||||
uint32 numChildren = 0;
|
||||
xfile->getEnum().getChildren(numChildren);
|
||||
for (uint i = 0; i < numChildren; i++) {
|
||||
resLoop = xfile->getEnum().getChild(i, xobj);
|
||||
if (!resLoop)
|
||||
break;
|
||||
|
||||
res = _rootFrame->mergeFromXData(filename, this, &xobj);
|
||||
if (!res) {
|
||||
BaseEngine::LOG(0, "Error loading top level object from '%s'", filename.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
res = findBones(true);
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (uint i = 0; i < _mergedModels.size(); ++i) {
|
||||
if (scumm_stricmp(_mergedModels[i], filename.c_str()) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
char *path = new char[filename.size() + 1];
|
||||
strcpy(path, filename.c_str());
|
||||
_mergedModels.add(path);
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
delete xfile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::loadAnimationSet(XFileLexer &lexer, const Common::String &filename) {
|
||||
bool XModel::loadAnimationSet(const Common::String &filename, XFileData *xobj) {
|
||||
bool res = true;
|
||||
|
||||
AnimationSet *animSet = new AnimationSet(_gameRef, this);
|
||||
|
||||
if (animSet->loadFromX(lexer, filename)) {
|
||||
_animationSets.add(animSet);
|
||||
} else {
|
||||
res = loadName(animSet, xobj);
|
||||
if (!res) {
|
||||
delete animSet;
|
||||
res = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
// use the filename for unnamed animation sets
|
||||
if (animSet->_name[0] == '\0') {
|
||||
animSet->setName(PathUtil::getFileName(filename).c_str());
|
||||
}
|
||||
|
||||
XFileData xchildData;
|
||||
XClassType objectType;
|
||||
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
_gameRef->miniUpdate();
|
||||
|
||||
res = xobj->getChild(i, xchildData);
|
||||
if (res) {
|
||||
res = xchildData.getType(objectType);
|
||||
if (!res) {
|
||||
delete animSet;
|
||||
BaseEngine::LOG(0, "Error getting object type while loading animation set");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (objectType == kXClassAnimation) {
|
||||
res = loadAnimation(filename, &xchildData, animSet);
|
||||
if (!res) {
|
||||
delete animSet;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_animationSets.add(animSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::loadAnimation(const Common::String &filename, AnimationSet *parentAnimSet) {
|
||||
// not sure if we need this here (not completely implemented anyways and also not called)
|
||||
// are there animation objects in .X outside of an animation set?
|
||||
|
||||
bool XModel::loadAnimation(const Common::String &filename, XFileData *xobj, AnimationSet *parentAnimSet) {
|
||||
// if no parent anim set is specified, create one
|
||||
bool newAnimSet = false;
|
||||
if (parentAnimSet == nullptr) {
|
||||
parentAnimSet = new AnimationSet(_gameRef, this);
|
||||
|
||||
parentAnimSet->setName(PathUtil::getFileName(filename).c_str());
|
||||
|
||||
newAnimSet = true;
|
||||
}
|
||||
|
||||
// create the new object
|
||||
Animation *Anim = new Animation(_gameRef);
|
||||
Animation *anim = new Animation(_gameRef);
|
||||
|
||||
parentAnimSet->addAnimation(Anim);
|
||||
uint32 numChildren = 0;
|
||||
xobj->getChildren(numChildren);
|
||||
|
||||
for (uint32 i = 0; i < numChildren; i++) {
|
||||
XFileData xchildData;
|
||||
bool res = xobj->getChild(i, xchildData);
|
||||
if (res) {
|
||||
res = anim->load(&xchildData, parentAnimSet);
|
||||
if (!res) {
|
||||
delete anim;
|
||||
if (newAnimSet) {
|
||||
delete parentAnimSet;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
parentAnimSet->addAnimation(anim);
|
||||
|
||||
if (newAnimSet) {
|
||||
_animationSets.add(parentAnimSet);
|
||||
@ -340,22 +341,23 @@ bool XModel::findBones(bool animOnly, XModel *parentModel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void XModel::parseFrameDuringMerge(XFileLexer &lexer, const Common::String &filename) {
|
||||
while (!lexer.eof()) {
|
||||
if (lexer.tokenIsIdentifier("Frame")) {
|
||||
lexer.advanceToNextToken();
|
||||
parseFrameDuringMerge(lexer, filename);
|
||||
} else if (lexer.tokenIsIdentifier("AnimationSet")) {
|
||||
lexer.advanceToNextToken();
|
||||
loadAnimationSet(lexer, filename);
|
||||
} else if (lexer.tokenIsOfType(IDENTIFIER)) {
|
||||
lexer.skipObject();
|
||||
} else {
|
||||
lexer.advanceToNextToken(); // we ignore anything else here
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::loadName(BaseNamedObject *obj, XFileData *data) {
|
||||
Common::String name;
|
||||
if (data->getName(name)) {
|
||||
obj->_name = new char[name.size() + 1];
|
||||
Common::strlcpy(obj->_name, name.c_str(), name.size() + 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::loadName(Common::String &targetStr, XFileData *data) {
|
||||
return data->getName(targetStr);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XModel::update() {
|
||||
// reset all bones to default position
|
||||
|
@ -46,7 +46,7 @@ class AnimationSet;
|
||||
class FrameNode;
|
||||
class Material;
|
||||
class ShadowVolume;
|
||||
class XFileLexer;
|
||||
class XFileData;
|
||||
|
||||
struct MaterialReference {
|
||||
Common::String _name;
|
||||
@ -152,8 +152,11 @@ public:
|
||||
|
||||
bool isTransparentAt(int x, int y);
|
||||
|
||||
bool loadAnimationSet(XFileLexer &lexer, const Common::String &filename);
|
||||
bool loadAnimation(const Common::String &filename, AnimationSet *parentAnimSet);
|
||||
static bool loadName(BaseNamedObject *obj, XFileData *data);
|
||||
static bool loadName(Common::String &targetStr, XFileData *data);
|
||||
|
||||
bool loadAnimationSet(const Common::String &filename, XFileData *xobj);
|
||||
bool loadAnimation(const Common::String &filename, XFileData *xobj, AnimationSet *parentAnimSet = nullptr);
|
||||
|
||||
Math::Matrix4 _lastWorldMat;
|
||||
Rect32 _boundingRect;
|
||||
@ -186,8 +189,6 @@ private:
|
||||
void cleanup(bool complete = true);
|
||||
bool findBones(bool animOnly = false, XModel *parentModel = nullptr);
|
||||
|
||||
void parseFrameDuringMerge(XFileLexer &lexer, const Common::String &filename);
|
||||
|
||||
void updateBoundingRect();
|
||||
void static inline updateRect(Rect32 *rc, int32 x, int32 y);
|
||||
Rect32 _drawingViewport;
|
||||
|
@ -168,11 +168,12 @@ MODULE_OBJS += \
|
||||
base/gfx/xanimation.o \
|
||||
base/gfx/xanimation_channel.o \
|
||||
base/gfx/xanimation_set.o \
|
||||
base/gfx/xfile.o \
|
||||
base/gfx/xfile_loader.o \
|
||||
base/gfx/xframe_node.o \
|
||||
base/gfx/xmaterial.o \
|
||||
base/gfx/xmesh.o \
|
||||
base/gfx/xmodel.o \
|
||||
base/gfx/xloader.o \
|
||||
base/gfx/opengl/base_surface_opengl3d.o \
|
||||
base/gfx/opengl/base_render_opengl3d.o \
|
||||
base/gfx/opengl/base_render_opengl3d_shader.o \
|
||||
|
Loading…
x
Reference in New Issue
Block a user