//-------------------------------------------------------------------------- // PathedInterior.cc: // // //-------------------------------------------------------------------------- #include "interior/pathedInterior.h" #include "core/stream.h" #include "console/consoleTypes.h" #include "dgl/dgl.h" #include "sceneGraph/sceneState.h" #include "math/mathIO.h" #include "core/bitStream.h" #include "interior/interior.h" #include "sim/simPath.h" #include "sim/pathManager.h" #include "core/frameAllocator.h" #include "sceneGraph/sceneGraph.h" IMPLEMENT_CO_NETOBJECT_V1(PathedInterior); IMPLEMENT_CO_DATABLOCK_V1(PathedInteriorData); //-------------------------------------------------------------------------- PathedInteriorData::PathedInteriorData() { for(U32 i = 0; i < MaxSounds; i++) sound[i] = NULL; } void PathedInteriorData::initPersistFields() { addField("StartSound", TypeAudioProfilePtr, Offset(sound[StartSound], PathedInteriorData)); addField("SustainSound", TypeAudioProfilePtr, Offset(sound[SustainSound], PathedInteriorData)); addField("StopSound", TypeAudioProfilePtr, Offset(sound[StopSound], PathedInteriorData)); Parent::initPersistFields(); } void PathedInteriorData::packData(BitStream *stream) { for (S32 i = 0; i < MaxSounds; i++) { if (stream->writeFlag(sound[i])) stream->writeRangedU32(packed? SimObjectId(sound[i]): sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); } Parent::packData(stream); } void PathedInteriorData::unpackData(BitStream* stream) { for (S32 i = 0; i < MaxSounds; i++) { sound[i] = NULL; if (stream->readFlag()) sound[i] = (AudioProfile*)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); } Parent::unpackData(stream); } bool PathedInteriorData::preload(bool server, char errorBuffer[256]) { if(!Parent::preload(server, errorBuffer)) return false; // Resolve objects transmitted from server if (!server) { for (S32 i = 0; i < MaxSounds; i++) if (sound[i]) Sim::findObject(SimObjectId(sound[i]),sound[i]); } return true; } PathedInterior::PathedInterior() { mNetFlags.set(Ghostable); mTypeMask = InteriorObjectType; mCurrentPosition = 0; mTargetPosition = 0; mPathKey = 0xFFFFFFFF; mStopped = false; mSustainHandle = 0; } PathedInterior::~PathedInterior() { // } PathedInterior *PathedInterior::mClientPathedInteriors = NULL; //-------------------------------------------------------------------------- void PathedInterior::initPersistFields() { Parent::initPersistFields(); addField("interiorResource", TypeFilename, Offset(mInteriorResName, PathedInterior)); addField("interiorIndex", TypeS32, Offset(mInteriorResIndex, PathedInterior)); addField("basePosition", TypeMatrixPosition, Offset(mBaseTransform, PathedInterior)); addField("baseRotation", TypeMatrixRotation, Offset(mBaseTransform, PathedInterior)); addField("baseScale", TypePoint3F, Offset(mBaseScale, PathedInterior)); } //-------------------------------------------------------------------------- bool PathedInterior::onAdd() { if(!Parent::onAdd()) return false; // Load the interior resource and extract the interior that is us. char buffer[256]; mInteriorRes = ResourceManager->load(mInteriorResName); if (bool(mInteriorRes) == false) return false; mInterior = mInteriorRes->getSubObject(mInteriorResIndex); if (mInterior == NULL) return false; // Setup bounding information mObjBox = mInterior->getBoundingBox(); resetWorldBox(); setScale(mBaseScale); setTransform(mBaseTransform); if (isClientObject()) { mNextClientPI = mClientPathedInteriors; mClientPathedInteriors = this; mInterior->prepForRendering(mInteriorRes.getFilePath()); // gInteriorLMManager.addInstance(mInterior->getLMHandle(), mLMHandle, NULL, this); } if(isClientObject()) { Point3F initialPos; mBaseTransform.getColumn(3, &initialPos); Point3F pathPos; //gClientPathManager->getPathPosition(mPathKey, 0, pathPos); mOffset = initialPos - pathPos; //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos); MatrixF mat = getTransform(); mat.setColumn(3, pathPos + mOffset); setTransform(mat); } addToScene(); return true; } bool PathedInterior::onSceneAdd(SceneGraph *g) { if(!Parent::onSceneAdd(g)) return false; return true; } void PathedInterior::onSceneRemove() { Parent::onSceneRemove(); } bool PathedInterior::onNewDataBlock(GameBaseData* dptr) { mDataBlock = dynamic_cast(dptr); return Parent::onNewDataBlock(dptr); } void PathedInterior::onRemove() { if(isClientObject()) { if(mSustainHandle) { alxStop(mSustainHandle); mSustainHandle = 0; } PathedInterior **walk = &mClientPathedInteriors; while(*walk) { if(*walk == this) { *walk = mNextClientPI; break; } walk = &((*walk)->mNextClientPI); } /* if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF) { if (mInterior->getLMHandle() != 0xFFFFFFFF) gInteriorLMManager.removeInstance(mInterior->getLMHandle(), mLMHandle); }*/ } removeFromScene(); Parent::onRemove(); } //------------------------------------------------------------------------------ bool PathedInterior::buildPolyList(AbstractPolyList* list, const Box3F& wsBox, const SphereF&) { if (bool(mInteriorRes) == false) return false; // Setup collision state data list->setTransform(&getTransform(), getScale()); list->setObject(this); return mInterior->buildPolyList(list, wsBox, mWorldToObj, getScale()); } //-------------------------------------------------------------------------- bool PathedInterior::prepRenderImage(SceneState* state, const U32 stateKey, const U32 /*startZone*/, const bool /*modifyBaseState*/) { if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if (mPathKey == Path::NoPathIndex) return false; // This should be sufficient for most objects that don't manage zones, and // don't need to return a specialized RenderImage... bool render = false; for (U32 i = 0; i < getNumCurrZones(); i++) if (state->getZoneState(getCurrZone(i)).render == true) render = true; if (render == true) { SceneRenderImage* image = new SceneRenderImage; image->obj = this; state->insertRenderImage(image); } return false; } extern ColorF gInteriorFogColor; void PathedInterior::renderObject(SceneState* state, SceneRenderImage*) { U32 storedWaterMark = FrameAllocator::getWaterMark(); Point3F worldOrigin; getRenderTransform().getColumn(3, &worldOrigin); // Set up the model view and the global render state... glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); ColorF oneColor(1,1,1,1); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, oneColor); gClientSceneGraph->getLightManager()->sgSetupLights(this); // dglMultMatrix(&newMat); dglMultMatrix(&mRenderObjToWorld); glScalef(mObjScale.x, mObjScale.y, mObjScale.z); glEnable(GL_CULL_FACE); // We need to decide if we're going to use the low-res textures //mInterior->setupFog(state); //gInteriorFogColor = state->getFogColor(); mInterior->renderAsShape(); //mInterior->clearFog(); gClientSceneGraph->getLightManager()->sgResetLights(); glDisable(GL_CULL_FACE); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit"); FrameAllocator::setWaterMark(storedWaterMark); dglSetRenderPrimType(0); // Reset the small textures... TextureManager::setSmallTexturesActive(false); } void PathedInterior::resolvePathKey() { if(mPathKey == 0xFFFFFFFF && !isGhost()) { mPathKey = getPathKey(); Point3F pathPos; Point3F initialPos; mBaseTransform.getColumn(3, &initialPos); //gServerPathManager->getPathPosition(mPathKey, 0, pathPos); mOffset = initialPos - pathPos; } } //-------------------------------------------------------------------------- U32 PathedInterior::packUpdate(NetConnection* con, U32 mask, BitStream* stream) { U32 retMask = Parent::packUpdate(con, mask, stream); resolvePathKey(); if (stream->writeFlag(mask & InitialUpdateMask)) { // Inital update... stream->writeString(mInteriorResName); stream->write(mInteriorResIndex); stream->writeAffineTransform(mBaseTransform); mathWrite(*stream, mBaseScale); stream->write(mPathKey); } if(stream->writeFlag((mask & NewPositionMask) && mPathKey != Path::NoPathIndex)) stream->writeInt(S32(mCurrentPosition), gServerPathManager->getPathTimeBits(mPathKey)); if(stream->writeFlag((mask & NewTargetMask) && mPathKey != Path::NoPathIndex)) { if(stream->writeFlag(mTargetPosition < 0)) { stream->writeFlag(mTargetPosition == -1); } else stream->writeInt(S32(mTargetPosition), gServerPathManager->getPathTimeBits(mPathKey)); } return retMask; } void PathedInterior::unpackUpdate(NetConnection* con, BitStream* stream) { Parent::unpackUpdate(con, stream); MatrixF tempXForm; Point3F tempScale; if (stream->readFlag()) { // Initial mInteriorResName = stream->readSTString(); stream->read(&mInteriorResIndex); stream->readAffineTransform(&tempXForm); mathRead(*stream, &tempScale); mBaseTransform = tempXForm; mBaseScale = tempScale; stream->read(&mPathKey); } if(stream->readFlag()) { Point3F pathPos; mCurrentPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey)); if(isProperlyAdded()) { //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos); MatrixF mat = getTransform(); mat.setColumn(3, pathPos + mOffset); setTransform(mat); } } if(stream->readFlag()) { if(stream->readFlag()) { mTargetPosition = stream->readFlag() ? -1 : -2; } else mTargetPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey)); } } void PathedInterior::processTick(const Move* move) { if(isServerObject()) { U32 timeMs = 32; if(mCurrentPosition != mTargetPosition) { S32 delta; if(mTargetPosition == -1) delta = timeMs; else if(mTargetPosition == -2) delta = -timeMs; else { delta = mTargetPosition - mCurrentPosition; if(delta < -timeMs) delta = -timeMs; else if(delta > timeMs) delta = timeMs; } mCurrentPosition += delta; U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey); while(mCurrentPosition > totalTime) mCurrentPosition -= totalTime; while(mCurrentPosition < 0) mCurrentPosition += totalTime; } } } void PathedInterior::computeNextPathStep(U32 timeDelta) { S32 timeMs = timeDelta; mStopped = false; if(mCurrentPosition == mTargetPosition) { mExtrudedBox = getWorldBox(); mCurrentVelocity.set(0,0,0); } else { S32 delta; if(mTargetPosition < 0) { if(mTargetPosition == -1) delta = timeMs; else if(mTargetPosition == -2) delta = -timeMs; mCurrentPosition += delta; U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey); while(mCurrentPosition >= totalTime) mCurrentPosition -= totalTime; while(mCurrentPosition < 0) mCurrentPosition += totalTime; } else { delta = mTargetPosition - mCurrentPosition; if(delta < -timeMs) delta = -timeMs; else if(delta > timeMs) delta = timeMs; mCurrentPosition += delta; } Point3F curPoint; Point3F newPoint = Point3F(0,0,0); //BJGNOTE - this shuts up the compiler, three lines down will actually set it. MatrixF mat = getTransform(); mat.getColumn(3, &curPoint); //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint); newPoint += mOffset; Point3F displaceDelta = newPoint - curPoint; mExtrudedBox = getWorldBox(); if(displaceDelta.x < 0) mExtrudedBox.min.x += displaceDelta.x; else mExtrudedBox.max.x += displaceDelta.x; if(displaceDelta.y < 0) mExtrudedBox.min.y += displaceDelta.y; else mExtrudedBox.max.y += displaceDelta.y; if(displaceDelta.z < 0) mExtrudedBox.min.z += displaceDelta.z; else mExtrudedBox.max.z += displaceDelta.z; mCurrentVelocity = displaceDelta * 1000 / F32(timeDelta); } Point3F pos; mExtrudedBox.getCenter(&pos); MatrixF mat = getTransform(); mat.setColumn(3, pos); if(!mSustainHandle && mDataBlock->sound[PathedInteriorData::SustainSound]) { //F32 volSave = mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume; //mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume = 0; mSustainHandle = alxPlay(mDataBlock->sound[PathedInteriorData::SustainSound], &mat); //mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume = volSave; } if(mSustainHandle) { Point3F pos; mExtrudedBox.getCenter(&pos); MatrixF mat = getTransform(); mat.setColumn(3, pos); alxSourceMatrixF(mSustainHandle, &mat); } } Point3F PathedInterior::getVelocity() { return mCurrentVelocity; } void PathedInterior::advance(F64 timeDelta) { if(mStopped) return; F64 timeMs = timeDelta; if(mCurrentVelocity.len() == 0) { // if(mSustainHandle) // { // alxStop(mSustainHandle); // mSustainHandle = 0; // } return; } MatrixF mat = getTransform(); Point3F newPoint; mat.getColumn(3, &newPoint); newPoint += mCurrentVelocity * timeDelta / 1000.0f; //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint); mat.setColumn(3, newPoint);// + mOffset); setTransform(mat); setRenderTransform(mat); } U32 PathedInterior::getPathKey() { AssertFatal(isServerObject(), "Error, must be a server object to call this..."); SimGroup* myGroup = getGroup(); AssertFatal(myGroup != NULL, "No group for this object?"); for (SimGroup::iterator itr = myGroup->begin(); itr != myGroup->end(); itr++) { Path* pPath = dynamic_cast(*itr); if (pPath != NULL) { U32 pathKey = pPath->getPathIndex(); AssertFatal(pathKey != Path::NoPathIndex, "Error, path must have event over at this point..."); return pathKey; } } return Path::NoPathIndex; } void PathedInterior::setPathPosition(S32 newPosition) { resolvePathKey(); if(newPosition < 0) newPosition = 0; if(newPosition > gServerPathManager->getPathTotalTime(mPathKey)) newPosition = gServerPathManager->getPathTotalTime(mPathKey); mCurrentPosition = mTargetPosition = newPosition; setMaskBits(NewPositionMask | NewTargetMask); } void PathedInterior::setTargetPosition(S32 newPosition) { resolvePathKey(); if(newPosition < -2) newPosition = 0; if(newPosition > S32(gServerPathManager->getPathTotalTime(mPathKey))) newPosition = gServerPathManager->getPathTotalTime(mPathKey); if(mTargetPosition != newPosition) { mTargetPosition = newPosition; setMaskBits(NewTargetMask); } } ConsoleMethod(PathedInterior, setPathPosition, void, 3, 3, "") { object->setPathPosition(dAtoi(argv[2])); } ConsoleMethod(PathedInterior, setTargetPosition, void, 3, 3, "") { object->setTargetPosition(dAtoi(argv[2])); } //-------------------------------------------------------------------------- bool PathedInterior::readPI(Stream& stream) { mName = stream.readSTString(); mInteriorResName = stream.readSTString(); stream.read(&mInteriorResIndex); stream.read(&mPathIndex); mathRead(stream, &mOffset); U32 numTriggers; stream.read(&numTriggers); mTriggers.setSize(numTriggers); for (U32 i = 0; i < mTriggers.size(); i++) mTriggers[i] = stream.readSTString(); return (stream.getStatus() == Stream::Ok); } bool PathedInterior::writePI(Stream& stream) const { stream.writeString(mName); stream.writeString(mInteriorResName); stream.write(mInteriorResIndex); stream.write(mPathIndex); mathWrite(stream, mOffset); stream.write(mTriggers.size()); for (U32 i = 0; i < mTriggers.size(); i++) stream.writeString(mTriggers[i]); return (stream.getStatus() == Stream::Ok); } PathedInterior* PathedInterior::clone() const { PathedInterior* pClone = new PathedInterior; pClone->mName = mName; pClone->mInteriorResName = mInteriorResName; pClone->mInteriorResIndex = mInteriorResIndex; pClone->mPathIndex = mPathIndex; pClone->mOffset = mOffset; return pClone; }