/* 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 . * */ /* * This file is based on WME. * http://dead-code.org/redir.php?target=wme * Copyright (c) 2003-2013 Jan Nedoma and contributors */ #include "common/compression/deflate.h" #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" #include "engines/wintermute/base/gfx/xactive_animation.h" #include "engines/wintermute/base/gfx/xanimation_channel.h" #include "engines/wintermute/base/gfx/xanimation_set.h" #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/3deffect.h" #include "engines/wintermute/base/gfx/xfile.h" #include "engines/wintermute/base/gfx/xfile_loader.h" #include "engines/wintermute/dcgf.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/utils/path_util.h" #include "engines/wintermute/utils/utils.h" #include "engines/wintermute/wintermute.h" #include "engines/wintermute/dcgf.h" namespace Wintermute { IMPLEMENT_PERSISTENT(XModel, false) ////////////////////////////////////////////////////////////////////////// XModel::XModel(BaseGame *inGame, BaseObject *owner) : BaseObject(inGame) { _owner = owner; _rootFrame = nullptr; memset(&_drawingViewport, 0, sizeof(DXViewport)); DXMatrixIdentity(&_lastWorldMat); DXMatrixIdentity(&_lastViewMat); DXMatrixIdentity(&_lastProjMat); _lastOffsetX = _lastOffsetY = 0; _BBoxStart = _BBoxEnd = DXVector3(0.0f, 0.0f, 0.0f); BasePlatform::setRectEmpty(&_boundingRect); for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; i++) { _channels[i] = nullptr; } _ticksPerSecond = kDefaultTicksPerSecond; } ////////////////////////////////////////////////////////////////////////// XModel::~XModel() { cleanup(); } ////////////////////////////////////////////////////////////////////////// void XModel::cleanup(bool complete) { // empty animation channels for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; i++) { SAFE_DELETE(_channels[i]); } // remove animation sets for (int32 i = 0; i < _animationSets.getSize(); i++) { delete _animationSets[i]; } _animationSets.removeAll(); if (complete) { for (int32 i = 0; i < _mergedModels.getSize(); ++i) { SAFE_DELETE_ARRAY(_mergedModels[i]); } _mergedModels.removeAll(); } for (int32 i = 0; i < _matSprites.getSize(); i++) { SAFE_DELETE(_matSprites[i]); } _matSprites.removeAll(); // remove root frame SAFE_DELETE(_rootFrame); _parentModel = nullptr; _ticksPerSecond = kDefaultTicksPerSecond; } ////////////////////////////////////////////////////////////////////////// bool XModel::loadFromFile(const char *filename, XModel *parentModel) { cleanup(false); XFile *xfile = new XFile(_game); if (!xfile) return false; XFileData xobj; bool resLoop = false; _parentModel = parentModel; bool res = xfile->openFile(filename); if (!res) { delete xfile; BaseEngine::LOG(0, "Error loading X file: %s", filename); return false; } // get top level objects _rootFrame = new FrameNode(_game); 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->loadFromXData(filename, this, &xobj); if (!res) { BaseEngine::LOG(0, "Error loading top level object from '%s'", filename); break; } } if (!_rootFrame->hasChildren()) { BaseEngine::LOG(0, "Error getting any top level objects in '%s'", filename); 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(_game, this); } if (_filename != filename) BaseUtils::setString(&_filename, filename); delete xfile; return res; } ////////////////////////////////////////////////////////////////////////// bool XModel::mergeFromFile(const char *filename) { if (!_rootFrame) { BaseEngine::LOG(0, "Error: XModel::mergeFromFile called on an empty model"); return false; } XFile *xfile = new XFile(_game); if (!xfile) return false; bool res = xfile->openFile(filename); if (!res) { delete xfile; return false; } 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); break; } } if (res) { res = findBones(true); } // remember path for save/load purposes bool found = false; for (int32 i = 0; i < _mergedModels.getSize(); ++i) { if (scumm_stricmp(_mergedModels[i], filename) == 0) { found = true; break; } } if (!found) { size_t filenameSize = strlen(filename) + 1; char *path = new char[filenameSize]; Common::strcpy_s(path, filenameSize, filename); _mergedModels.add(path); } delete xfile; return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::loadAnimationSet(const char *filename, XFileData *xobj) { bool res = true; // create the animation set object AnimationSet *animSet = new AnimationSet(_game, this); res = loadName(animSet, xobj); if (!res) { SAFE_DELETE(animSet); return res; } // use the filename for unnamed animation sets if (animSet->_name[0] == '\0') { animSet->setName(PathUtil::getFileName(filename).c_str()); } // query through the child objects to load the animations XFileData xchildData; XClassType objectType; uint32 numChildren = 0; xobj->getChildren(numChildren); for (uint32 i = 0; i < numChildren; i++) { _game->miniUpdate(); res = xobj->getChild(i, xchildData); if (res) { res = xchildData.getType(objectType); if (!res) { SAFE_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) { SAFE_DELETE(animSet); return res; } } } } _animationSets.add(animSet); return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::loadAnimation(const char *filename, XFileData *xobj, AnimationSet *parentAnimSet) { // if no parent anim set is specified, create one bool newAnimSet = false; if (parentAnimSet == nullptr) { parentAnimSet = new AnimationSet(_game, this); parentAnimSet->setName(PathUtil::getFileName(filename).c_str()); newAnimSet = true; } // create the new object Animation *anim = new Animation(_game); // load the animation 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) { SAFE_DELETE(anim); if (newAnimSet) SAFE_DELETE(parentAnimSet); return res; } } } parentAnimSet->addAnimation(anim); if (newAnimSet) _animationSets.add(parentAnimSet); return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::findBones(bool animOnly, XModel *parentModel) { FrameNode *rootFrame; if (parentModel == nullptr) rootFrame = _rootFrame; else rootFrame = parentModel->getRootFrame(); if (rootFrame && !animOnly) { _rootFrame->findBones(rootFrame); } for (int32 i = 0; i < _animationSets.getSize(); i++) { _animationSets[i]->findBones(rootFrame); } return true; } ////////////////////////////////////////////////////////////////////////// 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 reset(); // update all animation channels for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; i++) { _channels[i]->update(i == 1); } // update matrices if (_rootFrame) { DXMatrix tempMat; DXMatrixIdentity(&tempMat); _rootFrame->updateMatrices(&tempMat); return _rootFrame->updateMeshes(); } else { return false; } } ////////////////////////////////////////////////////////////////////////// bool XModel::playAnim(int channel, const char *name, uint32 transitionTime, bool forceReset, uint32 stopTransitionTime) { if (channel < 0 || channel >= X_NUM_ANIMATION_CHANNELS) { return false; } // are we already playing this animation? if (!forceReset) { if (_channels[channel]->getName() && scumm_stricmp(name, _channels[channel]->getName()) == 0) { return true; } } // find animation set by name AnimationSet *anim = getAnimationSetByName(name); if (anim) { char *currentAnim = _channels[channel]->getName(); if (_owner && currentAnim && name) { transitionTime = _owner->getAnimTransitionTime(currentAnim, name); } return _channels[channel]->playAnim(anim, transitionTime, stopTransitionTime); } else { return false; } } ////////////////////////////////////////////////////////////////////////// bool XModel::stopAnim(int channel, uint32 transitionTime) { if (channel < 0 || channel >= X_NUM_ANIMATION_CHANNELS) { return false; } return _channels[channel]->stopAnim(transitionTime); } ////////////////////////////////////////////////////////////////////////// bool XModel::stopAnim(uint32 transitionTime) { const int NUM_SKEL_ANI_CHANNELS = 10; for (int channel = 0; channel < NUM_SKEL_ANI_CHANNELS; channel++) { stopAnim(channel, transitionTime); } return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::reset() { if (_rootFrame) { _rootFrame->resetMatrices(); } return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::isAnimPending(int channel, const char *animName) { if (!animName) { if (_channels[channel]->isPlaying()) { return true; } } else { if (_channels[channel]->isPlaying() && _channels[channel]->getName() && scumm_stricmp(animName, _channels[channel]->getName()) == 0) { return true; } } return false; } ////////////////////////////////////////////////////////////////////////// bool XModel::isAnimPending(char *animName) { for (int channel = 0; channel < X_NUM_ANIMATION_CHANNELS; channel++) { if (isAnimPending(channel, animName)) { return true; } } return false; } ////////////////////////////////////////////////////////////////////////// bool XModel::updateShadowVol(ShadowVolume *shadow, DXMatrix *modelMat, DXVector3 *light, float extrusionDepth) { if (_rootFrame) { return _rootFrame->updateShadowVol(shadow, modelMat, light, extrusionDepth); } else { return false; } } ////////////////////////////////////////////////////////////////////////// bool XModel::render() { BaseRenderer3D *renderer = _game->_renderer3D; if (_rootFrame) { // set culling if (_owner && !_owner->_drawBackfaces) { renderer->enableCulling(); } else { renderer->disableCulling(); } // render everything bool res = _rootFrame->render(this); // remember matrices for object picking purposes renderer->getWorldTransform(&_lastWorldMat); renderer->getViewTransform(&_lastViewMat); renderer->getProjectionTransform(&_lastProjMat); // remember scene offset Common::Rect32 rc; _game->getCurrentViewportRect(&rc); float width = (float)rc.right - (float)rc.left; float height = (float)rc.bottom - (float)rc.top; // margins int mleft = rc.left; int mright = renderer->getWidth() - width - rc.left; int mtop = rc.top; int mbottom = renderer->getHeight() - height - rc.top; _lastOffsetX = _game->_offsetX + (mleft - mright) / 2; _lastOffsetY = _game->_offsetY + (mtop - mbottom) / 2; // update bounding box and 2D bounding rectangle updateBoundingRect(); return res; } else { return false; } } bool XModel::renderFlatShadowModel(uint32 shadowColor) { if (_rootFrame) { if(_owner && !_owner->_drawBackfaces) { _game->_renderer3D->enableCulling(); } else { _game->_renderer3D->disableCulling(); } return _rootFrame->renderFlatShadowModel(shadowColor); } else { return false; } } ////////////////////////////////////////////////////////////////////////// DXMatrix *XModel::getBoneMatrix(const char *boneName) { FrameNode *bone = _rootFrame->findFrame(boneName); if (bone) { return bone->getCombinedMatrix(); } else { return nullptr; } } ////////////////////////////////////////////////////////////////////////// FrameNode *XModel::getRootFrame() { return _rootFrame; } ////////////////////////////////////////////////////////////////////////// bool XModel::isTransparentAt(int x, int y) { if (!_rootFrame) { return false; } x += _lastOffsetX; y += _lastOffsetY; BaseRenderer3D *renderer = _game->_renderer3D; if (!_game->_renderer3D->_camera) return true; float resWidth, resHeight; float layerWidth, layerHeight; float modWidth, modHeight; bool customViewport; _game->_renderer3D->getProjectionParams(&resWidth, &resHeight, &layerWidth, &layerHeight, &modWidth, &modHeight, &customViewport); x -= _drawingViewport._x + modWidth; y -= _drawingViewport._y + modHeight; if (customViewport) { x += renderer->_drawOffsetX; y += renderer->_drawOffsetY; } DXVector3 pickRayDir; DXVector3 pickRayOrig; // Compute the vector of the pick ray in screen space DXVector3 vec; vec._x = (((2.0f * x) / (_drawingViewport._width)) - 1) / _lastProjMat.matrix._11; vec._y = -(((2.0f * y) / (_drawingViewport._height)) - 1) / _lastProjMat.matrix._22; vec._z = 1.0f; // Get the inverse view matrix DXMatrix m; DXMatrixInverse(&m, nullptr, &_lastViewMat); // Transform the screen space pick ray into 3D space pickRayDir._x = vec._x * m.matrix._11 + vec._y * m.matrix._21 + vec._z * m.matrix._31; pickRayDir._y = vec._x * m.matrix._12 + vec._y * m.matrix._22 + vec._z * m.matrix._32; pickRayDir._z = vec._x * m.matrix._13 + vec._y * m.matrix._23 + vec._z * m.matrix._33; pickRayOrig._x = m.matrix._41; pickRayOrig._y = m.matrix._42; pickRayOrig._z = m.matrix._43; // transform to model space DXVector3 end = pickRayOrig + pickRayDir; DXMatrixInverse(&m, nullptr, &_lastWorldMat); DXVec3TransformCoord(&pickRayOrig, &pickRayOrig, &m); DXVec3TransformCoord(&end, &end, &m); pickRayDir = end - pickRayOrig; return !_rootFrame->pickPoly(&pickRayOrig, &pickRayDir); } ////////////////////////////////////////////////////////////////////////// void XModel::updateBoundingRect() { _BBoxStart = _BBoxEnd = DXVector3(0, 0, 0); if (_rootFrame) { _rootFrame->getBoundingBox(&_BBoxStart, &_BBoxEnd); } _boundingRect.left = _boundingRect.top = INT_MAX_VALUE; _boundingRect.right = _boundingRect.bottom = INT_MIN_VALUE; BaseRenderer3D *renderer = _game->_renderer3D; DXMatrix viewMat, projMat, worldMat; DXVector3 vec2d(0, 0, 0); renderer->getViewTransform(&viewMat); renderer->getProjectionTransform(&projMat); renderer->getWorldTransform(&worldMat); _drawingViewport = renderer->getViewPort(); float x1 = _BBoxStart._x; float x2 = _BBoxEnd._x; float y1 = _BBoxStart._y; float y2 = _BBoxEnd._y; float z1 = _BBoxStart._z; float z2 = _BBoxEnd._z; DXVector3 v111(x1 ,y1, z1); DXVec3Project(&vec2d, &v111, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v211(x2, y1, z1); DXVec3Project(&vec2d, &v211, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v112(x1, y1, z2); DXVec3Project(&vec2d, &v112, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v212(x2, y1, z2); DXVec3Project(&vec2d, &v212, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v121(x1, y2, z1); DXVec3Project(&vec2d, &v121, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v221(x2, y2, z1); DXVec3Project(&vec2d, &v221, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v122(x1, y2, z2); DXVec3Project(&vec2d, &v122, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); DXVector3 v222(x2, y2, z2); DXVec3Project(&vec2d, &v222, &_drawingViewport, &projMat, &viewMat, &worldMat); updateRect(&_boundingRect, &vec2d); _boundingRect.left -= renderer->_drawOffsetX; _boundingRect.right -= renderer->_drawOffsetX; _boundingRect.bottom -= renderer->_drawOffsetY; _boundingRect.top -= renderer->_drawOffsetY; } ////////////////////////////////////////////////////////////////////////// void XModel::updateRect(Common::Rect32 *rc, DXVector3 *vec) { rc->left = MIN(rc->left, (int32)vec->_x); rc->right = MAX(rc->right, (int32)vec->_x); rc->top = MIN(rc->top, (int32)vec->_y); rc->bottom = MAX(rc->bottom, (int32)vec->_y); } ////////////////////////////////////////////////////////////////////////// AnimationSet *XModel::getAnimationSetByName(const char *name) { for (int32 i = 0; i < _animationSets.getSize(); i++) { if (scumm_stricmp(name, _animationSets[i]->_name) == 0) { return _animationSets[i]; } } return nullptr; } TOKEN_DEF_START TOKEN_DEF(NAME) TOKEN_DEF(LOOPING) TOKEN_DEF(EVENT) TOKEN_DEF(FRAME) TOKEN_DEF_END ////////////////////////////////////////////////////////////////////////// bool XModel::parseAnim(char *buffer) { TOKEN_TABLE_START(commands) TOKEN_TABLE(NAME) TOKEN_TABLE(LOOPING) TOKEN_TABLE(EVENT) TOKEN_TABLE_END char *params; int cmd; BaseParser parser(_game); char *name = nullptr; bool looping = false; bool loopingSet = false; while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) { switch (cmd) { case TOKEN_NAME: { BaseUtils::setString(&name, params); AnimationSet *anim = getAnimationSetByName(name); if (!anim) { _game->LOG(0, "Error: Animation '%s' cannot be found in the model.", name); } break; } case TOKEN_LOOPING: parser.scanStr(params, "%b", &looping); loopingSet = true; break; case TOKEN_EVENT: if (!name) { _game->LOG(0, "Error: NAME filed must precede any EVENT fields in actor definition files."); } else { AnimationSet *anim = getAnimationSetByName(name); if (anim) parseEvent(anim, params); } break; } } if (cmd != PARSERR_EOF) { return false; } bool ret = true; if (name) { AnimationSet *anim = getAnimationSetByName(name); if (anim) { if (loopingSet) anim->_looping = looping; } } delete[] name; return ret; } ////////////////////////////////////////////////////////////////////////// bool XModel::parseEvent(AnimationSet *anim, char *buffer) { TOKEN_TABLE_START(commands) TOKEN_TABLE(NAME) TOKEN_TABLE(FRAME) TOKEN_TABLE_END char *params; int cmd; BaseParser parser(_game); AnimationSet::AnimationEvent *event = new AnimationSet::AnimationEvent(); if (!event) { return false; } while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) { switch (cmd) { case TOKEN_NAME: BaseUtils::setString(&event->_eventName, params); break; case TOKEN_FRAME: parser.scanStr(params, "%d", &event->_frame); break; } } if (cmd != PARSERR_EOF) { SAFE_DELETE(event); return false; } if (event->_eventName) { anim->addEvent(event); } else { SAFE_DELETE(event); } return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::setMaterialSprite(const char *materialName, const char *spriteFilename) { if (!materialName || !spriteFilename) { return false; } if (!_rootFrame) { return false; } BaseSprite *sprite = new BaseSprite(_game); if (!sprite || !sprite->loadFile(spriteFilename)) { SAFE_DELETE(sprite); return false; } XModelMatSprite *matSprite = nullptr; for (int32 i = 0; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { matSprite = _matSprites[i]; break; } } if (matSprite) { matSprite->setSprite(sprite); } else { matSprite = new XModelMatSprite(materialName, sprite); _matSprites.add(matSprite); } _rootFrame->setMaterialSprite(matSprite->_matName, matSprite->_sprite); return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::setMaterialTheora(const char *materialName, const char *theoraFilename) { if (!materialName || !theoraFilename) { return false; } if (!_rootFrame) { return false; } VideoTheoraPlayer *theora = new VideoTheoraPlayer(_game); if (!theora || theora->initialize(theoraFilename)) { SAFE_DELETE(theora); return false; } theora->play(VID_PLAY_POS, 0, 0, false, false, true); XModelMatSprite *matSprite = nullptr; for (int32 i = 0; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { matSprite = _matSprites[i]; break; } } if (matSprite) { matSprite->setTheora(theora); } else { matSprite = new XModelMatSprite(materialName, theora); _matSprites.add(matSprite); } _rootFrame->setMaterialTheora(matSprite->_matName, matSprite->_theora); return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::setMaterialEffect(const char *materialName, const char *effectFilename) { if (!materialName || !effectFilename) return false; if (!_rootFrame) return false; Effect3D *effect = new Effect3D(_game); if (!effect->createFromFile(effectFilename)) { SAFE_DELETE(effect); return false; } XModelMatSprite *matSprite = nullptr; for (int32 i = 0 ; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { matSprite = _matSprites[i]; break; } } if (matSprite) { matSprite->setEffect(effect); } else { matSprite = new XModelMatSprite(materialName, effect); _matSprites.add(matSprite); } _rootFrame->setMaterialEffect(matSprite->_matName, matSprite->_effect, matSprite->_effectParams); return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::removeMaterialEffect(const char *materialName) { if (!materialName) return false; if (!_rootFrame) return false; for (int32 i = 0; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { SAFE_DELETE(_matSprites[i]); _matSprites.removeAt(i); _rootFrame->removeMaterialEffect(materialName); return true; } } return false; } ////////////////////////////////////////////////////////////////////////// bool XModel::setMaterialEffectParam(const char *materialName, const char *paramName, ScValue *val) { if (!materialName) return false; if (!_rootFrame) return false; for (int32 i = 0 ; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { if (_matSprites[i]->_effectParams) { _matSprites[i]->_effectParams->setParam(paramName, val); return true; } else return false; } } return false; } ////////////////////////////////////////////////////////////////////////// bool XModel::setMaterialEffectParam(const char *materialName, const char *paramName, DXVector4 val) { if (!materialName) return false; if (!_rootFrame) return false; for (int32 i = 0; i < _matSprites.getSize(); i++) { if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) { if (_matSprites[i]->_effectParams) { _matSprites[i]->_effectParams->setParam(paramName, val); return true; } else return false; } } return false; } ////////////////////////////////////////////////////////////////////////// bool XModel::initializeSimple() { if (!_rootFrame) { return false; } // init after load for (int32 i = 0; i < _matSprites.getSize(); i++) { if (_matSprites[i]->_theora) { _rootFrame->setMaterialTheora(_matSprites[i]->_matName, _matSprites[i]->_theora); } else if (_matSprites[i]->_sprite) { _rootFrame->setMaterialSprite(_matSprites[i]->_matName, _matSprites[i]->_sprite); } if (_matSprites[i]->_effectFile) { Effect3D *effect = new Effect3D(_game); if (effect->createFromFile(_matSprites[i]->_effectFile)) { _matSprites[i]->_effect = effect; _rootFrame->setMaterialEffect(_matSprites[i]->_matName, _matSprites[i]->_effect, _matSprites[i]->_effectParams); } else { SAFE_DELETE(effect); } } } if (_parentModel) { findBones(false, _parentModel); } return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::persist(BasePersistenceManager *persistMgr) { BaseObject::persist(persistMgr); persistMgr->transferVector3d(TMEMBER(_BBoxStart)); persistMgr->transferVector3d(TMEMBER(_BBoxEnd)); persistMgr->transferRect32(TMEMBER(_boundingRect)); if (!persistMgr->getIsSaving()) { memset(&_drawingViewport, 0, sizeof(DXViewport)); } persistMgr->transferSint32(TMEMBER(_lastOffsetX)); persistMgr->transferSint32(TMEMBER(_lastOffsetY)); persistMgr->transferMatrix4(TMEMBER(_lastProjMat)); persistMgr->transferMatrix4(TMEMBER(_lastViewMat)); persistMgr->transferMatrix4(TMEMBER(_lastWorldMat)); persistMgr->transferPtr(TMEMBER(_owner)); _mergedModels.persist(persistMgr); // load model if (!persistMgr->getIsSaving()) { for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; i++) { _channels[i] = nullptr; } _rootFrame = nullptr; if (_filename && _filename[0]) { loadFromFile(_filename); } for (int32 i = 0; i < _mergedModels.getSize(); ++i) { mergeFromFile(_mergedModels[i]); } } persistMgr->transferPtr(TMEMBER(_parentModel)); // animation properties int32 numAnims; if (persistMgr->getIsSaving()) { numAnims = _animationSets.getSize(); } persistMgr->transferSint32(TMEMBER(numAnims)); if (persistMgr->getIsSaving()) { for (int32 i = 0; i < _animationSets.getSize(); i++) { persistMgr->transferCharPtr(TMEMBER(_animationSets[i]->_name)); _animationSets[i]->persist(persistMgr); } } else { for (int i = 0; i < numAnims; i++) { bool needsDelete = false; char *animName; persistMgr->transferCharPtr(TMEMBER(animName)); AnimationSet *animSet = getAnimationSetByName(animName); if (!animSet) { animSet = new AnimationSet(_game, this); needsDelete = true; } animSet->persist(persistMgr); if (needsDelete) { delete animSet; } delete[] animName; } } // persist channels for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; i++) { _channels[i]->persist(persistMgr); } // persist material sprites int32 numMatSprites; if (persistMgr->getIsSaving()) { numMatSprites = _matSprites.getSize(); } persistMgr->transferSint32(TMEMBER(numMatSprites)); for (int i = 0; i < numMatSprites; i++) { if (persistMgr->getIsSaving()) { _matSprites[i]->persist(persistMgr); } else { XModelMatSprite *MatSprite = new XModelMatSprite(); MatSprite->persist(persistMgr); _matSprites.add(MatSprite); } } return true; } ////////////////////////////////////////////////////////////////////////// bool XModel::invalidateDeviceObjects() { if (_rootFrame) { return _rootFrame->invalidateDeviceObjects(); } else { return false; } } ////////////////////////////////////////////////////////////////////////// bool XModel::restoreDeviceObjects() { if (_rootFrame) { return _rootFrame->restoreDeviceObjects(); } else { return false; } } ////////////////////////////////////////////////////////////////////////// bool XModel::unloadAnimation(const char *animName) { bool found = false; for (int32 i = 0; i < _animationSets.getSize(); i++) { if (scumm_stricmp(animName, _animationSets[i]->_name) == 0) { for (int32 j = 0; j < X_NUM_ANIMATION_CHANNELS; j++) { if (_channels[j]) _channels[j]->unloadAnim(_animationSets[i]); } found = true; SAFE_DELETE(_animationSets[i]); _animationSets.removeAt(i); i++; } } return found; } } // namespace Wintermute