Initial commit
This commit is contained in:
647
engines/tetraedge/te/te_model.cpp
Normal file
647
engines/tetraedge/te/te_model.cpp
Normal file
@@ -0,0 +1,647 @@
|
||||
/* 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/file.h"
|
||||
#include "common/util.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/compression/deflate.h"
|
||||
|
||||
#include "tetraedge/tetraedge.h"
|
||||
#include "tetraedge/te/te_light.h"
|
||||
#include "tetraedge/te/te_model.h"
|
||||
#include "tetraedge/te/te_model_animation.h"
|
||||
#include "tetraedge/te/te_renderer.h"
|
||||
#include "tetraedge/te/te_trs.h"
|
||||
|
||||
namespace Tetraedge {
|
||||
|
||||
TeModel::TeModel() : _enableLights(false), _skipSkinOffsets(false), _matrixForced(false) {
|
||||
_modelAnim.setDeleteFn(&TeModelAnimation::deleteLaterStatic);
|
||||
_modelVertexAnim.setDeleteFn(&TeModelVertexAnimation::deleteLaterStatic);
|
||||
create();
|
||||
}
|
||||
|
||||
TeModel::~TeModel() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation> &anim, float seconds, bool repeat) {
|
||||
if (!_modelAnim) {
|
||||
setAnim(anim, repeat);
|
||||
} else {
|
||||
BonesBlender *blender = new BonesBlender(anim, seconds);
|
||||
anim->_repeatCount = (repeat ? -1 : 1);
|
||||
anim->play();
|
||||
_boneBlenders.push_back(blender);
|
||||
}
|
||||
}
|
||||
|
||||
void TeModel::blendMesh(const Common::String &s1, const Common::String &s2, float amount) {
|
||||
_meshBlenders.push_back(new MeshBlender(s1, s2, amount, this));
|
||||
}
|
||||
|
||||
int TeModel::checkFileType(Common::SeekableReadStream &instream) const {
|
||||
char buf[4];
|
||||
instream.seek(0);
|
||||
int sz = instream.read(buf, 4);
|
||||
instream.seek(0);
|
||||
if (sz == 4 && strncmp("TEMD", buf, 4) == 0) {
|
||||
return 1;
|
||||
} else if (sz == 4 && strncmp("TEAN", buf, 4) == 0) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TeModel::create() {
|
||||
// TODO: set field_0x158 to 0
|
||||
_modelAnim.release();
|
||||
_modelVertexAnim.release();
|
||||
_matrixForced = false;
|
||||
_skipSkinOffsets = false;
|
||||
}
|
||||
|
||||
void TeModel::destroy() {
|
||||
_weightElements.clear();
|
||||
_lerpedElements.clear();
|
||||
_meshes.clear();
|
||||
_bones.clear();
|
||||
_boneMatricies.clear();
|
||||
_skinOffsets.clear();
|
||||
for (MeshBlender *blender : _meshBlenders)
|
||||
delete blender;
|
||||
_meshBlenders.clear();
|
||||
for (BonesBlender *blender : _boneBlenders)
|
||||
delete blender;
|
||||
_boneBlenders.clear();
|
||||
}
|
||||
|
||||
void TeModel::draw() {
|
||||
TeRenderer *renderer = g_engine->getRenderer();
|
||||
|
||||
if (worldVisible()) {
|
||||
const TeMatrix4x4 transform = transformationMatrix();
|
||||
renderer->sendModelMatrix(transform);
|
||||
renderer->pushMatrix();
|
||||
renderer->multiplyMatrix(transform);
|
||||
/*
|
||||
if (name().contains("DEPLIANT")) {
|
||||
debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
|
||||
debug(" renderMatrix %s", renderer->currentMatrix().toString().c_str());
|
||||
//debug(" position %s", position().dump().c_str());
|
||||
debug(" worldPos %s", worldPosition().dump().c_str());
|
||||
//debug(" scale %s", scale().dump().c_str());
|
||||
debug(" worldScale %s", worldScale().dump().c_str());
|
||||
//debug(" rotation %s", rotation().dump().c_str());
|
||||
debug(" worldRot %s", worldRotation().dump().c_str());
|
||||
}*/
|
||||
for (auto &mesh : _meshes) {
|
||||
// TODO: Set some flag (_drawWires?) in mesh to this->field_0x158??
|
||||
mesh->draw();
|
||||
}
|
||||
renderer->popMatrix();
|
||||
// Note: no corresponding enableAll - they can be enabled inside TeMaterial::apply
|
||||
renderer->disableAllLights();
|
||||
}
|
||||
}
|
||||
|
||||
void TeModel::forceMatrix(const TeMatrix4x4 &matrix) {
|
||||
_matrixForced = true;
|
||||
_forcedMatrix = matrix;
|
||||
}
|
||||
|
||||
TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, uint num) {
|
||||
if (anim) {
|
||||
int boneNo = anim->findBone(_bones[num]._name);
|
||||
if (boneNo != -1)
|
||||
return anim->getTRS(boneNo, anim->curFrame2(), false);
|
||||
}
|
||||
return _bones[num]._trs;
|
||||
}
|
||||
|
||||
void TeModel::invertNormals() {
|
||||
for (auto &mesh : _meshes) {
|
||||
for (uint i = 0; i < mesh->numIndexes() / 3; i += 3) {
|
||||
// Swap order of verticies in each triangle.
|
||||
uint idx0 = mesh->index(i * 3);
|
||||
uint idx2 = mesh->index(i * 3 + 2);
|
||||
mesh->setIndex(i * 3, idx2);
|
||||
mesh->setIndex(i * 3 + 2, idx0);
|
||||
}
|
||||
for (uint i = 0; i < mesh->numVerticies(); i++) {
|
||||
mesh->setNormal(i, -mesh->normal(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TeMatrix4x4 TeModel::lerpElementsMatrix(uint weightsNum, const Common::Array<TeMatrix4x4> &matricies) {
|
||||
TeMatrix4x4 retval;
|
||||
// Start with a 0 matrix.
|
||||
for (uint i = 0; i < 4; i++)
|
||||
retval.setValue(i, i, 0);
|
||||
|
||||
const Common::Array<weightElement> &weights = _weightElements[weightsNum];
|
||||
for (const auto &weight : weights) {
|
||||
const TeMatrix4x4 offset = matricies[weight._x].meshScale(weight._weight);
|
||||
retval.meshAdd(offset);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void TeModel::removeAnim() {
|
||||
for (TeModel::BonesBlender *blender : _boneBlenders) {
|
||||
delete blender;
|
||||
}
|
||||
_boneBlenders.clear();
|
||||
_modelAnim.release();
|
||||
}
|
||||
|
||||
void TeModel::setColor(const TeColor &col) {
|
||||
Te3DObject2::setColor(col);
|
||||
for (auto &mesh : _meshes) {
|
||||
mesh->setColor(col);
|
||||
}
|
||||
}
|
||||
|
||||
void TeModel::update() {
|
||||
//if (name().contains("Kate"))
|
||||
// debug("TeModel::update model %s", name().c_str());
|
||||
if (_bones.size()) {
|
||||
Common::Array<TeMatrix4x4> matricies(_bones.size());
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
const Bone &b = _bones[i];
|
||||
const TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
|
||||
if (b._parentBone == -1 || _bones.size() < 2) {
|
||||
matricies[0] = matrix;
|
||||
} else {
|
||||
matricies[i] = matricies[b._parentBone] * matrix;
|
||||
}
|
||||
}
|
||||
|
||||
_boneMatricies.resize(_bones.size());
|
||||
_lerpedElements.resize(_weightElements.size());
|
||||
|
||||
TeMatrix4x4 invertx;
|
||||
invertx.scale(TeVector3f32(-1, 1, 1));
|
||||
for (uint b = 0; b < _bones.size(); b++) {
|
||||
TeTRS trs = getBone(_modelAnim, b);
|
||||
for (uint i = 0; i < _boneBlenders.size(); i++) {
|
||||
BonesBlender *blender = _boneBlenders[i];
|
||||
float complete = blender->coef();
|
||||
TeTRS endTRS = getBone(blender->_anim, b);
|
||||
if (complete == 1.0f) {
|
||||
_modelAnim = blender->_anim;
|
||||
delete blender;
|
||||
_boneBlenders.remove_at(i);
|
||||
trs = endTRS;
|
||||
i--;
|
||||
} else {
|
||||
trs = trs.lerp(endTRS, complete);
|
||||
}
|
||||
}
|
||||
|
||||
TeMatrix4x4 newBoneMatrix;
|
||||
if (!_matrixForced) {
|
||||
newBoneMatrix = TeMatrix4x4::fromTRS(trs);
|
||||
} else {
|
||||
newBoneMatrix = invertx * _forcedMatrix;
|
||||
_matrixForced = false;
|
||||
}
|
||||
|
||||
if (_bones.size() < 2 || _bones[b]._parentBone == -1) {
|
||||
_boneMatricies[b] = newBoneMatrix;
|
||||
_boneMatricies[b].rotate(_boneRotation);
|
||||
} else {
|
||||
_boneMatricies[b] = (invertx * _boneMatricies[_bones[b]._parentBone]) * newBoneMatrix;
|
||||
}
|
||||
_boneMatricies[b] = invertx * _boneMatricies[b];
|
||||
_bonesUpdatedSignal.call(_bones[b]._name, _boneMatricies[b]);
|
||||
}
|
||||
|
||||
if (!_skinOffsets.empty() && !_bones.empty()) {
|
||||
for (uint b = 0; b < _bones.size(); b++) {
|
||||
_boneMatricies[b] = _boneMatricies[b] * _skinOffsets[b];
|
||||
}
|
||||
}
|
||||
|
||||
if (!_skipSkinOffsets && !_weightElements.empty()) {
|
||||
for (uint i = 0; i < _weightElements.size(); i++) {
|
||||
_lerpedElements[i] = lerpElementsMatrix(i, _boneMatricies);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint m = 0; m < _meshes.size(); m++) {
|
||||
TeMesh &mesh = *_meshes[m];
|
||||
if (!mesh.visible())
|
||||
continue;
|
||||
|
||||
if (!_skipSkinOffsets && _bones.size() < 2 ) {
|
||||
if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head()) {
|
||||
mesh.update(_modelVertexAnim);
|
||||
// TODO: lines 422 - 427.. set some vals and goto LAB_doMeshBlends;
|
||||
}
|
||||
mesh.update(&_boneMatricies, &_lerpedElements);
|
||||
} else {
|
||||
mesh.resizeUpdatedTables(mesh.numVerticies());
|
||||
Common::Array<TeVector3f32> verticies;
|
||||
if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head())
|
||||
verticies = _modelVertexAnim->getVertices();
|
||||
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
TeVector3f32 vertex;
|
||||
if (verticies.empty()) {
|
||||
vertex = mesh.preUpdatedVertex(i);
|
||||
} else {
|
||||
if (i < verticies.size())
|
||||
vertex = verticies[i];
|
||||
}
|
||||
TeVector3f32 normal = mesh.preUpdatedNormal(i);
|
||||
int idx = (int)mesh.matrixIndex(i);
|
||||
|
||||
TeVector3f32 updatedvertex;
|
||||
TeVector3f32 updatednormal;
|
||||
|
||||
if (idx < (int)_bones.size()) {
|
||||
updatedvertex = vertex;
|
||||
if (verticies.empty())
|
||||
updatedvertex = _boneMatricies[idx] * updatedvertex;
|
||||
updatednormal = _boneMatricies[idx].mult3x3(normal);
|
||||
} else {
|
||||
idx -= _bones.size();
|
||||
for (uint w = 0; w < _weightElements[idx].size(); w++) {
|
||||
const TeMatrix4x4 &wmatrix = _boneMatricies[_weightElements[idx][w]._x];
|
||||
float weight = _weightElements[idx][w]._weight;
|
||||
updatedvertex = updatedvertex + ((wmatrix * vertex) * weight);
|
||||
updatednormal = updatednormal + (wmatrix.mult3x3(normal) * weight);
|
||||
}
|
||||
}
|
||||
|
||||
mesh.setUpdatedVertex(i, updatedvertex);
|
||||
mesh.setUpdatedNormal(i, updatednormal);
|
||||
}
|
||||
}
|
||||
|
||||
for (MeshBlender *mb : _meshBlenders) {
|
||||
if (mesh.name().contains(mb->_name)) {
|
||||
error("TODO: Finish meshblend part of model::update");
|
||||
// TODO: Finish TeModel::update. (disasm 585 ~ 644), LAB_doMeshBlends
|
||||
//float blendamount = MIN(mb->_timer.getTimeFromStart() / 1000000.0f, 1.0f);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No bones..
|
||||
for (auto &mesh : _meshes) {
|
||||
if (!_modelVertexAnim) {
|
||||
mesh->update(nullptr, nullptr);
|
||||
} else {
|
||||
if (mesh->name() != _modelVertexAnim->head()) {
|
||||
mesh->update(nullptr, nullptr);
|
||||
} else {
|
||||
mesh->update(_modelVertexAnim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TeModel::MeshBlender::MeshBlender(const Common::String &name, const Common::String &meshName, float amount, TeModel *model) :
|
||||
_name(name), _amount(amount) {
|
||||
const auto &meshes = model->_meshes;
|
||||
uint i = 0;
|
||||
for (; i < meshes.size(); i++) {
|
||||
if (meshes[i]->name().contains(meshName))
|
||||
break;
|
||||
}
|
||||
_meshNo = i;
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void TeModel::loadAlign(Common::SeekableReadStream &stream) {
|
||||
int64 pos = stream.pos();
|
||||
if (pos % 4) {
|
||||
stream.seek(4 - (pos % 4), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void TeModel::saveAlign(Common::SeekableWriteStream &stream) {
|
||||
int64 pos = stream.pos();
|
||||
if (pos % 4) {
|
||||
stream.seek(4 - (pos % 4), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
bool TeModel::load(Common::SeekableReadStream &stream) {
|
||||
destroy();
|
||||
create();
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "TEMD")) {
|
||||
error("[TeModel::load] Unknown format.");
|
||||
}
|
||||
|
||||
uint version = stream.readUint32LE();
|
||||
if (!((version == 11) || (version == 13))) {
|
||||
error("[TeModel::load] Unsupported version %d", version);
|
||||
}
|
||||
|
||||
uint32 meshCount = stream.readUint32LE();
|
||||
if (meshCount > 100000)
|
||||
error("TeModel::load: Unexpected number of meshes %d", meshCount);
|
||||
_meshes.resize(meshCount);
|
||||
for (uint i = 0; i < meshCount; i++)
|
||||
_meshes[i].reset(TeMesh::makeInstance());
|
||||
|
||||
uint32 weightCount = stream.readUint32LE();
|
||||
if (weightCount > 100000)
|
||||
error("TeModel::load: Unexpected number of weights %d", weightCount);
|
||||
_weightElements.resize(weightCount);
|
||||
uint32 bonecount = stream.readUint32LE();
|
||||
if (bonecount > 100000)
|
||||
error("TeModel::load: Unexpected number of bones %d", bonecount);
|
||||
_bones.resize(bonecount);
|
||||
_skinOffsets.resize(bonecount);
|
||||
|
||||
if (version == 13) {
|
||||
_skipSkinOffsets = stream.readUint32LE();
|
||||
}
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "SKEL")) {
|
||||
error("[TeModel::load] Unable to find skeleton.");
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
_bones[i]._name = Te3DObject2::deserializeString(stream);
|
||||
loadAlign(stream);
|
||||
_bones[i]._parentBone = stream.readUint32LE();
|
||||
TeTRS::deserialize(stream, _bones[i]._trs);
|
||||
if (!_skipSkinOffsets || g_engine->gameType() == TetraedgeEngine::kSyberia2) {
|
||||
_skinOffsets[i].deserialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint m = 0; m < _meshes.size(); m++) {
|
||||
if (!loadMesh(stream, *_meshes[m])) {
|
||||
error("[TeModel::load] Error on meshes loading.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "WEIG")) {
|
||||
error("[TeModel::load] Unable to load weight.");
|
||||
}
|
||||
for (uint i = 0; i < _weightElements.size(); i++) {
|
||||
loadWeights(stream, _weightElements[i]);
|
||||
}
|
||||
|
||||
if (_bones.empty())
|
||||
_bones.resize(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TeModel::load(const Common::Path &path) {
|
||||
Common::File modelFile;
|
||||
if (!modelFile.open(path)) {
|
||||
warning("[TeModel::load] Can't open file : %s.", path.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retval;
|
||||
if (loadAndCheckFourCC(modelFile, "TEZ0")) {
|
||||
Common::SeekableReadStream *zlibStream = tryLoadZlibStream(modelFile);
|
||||
if (!zlibStream)
|
||||
return false;
|
||||
retval = load(*zlibStream);
|
||||
delete zlibStream;
|
||||
} else {
|
||||
modelFile.seek(0);
|
||||
retval = load(modelFile);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *TeModel::tryLoadZlibStream(Common::SeekableReadStream &stream) {
|
||||
byte version = stream.readByte();
|
||||
if (version != 1) {
|
||||
warning("[TeModel::load] invalid version number %d (expect 1)", version);
|
||||
return nullptr;
|
||||
}
|
||||
uint32 compressedSize = stream.readUint32LE();
|
||||
if (compressedSize > stream.size()) {
|
||||
warning("[TeModel::load] invalid size %d (file size %d)", compressedSize, (int)stream.size());
|
||||
return nullptr;
|
||||
}
|
||||
uint32 uncompressedSize = stream.readUint32LE();
|
||||
Common::SeekableSubReadStream *substream = new Common::SeekableSubReadStream(&stream, stream.pos(), stream.size());
|
||||
return Common::wrapCompressedReadStream(substream, DisposeAfterUse::YES, uncompressedSize);
|
||||
}
|
||||
|
||||
bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElement> &weights) {
|
||||
uint32 nweights = stream.readUint32LE();
|
||||
if (nweights > 100000)
|
||||
error("Improbable number of weights %d", (int)nweights);
|
||||
weights.resize(nweights);
|
||||
for (uint i = 0; i < nweights; i++) {
|
||||
weights[i]._weight = stream.readFloatLE();
|
||||
weights[i]._x = stream.readUint16LE();
|
||||
stream.readUint16LE();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
|
||||
if (!loadAndCheckFourCC(stream, "MESH"))
|
||||
return false;
|
||||
|
||||
uint32 vertcount = stream.readUint32LE();
|
||||
uint32 matcount = stream.readUint32LE();
|
||||
uint32 matidxcount = stream.readUint32LE();
|
||||
uint32 idxcount = stream.readUint32LE();
|
||||
|
||||
if (vertcount > 100000 || matcount > 100000 || matidxcount > 100000 || idxcount > 100000)
|
||||
error("Improbable mesh sizes %d %d %d %d", vertcount, matcount, matidxcount, idxcount);
|
||||
|
||||
mesh.setConf(vertcount, idxcount, TeMesh::MeshMode_Triangles, matcount, matidxcount);
|
||||
|
||||
uint32 flags = stream.readUint32LE();
|
||||
if (flags & 1)
|
||||
mesh.setColor(0, TeColor(0xff, 0xff, 0xff, 0xff));
|
||||
if (flags & 2)
|
||||
mesh.setTextureUV(0, TeVector2f32(0.0f, 0.0f));
|
||||
|
||||
mesh.setName(Te3DObject2::deserializeString(stream));
|
||||
loadAlign(stream);
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "MTRL"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.materials().size(); i++) {
|
||||
TeMaterial mat;
|
||||
TeMaterial::deserialize(stream, mat, _texturePath);
|
||||
if (_enableLights)
|
||||
mat._enableLights = true;
|
||||
mesh.attachMaterial(i, mat);
|
||||
}
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "VERT"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
TeVector3f32 v;
|
||||
TeVector3f32::deserialize(stream, v);
|
||||
mesh.setVertex(i, v);
|
||||
}
|
||||
if (mesh.hasUvs()) {
|
||||
if (!loadAndCheckFourCC(stream, "TUVS"))
|
||||
return false;
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
TeVector2f32 v;
|
||||
TeVector2f32::deserialize(stream, v);
|
||||
mesh.setTextureUV(i, v);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "NORM"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
TeVector3f32 v;
|
||||
TeVector3f32::deserialize(stream, v);
|
||||
mesh.setNormal(i, v);
|
||||
}
|
||||
|
||||
if (mesh.hasColor()) {
|
||||
if (!loadAndCheckFourCC(stream, "COLS"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
TeColor c;
|
||||
c.deserialize(stream);
|
||||
mesh.setColor(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadAndCheckFourCC(stream, "FCPM"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.materials().size(); i++) {
|
||||
mesh.facesPerMaterial(i, stream.readUint16LE());
|
||||
}
|
||||
|
||||
loadAlign(stream);
|
||||
if (!loadAndCheckFourCC(stream, "MTXI"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.numVerticies(); i++) {
|
||||
mesh.matrixIndex(i, stream.readUint16LE());
|
||||
}
|
||||
|
||||
loadAlign(stream);
|
||||
if (!loadAndCheckFourCC(stream, "IDXS"))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < mesh.numIndexes(); i++) {
|
||||
mesh.setIndex(i, stream.readUint16LE());
|
||||
}
|
||||
|
||||
loadAlign(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TeModel::setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Array<TeVector3f32> &verts, const TeColor &col) {
|
||||
_meshes.clear();
|
||||
Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
|
||||
mesh->setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
|
||||
mesh->defaultMaterial(tex);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
float f = (i == 0 ? 0.0f : 1.0f);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int index = i * 2 + j;
|
||||
mesh->setVertex(index, verts[i * 2 + j]);
|
||||
mesh->setTextureUV(index, TeVector2f32(f, (j == 0 ? 0.0f : 1.0f)));
|
||||
mesh->setIndex(index, index);
|
||||
if (col.a() != 0)
|
||||
mesh->setColor(index, col);
|
||||
}
|
||||
}
|
||||
|
||||
const TeVector3f32 v1 = verts[1] - verts[0];
|
||||
const TeVector3f32 v2 = verts[2] - verts[0];
|
||||
TeVector3f32 v3 = TeVector3f32::crossProduct(v1, v2);
|
||||
v3.normalize();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mesh->setNormal(i, v3);
|
||||
}
|
||||
_meshes.push_back(mesh);
|
||||
}
|
||||
|
||||
void TeModel::setAnim(TeIntrusivePtr<TeModelAnimation> &anim, bool repeat) {
|
||||
for (TeModel::BonesBlender *blender : _boneBlenders) {
|
||||
delete blender;
|
||||
}
|
||||
_boneBlenders.clear();
|
||||
anim->_repeatCount = (repeat ? -1 : 1);
|
||||
_modelAnim = anim;
|
||||
}
|
||||
|
||||
void TeModel::setVertexAnim(TeIntrusivePtr<TeModelVertexAnimation> &anim, bool repeat) {
|
||||
anim->_repeatCount = (repeat ? -1 : 1);
|
||||
_modelVertexAnim = anim;
|
||||
}
|
||||
|
||||
void TeModel::setVisibleByName(const Common::String &name, bool vis) {
|
||||
for (auto &mesh : _meshes) {
|
||||
if (mesh->name().contains(name)) {
|
||||
mesh->setVisible(vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TeMatrix4x4 TeModel::skinOffset(uint boneno) const {
|
||||
if (boneno >= _skinOffsets.size())
|
||||
return TeMatrix4x4();
|
||||
return _skinOffsets[boneno];
|
||||
}
|
||||
|
||||
void TeModel::setMeshCount(uint count) {
|
||||
assert(count < 100000);
|
||||
while (_meshes.size() < count)
|
||||
_meshes.push_back(Common::SharedPtr<TeMesh>(TeMesh::makeInstance()));
|
||||
|
||||
if (_meshes.size() > count)
|
||||
_meshes.resize(count);
|
||||
}
|
||||
|
||||
TeModel::BonesBlender::BonesBlender(TeIntrusivePtr<TeModelAnimation> anim, float seconds) : _anim(anim), _seconds(seconds) {
|
||||
_anim.setDeleteFn(&TeModelAnimation::deleteLaterStatic);
|
||||
_timer.stop();
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
float TeModel::BonesBlender::coef() {
|
||||
float elapsed = (_timer.getTimeFromStart() / 1000000.0) / _seconds;
|
||||
return MIN(elapsed, 1.0f);
|
||||
}
|
||||
|
||||
} // end namespace Tetraedge
|
||||
Reference in New Issue
Block a user