//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // 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 "arcaneFX.h" #include "T3D/aiPlayer.h" #include "T3D/tsStatic.h" #include "sim/netConnection.h" #include "ts/tsShapeInstance.h" #include "afxConstraint.h" #include "afxChoreographer.h" #include "afxEffectWrapper.h" //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxConstraintDef // static StringTableEntry afxConstraintDef::SCENE_CONS_KEY; StringTableEntry afxConstraintDef::EFFECT_CONS_KEY; StringTableEntry afxConstraintDef::GHOST_CONS_KEY; afxConstraintDef::afxConstraintDef() { if (SCENE_CONS_KEY == 0) { SCENE_CONS_KEY = StringTable->insert("#scene"); EFFECT_CONS_KEY = StringTable->insert("#effect"); GHOST_CONS_KEY = StringTable->insert("#ghost"); } reset(); } bool afxConstraintDef::isDefined() { return (mDef_type != CONS_UNDEFINED); } bool afxConstraintDef::isArbitraryObject() { return ((mCons_src_name != ST_NULLSTRING) && (mDef_type == CONS_SCENE)); } void afxConstraintDef::reset() { mCons_src_name = ST_NULLSTRING; mCons_node_name = ST_NULLSTRING; mDef_type = CONS_UNDEFINED; mHistory_time = 0; mSample_rate = 30; mRuns_on_server = false; mRuns_on_client = false; mPos_at_box_center = false; mTreat_as_camera = false; } bool afxConstraintDef::parseSpec(const char* spec, bool runs_on_server, bool runs_on_client) { reset(); if (spec == 0 || spec[0] == '\0') return false; mHistory_time = 0.0f; mSample_rate = 30; mRuns_on_server = runs_on_server; mRuns_on_client = runs_on_client; // spec should be in one of these forms: // CONSTRAINT_NAME (only) // CONSTRAINT_NAME.NODE (shapeBase objects only) // CONSTRAINT_NAME.#center // object.OBJECT_NAME // object.OBJECT_NAME.NODE (shapeBase objects only) // object.OBJECT_NAME.#center // effect.EFFECT_NAME // effect.EFFECT_NAME.NODE // effect.EFFECT_NAME.#center // #ghost.EFFECT_NAME // #ghost.EFFECT_NAME.NODE // #ghost.EFFECT_NAME.#center // // create scratch buffer by duplicating spec. char special = '\b'; char* buffer = dStrdup(spec); // substitute a dots not inside parens with special character S32 n_nested = 0; for (char* b = buffer; (*b) != '\0'; b++) { if ((*b) == '(') n_nested++; else if ((*b) == ')') n_nested--; else if ((*b) == '.' && n_nested == 0) (*b) = special; } // divide name into '.' separated tokens (up to 8) char* words[8] = {0, 0, 0, 0, 0, 0, 0, 0}; char* dot = buffer; int wdx = 0; while (wdx < 8) { words[wdx] = dot; dot = dStrchr(words[wdx++], special); if (!dot) break; *(dot++) = '\0'; if ((*dot) == '\0') break; } int n_words = wdx; // at this point the spec has been split into words. // n_words indicates how many words we have. // no words found (must have been all whitespace) if (n_words < 1) { dFree(buffer); return false; } char* hist_spec = 0; char* words2[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int n_words2 = 0; // move words to words2 while extracting #center and #history for (S32 i = 0; i < n_words; i++) { if (String::compare(words[i], "#center") == 0) mPos_at_box_center = true; else if (dStrncmp(words[i], "#history(", 9) == 0) hist_spec = words[i]; else words2[n_words2++] = words[i]; } // words2[] now contains just the constraint part // no words found (must have been all #center and #history) if (n_words2 < 1) { dFree(buffer); return false; } if (hist_spec) { char* open_paren = dStrchr(hist_spec, '('); if (open_paren) { hist_spec = open_paren+1; if ((*hist_spec) != '\0') { char* close_paren = dStrchr(hist_spec, ')'); if (close_paren) (*close_paren) = '\0'; char* slash = dStrchr(hist_spec, '/'); if (slash) (*slash) = ' '; F32 hist_age = 0.0; U32 hist_rate = 30; S32 args = dSscanf(hist_spec,"%g %d", &hist_age, &hist_rate); if (args > 0) mHistory_time = hist_age; if (args > 1) mSample_rate = hist_rate; } } } StringTableEntry cons_name_key = StringTable->insert(words2[0]); // must be in CONSTRAINT_NAME (only) form if (n_words2 == 1) { // arbitrary object/effect constraints must have a name if (cons_name_key == SCENE_CONS_KEY || cons_name_key == EFFECT_CONS_KEY) { dFree(buffer); return false; } mCons_src_name = cons_name_key; mDef_type = CONS_PREDEFINED; dFree(buffer); return true; } // "#scene.NAME" or "#scene.NAME.NODE"" if (cons_name_key == SCENE_CONS_KEY) { mCons_src_name = StringTable->insert(words2[1]); if (n_words2 > 2) mCons_node_name = StringTable->insert(words2[2]); mDef_type = CONS_SCENE; dFree(buffer); return true; } // "#effect.NAME" or "#effect.NAME.NODE" if (cons_name_key == EFFECT_CONS_KEY) { mCons_src_name = StringTable->insert(words2[1]); if (n_words2 > 2) mCons_node_name = StringTable->insert(words2[2]); mDef_type = CONS_EFFECT; dFree(buffer); return true; } // "#ghost.NAME" or "#ghost.NAME.NODE" if (cons_name_key == GHOST_CONS_KEY) { if (runs_on_server) { dFree(buffer); return false; } mCons_src_name = StringTable->insert(words2[1]); if (n_words2 > 2) mCons_node_name = StringTable->insert(words2[2]); mDef_type = CONS_GHOST; dFree(buffer); return true; } // "CONSTRAINT_NAME.NODE" if (n_words2 == 2) { mCons_src_name = cons_name_key; mCons_node_name = StringTable->insert(words2[1]); mDef_type = CONS_PREDEFINED; dFree(buffer); return true; } // must be in unsupported form dFree(buffer); return false; } void afxConstraintDef::gather_cons_defs(Vector& defs, afxEffectList& fx) { for (S32 i = 0; i < fx.size(); i++) { if (fx[i]) fx[i]->gather_cons_defs(defs); } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxConstraint afxConstraint::afxConstraint(afxConstraintMgr* mgr) { mMgr = mgr; mIs_defined = false; mIs_valid = false; mLast_pos.zero(); mLast_xfm.identity(); mHistory_time = 0.0f; mIs_alive = true; mGone_missing = false; mChange_code = 0; } afxConstraint::~afxConstraint() { } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// inline afxPointConstraint* newPointCons(afxConstraintMgr* mgr, bool hist) { return (hist) ? new afxPointHistConstraint(mgr) : new afxPointConstraint(mgr); } inline afxTransformConstraint* newTransformCons(afxConstraintMgr* mgr, bool hist) { return (hist) ? new afxTransformHistConstraint(mgr) : new afxTransformConstraint(mgr); } inline afxShapeConstraint* newShapeCons(afxConstraintMgr* mgr, bool hist) { return (hist) ? new afxShapeHistConstraint(mgr) : new afxShapeConstraint(mgr); } inline afxShapeConstraint* newShapeCons(afxConstraintMgr* mgr, StringTableEntry name, bool hist) { return (hist) ? new afxShapeHistConstraint(mgr, name) : new afxShapeConstraint(mgr, name); } inline afxShapeNodeConstraint* newShapeNodeCons(afxConstraintMgr* mgr, StringTableEntry name, StringTableEntry node, bool hist) { return (hist) ? new afxShapeNodeHistConstraint(mgr, name, node) : new afxShapeNodeConstraint(mgr, name, node); } inline afxObjectConstraint* newObjectCons(afxConstraintMgr* mgr, bool hist) { return (hist) ? new afxObjectHistConstraint(mgr) : new afxObjectConstraint(mgr); } inline afxObjectConstraint* newObjectCons(afxConstraintMgr* mgr, StringTableEntry name, bool hist) { return (hist) ? new afxObjectHistConstraint(mgr, name) : new afxObjectConstraint(mgr, name); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxConstraintMgr #define CONS_BY_ID(id) ((*mConstraints_v[(id).index])[(id).sub_index]) #define CONS_BY_IJ(i,j) ((*mConstraints_v[(i)])[(j)]) afxConstraintMgr::afxConstraintMgr() { mStartTime = 0; mOn_server = false; mInitialized = false; mScoping_dist_sq = 1000.0f*1000.0f; missing_objs = &missing_objs_a; missing_objs2 = &missing_objs_b; } afxConstraintMgr::~afxConstraintMgr() { for (S32 i = 0; i < mConstraints_v.size(); i++) { for (S32 j = 0; j < (*mConstraints_v[i]).size(); j++) delete CONS_BY_IJ(i,j); delete mConstraints_v[i]; } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// S32 afxConstraintMgr::find_cons_idx_from_name(StringTableEntry which) { for (S32 i = 0; i < mConstraints_v.size(); i++) { afxConstraint* cons = CONS_BY_IJ(i,0); if (cons && afxConstraintDef::CONS_EFFECT != cons->mCons_def.mDef_type && which == cons->mCons_def.mCons_src_name) { return i; } } return -1; } S32 afxConstraintMgr::find_effect_cons_idx_from_name(StringTableEntry which) { for (S32 i = 0; i < mConstraints_v.size(); i++) { afxConstraint* cons = CONS_BY_IJ(i,0); if (cons && afxConstraintDef::CONS_EFFECT == cons->mCons_def.mDef_type && which == cons->mCons_def.mCons_src_name) { return i; } } return -1; } // Defines a predefined constraint with given name and type void afxConstraintMgr::defineConstraint(U32 type, StringTableEntry name) { preDef predef = { name, type }; mPredefs.push_back(predef); } afxConstraintID afxConstraintMgr::setReferencePoint(StringTableEntry which, Point3F point, Point3F vector) { S32 idx = find_cons_idx_from_name(which); if (idx < 0) return afxConstraintID(); afxConstraintID id = afxConstraintID(idx); setReferencePoint(id, point, vector); return id; } afxConstraintID afxConstraintMgr::setReferenceTransform(StringTableEntry which, MatrixF& xfm) { S32 idx = find_cons_idx_from_name(which); if (idx < 0) return afxConstraintID(); afxConstraintID id = afxConstraintID(idx); setReferenceTransform(id, xfm); return id; } // Assigns an existing scene-object to the named constraint afxConstraintID afxConstraintMgr::setReferenceObject(StringTableEntry which, SceneObject* obj) { S32 idx = find_cons_idx_from_name(which); if (idx < 0) return afxConstraintID(); afxConstraintID id = afxConstraintID(idx); setReferenceObject(id, obj); return id; } // Assigns an un-scoped scene-object by scope_id to the named constraint afxConstraintID afxConstraintMgr::setReferenceObjectByScopeId(StringTableEntry which, U16 scope_id, bool is_shape) { S32 idx = find_cons_idx_from_name(which); if (idx < 0) return afxConstraintID(); afxConstraintID id = afxConstraintID(idx); setReferenceObjectByScopeId(id, scope_id, is_shape); return id; } afxConstraintID afxConstraintMgr::setReferenceEffect(StringTableEntry which, afxEffectWrapper* ew) { S32 idx = find_effect_cons_idx_from_name(which); if (idx < 0) return afxConstraintID(); afxConstraintID id = afxConstraintID(idx); setReferenceEffect(id, ew); return id; } afxConstraintID afxConstraintMgr::createReferenceEffect(StringTableEntry which, afxEffectWrapper* ew) { afxEffectConstraint* cons = new afxEffectConstraint(this, which); //cons->cons_def = def; cons->mCons_def.mDef_type = afxConstraintDef::CONS_EFFECT; cons->mCons_def.mCons_src_name = which; afxConstraintList* list = new afxConstraintList(); list->push_back(cons); mConstraints_v.push_back(list); return setReferenceEffect(which, ew); } void afxConstraintMgr::setReferencePoint(afxConstraintID id, Point3F point, Point3F vector) { afxPointConstraint* pt_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!pt_cons) { afxConstraint* cons = CONS_BY_ID(id); pt_cons = newPointCons(this, cons->mCons_def.mHistory_time > 0.0f); pt_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = pt_cons; delete cons; } pt_cons->set(point, vector); // nullify all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) cons->unset(); } } void afxConstraintMgr::setReferenceTransform(afxConstraintID id, MatrixF& xfm) { afxTransformConstraint* xfm_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!xfm_cons) { afxConstraint* cons = CONS_BY_ID(id); xfm_cons = newTransformCons(this, cons->mCons_def.mHistory_time > 0.0f); xfm_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = xfm_cons; delete cons; } xfm_cons->set(xfm); // nullify all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) cons->unset(); } } void afxConstraintMgr::set_ref_shape(afxConstraintID id, ShapeBase* shape) { id.sub_index = 0; afxShapeConstraint* shape_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!shape_cons) { afxConstraint* cons = CONS_BY_ID(id); shape_cons = newShapeCons(this, cons->mCons_def.mHistory_time > 0.0f); shape_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = shape_cons; delete cons; } // set new shape on root shape_cons->set(shape); // update all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) { if (dynamic_cast(cons)) ((afxShapeNodeConstraint*)cons)->set(shape); else if (dynamic_cast(cons)) ((afxShapeConstraint*)cons)->set(shape); else if (dynamic_cast(cons)) ((afxObjectConstraint*)cons)->set(shape); else cons->unset(); } } } void afxConstraintMgr::set_ref_shape(afxConstraintID id, U16 scope_id) { id.sub_index = 0; afxShapeConstraint* shape_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!shape_cons) { afxConstraint* cons = CONS_BY_ID(id); shape_cons = newShapeCons(this, cons->mCons_def.mHistory_time > 0.0f); shape_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = shape_cons; delete cons; } // set new shape on root shape_cons->set_scope_id(scope_id); // update all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) cons->set_scope_id(scope_id); } } // Assigns an existing scene-object to the constraint matching the given constraint-id. void afxConstraintMgr::setReferenceObject(afxConstraintID id, SceneObject* obj) { if (!mInitialized) Con::errorf("afxConstraintMgr::setReferenceObject() -- constraint manager not initialized"); if (!CONS_BY_ID(id)->mCons_def.mTreat_as_camera) { ShapeBase* shape = dynamic_cast(obj); if (shape) { set_ref_shape(id, shape); return; } } afxObjectConstraint* obj_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!obj_cons) { afxConstraint* cons = CONS_BY_ID(id); obj_cons = newObjectCons(this, cons->mCons_def.mHistory_time > 0.0f); obj_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = obj_cons; delete cons; } obj_cons->set(obj); // update all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) { if (dynamic_cast(cons)) ((afxObjectConstraint*)cons)->set(obj); else cons->unset(); } } } // Assigns an un-scoped scene-object by scope_id to the constraint matching the // given constraint-id. void afxConstraintMgr::setReferenceObjectByScopeId(afxConstraintID id, U16 scope_id, bool is_shape) { if (!mInitialized) Con::errorf("afxConstraintMgr::setReferenceObject() -- constraint manager not initialized"); if (is_shape) { set_ref_shape(id, scope_id); return; } afxObjectConstraint* obj_cons = dynamic_cast(CONS_BY_ID(id)); // need to change type if (!obj_cons) { afxConstraint* cons = CONS_BY_ID(id); obj_cons = newObjectCons(this, cons->mCons_def.mHistory_time > 0.0f); obj_cons->mCons_def = cons->mCons_def; CONS_BY_ID(id) = obj_cons; delete cons; } obj_cons->set_scope_id(scope_id); // update all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) cons->set_scope_id(scope_id); } } void afxConstraintMgr::setReferenceEffect(afxConstraintID id, afxEffectWrapper* ew) { afxEffectConstraint* eff_cons = dynamic_cast(CONS_BY_ID(id)); if (!eff_cons) return; eff_cons->set(ew); // update all subnodes for (S32 j = 1; j < (*mConstraints_v[id.index]).size(); j++) { afxConstraint* cons = CONS_BY_IJ(id.index,j); if (cons) { if (dynamic_cast(cons)) ((afxEffectNodeConstraint*)cons)->set(ew); else if (dynamic_cast(cons)) ((afxEffectConstraint*)cons)->set(ew); else cons->unset(); } } } void afxConstraintMgr::invalidateReference(afxConstraintID id) { afxConstraint* cons = CONS_BY_ID(id); if (cons) cons->mIs_valid = false; } void afxConstraintMgr::create_constraint(const afxConstraintDef& def) { if (def.mDef_type == afxConstraintDef::CONS_UNDEFINED) return; //Con::printf("CON - %s [%s] [%s] h=%g", def.cons_type_name, def.cons_src_name, def.cons_node_name, def.history_time); bool want_history = (def.mHistory_time > 0.0f); // constraint is an arbitrary named scene object // if (def.mDef_type == afxConstraintDef::CONS_SCENE) { if (def.mCons_src_name == ST_NULLSTRING) return; // find the arbitrary object by name SceneObject* arb_obj; if (mOn_server) { arb_obj = dynamic_cast(Sim::findObject(def.mCons_src_name)); if (!arb_obj) Con::errorf("afxConstraintMgr -- failed to find scene constraint source, \"%s\" on server.", def.mCons_src_name); } else { arb_obj = find_object_from_name(def.mCons_src_name); if (!arb_obj) Con::errorf("afxConstraintMgr -- failed to find scene constraint source, \"%s\" on client.", def.mCons_src_name); } // if it's a shapeBase object, create a Shape or ShapeNode constraint if (dynamic_cast(arb_obj)) { if (def.mCons_node_name == ST_NULLSTRING && !def.mPos_at_box_center) { afxShapeConstraint* cons = newShapeCons(this, def.mCons_src_name, want_history); cons->mCons_def = def; cons->set((ShapeBase*)arb_obj); afxConstraintList* list = new afxConstraintList(); list->push_back(cons); mConstraints_v.push_back(list); } else if (def.mPos_at_box_center) { afxShapeConstraint* cons = newShapeCons(this, def.mCons_src_name, want_history); cons->mCons_def = def; cons->set((ShapeBase*)arb_obj); afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; // SHAPE-NODE CONS-LIST (#scene)(#center) if (list && (*list)[0]) list->push_back(cons); } else { afxShapeNodeConstraint* sub = newShapeNodeCons(this, def.mCons_src_name, def.mCons_node_name, want_history); sub->mCons_def = def; sub->set((ShapeBase*)arb_obj); afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; if (list && (*list)[0]) list->push_back(sub); } } // if it's not a shapeBase object, create an Object constraint else if (arb_obj) { if (!def.mPos_at_box_center) { afxObjectConstraint* cons = newObjectCons(this, def.mCons_src_name, want_history); cons->mCons_def = def; cons->set(arb_obj); afxConstraintList* list = new afxConstraintList(); // OBJECT CONS-LIST (#scene) list->push_back(cons); mConstraints_v.push_back(list); } else // if (def.pos_at_box_center) { afxObjectConstraint* cons = newObjectCons(this, def.mCons_src_name, want_history); cons->mCons_def = def; cons->set(arb_obj); afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; // OBJECT CONS-LIST (#scene)(#center) if (list && (*list)[0]) list->push_back(cons); } } } // constraint is an arbitrary named effect // else if (def.mDef_type == afxConstraintDef::CONS_EFFECT) { if (def.mCons_src_name == ST_NULLSTRING) return; // create an Effect constraint if (def.mCons_node_name == ST_NULLSTRING && !def.mPos_at_box_center) { afxEffectConstraint* cons = new afxEffectConstraint(this, def.mCons_src_name); cons->mCons_def = def; afxConstraintList* list = new afxConstraintList(); list->push_back(cons); mConstraints_v.push_back(list); } // create an Effect #center constraint else if (def.mPos_at_box_center) { afxEffectConstraint* cons = new afxEffectConstraint(this, def.mCons_src_name); cons->mCons_def = def; afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; // EFFECT-NODE CONS-LIST (#effect) if (list && (*list)[0]) list->push_back(cons); } // create an EffectNode constraint else { afxEffectNodeConstraint* sub = new afxEffectNodeConstraint(this, def.mCons_src_name, def.mCons_node_name); sub->mCons_def = def; afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; if (list && (*list)[0]) list->push_back(sub); } } // constraint is a predefined constraint // else { afxConstraint* cons = 0; afxConstraint* cons_ctr = 0; afxConstraint* sub = 0; if (def.mDef_type == afxConstraintDef::CONS_GHOST) { for (S32 i = 0; i < mPredefs.size(); i++) { if (mPredefs[i].name == def.mCons_src_name) { if (def.mCons_node_name == ST_NULLSTRING && !def.mPos_at_box_center) { cons = newShapeCons(this, want_history); cons->mCons_def = def; } else if (def.mPos_at_box_center) { cons_ctr = newShapeCons(this, want_history); cons_ctr->mCons_def = def; } else { sub = newShapeNodeCons(this, ST_NULLSTRING, def.mCons_node_name, want_history); sub->mCons_def = def; } break; } } } else { for (S32 i = 0; i < mPredefs.size(); i++) { if (mPredefs[i].name == def.mCons_src_name) { switch (mPredefs[i].type) { case POINT_CONSTRAINT: cons = newPointCons(this, want_history); cons->mCons_def = def; break; case TRANSFORM_CONSTRAINT: cons = newTransformCons(this, want_history); cons->mCons_def = def; break; case OBJECT_CONSTRAINT: if (def.mCons_node_name == ST_NULLSTRING && !def.mPos_at_box_center) { cons = newShapeCons(this, want_history); cons->mCons_def = def; } else if (def.mPos_at_box_center) { cons_ctr = newShapeCons(this, want_history); cons_ctr->mCons_def = def; } else { sub = newShapeNodeCons(this, ST_NULLSTRING, def.mCons_node_name, want_history); sub->mCons_def = def; } break; case CAMERA_CONSTRAINT: cons = newObjectCons(this, want_history); cons->mCons_def = def; cons->mCons_def.mTreat_as_camera = true; break; } break; } } } if (cons) { afxConstraintList* list = new afxConstraintList(); list->push_back(cons); mConstraints_v.push_back(list); } else if (cons_ctr && mConstraints_v.size() > 0) { afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; // PREDEF-NODE CONS-LIST if (list && (*list)[0]) list->push_back(cons_ctr); } else if (sub && mConstraints_v.size() > 0) { afxConstraintList* list = mConstraints_v[mConstraints_v.size()-1]; if (list && (*list)[0]) list->push_back(sub); } else Con::printf("predef not found %s", def.mCons_src_name); } } afxConstraintID afxConstraintMgr::getConstraintId(const afxConstraintDef& def) { if (def.mDef_type == afxConstraintDef::CONS_UNDEFINED) return afxConstraintID(); if (def.mCons_src_name != ST_NULLSTRING) { for (S32 i = 0; i < mConstraints_v.size(); i++) { afxConstraintList* list = mConstraints_v[i]; afxConstraint* cons = (*list)[0]; if (def.mCons_src_name == cons->mCons_def.mCons_src_name) { for (S32 j = 0; j < list->size(); j++) { afxConstraint* sub = (*list)[j]; if (def.mCons_node_name == sub->mCons_def.mCons_node_name && def.mPos_at_box_center == sub->mCons_def.mPos_at_box_center && def.mCons_src_name == sub->mCons_def.mCons_src_name) { return afxConstraintID(i, j); } } // if we're here, it means the root object name matched but the node name // did not. if (def.mDef_type == afxConstraintDef::CONS_PREDEFINED && !def.mPos_at_box_center) { afxShapeConstraint* shape_cons = dynamic_cast(cons); if (shape_cons) { //Con::errorf("Append a Node constraint [%s.%s] [%d,%d]", def.cons_src_name, def.cons_node_name, i, list->size()); bool want_history = (def.mHistory_time > 0.0f); afxConstraint* sub = newShapeNodeCons(this, ST_NULLSTRING, def.mCons_node_name, want_history); sub->mCons_def = def; ((afxShapeConstraint*)sub)->set(shape_cons->mShape); list->push_back(sub); return afxConstraintID(i, list->size()-1); } } break; } } } return afxConstraintID(); } afxConstraint* afxConstraintMgr::getConstraint(afxConstraintID id) { if (id.undefined()) return 0; afxConstraint* cons = CONS_BY_IJ(id.index,id.sub_index); if (cons && !cons->isDefined()) return NULL; return cons; } void afxConstraintMgr::sample(F32 dt, U32 now, const Point3F* cam_pos) { U32 elapsed = now - mStartTime; for (S32 i = 0; i < mConstraints_v.size(); i++) { afxConstraintList* list = mConstraints_v[i]; for (S32 j = 0; j < list->size(); j++) (*list)[j]->sample(dt, elapsed, cam_pos); } } S32 QSORT_CALLBACK cmp_cons_defs(const void* a, const void* b) { afxConstraintDef* def_a = (afxConstraintDef*) a; afxConstraintDef* def_b = (afxConstraintDef*) b; if (def_a->mDef_type == def_b->mDef_type) { if (def_a->mCons_src_name == def_b->mCons_src_name) { if (def_a->mPos_at_box_center == def_b->mPos_at_box_center) return (def_a->mCons_node_name - def_b->mCons_node_name); else return (def_a->mPos_at_box_center) ? 1 : -1; } return (def_a->mCons_src_name - def_b->mCons_src_name); } return (def_a->mDef_type - def_b->mDef_type); } void afxConstraintMgr::initConstraintDefs(Vector& all_defs, bool on_server, F32 scoping_dist) { mInitialized = true; mOn_server = on_server; if (scoping_dist > 0.0) mScoping_dist_sq = scoping_dist*scoping_dist; else { SceneManager* sg = (on_server) ? gServerSceneGraph : gClientSceneGraph; F32 vis_dist = (sg) ? sg->getVisibleDistance() : 1000.0f; mScoping_dist_sq = vis_dist*vis_dist; } if (all_defs.size() < 1) return; // find effect ghost constraints if (!on_server) { Vector ghost_defs; for (S32 i = 0; i < all_defs.size(); i++) if (all_defs[i].mDef_type == afxConstraintDef::CONS_GHOST && all_defs[i].mCons_src_name != ST_NULLSTRING) ghost_defs.push_back(all_defs[i]); if (ghost_defs.size() > 0) { // sort the defs if (ghost_defs.size() > 1) dQsort(ghost_defs.address(), ghost_defs.size(), sizeof(afxConstraintDef), cmp_cons_defs); S32 last = 0; defineConstraint(OBJECT_CONSTRAINT, ghost_defs[0].mCons_src_name); for (S32 i = 1; i < ghost_defs.size(); i++) { if (ghost_defs[last].mCons_src_name != ghost_defs[i].mCons_src_name) { defineConstraint(OBJECT_CONSTRAINT, ghost_defs[i].mCons_src_name); last++; } } } } Vector defs; // collect defs that run here (server or client) if (on_server) { for (S32 i = 0; i < all_defs.size(); i++) if (all_defs[i].mRuns_on_server) defs.push_back(all_defs[i]); } else { for (S32 i = 0; i < all_defs.size(); i++) if (all_defs[i].mRuns_on_client) defs.push_back(all_defs[i]); } // create unique set of constraints. // if (defs.size() > 0) { // sort the defs if (defs.size() > 1) dQsort(defs.address(), defs.size(), sizeof(afxConstraintDef), cmp_cons_defs); Vector unique_defs; S32 last = 0; // manufacture root-object def if absent if (defs[0].mCons_node_name != ST_NULLSTRING) { afxConstraintDef root_def = defs[0]; root_def.mCons_node_name = ST_NULLSTRING; unique_defs.push_back(root_def); last++; } else if (defs[0].mPos_at_box_center) { afxConstraintDef root_def = defs[0]; root_def.mPos_at_box_center = false; unique_defs.push_back(root_def); last++; } unique_defs.push_back(defs[0]); for (S32 i = 1; i < defs.size(); i++) { if (unique_defs[last].mCons_node_name != defs[i].mCons_node_name || unique_defs[last].mCons_src_name != defs[i].mCons_src_name || unique_defs[last].mPos_at_box_center != defs[i].mPos_at_box_center || unique_defs[last].mDef_type != defs[i].mDef_type) { // manufacture root-object def if absent if (defs[i].mCons_src_name != ST_NULLSTRING && unique_defs[last].mCons_src_name != defs[i].mCons_src_name) { if (defs[i].mCons_node_name != ST_NULLSTRING || defs[i].mPos_at_box_center) { afxConstraintDef root_def = defs[i]; root_def.mCons_node_name = ST_NULLSTRING; root_def.mPos_at_box_center = false; unique_defs.push_back(root_def); last++; } } unique_defs.push_back(defs[i]); last++; } else { if (defs[i].mHistory_time > unique_defs[last].mHistory_time) unique_defs[last].mHistory_time = defs[i].mHistory_time; if (defs[i].mSample_rate > unique_defs[last].mSample_rate) unique_defs[last].mSample_rate = defs[i].mSample_rate; } } //Con::printf("\nConstraints on %s", (on_server) ? "server" : "client"); for (S32 i = 0; i < unique_defs.size(); i++) create_constraint(unique_defs[i]); } // collect the names of all the arbitrary object constraints // that run on clients and store in names_on_server array. // if (on_server) { mNames_on_server.clear(); defs.clear(); for (S32 i = 0; i < all_defs.size(); i++) if (all_defs[i].mRuns_on_client && all_defs[i].isArbitraryObject()) defs.push_back(all_defs[i]); if (defs.size() < 1) return; // sort the defs if (defs.size() > 1) dQsort(defs.address(), defs.size(), sizeof(afxConstraintDef), cmp_cons_defs); S32 last = 0; mNames_on_server.push_back(defs[0].mCons_src_name); for (S32 i = 1; i < defs.size(); i++) { if (mNames_on_server[last] != defs[i].mCons_src_name) { mNames_on_server.push_back(defs[i].mCons_src_name); last++; } } } } void afxConstraintMgr::packConstraintNames(NetConnection* conn, BitStream* stream) { // pack any named constraint names and ghost indices if (stream->writeFlag(mNames_on_server.size() > 0)) //-- ANY NAMED CONS_BY_ID? { stream->write(mNames_on_server.size()); for (S32 i = 0; i < mNames_on_server.size(); i++) { stream->writeString(mNames_on_server[i]); NetObject* obj = dynamic_cast(Sim::findObject(mNames_on_server[i])); if (!obj) { //Con::printf("CONSTRAINT-OBJECT %s does not exist.", names_on_server[i]); stream->write((S32)-1); } else { S32 ghost_id = conn->getGhostIndex(obj); /* if (ghost_id == -1) Con::printf("CONSTRAINT-OBJECT %s does not have a ghost.", names_on_server[i]); else Con::printf("CONSTRAINT-OBJECT %s name to server.", names_on_server[i]); */ stream->write(ghost_id); } } } } void afxConstraintMgr::unpackConstraintNames(BitStream* stream) { if (stream->readFlag()) //-- ANY NAMED CONS_BY_ID? { mNames_on_server.clear(); S32 sz; stream->read(&sz); for (S32 i = 0; i < sz; i++) { mNames_on_server.push_back(stream->readSTString()); S32 ghost_id; stream->read(&ghost_id); mGhost_ids.push_back(ghost_id); } } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// SceneObject* afxConstraintMgr::find_object_from_name(StringTableEntry name) { if (mNames_on_server.size() > 0) { for (S32 i = 0; i < mNames_on_server.size(); i++) if (mNames_on_server[i] == name) { if (mGhost_ids[i] == -1) return 0; NetConnection* conn = NetConnection::getConnectionToServer(); if (!conn) return 0; return dynamic_cast(conn->resolveGhost(mGhost_ids[i])); } } return dynamic_cast(Sim::findObject(name)); } void afxConstraintMgr::addScopeableObject(SceneObject* object) { for (S32 i = 0; i < scopeable_objs.size(); i++) { if (scopeable_objs[i] == object) return; } object->addScopeRef(); scopeable_objs.push_back(object); } void afxConstraintMgr::removeScopeableObject(SceneObject* object) { for (S32 i = 0; i < scopeable_objs.size(); i++) if (scopeable_objs[i] == object) { object->removeScopeRef(); scopeable_objs.erase_fast(i); return; } } void afxConstraintMgr::clearAllScopeableObjs() { for (S32 i = 0; i < scopeable_objs.size(); i++) scopeable_objs[i]->removeScopeRef(); scopeable_objs.clear(); } void afxConstraintMgr::postMissingConstraintObject(afxConstraint* cons, bool is_deleting) { if (cons->mGone_missing) return; if (!is_deleting) { SceneObject* obj = arcaneFX::findScopedObject(cons->getScopeId()); if (obj) { cons->restoreObject(obj); return; } } cons->mGone_missing = true; missing_objs->push_back(cons); } void afxConstraintMgr::restoreScopedObject(SceneObject* obj, afxChoreographer* ch) { for (S32 i = 0; i < missing_objs->size(); i++) { if ((*missing_objs)[i]->getScopeId() == obj->getScopeId()) { (*missing_objs)[i]->mGone_missing = false; (*missing_objs)[i]->restoreObject(obj); if (ch) ch->restoreObject(obj); } else missing_objs2->push_back((*missing_objs)[i]); } Vector* tmp = missing_objs; missing_objs = missing_objs2; missing_objs2 = tmp; missing_objs2->clear(); } void afxConstraintMgr::adjustProcessOrdering(afxChoreographer* ch) { Vector cons_sources; // add choreographer to the list cons_sources.push_back(ch); // collect all the ProcessObject related constraint sources for (S32 i = 0; i < mConstraints_v.size(); i++) { afxConstraintList* list = mConstraints_v[i]; afxConstraint* cons = (*list)[0]; if (cons) { ProcessObject* pobj = dynamic_cast(cons->getSceneObject()); if (pobj) cons_sources.push_back(pobj); } } ProcessList* proc_list; if (ch->isServerObject()) proc_list = ServerProcessList::get(); else proc_list = ClientProcessList::get(); if (!proc_list) return; GameBase* nearest_to_end = dynamic_cast(proc_list->findNearestToEnd(cons_sources)); GameBase* chor = (GameBase*) ch; // choreographer needs to be processed after the latest process object if (chor != nearest_to_end && nearest_to_end != 0) { //Con::printf("Choreographer processing BEFORE some of its constraints... fixing. [%s] -- %s", // (ch->isServerObject()) ? "S" : "C", nearest_to_end->getClassName()); if (chor->isProperlyAdded()) ch->processAfter(nearest_to_end); else ch->postProcessAfterObject(nearest_to_end); } else { //Con::printf("Choreographer processing AFTER its constraints... fine. [%s]", // (ch->isServerObject()) ? "S" : "C"); } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxPointConstraint afxPointConstraint::afxPointConstraint(afxConstraintMgr* mgr) : afxConstraint(mgr) { mPoint.zero(); mVector.set(0,0,1); } afxPointConstraint::~afxPointConstraint() { } void afxPointConstraint::set(Point3F point, Point3F vector) { mPoint = point; mVector = vector; mIs_defined = true; mIs_valid = true; mChange_code++; sample(0.0f, 0, 0); } void afxPointConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { if (cam_pos) { Point3F dir = (*cam_pos) - mPoint; F32 dist_sq = dir.lenSquared(); if (dist_sq > mMgr->getScopingDistanceSquared()) { mIs_valid = false; return; } mIs_valid = true; } mLast_pos = mPoint; mLast_xfm.identity(); mLast_xfm.setPosition(mPoint); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxTransformConstraint afxTransformConstraint::afxTransformConstraint(afxConstraintMgr* mgr) : afxConstraint(mgr) { mXfm.identity(); } afxTransformConstraint::~afxTransformConstraint() { } void afxTransformConstraint::set(const MatrixF& xfm) { mXfm = xfm; mIs_defined = true; mIs_valid = true; mChange_code++; sample(0.0f, 0, 0); } void afxTransformConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { if (cam_pos) { Point3F dir = (*cam_pos) - mXfm.getPosition(); F32 dist_sq = dir.lenSquared(); if (dist_sq > mMgr->getScopingDistanceSquared()) { mIs_valid = false; return; } mIs_valid = true; } mLast_xfm = mXfm; mLast_pos = mXfm.getPosition(); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxShapeConstraint afxShapeConstraint::afxShapeConstraint(afxConstraintMgr* mgr) : afxConstraint(mgr) { mArb_name = ST_NULLSTRING; mShape = 0; mScope_id = 0; mClip_tag = 0; mLock_tag = 0; } afxShapeConstraint::afxShapeConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name) : afxConstraint(mgr) { mArb_name = arb_name; mShape = 0; mScope_id = 0; mClip_tag = 0; mLock_tag = 0; } afxShapeConstraint::~afxShapeConstraint() { if (mShape) clearNotify(mShape); } void afxShapeConstraint::set(ShapeBase* shape) { if (mShape) { mScope_id = 0; clearNotify(mShape); if (mClip_tag > 0) remapAnimation(mClip_tag, shape); if (mLock_tag > 0) unlockAnimation(mLock_tag); } mShape = shape; if (mShape) { deleteNotify(mShape); mScope_id = mShape->getScopeId(); } if (mShape != NULL) { mIs_defined = true; mIs_valid = true; mChange_code++; sample(0.0f, 0, 0); } else mIs_valid = false; } void afxShapeConstraint::set_scope_id(U16 scope_id) { if (mShape) clearNotify(mShape); mShape = 0; mScope_id = scope_id; mIs_defined = (mScope_id > 0); mIs_valid = false; mMgr->postMissingConstraintObject(this); } void afxShapeConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { if (mGone_missing) return; if (mShape) { mLast_xfm = mShape->getRenderTransform(); if (mCons_def.mPos_at_box_center) mLast_pos = mShape->getBoxCenter(); else mLast_pos = mShape->getRenderPosition(); } } void afxShapeConstraint::restoreObject(SceneObject* obj) { if (mShape) { mScope_id = 0; clearNotify(mShape); } mShape = (ShapeBase* )obj; if (mShape) { deleteNotify(mShape); mScope_id = mShape->getScopeId(); } mIs_valid = (mShape != NULL); } void afxShapeConstraint::onDeleteNotify(SimObject* obj) { if (mShape == dynamic_cast(obj)) { mShape = 0; mIs_valid = false; if (mScope_id > 0) mMgr->postMissingConstraintObject(this, true); } Parent::onDeleteNotify(obj); } U32 afxShapeConstraint::setAnimClip(const char* clip, F32 pos, F32 rate, F32 trans, bool is_death_anim) { if (!mShape) return 0; if (mShape->isServerObject()) { AIPlayer* ai_player = dynamic_cast(mShape); if (ai_player && !ai_player->isBlendAnimation(clip)) { ai_player->saveMoveState(); ai_player->stopMove(); } } mClip_tag = mShape->playAnimation(clip, pos, rate, trans, false/*hold*/, true/*wait*/, is_death_anim); return mClip_tag; } void afxShapeConstraint::remapAnimation(U32 tag, ShapeBase* other_shape) { if (mClip_tag == 0) return; if (!mShape) return; if (!other_shape) { resetAnimation(tag); return; } Con::errorf("remapAnimation -- Clip name, %s.", mShape->getLastClipName(tag)); if (mShape->isClientObject()) { mShape->restoreAnimation(tag); } else { AIPlayer* ai_player = dynamic_cast(mShape); if (ai_player) ai_player->restartMove(tag); else mShape->restoreAnimation(tag); } mClip_tag = 0; } void afxShapeConstraint::resetAnimation(U32 tag) { if (mClip_tag == 0) return; if (!mShape) return; if (mShape->isClientObject()) { mShape->restoreAnimation(tag); } else { AIPlayer* ai_player = dynamic_cast(mShape); if (ai_player) ai_player->restartMove(tag); else mShape->restoreAnimation(tag); } if ((tag & 0x80000000) == 0 && tag == mClip_tag) mClip_tag = 0; } U32 afxShapeConstraint::lockAnimation() { if (!mShape) return 0; mLock_tag = mShape->lockAnimation(); return mLock_tag; } void afxShapeConstraint::unlockAnimation(U32 tag) { if (mLock_tag == 0) return; if (!mShape) return; mShape->unlockAnimation(tag); mLock_tag = 0; } F32 afxShapeConstraint::getAnimClipDuration(const char* clip) { return (mShape) ? mShape->getAnimationDuration(clip) : 0.0f; } S32 afxShapeConstraint::getDamageState() { return (mShape) ? mShape->getDamageState() : -1; } U32 afxShapeConstraint::getTriggers() { return (mShape) ? mShape->getShapeInstance()->getTriggerStateMask() : 0; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxShapeNodeConstraint afxShapeNodeConstraint::afxShapeNodeConstraint(afxConstraintMgr* mgr) : afxShapeConstraint(mgr) { mArb_node = ST_NULLSTRING; mShape_node_ID = -1; } afxShapeNodeConstraint::afxShapeNodeConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name, StringTableEntry arb_node) : afxShapeConstraint(mgr, arb_name) { mArb_node = arb_node; mShape_node_ID = -1; } void afxShapeNodeConstraint::set(ShapeBase* shape) { if (shape) { mShape_node_ID = shape->getShape()->findNode(mArb_node); if (mShape_node_ID == -1) Con::errorf("Failed to find node [%s]", mArb_node); } else mShape_node_ID = -1; Parent::set(shape); } void afxShapeNodeConstraint::set_scope_id(U16 scope_id) { mShape_node_ID = -1; Parent::set_scope_id(scope_id); } void afxShapeNodeConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { if (mShape && mShape_node_ID != -1) { mLast_xfm = mShape->getRenderTransform(); mLast_xfm.scale(mShape->getScale()); mLast_xfm.mul(mShape->getShapeInstance()->mNodeTransforms[mShape_node_ID]); mLast_pos = mLast_xfm.getPosition(); } } void afxShapeNodeConstraint::restoreObject(SceneObject* obj) { ShapeBase* shape = dynamic_cast(obj); if (shape) { mShape_node_ID = shape->getShape()->findNode(mArb_node); if (mShape_node_ID == -1) Con::errorf("Failed to find node [%s]", mArb_node); } else mShape_node_ID = -1; Parent::restoreObject(obj); } void afxShapeNodeConstraint::onDeleteNotify(SimObject* obj) { Parent::onDeleteNotify(obj); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxObjectConstraint afxObjectConstraint::afxObjectConstraint(afxConstraintMgr* mgr) : afxConstraint(mgr) { mArb_name = ST_NULLSTRING; mObj = 0; mScope_id = 0; mIs_camera = false; } afxObjectConstraint::afxObjectConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name) : afxConstraint(mgr) { mArb_name = arb_name; mObj = 0; mScope_id = 0; mIs_camera = false; } afxObjectConstraint::~afxObjectConstraint() { if (mObj) clearNotify(mObj); } void afxObjectConstraint::set(SceneObject* obj) { if (mObj) { mScope_id = 0; clearNotify(mObj); } mObj = obj; if (mObj) { deleteNotify(mObj); mScope_id = mObj->getScopeId(); } if (mObj != NULL) { mIs_camera = mObj->isCamera(); mIs_defined = true; mIs_valid = true; mChange_code++; sample(0.0f, 0, 0); } else mIs_valid = false; } void afxObjectConstraint::set_scope_id(U16 scope_id) { if (mObj) clearNotify(mObj); mObj = 0; mScope_id = scope_id; mIs_defined = (scope_id > 0); mIs_valid = false; mMgr->postMissingConstraintObject(this); } void afxObjectConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { if (mGone_missing) return; if (mObj) { if (!mIs_camera && mCons_def.mTreat_as_camera && dynamic_cast(mObj)) { ShapeBase* cam_obj = (ShapeBase*) mObj; F32 pov = 1.0f; cam_obj->getCameraTransform(&pov, &mLast_xfm); mLast_xfm.getColumn(3, &mLast_pos); } else { mLast_xfm = mObj->getRenderTransform(); if (mCons_def.mPos_at_box_center) mLast_pos = mObj->getBoxCenter(); else mLast_pos = mObj->getRenderPosition(); } } } void afxObjectConstraint::restoreObject(SceneObject* obj) { if (mObj) { mScope_id = 0; clearNotify(mObj); } mObj = obj; if (mObj) { deleteNotify(mObj); mScope_id = mObj->getScopeId(); } mIs_valid = (mObj != NULL); } void afxObjectConstraint::onDeleteNotify(SimObject* obj) { if (mObj == dynamic_cast(obj)) { mObj = 0; mIs_valid = false; if (mScope_id > 0) mMgr->postMissingConstraintObject(this, true); } Parent::onDeleteNotify(obj); } U32 afxObjectConstraint::getTriggers() { TSStatic* ts_static = dynamic_cast(mObj); if (ts_static) { TSShapeInstance* obj_inst = ts_static->getShapeInstance(); return (obj_inst) ? obj_inst->getTriggerStateMask() : 0; } ShapeBase* shape_base = dynamic_cast(mObj); if (shape_base) { TSShapeInstance* obj_inst = shape_base->getShapeInstance(); return (obj_inst) ? obj_inst->getTriggerStateMask() : 0; } return 0; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxEffectConstraint afxEffectConstraint::afxEffectConstraint(afxConstraintMgr* mgr) : afxConstraint(mgr) { mEffect_name = ST_NULLSTRING; mEffect = 0; mClip_tag = 0; mIs_death_clip = false; mLock_tag = 0; } afxEffectConstraint::afxEffectConstraint(afxConstraintMgr* mgr, StringTableEntry effect_name) : afxConstraint(mgr) { mEffect_name = effect_name; mEffect = 0; mClip_tag = 0; mIs_death_clip = false; mLock_tag = 0; } afxEffectConstraint::~afxEffectConstraint() { } bool afxEffectConstraint::getPosition(Point3F& pos, F32 hist) { if (!mEffect || !mEffect->inScope()) return false; if (mCons_def.mPos_at_box_center) mEffect->getUpdatedBoxCenter(pos); else mEffect->getUpdatedPosition(pos); return true; } bool afxEffectConstraint::getTransform(MatrixF& xfm, F32 hist) { if (!mEffect || !mEffect->inScope()) return false; mEffect->getUpdatedTransform(xfm); return true; } bool afxEffectConstraint::getAltitudes(F32& terrain_alt, F32& interior_alt) { if (!mEffect) return false; mEffect->getAltitudes(terrain_alt, interior_alt); return true; } void afxEffectConstraint::set(afxEffectWrapper* effect) { mEffect = effect; if (mEffect != NULL) { mIs_defined = true; mIs_valid = true; mChange_code++; } else mIs_valid = false; } U32 afxEffectConstraint::setAnimClip(const char* clip, F32 pos, F32 rate, F32 trans, bool is_death_anim) { return (mEffect) ? mEffect->setAnimClip(clip, pos, rate, trans) : 0; } void afxEffectConstraint::resetAnimation(U32 tag) { if (mEffect) mEffect->resetAnimation(tag); } F32 afxEffectConstraint::getAnimClipDuration(const char* clip) { return (mEffect) ? getAnimClipDuration(clip) : 0; } U32 afxEffectConstraint::getTriggers() { return (mEffect) ? mEffect->getTriggers() : 0; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxEffectNodeConstraint afxEffectNodeConstraint::afxEffectNodeConstraint(afxConstraintMgr* mgr) : afxEffectConstraint(mgr) { mEffect_node = ST_NULLSTRING; mEffect_node_ID = -1; } afxEffectNodeConstraint::afxEffectNodeConstraint(afxConstraintMgr* mgr, StringTableEntry name, StringTableEntry node) : afxEffectConstraint(mgr, name) { mEffect_node = node; mEffect_node_ID = -1; } bool afxEffectNodeConstraint::getPosition(Point3F& pos, F32 hist) { if (!mEffect || !mEffect->inScope()) return false; TSShapeInstance* ts_shape_inst = mEffect->getTSShapeInstance(); if (!ts_shape_inst) return false; if (mEffect_node_ID == -1) { TSShape* ts_shape = mEffect->getTSShape(); mEffect_node_ID = (ts_shape) ? ts_shape->findNode(mEffect_node) : -1; } if (mEffect_node_ID == -1) return false; mEffect->getUpdatedTransform(mLast_xfm); Point3F scale; mEffect->getUpdatedScale(scale); MatrixF gag = ts_shape_inst->mNodeTransforms[mEffect_node_ID]; gag.setPosition( gag.getPosition()*scale ); MatrixF xfm; xfm.mul(mLast_xfm, gag); // pos = xfm.getPosition(); return true; } bool afxEffectNodeConstraint::getTransform(MatrixF& xfm, F32 hist) { if (!mEffect || !mEffect->inScope()) return false; TSShapeInstance* ts_shape_inst = mEffect->getTSShapeInstance(); if (!ts_shape_inst) return false; if (mEffect_node_ID == -1) { TSShape* ts_shape = mEffect->getTSShape(); mEffect_node_ID = (ts_shape) ? ts_shape->findNode(mEffect_node) : -1; } if (mEffect_node_ID == -1) return false; mEffect->getUpdatedTransform(mLast_xfm); Point3F scale; mEffect->getUpdatedScale(scale); MatrixF gag = ts_shape_inst->mNodeTransforms[mEffect_node_ID]; gag.setPosition( gag.getPosition()*scale ); xfm.mul(mLast_xfm, gag); return true; } void afxEffectNodeConstraint::set(afxEffectWrapper* effect) { if (effect) { TSShape* ts_shape = effect->getTSShape(); mEffect_node_ID = (ts_shape) ? ts_shape->findNode(mEffect_node) : -1; } else mEffect_node_ID = -1; Parent::set(effect); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxSampleBuffer afxSampleBuffer::afxSampleBuffer() { mBuffer_sz = 0; mBuffer_ms = 0; mMS_per_sample = 33; mElapsed_ms = 0; mLast_sample_ms = 0; mNext_sample_num = 0; mNum_samples = 0; } afxSampleBuffer::~afxSampleBuffer() { } void afxSampleBuffer::configHistory(F32 hist_len, U8 sample_rate) { mBuffer_sz = mCeil(hist_len*sample_rate) + 1; mMS_per_sample = mCeil(1000.0f/sample_rate); mBuffer_ms = mBuffer_sz*mMS_per_sample; } void afxSampleBuffer::recordSample(F32 dt, U32 elapsed_ms, void* data) { mElapsed_ms = elapsed_ms; if (!data) return; U32 now_sample_num = elapsed_ms/mMS_per_sample; if (mNext_sample_num <= now_sample_num) { mLast_sample_ms = elapsed_ms; while (mNext_sample_num <= now_sample_num) { recSample(mNext_sample_num % mBuffer_sz, data); mNext_sample_num++; mNum_samples++; } } } inline bool afxSampleBuffer::compute_idx_from_lag(F32 lag, U32& idx) { bool in_bounds = true; U32 lag_ms = lag*1000.0f; U32 rec_ms = (mElapsed_ms < mBuffer_ms) ? mElapsed_ms : mBuffer_ms; if (lag_ms > rec_ms) { // hasn't produced enough history lag_ms = rec_ms; in_bounds = false; } U32 latest_sample_num = mLast_sample_ms/mMS_per_sample; U32 then_sample_num = (mElapsed_ms - lag_ms)/mMS_per_sample; if (then_sample_num > latest_sample_num) { // latest sample is older than lag then_sample_num = latest_sample_num; in_bounds = false; } idx = then_sample_num % mBuffer_sz; return in_bounds; } inline bool afxSampleBuffer::compute_idx_from_lag(F32 lag, U32& idx1, U32& idx2, F32& t) { bool in_bounds = true; F32 lag_ms = lag*1000.0f; F32 rec_ms = (mElapsed_ms < mBuffer_ms) ? mElapsed_ms : mBuffer_ms; if (lag_ms > rec_ms) { // hasn't produced enough history lag_ms = rec_ms; in_bounds = false; } F32 per_samp = mMS_per_sample; F32 latest_sample_num = mLast_sample_ms/per_samp; F32 then_sample_num = (mElapsed_ms - lag_ms)/per_samp; U32 latest_sample_num_i = latest_sample_num; U32 then_sample_num_i = then_sample_num; if (then_sample_num_i >= latest_sample_num_i) { if (latest_sample_num_i < then_sample_num_i) in_bounds = false; t = 0.0; idx1 = then_sample_num_i % mBuffer_sz; idx2 = idx1; } else { t = then_sample_num - then_sample_num_i; idx1 = then_sample_num_i % mBuffer_sz; idx2 = (then_sample_num_i+1) % mBuffer_sz; } return in_bounds; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxSampleXfmBuffer afxSampleXfmBuffer::afxSampleXfmBuffer() { mXfm_buffer = 0; } afxSampleXfmBuffer::~afxSampleXfmBuffer() { delete [] mXfm_buffer; } void afxSampleXfmBuffer::configHistory(F32 hist_len, U8 sample_rate) { if (!mXfm_buffer) { afxSampleBuffer::configHistory(hist_len, sample_rate); if (mBuffer_sz > 0) mXfm_buffer = new MatrixF[mBuffer_sz]; } } void afxSampleXfmBuffer::recSample(U32 idx, void* data) { mXfm_buffer[idx] = *((MatrixF*)data); } void afxSampleXfmBuffer::getSample(F32 lag, void* data, bool& in_bounds) { U32 idx1, idx2; F32 t; in_bounds = compute_idx_from_lag(lag, idx1, idx2, t); if (idx1 == idx2) { MatrixF* m1 = &mXfm_buffer[idx1]; *((MatrixF*)data) = *m1; } else { MatrixF* m1 = &mXfm_buffer[idx1]; MatrixF* m2 = &mXfm_buffer[idx2]; Point3F p1 = m1->getPosition(); Point3F p2 = m2->getPosition(); Point3F p; p.interpolate(p1, p2, t); if (t < 0.5f) *((MatrixF*)data) = *m1; else *((MatrixF*)data) = *m2; ((MatrixF*)data)->setPosition(p); } } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxPointHistConstraint afxPointHistConstraint::afxPointHistConstraint(afxConstraintMgr* mgr) : afxPointConstraint(mgr) { mSamples = 0; } afxPointHistConstraint::~afxPointHistConstraint() { delete mSamples; } void afxPointHistConstraint::set(Point3F point, Point3F vector) { if (!mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set(point, vector); } void afxPointHistConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { Parent::sample(dt, elapsed_ms, cam_pos); if (isDefined()) { if (isValid()) mSamples->recordSample(dt, elapsed_ms, &mLast_xfm); else mSamples->recordSample(dt, elapsed_ms, 0); } } bool afxPointHistConstraint::getPosition(Point3F& pos, F32 hist) { bool in_bounds; MatrixF xfm; mSamples->getSample(hist, &xfm, in_bounds); pos = xfm.getPosition(); return in_bounds; } bool afxPointHistConstraint::getTransform(MatrixF& xfm, F32 hist) { bool in_bounds; mSamples->getSample(hist, &xfm, in_bounds); return in_bounds; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxPointHistConstraint afxTransformHistConstraint::afxTransformHistConstraint(afxConstraintMgr* mgr) : afxTransformConstraint(mgr) { mSamples = 0; } afxTransformHistConstraint::~afxTransformHistConstraint() { delete mSamples; } void afxTransformHistConstraint::set(const MatrixF& xfm) { if (!mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set(xfm); } void afxTransformHistConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { Parent::sample(dt, elapsed_ms, cam_pos); if (isDefined()) { if (isValid()) mSamples->recordSample(dt, elapsed_ms, &mLast_xfm); else mSamples->recordSample(dt, elapsed_ms, 0); } } bool afxTransformHistConstraint::getPosition(Point3F& pos, F32 hist) { bool in_bounds; MatrixF xfm; mSamples->getSample(hist, &xfm, in_bounds); pos = xfm.getPosition(); return in_bounds; } bool afxTransformHistConstraint::getTransform(MatrixF& xfm, F32 hist) { bool in_bounds; mSamples->getSample(hist, &xfm, in_bounds); return in_bounds; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxShapeHistConstraint afxShapeHistConstraint::afxShapeHistConstraint(afxConstraintMgr* mgr) : afxShapeConstraint(mgr) { mSamples = 0; } afxShapeHistConstraint::afxShapeHistConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name) : afxShapeConstraint(mgr, arb_name) { mSamples = 0; } afxShapeHistConstraint::~afxShapeHistConstraint() { delete mSamples; } void afxShapeHistConstraint::set(ShapeBase* shape) { if (shape && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set(shape); } void afxShapeHistConstraint::set_scope_id(U16 scope_id) { if (scope_id > 0 && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set_scope_id(scope_id); } void afxShapeHistConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { Parent::sample(dt, elapsed_ms, cam_pos); if (isDefined()) { if (isValid()) mSamples->recordSample(dt, elapsed_ms, &mLast_xfm); else mSamples->recordSample(dt, elapsed_ms, 0); } } bool afxShapeHistConstraint::getPosition(Point3F& pos, F32 hist) { bool in_bounds; MatrixF xfm; mSamples->getSample(hist, &xfm, in_bounds); pos = xfm.getPosition(); return in_bounds; } bool afxShapeHistConstraint::getTransform(MatrixF& xfm, F32 hist) { bool in_bounds; mSamples->getSample(hist, &xfm, in_bounds); return in_bounds; } void afxShapeHistConstraint::onDeleteNotify(SimObject* obj) { Parent::onDeleteNotify(obj); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxShapeNodeHistConstraint afxShapeNodeHistConstraint::afxShapeNodeHistConstraint(afxConstraintMgr* mgr) : afxShapeNodeConstraint(mgr) { mSamples = 0; } afxShapeNodeHistConstraint::afxShapeNodeHistConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name, StringTableEntry arb_node) : afxShapeNodeConstraint(mgr, arb_name, arb_node) { mSamples = 0; } afxShapeNodeHistConstraint::~afxShapeNodeHistConstraint() { delete mSamples; } void afxShapeNodeHistConstraint::set(ShapeBase* shape) { if (shape && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set(shape); } void afxShapeNodeHistConstraint::set_scope_id(U16 scope_id) { if (scope_id > 0 && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set_scope_id(scope_id); } void afxShapeNodeHistConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { Parent::sample(dt, elapsed_ms, cam_pos); if (isDefined()) { if (isValid()) mSamples->recordSample(dt, elapsed_ms, &mLast_xfm); else mSamples->recordSample(dt, elapsed_ms, 0); } } bool afxShapeNodeHistConstraint::getPosition(Point3F& pos, F32 hist) { bool in_bounds; MatrixF xfm; mSamples->getSample(hist, &xfm, in_bounds); pos = xfm.getPosition(); return in_bounds; } bool afxShapeNodeHistConstraint::getTransform(MatrixF& xfm, F32 hist) { bool in_bounds; mSamples->getSample(hist, &xfm, in_bounds); return in_bounds; } void afxShapeNodeHistConstraint::onDeleteNotify(SimObject* obj) { Parent::onDeleteNotify(obj); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // afxObjectHistConstraint afxObjectHistConstraint::afxObjectHistConstraint(afxConstraintMgr* mgr) : afxObjectConstraint(mgr) { mSamples = 0; } afxObjectHistConstraint::afxObjectHistConstraint(afxConstraintMgr* mgr, StringTableEntry arb_name) : afxObjectConstraint(mgr, arb_name) { mSamples = 0; } afxObjectHistConstraint::~afxObjectHistConstraint() { delete mSamples; } void afxObjectHistConstraint::set(SceneObject* obj) { if (obj && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set(obj); } void afxObjectHistConstraint::set_scope_id(U16 scope_id) { if (scope_id > 0 && !mSamples) { mSamples = new afxSampleXfmBuffer; mSamples->configHistory(mCons_def.mHistory_time, mCons_def.mSample_rate); } Parent::set_scope_id(scope_id); } void afxObjectHistConstraint::sample(F32 dt, U32 elapsed_ms, const Point3F* cam_pos) { Parent::sample(dt, elapsed_ms, cam_pos); if (isDefined()) { if (isValid()) mSamples->recordSample(dt, elapsed_ms, &mLast_xfm); else mSamples->recordSample(dt, elapsed_ms, 0); } } bool afxObjectHistConstraint::getPosition(Point3F& pos, F32 hist) { bool in_bounds; MatrixF xfm; mSamples->getSample(hist, &xfm, in_bounds); pos = xfm.getPosition(); return in_bounds; } bool afxObjectHistConstraint::getTransform(MatrixF& xfm, F32 hist) { bool in_bounds; mSamples->getSample(hist, &xfm, in_bounds); return in_bounds; } void afxObjectHistConstraint::onDeleteNotify(SimObject* obj) { Parent::onDeleteNotify(obj); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//