//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames // Copyright (C) 2015 Faust Logic, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "afx/arcaneFX.h" #include "console/engineAPI.h" #include "T3D/gameBase/gameConnection.h" #include "math/mathIO.h" #include "console/compiler.h" #include "afx/afxConstraint.h" #include "afx/afxChoreographer.h" #include "afx/afxEffectWrapper.h" #include "afx/util/afxParticlePool.h" #include "afx/forces/afxForceSet.h" //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// IMPLEMENT_CO_DATABLOCK_V1(afxChoreographerData); ConsoleDocClass( afxChoreographerData, "@brief Datablock base class used by choreographers.\n\n" "@ingroup afxChoreographers\n" "@ingroup AFX\n" "@ingroup Datablocks\n" ); afxChoreographerData::afxChoreographerData() { exec_on_new_clients = false; echo_packet_usage = 100; client_script_file = ST_NULLSTRING; client_init_func = ST_NULLSTRING; } afxChoreographerData::afxChoreographerData(const afxChoreographerData& other, bool temp_clone) : GameBaseData(other, temp_clone) { exec_on_new_clients = other.exec_on_new_clients; echo_packet_usage = other.echo_packet_usage; client_script_file = other.client_script_file; client_init_func = other.client_init_func; } #define myOffset(field) Offset(field, afxChoreographerData) void afxChoreographerData::initPersistFields() { addField("execOnNewClients", TypeBool, myOffset(exec_on_new_clients), "..."); addField("echoPacketUsage", TypeS8, myOffset(echo_packet_usage), "..."); addField("clientScriptFile", TypeFilename, myOffset(client_script_file), "..."); addField("clientInitFunction", TypeString, myOffset(client_init_func), "..."); Parent::initPersistFields(); } void afxChoreographerData::packData(BitStream* stream) { Parent::packData(stream); stream->write(exec_on_new_clients); stream->write(echo_packet_usage); stream->writeString(client_script_file); stream->writeString(client_init_func); } void afxChoreographerData::unpackData(BitStream* stream) { Parent::unpackData(stream); stream->read(&exec_on_new_clients); stream->read(&echo_packet_usage); client_script_file = stream->readSTString(); client_init_func = stream->readSTString(); } bool afxChoreographerData::preload(bool server, String &errorStr) { if (!Parent::preload(server, errorStr)) return false; if (!server && client_script_file != ST_NULLSTRING) { Compiler::gSyntaxError = false; Con::evaluate(avar("exec(\"%s\");", client_script_file), false, 0); if (Compiler::gSyntaxError) { Con::errorf("afxChoreographerData: failed to exec clientScriptFile \"%s\" -- syntax error", client_script_file); Compiler::gSyntaxError = false; } } return true; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// IMPLEMENT_CO_DATABLOCK_V1(afxChoreographer); ConsoleDocClass( afxChoreographer, "@brief Base class used by choreographers.\n\n" "@ingroup afxChoreographers\n" "@ingroup AFX\n" ); afxChoreographer::afxChoreographer() { datablock = 0; // datablock initializer (union these?) exeblock = 0; // object to use in executef callouts // create the constraint manager constraint_mgr = new afxConstraintMgr(); ranking = 0; lod = 0; exec_conds_mask = 0; choreographer_id = 0; mExtra = 0; started_with_newop = false; postpone_activation = false; remapped_cons_sent = false; // CONSTRAINT REMAPPING dyn_cons_defs = &dc_defs_a; dyn_cons_defs2 = &dc_defs_b; proc_after_obj = 0; trigger_mask = 0; force_set_mgr = 0; mOrderGUID = 0x0FFFFFFF; } afxChoreographer::~afxChoreographer() { explicit_clients.clear(); for (S32 i = 0; i < dyn_cons_defs->size(); i++) { if ((*dyn_cons_defs)[i].cons_type == POINT_CONSTRAINT && (*dyn_cons_defs)[i].cons_obj.point != NULL) delete (*dyn_cons_defs)[i].cons_obj.point; else if ((*dyn_cons_defs)[i].cons_type == TRANSFORM_CONSTRAINT && (*dyn_cons_defs)[i].cons_obj.xfm != NULL) delete (*dyn_cons_defs)[i].cons_obj.xfm; } constraint_mgr->clearAllScopeableObjs(); delete constraint_mgr; delete force_set_mgr; } void afxChoreographer::initPersistFields() { // conditionals addField("extra", TYPEID(), Offset(mExtra, afxChoreographer), "..."); addField("postponeActivation", TypeBool, Offset(postpone_activation, afxChoreographer), "..."); Parent::initPersistFields(); } bool afxChoreographer::onAdd() { if (!Parent::onAdd()) return(false); if (isServerObject()) choreographer_id = arcaneFX::registerChoreographer(this); else { if (proc_after_obj) { processAfter(proc_after_obj); proc_after_obj = 0; } force_set_mgr = new afxForceSetMgr(); if (datablock && datablock->client_init_func != ST_NULLSTRING) Con::executef(datablock->client_init_func, getIdString()); } return(true); } void afxChoreographer::onRemove() { for (S32 i = 0; i < particle_pools.size(); i++) if (particle_pools[i]) particle_pools[i]->setChoreographer(0); particle_pools.clear(); if (isServerObject()) arcaneFX::unregisterChoreographer(this); else arcaneFX::unregisterClientChoreographer(this); choreographer_id = 0; Parent::onRemove(); } void afxChoreographer::onDeleteNotify(SimObject* obj) { if (dynamic_cast(obj)) { SceneObject* scn_obj = (SceneObject*)(obj); for (S32 i = 0; i < dyn_cons_defs->size(); i++) if ((*dyn_cons_defs)[i].cons_type != OBJECT_CONSTRAINT || (*dyn_cons_defs)[i].cons_obj.object != scn_obj) dyn_cons_defs2->push_back((*dyn_cons_defs)[i]); Vector* tmp = dyn_cons_defs; dyn_cons_defs = dyn_cons_defs2; dyn_cons_defs2 = tmp; dyn_cons_defs2->clear(); if (isServerObject() && scn_obj->isScopeable()) constraint_mgr->removeScopeableObject(scn_obj); } else if (dynamic_cast(obj)) { removeExplicitClient((NetConnection*)obj); } Parent::onDeleteNotify(obj); } bool afxChoreographer::onNewDataBlock(GameBaseData* dptr, bool reload) { datablock = dynamic_cast(dptr); if (!datablock || !Parent::onNewDataBlock(dptr, reload)) return false; exeblock = datablock; return true; } void afxChoreographer::pack_constraint_info(NetConnection* conn, BitStream* stream) { stream->write(dyn_cons_defs->size()); for (S32 i = 0; i < dyn_cons_defs->size(); i++) { if ((*dyn_cons_defs)[i].cons_type == OBJECT_CONSTRAINT && (*dyn_cons_defs)[i].cons_obj.object != NULL) { stream->writeString((*dyn_cons_defs)[i].cons_name); stream->write((*dyn_cons_defs)[i].cons_type); SceneObject* object = (*dyn_cons_defs)[i].cons_obj.object; S32 ghost_idx = conn->getGhostIndex(object); if (stream->writeFlag(ghost_idx != -1)) { stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount); } else { if (stream->writeFlag(object->getScopeId() > 0)) { stream->writeInt(object->getScopeId(), NetObject::SCOPE_ID_BITS); stream->writeFlag(dynamic_cast(object) != NULL); } } } else if ((*dyn_cons_defs)[i].cons_type == POINT_CONSTRAINT && (*dyn_cons_defs)[i].cons_obj.point != NULL) { stream->writeString((*dyn_cons_defs)[i].cons_name); stream->write((*dyn_cons_defs)[i].cons_type); mathWrite(*stream, *(*dyn_cons_defs)[i].cons_obj.point); } else if ((*dyn_cons_defs)[i].cons_type == TRANSFORM_CONSTRAINT && (*dyn_cons_defs)[i].cons_obj.xfm != NULL) { stream->writeString((*dyn_cons_defs)[i].cons_name); stream->write((*dyn_cons_defs)[i].cons_type); mathWrite(*stream, *(*dyn_cons_defs)[i].cons_obj.xfm); } } constraint_mgr->packConstraintNames(conn, stream); } void afxChoreographer::unpack_constraint_info(NetConnection* conn, BitStream* stream) { S32 n_defs; stream->read(&n_defs); dyn_cons_defs->clear(); for (S32 i = 0; i < n_defs; i++) { StringTableEntry cons_name = stream->readSTString(); U8 cons_type; stream->read(&cons_type); if (cons_type == OBJECT_CONSTRAINT) { SceneObject* scn_obj = NULL; if (stream->readFlag()) { S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount); scn_obj = dynamic_cast(conn->resolveGhost(ghost_idx)); if (scn_obj) { addObjectConstraint(scn_obj, cons_name); } else Con::errorf("CANNOT RESOLVE GHOST %d %s", ghost_idx, cons_name); } else { if (stream->readFlag()) { mScope_id = stream->readInt(NetObject::SCOPE_ID_BITS); bool is_shape = stream->readFlag(); addObjectConstraint(mScope_id, cons_name, is_shape); } } } else if (cons_type == POINT_CONSTRAINT) { Point3F point; mathRead(*stream, &point); addPointConstraint(point, cons_name); } else if (cons_type == TRANSFORM_CONSTRAINT) { MatrixF xfm; mathRead(*stream, &xfm); addTransformConstraint(xfm, cons_name); } } constraint_mgr->unpackConstraintNames(stream); } void afxChoreographer::setup_dynamic_constraints() { for (S32 i = 0; i < dyn_cons_defs->size(); i++) { switch ((*dyn_cons_defs)[i].cons_type) { case OBJECT_CONSTRAINT: constraint_mgr->setReferenceObject((*dyn_cons_defs)[i].cons_name, (*dyn_cons_defs)[i].cons_obj.object); break; case POINT_CONSTRAINT: constraint_mgr->setReferencePoint((*dyn_cons_defs)[i].cons_name, *(*dyn_cons_defs)[i].cons_obj.point); break; case TRANSFORM_CONSTRAINT: constraint_mgr->setReferenceTransform((*dyn_cons_defs)[i].cons_name, *(*dyn_cons_defs)[i].cons_obj.xfm); break; case OBJECT_CONSTRAINT_SANS_OBJ: constraint_mgr->setReferenceObjectByScopeId((*dyn_cons_defs)[i].cons_name, (*dyn_cons_defs)[i].cons_obj.scope_id, false); break; case OBJECT_CONSTRAINT_SANS_SHAPE: constraint_mgr->setReferenceObjectByScopeId((*dyn_cons_defs)[i].cons_name, (*dyn_cons_defs)[i].cons_obj.scope_id, true); break; } } } afxChoreographer::dynConstraintDef* afxChoreographer::find_cons_def_by_name(const char* cons_name) { StringTableEntry cons_name_ste = StringTable->insert(cons_name); for (S32 i = 0; i < dyn_cons_defs->size(); i++) { if ((*dyn_cons_defs)[i].cons_name == cons_name_ste) return &((*dyn_cons_defs)[i]); } return 0; } void afxChoreographer::check_packet_usage(NetConnection* conn, BitStream* stream, S32 mark_stream_pos, const char* msg_tag) { S32 packed_size = stream->getCurPos() - mark_stream_pos; S32 current_headroom = stream->getMaxWriteBitNum()-(stream->getStreamSize()<<3); S32 max_headroom = stream->getMaxWriteBitNum()-(conn->getMaxRatePacketSize()<<3); if (packed_size > max_headroom) { Con::errorf("%s [%s] WARNING -- packed-bits (%d) > limit (%d) for max PacketSize settings. [%s]", msg_tag, datablock->getName(), packed_size, max_headroom, "PACKET OVERRUN POSSIBLE"); Con::errorf("getMaxRatePacketSize()=%d getCurRatePacketSize()=%d", conn->getMaxRatePacketSize(), conn->getCurRatePacketSize()); } // JTF Note: this current_headroom > 0 check is odd and occurs when object is created real early // in the startup, such as when the dead orc gets fatal damage and we try to post a text effect. else if (packed_size > current_headroom && current_headroom > 0) { Con::errorf("%s [%s] WARNING -- packed-bits (%d) > limit (%d) for current PacketSize settings. [%s]", msg_tag, datablock->getName(), packed_size, current_headroom, "PACKET OVERRUN POSSIBLE"); } else { F32 percentage = 100.0f*((F32)packed_size/(F32)max_headroom); if (percentage >= datablock->echo_packet_usage) { Con::warnf("%s [%s] -- packed-bits (%d) < limit (%d). [%.1f%% full]", msg_tag, datablock->getName(), packed_size, max_headroom, percentage); } } } SceneObject* afxChoreographer::get_camera(Point3F* cam_pos) const { GameConnection* conn = GameConnection::getConnectionToServer(); if (!conn) { if (cam_pos) cam_pos->zero(); return 0; } SceneObject* cam_obj = conn->getCameraObject(); if (cam_pos) { if (cam_obj) { MatrixF cam_mtx; conn->getControlCameraTransform(0, &cam_mtx); cam_mtx.getColumn(3, cam_pos); } else cam_pos->zero(); } return cam_obj; } U32 afxChoreographer::packUpdate(NetConnection* conn, U32 mask, BitStream* stream) { U32 retMask = Parent::packUpdate(conn, mask, stream); if (stream->writeFlag(mask & InitialUpdateMask)) //-- INITIAL UPDATE ? { stream->write(ranking); stream->write(lod); stream->write(exec_conds_mask); stream->write(choreographer_id); // write dynamic fields beginning with arcaneFX::sParameterFieldPrefix SimFieldDictionaryIterator itr(getFieldDictionary()); SimFieldDictionary::Entry* entry; U32 prefix_len = dStrlen(arcaneFX::sParameterFieldPrefix); if (prefix_len > 0) { while ((entry = *itr) != NULL) { if (dStrncmp(entry->slotName, arcaneFX::sParameterFieldPrefix, prefix_len) == 0) { stream->writeFlag(true); stream->writeString(entry->slotName); stream->writeLongString(1023, entry->value); } ++itr; } } stream->writeFlag(false); } if (stream->writeFlag(mask & TriggerMask)) { stream->write(trigger_mask); } // CONSTRAINT REMAPPING << if (stream->writeFlag((mask & RemapConstraintMask) && !remapped_cons_defs.empty())) { remapped_cons_sent = true; //Con::errorf("PACKING CONS REMAP %d conn:%d", remapped_cons_defs.size(), (U32) conn); stream->write(remapped_cons_defs.size()); for (S32 i = 0; i < remapped_cons_defs.size(); i++) { if (remapped_cons_defs[i]->cons_type == OBJECT_CONSTRAINT && remapped_cons_defs[i]->cons_obj.object != NULL) { //Con::errorf("PACKING CONS REMAP: name %s", remapped_cons_defs[i]->cons_name); stream->writeString(remapped_cons_defs[i]->cons_name); //Con::errorf("PACKING CONS REMAP: type %d", remapped_cons_defs[i]->cons_type); stream->write(remapped_cons_defs[i]->cons_type); SceneObject* object = remapped_cons_defs[i]->cons_obj.object; S32 ghost_idx = conn->getGhostIndex(object); //Con::errorf("PACKING CONS REMAP: ghost %d", ghost_idx); if (stream->writeFlag(ghost_idx != -1)) { stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount); } else { if (stream->writeFlag(object->getScopeId() > 0)) { stream->writeInt(object->getScopeId(), NetObject::SCOPE_ID_BITS); stream->writeFlag(dynamic_cast(object) != NULL); } } } else if (remapped_cons_defs[i]->cons_type == POINT_CONSTRAINT && remapped_cons_defs[i]->cons_obj.point != NULL) { stream->writeString(remapped_cons_defs[i]->cons_name); stream->write(remapped_cons_defs[i]->cons_type); mathWrite(*stream, *remapped_cons_defs[i]->cons_obj.point); } else if (remapped_cons_defs[i]->cons_type == TRANSFORM_CONSTRAINT && remapped_cons_defs[i]->cons_obj.xfm != NULL) { stream->writeString(remapped_cons_defs[i]->cons_name); stream->write(remapped_cons_defs[i]->cons_type); mathWrite(*stream, *remapped_cons_defs[i]->cons_obj.xfm); } } } // CONSTRAINT REMAPPING >> AssertISV(stream->isValid(), "afxChoreographer::packUpdate(): write failure occurred, possibly caused by packet-size overrun."); return retMask; } void afxChoreographer::unpackUpdate(NetConnection * conn, BitStream * stream) { Parent::unpackUpdate(conn, stream); // InitialUpdate Only if (stream->readFlag()) { stream->read(&ranking); stream->read(&lod); stream->read(&exec_conds_mask); stream->read(&choreographer_id); // read dynamic fields while(stream->readFlag()) { char slotName[256]; char value[1024]; stream->readString(slotName); stream->readLongString(1023, value); setDataField(StringTable->insert(slotName), 0, value); } arcaneFX::registerClientChoreographer(this); } if (stream->readFlag()) // TriggerMask { stream->read(&trigger_mask); } // CONSTRAINT REMAPPING << if (stream->readFlag()) // RemapConstraintMask { S32 n_defs; stream->read(&n_defs); for (S32 i = 0; i < n_defs; i++) { StringTableEntry cons_name = stream->readSTString(); U8 cons_type; stream->read(&cons_type); if (cons_type == OBJECT_CONSTRAINT) { SceneObject* scn_obj = NULL; if (stream->readFlag()) { S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount); scn_obj = dynamic_cast(conn->resolveGhost(ghost_idx)); if (scn_obj) { remapObjectConstraint(scn_obj, cons_name); } else Con::errorf("CANNOT RESOLVE GHOST %d %s", ghost_idx, cons_name); } else { if (stream->readFlag()) { U16 scope_id = stream->readInt(NetObject::SCOPE_ID_BITS); bool is_shape = stream->readFlag(); remapObjectConstraint(scope_id, cons_name, is_shape); } } } else if (cons_type == POINT_CONSTRAINT) { Point3F point; mathRead(*stream, &point); remapPointConstraint(point, cons_name); } else if (cons_type == TRANSFORM_CONSTRAINT) { MatrixF xfm; mathRead(*stream, &xfm); remapTransformConstraint(xfm, cons_name); } } } // CONSTRAINT REMAPPING >> } void afxChoreographer::executeScriptEvent(const char* method, afxConstraint* cons, const MatrixF& xfm, const char* data) { SceneObject* cons_obj = (cons) ? cons->getSceneObject() : NULL; char *arg_buf = Con::getArgBuffer(256); Point3F pos; xfm.getColumn(3,&pos); AngAxisF aa(xfm); dSprintf(arg_buf,256,"%g %g %g %g %g %g %g", pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle); // CALL SCRIPT afxChoreographerData::method(%choreographer, %constraint, %transform, %data) Con::executef(exeblock, method, getIdString(), (cons_obj) ? cons_obj->getIdString() : "", arg_buf, data); } void afxChoreographer::addObjectConstraint(SceneObject* object, const char* cons_name) { if (!object || !cons_name) return; dynConstraintDef dyn_def; dyn_def.cons_name = StringTable->insert(cons_name); dyn_def.cons_type = OBJECT_CONSTRAINT; dyn_def.cons_obj.object = object; dyn_cons_defs->push_back(dyn_def); #if defined(AFX_CAP_SCOPE_TRACKING) if (isServerObject() && object->isScopeable()) constraint_mgr->addScopeableObject(object); #endif constraint_mgr->defineConstraint(OBJECT_CONSTRAINT, dyn_def.cons_name); deleteNotify(object); } void afxChoreographer::addObjectConstraint(U16 scope_id, const char* cons_name, bool is_shape) { if (!cons_name) return; dynConstraintDef dyn_def; dyn_def.cons_name = StringTable->insert(cons_name); dyn_def.cons_type = (is_shape) ? OBJECT_CONSTRAINT_SANS_SHAPE : OBJECT_CONSTRAINT_SANS_OBJ; dyn_def.cons_obj.scope_id = scope_id; dyn_cons_defs->push_back(dyn_def); constraint_mgr->defineConstraint(OBJECT_CONSTRAINT, dyn_def.cons_name); } void afxChoreographer::addPointConstraint(Point3F& point, const char* cons_name) { dynConstraintDef dyn_def; dyn_def.cons_name = StringTable->insert(cons_name); dyn_def.cons_type = POINT_CONSTRAINT; dyn_def.cons_obj.point = new Point3F(point); dyn_cons_defs->push_back(dyn_def); constraint_mgr->defineConstraint(POINT_CONSTRAINT, dyn_def.cons_name); } void afxChoreographer::addTransformConstraint(MatrixF& xfm, const char* cons_name) { dynConstraintDef dyn_def; dyn_def.cons_name = StringTable->insert(cons_name); dyn_def.cons_type = TRANSFORM_CONSTRAINT; dyn_def.cons_obj.xfm = new MatrixF(xfm); dyn_cons_defs->push_back(dyn_def); constraint_mgr->defineConstraint(TRANSFORM_CONSTRAINT, dyn_def.cons_name); } // CONSTRAINT REMAPPING << static inline U32 resolve_cons_spec(const char* source_spec, Point3F& pos, MatrixF& xfm, SceneObject** scn_obj) { AngAxisF aa; S32 args_n = dSscanf(source_spec, "%g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z, &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle); // TRANSFORM CONSTRAINT SRC if (args_n == 7) { aa.setMatrix(&xfm); xfm.setColumn(3,pos); return afxEffectDefs::TRANSFORM_CONSTRAINT; } // POINT CONSTRAINT SRC if (args_n == 3) { return afxEffectDefs::POINT_CONSTRAINT; } SimObject* cons_sim_obj = Sim::findObject(source_spec); *scn_obj = dynamic_cast(cons_sim_obj); if (*scn_obj) { return afxEffectDefs::OBJECT_CONSTRAINT; } return afxEffectDefs::UNDEFINED_CONSTRAINT_TYPE; } // CONSTRAINT REMAPPING >> bool afxChoreographer::addConstraint(const char* source_spec, const char* cons_name) { VectorF pos; MatrixF xfm; SceneObject* scn_obj; switch (resolve_cons_spec(source_spec, pos, xfm, &scn_obj)) { case TRANSFORM_CONSTRAINT: addTransformConstraint(xfm, cons_name); return true; case POINT_CONSTRAINT: addPointConstraint(pos, cons_name); return true; case OBJECT_CONSTRAINT: addObjectConstraint(scn_obj, cons_name); return true; } return false; } void afxChoreographer::addNamedEffect(afxEffectWrapper* ew) { named_effects.addObject(ew); } void afxChoreographer::removeNamedEffect(afxEffectWrapper* ew) { named_effects.removeObject(ew); } afxEffectWrapper* afxChoreographer::findNamedEffect(StringTableEntry name) { return (afxEffectWrapper*) named_effects.findObject(name); } void afxChoreographer::setGhostConstraintObject(SceneObject* obj, StringTableEntry cons_name) { if (constraint_mgr) constraint_mgr->setReferenceObject(cons_name, obj); } void afxChoreographer::restoreScopedObject(SceneObject* obj) { constraint_mgr->restoreScopedObject(obj, this); constraint_mgr->adjustProcessOrdering(this); } void afxChoreographer::addExplicitClient(NetConnection* conn) { if (!conn) return; for (S32 i = 0; i < explicit_clients.size(); i++) { if (explicit_clients[i] == conn) return; } explicit_clients.push_back(conn); deleteNotify(conn); } void afxChoreographer::removeExplicitClient(NetConnection* conn) { if (!conn) return; for (S32 i = 0; i < explicit_clients.size(); i++) { if (explicit_clients[i] == conn) { clearNotify(conn); explicit_clients.erase_fast(i); return; } } } void afxChoreographer::postProcessAfterObject(GameBase* obj) { proc_after_obj = obj; } void afxChoreographer::setTriggerMask(U32 mask) { if (mask != trigger_mask) { trigger_mask = mask; setMaskBits(TriggerMask); } } afxParticlePool* afxChoreographer::findParticlePool(afxParticlePoolData* key_block, U32 key_index) { for (S32 i = 0; i < particle_pools.size(); i++) if (particle_pools[i] && particle_pools[i]->hasMatchingKeyBlock(key_block, key_index)) return particle_pools[i]; return 0; } void afxChoreographer::registerParticlePool(afxParticlePool* pool) { particle_pools.push_back(pool); } void afxChoreographer::unregisterParticlePool(afxParticlePool* pool) { for (S32 i = 0; i < particle_pools.size(); i++) if (particle_pools[i] == pool) { particle_pools.erase_fast(i); return; } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// DefineEngineMethod( afxChoreographer, setRanking, void, ( U32 ranking ),, "Set a ranking value (0-255) for the choreographer.\n" ) { object->setRanking((U8)ranking); } DefineEngineMethod( afxChoreographer, setLevelOfDetail, void, ( U32 lod ),, "Set a level-of-detail value (0-255) for the choreographer.\n" ) { object->setLevelOfDetail((U8)lod); } DefineEngineMethod( afxChoreographer, setExecConditions, void, ( U32 mask ),, "Set a bitmask to specifiy the state of exec-conditions.\n" ) { object->setExecConditions(afxChoreographer::USER_EXEC_CONDS_MASK & mask); } DefineEngineMethod( afxChoreographer, addConstraint, void, ( const char* source, const char* name),, "Add a dynamic constraint consistiing of a source and name. The source can be a SceneObject, a 3-valued position, or a 7-valued transform.\n" ) { if (!object->addConstraint(source, name)) Con::errorf("afxChoreographer::addConstraint() -- failed to resolve constraint source [%s].", source); } DefineEngineMethod( afxChoreographer, addExplicitClient, void, ( NetConnection* client ),, "Add an explicit client.\n" ) { if (!client) { Con::errorf(ConsoleLogEntry::General, "afxChoreographer::addExplicitClient: Failed to resolve client connection"); return; } object->addExplicitClient(client); } DefineEngineMethod( afxChoreographer, setTriggerBit, void, ( U32 bit_num ),, "Set a bit of the trigger-mask.\n" ) { U32 set_bit = 1 << bit_num; object->setTriggerMask(set_bit | object->getTriggerMask()); } DefineEngineMethod( afxChoreographer, clearTriggerBit, void, ( U32 bit_num ),, "Unset a bit of the trigger-mask.\n" ) { U32 clear_bit = 1 << bit_num; object->setTriggerMask(~clear_bit & object->getTriggerMask()); } DefineEngineMethod( afxChoreographer, testTriggerBit, bool, ( U32 bit_num ),, "Test state of a trigger-mask bit.\n" ) { U32 test_bit = 1 << bit_num; return ((test_bit & object->getTriggerMask()) != 0); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // CONSTRAINT REMAPPING << void afxChoreographer::remapObjectConstraint(SceneObject* object, const char* cons_name) { if (!object || !cons_name) return; // must be a constraint-def with a matching name in the list dynConstraintDef* dyn_def = find_cons_def_by_name(cons_name); if (!dyn_def) { Con::errorf("afxChoreographer::remapObjectConstraint() -- failed to find constraint name [%s].", cons_name); return; } // constraint-def must have matching name constraint-type if (dyn_def->cons_type != OBJECT_CONSTRAINT) { Con::errorf("afxChoreographer::remapObjectConstraint() -- remapped contraint type does not match existing constraint type."); return; } // nothing to do if new object is same as old if (dyn_def->cons_obj.object == object) { #ifdef TORQUE_DEBUG Con::warnf("afxChoreographer::remapObjectConstraint() -- remapped contraint object is same as existing object."); #endif return; } dyn_def->cons_obj.object = object; deleteNotify(object); constraint_mgr->setReferenceObject(StringTable->insert(cons_name), object); #if defined(AFX_CAP_SCOPE_TRACKING) if (isServerObject() && object->isScopeable()) constraint_mgr->addScopeableObject(object); #endif if (isServerObject()) { if (remapped_cons_sent) { remapped_cons_defs.clear(); remapped_cons_sent = false; } remapped_cons_defs.push_back(dyn_def); setMaskBits(RemapConstraintMask); } } void afxChoreographer::remapObjectConstraint(U16 scope_id, const char* cons_name, bool is_shape) { if (!cons_name) return; // must be a constraint-def with a matching name in the list dynConstraintDef* dyn_def = find_cons_def_by_name(cons_name); if (!dyn_def) { Con::errorf("afxChoreographer::remapObjectConstraint() -- failed to find constraint name [%s].", cons_name); return; } // constraint-def must have matching name constraint-type if (dyn_def->cons_type != OBJECT_CONSTRAINT) { Con::errorf("afxChoreographer::remapObjectConstraint() -- remapped contraint type does not match existing constraint type."); return; } constraint_mgr->setReferenceObjectByScopeId(StringTable->insert(cons_name), scope_id, is_shape); } void afxChoreographer::remapPointConstraint(Point3F& point, const char* cons_name) { // must be a constraint-def with a matching name in the list dynConstraintDef* dyn_def = find_cons_def_by_name(cons_name); if (!dyn_def) { Con::errorf("afxChoreographer::remapPointConstraint() -- failed to find constraint name [%s].", cons_name); return; } // constraint-def must have matching name constraint-type if (dyn_def->cons_type != POINT_CONSTRAINT) { Con::errorf("afxChoreographer::remapPointConstraint() -- remapped contraint type does not match existing constraint type."); return; } *dyn_def->cons_obj.point = point; constraint_mgr->setReferencePoint(StringTable->insert(cons_name), point); if (isServerObject()) { if (remapped_cons_sent) { remapped_cons_defs.clear(); remapped_cons_sent = false; } remapped_cons_defs.push_back(dyn_def); setMaskBits(RemapConstraintMask); } } void afxChoreographer::remapTransformConstraint(MatrixF& xfm, const char* cons_name) { // must be a constraint-def with a matching name in the list dynConstraintDef* dyn_def = find_cons_def_by_name(cons_name); if (!dyn_def) { Con::errorf("afxChoreographer::remapTransformConstraint() -- failed to find constraint name [%s].", cons_name); return; } // constraint-def must have matching name constraint-type if (dyn_def->cons_type != POINT_CONSTRAINT) { Con::errorf("afxChoreographer::remapTransformConstraint() -- remapped contraint type does not match existing constraint type."); return; } *dyn_def->cons_obj.xfm = xfm; constraint_mgr->setReferenceTransform(StringTable->insert(cons_name), xfm); if (isServerObject()) { if (remapped_cons_sent) { remapped_cons_defs.clear(); remapped_cons_sent = false; } remapped_cons_defs.push_back(dyn_def); setMaskBits(RemapConstraintMask); } } bool afxChoreographer::remapConstraint(const char* source_spec, const char* cons_name) { SceneObject* scn_obj; Point3F pos; MatrixF xfm; switch (resolve_cons_spec(source_spec, pos, xfm, &scn_obj)) { case TRANSFORM_CONSTRAINT: //addTransformConstraint(xfm, cons_name); return true; case POINT_CONSTRAINT: //addPointConstraint(pos, cons_name); return true; case OBJECT_CONSTRAINT: remapObjectConstraint(scn_obj, cons_name); return true; } return false; } DefineEngineMethod( afxChoreographer, remapConstraint, void, ( const char* source, const char* name),, "Remap a dynamic constraint to use a new source. The source can be a SceneObject, a 3-valued position, or a 7-valued transform. but must match type of existing source.\n" ) { if (!object->remapConstraint(source, name)) Con::errorf("afxChoreographer::remapConstraint() -- failed to resolve constraint source [%s].", source); } // CONSTRAINT REMAPPING >> //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//