BOOL plDistributor::ISpaceClear(int iRepNode, const Matrix3& l2w, Box3& clearBox, plMeshCacheTab& cache) const { if( !fDistTree ) return true; // If we have high isolation, // clearBox = Box3(Point3(-fSpacing*0.5f, -fSpacing*0.5f, 0), Point3(fSpacing*0.5f, fSpacing*0.5f, fSpacing)); // Else if we have medium isolation // clearBox = cache[iRepNode]->getBoundingBox(); // The mesh's bounds // Else if we have low isolation or None isolation // clearBox = Box3(Point3(-kSmallSpace, -kSmallSpace, 0), Point3(kSmallSpace, kSmallSpace, kSmallSpace)); // kSmallSpace ~= 0.5f or one or something // We want to set up the box (for high, low and none) in Post OTM space. So instead of multiplying // by l2w, we want to multiply box = box * invOTM * l2w (because l2w already has OTM folded in). When using // the mesh bounds (Medium), l2w is the right transform. // objectTM = otm * nodeTM // invOTM * objectTM = nodeTM // invOTM = nodeTM * invObjectTM const float kSmallSpace = 0.5f; switch( fIsolation ) { case kIsoHigh: { INode* repNode = fRepNodes[iRepNode]; Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); Matrix3 invOTM = nodeTM * Inverse(objectTM); clearBox = Box3(Point3(-fSpacing*0.5f, -fSpacing*0.5f, 0.f), Point3(fSpacing*0.5f, fSpacing*0.5f, fSpacing)); clearBox = clearBox * invOTM; } break; case kIsoMedium: clearBox = cache[iRepNode].fMesh->getBoundingBox(); // The mesh's bounds break; case kIsoLow: case kIsoNone: default: { INode* repNode = fRepNodes[iRepNode]; Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); Matrix3 invOTM = nodeTM * Inverse(objectTM); clearBox = Box3(Point3(-kSmallSpace, -kSmallSpace, 0.f), Point3(kSmallSpace, kSmallSpace, kSmallSpace)); clearBox = clearBox * invOTM; } break; } clearBox = clearBox * l2w; return fDistTree->BoxClear(clearBox, fFade); }
static float CalcScale(INode *bone, NiNodeRef node, vector<NiNodeRef>& children) { int n = bone->NumberOfChildren(); if (n > 0) { float len1 = 0.0f; float len2 = 0.0f; Matrix3 m = bone->GetNodeTM(0); Matrix3 m2 = TOMATRIX3(node->GetWorldTransform()); for (int i = 0; i<n; i++) { INode *child = bone->GetChildNode(i); LPCTSTR name = child->GetName(); if (HasBipedPosDOF(name)) continue; Matrix3 cm = child->GetObjectTM(0); len1 += Length(m.GetTrans()-cm.GetTrans()); if (NiNodeRef child2 = FindNodeByName(children, string(child->GetName()))){ Matrix3 cm2 = TOMATRIX3(child2->GetWorldTransform()); len2 += Length(m2.GetTrans()-cm2.GetTrans()); } } return (len2 != 0.0f && len1 != 0.0f) ? (len2/len1) : 1.0f; } return 1.0f; }
Point3 PBombField::Force(TimeValue t,const Point3 &pos, const Point3 &vel,int index) { float d,chaos,dv; Point3 dlta,xb,yb,zb,center,expv; int decaytype,symm; Point3 zero=Zero; fValid = FOREVER; if (!tmValid.InInterval(t)) { tmValid = FOREVER; tm = node->GetObjectTM(t,&tmValid); invtm = Inverse(tm); } xb=tm.GetRow(0); yb=tm.GetRow(1); zb=tm.GetRow(2); center=tm.GetTrans(); fValid &= tmValid; TimeValue t0,t2,lastsfor; obj->pblock->GetValue(PB_STARTTIME,t,t0,fValid); obj->pblock->GetValue(PB_LASTSFOR,t,lastsfor,fValid); t2=t0+lastsfor; dlta=Zero; if ((t>=t0)&&(t<=t2)) { float L=Length(dlta=pos-center); obj->pblock->GetValue(PB_DECAY,t,d,fValid); obj->pblock->GetValue(PB_DECAYTYPE,t,decaytype,fValid); if ((decaytype==0)||(L<=d)) { obj->pblock->GetValue(PB_DELTA_V,t,dv,fValid); obj->pblock->GetValue(PB_CHAOS,t,chaos,fValid); obj->pblock->GetValue(PB_SYMMETRY,t,symm,fValid); Point3 r; if (symm==SPHERE) expv=(r=dlta/L); else if (symm==PLANAR) { L=DotProd(dlta,zb); expv=(L<0.0f?L=-L,-zb:zb); } else { Point3 E; E=DotProd(dlta,xb)*xb+DotProd(dlta,yb)*yb; L=Length(E); expv=E/L; } dlta=(dv*expv)/(float)dtsq; if (decaytype==1) dlta*=(d-L)/d; else if (decaytype==2) dlta*=(1/(float)exp(L/d)); if ((!FloatEQ0(chaos))&&(lastsfor==0.0f)) { float theta; theta=HalfPI*chaos*RND01(); // Martell 4/14/01: Fix for order of ops bug. float ztmp=RND11(); float ytmp=RND11(); float xtmp=RND11(); Point3 d=Point3(xtmp,ytmp,ztmp); Point3 c=Normalize(dlta^d); RotateOnePoint(&dlta.x,&zero.x,&c.x,theta); } } else dlta=Zero; } return dlta; }
//================================================================= // Methods for CollectNodesTEP // int CollectNodesTEP::callback(INode *node) { INode *pnode = node; // Hungarian ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!"); if (::FNodeMarkedToSkip(pnode)) return TREE_CONTINUE; // Get pre-stored "index" int iNode = ::GetIndexOfINode(pnode); ASSERT_MBOX(iNode >= 0 && iNode <= m_phec->m_imaxnodeMac-1, "Bogus iNode"); // Get name, store name in array TSTR strNodeName(pnode->GetName()); strcpy(m_phec->m_rgmaxnode[iNode].szNodeName, (char*)strNodeName); // Get Node's time-zero Transformation Matrices m_phec->m_rgmaxnode[iNode].mat3NodeTM = pnode->GetNodeTM(0/*TimeValue*/); m_phec->m_rgmaxnode[iNode].mat3ObjectTM = pnode->GetObjectTM(0/*TimeValue*/); // I'll calculate this later m_phec->m_rgmaxnode[iNode].imaxnodeParent = SmdExportClass::UNDESIRABLE_NODE_MARKER; return TREE_CONTINUE; }
void PolyOpExtrudeAlongSpline::GetValues (IParamBlock2 *pblock, TimeValue t, Interval & ivalid) { PolyOperation::GetValues (pblock, t, ivalid); // Special case for the spline & transform. if (!mSplineValidity.InInterval (t)) { mSplineValidity.SetInfinite (); INode *pSplineNode; pblock->GetValue (kEPExtrudeSpline, t, pSplineNode, mSplineValidity); if (pSplineNode == NULL) mpSpline = NULL; else { bool del = FALSE; SplineShape *pSplineShape = NULL; ObjectState os = pSplineNode->GetObjectRef()->Eval(t); if (os.obj->IsSubClassOf(splineShapeClassID)) pSplineShape = (SplineShape *) os.obj; else { if (!os.obj->CanConvertToType(splineShapeClassID)) return; pSplineShape = (SplineShape*)os.obj->ConvertToType (t, splineShapeClassID); if (pSplineShape!=os.obj) del = TRUE; } BezierShape & bezShape = pSplineShape->GetShape(); Matrix3 mSplineXfm = pSplineNode->GetObjectTM (t, &mSplineValidity); if (mpSpline != NULL) delete mpSpline; mpSpline = new Spline3D(*bezShape.GetSpline(0)); if (del) delete pSplineShape; } } ivalid &= mSplineValidity; }
bool HoudiniEngineMesh::SetInputNode(int ch, INode* node) { bool result = false; // Input Nodes if (inputs.getAssetId() >= 0) { TimeValue t = GetCOREInterface()->GetTime(); INode * selfNode = GetINode(); Matrix3 baseTM(1); if (selfNode) baseTM = selfNode->GetObjectTM(t); bool conv_unit_i = pblock2->GetInt(pb_conv_unit_i) != 0; double scl = conv_unit_i ? GetRelativeScale(GetUSDefaultUnit(), 1, UNITS_METERS, 1) : 1.0; inputs.setNode(ch, node, t, baseTM, scl, needUpdateInputNode ); result = true; #if defined(USE_NOTIFYREFCHANGED) if (node->TestForLoop(FOREVER, this) == REF_SUCCEED) { ReplaceReference(1+ch, (RefTargetHandle)node); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); NotifyDependents(FOREVER, 0, REFMSG_SUBANIM_STRUCTURE_CHANGED); } #endif } return result; }
static float CalcLength(INode *bone) { int n = bone->NumberOfChildren(); float len = 0.0f; if (n > 0) { Matrix3 m = bone->GetNodeTM(0); Point3 p = m.GetTrans(); for (int i = 0; i<n; i++) { INode *child = bone->GetChildNode(i); Matrix3 cm = child->GetObjectTM(0); Point3 cp = cm.GetTrans(); float clen = Length(p-cp); len += clen; } len /= float(n); } else { len = Length(GetLocalTM(bone).GetTrans()); } return len; }
Matrix3 plDistributor::IOTM(int iRepNode) const { // objectTM = otm * nodeTM // objectTM * Inverse(nodeTM) = otm INode* repNode = fRepNodes[iRepNode]; Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); Matrix3 OTM = objectTM * Inverse(nodeTM); return OTM; }
Matrix3 plDistributor::IInvOTM(int iRepNode) const { // objectTM = otm * nodeTM // invOTM * objectTM = nodeTM // invOTM = nodeTM * invObjectTM INode* repNode = fRepNodes[iRepNode]; Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); Matrix3 invOTM = nodeTM * Inverse(objectTM); return invOTM; }
MeshTopoData *TweakMouseProc::HitTest(ViewExp& vpt, IPoint2 m, int &hitVert) { if ( ! vpt.IsAlive() ) { // why are we here DbgAssert(!_T("Invalid viewport!")); return NULL; } int savedLimits; TimeValue t = GetCOREInterface()->GetTime(); GraphicsWindow *gw = vpt.getGW(); HitRegion hr; int crossing = TRUE; int type = HITTYPE_POINT; MakeHitRegion(hr,type, crossing,8,&m); gw->setHitRegion(&hr); hitVert = -1; MeshTopoData *hitLD = NULL; mHitLDIndex = -1; // else gw->setRndLimits(gw->getRndLimits() & ~GW_BACKCULL); savedLimits = gw->getRndLimits(); for (int ldID = 0; ldID < mod->GetMeshTopoDataCount(); ldID++) { MeshTopoData *ld = mod->GetMeshTopoData(ldID); INode *inode = mod->GetMeshTopoDataNode(ldID); Matrix3 mat = inode->GetObjectTM(t); gw->setTransform(mat); gw->setRndLimits(((gw->getRndLimits() | GW_PICK) & ~GW_ILLUM) | GW_BACKCULL); ModContext mc; int hindex = mod->peltData.HitTestPointToPointMode(mod,ld,vpt,gw,&m,hr,inode,&mc); if (hindex != -1) { hitVert = hindex; hitLD = ld; mHitLDIndex = ldID; } } gw->setRndLimits(savedLimits); return hitLD; }
Point3 PinField::Force(TimeValue t,const Point3 &pos, const Point3 &vel,int index) //ok { fValid= FOREVER; if (!tmValid.InInterval(t)) { tmValid=FOREVER; tm=node->GetObjectTM(t,&tmValid); invtm=Inverse(tm); } fValid&=tmValid; TimeValue t1,t2; obj->pblock->GetValue(PB_ONTIME,t,t1,fValid); obj->pblock->GetValue(PB_OFFTIME,t,t2,fValid); Point3 OutPin; if ((t>=t1)&&(t<=t2)) OutPin = Zero; else OutPin = Zero; return OutPin*6.25e-03f; }
Interval BombMod::LocalValidity(TimeValue t) { BombObject *obj = GetWSMObject(t); if (obj && nodeRef && !waitPostLoad && (obj->ClassID() == Class_ID(BOMB_OBJECT_CLASS_ID,0))) { // Force a cache of input if being edited. if (TestAFlag(A_MOD_BEING_EDITED)) return NEVER; Interval valid = FOREVER; Matrix3 tm; obj->GetStrength(t,valid); obj->GetGravity(t,valid); obj->GetDetonation(t,valid); tm = nodeRef->GetObjectTM(t,&valid); return valid; } else { return FOREVER; } }
// Clone the replicant and set its transform appropriately. Should set the plMaxNode property // that the node's spans are collapseable? No, we'll make a separate component for that. void plDistributor::IReplicate(Matrix3& l2w, int iRepNode, plDistribInstTab& reps, plMeshCache& mCache) const { INode* repNode = fRepNodes[iRepNode]; plDistribInstance inst; inst.fNode = repNode; inst.fNodeTM = l2w; inst.fObjectTM = repNode->GetObjectTM(TimeValue(0)) * Inverse(repNode->GetNodeTM(TimeValue(0))) * l2w; inst.fMesh = mCache.fMesh; inst.fFlex = mCache.fFlex; inst.fFade = fFade; inst.fBone = fBone; inst.fRigid = fRigid; reps.Append(1, &inst); }
Point3 PinField::Force(TimeValue t,const Point3 &pos, const Point3 &vel,int index) //ok { fValid= FOREVER; if (!tmValid.InInterval(t)) { tmValid=FOREVER; tm=node->GetObjectTM(t,&tmValid); invtm=Inverse(tm); } fValid&=tmValid; TimeValue t1,t2; if (obj == NULL) return Point3(0.0f,0.0f,0.0f); //667105 watje xrefs can change the base object type through proxies which will cause this to be null or the user can copy a new object type over ours obj->pblock->GetValue(PB_ONTIME,t,t1,fValid); obj->pblock->GetValue(PB_OFFTIME,t,t2,fValid); Point3 OutPin; if ((t>=t1)&&(t<=t2)) OutPin = Zero; else OutPin = Zero; return OutPin*6.25e-03f; }
static AngAxis CalcTransform(INode *bone, NiNodeRef node, vector<NiNodeRef>& children) { Matrix3 mr(TRUE); int n = bone->NumberOfChildren(); if (n > 0) { int c = 0; Point3 vs(0.0f, 0.0f, 0.0f), vf(0.0f, 0.0f, 0.0f); Matrix3 m = bone->GetNodeTM(0); Matrix3 m2 = TOMATRIX3(node->GetWorldTransform()); for (int i = 0; i<n; i++) { INode *child = bone->GetChildNode(i); LPCTSTR name = child->GetName(); if (HasBipedPosDOF(name)) continue; Matrix3 cm = child->GetObjectTM(0); vs += (m.GetTrans()-cm.GetTrans()); if (NiNodeRef child2 = FindNodeByName(children, string(child->GetName()))){ Matrix3 cm2 = TOMATRIX3(child2->GetWorldTransform()); vf += (m2.GetTrans()-cm2.GetTrans()); } ++c; } vs = FNormalize(vs); vf = FNormalize(vf); Point3 cross = CrossProd(vs, vf); if (fabs(cross.x) < 0.01 && fabs(cross.y) < 0.01 && fabs(cross.z) < 0.01) return AngAxis(Point3(0.0f, 0.0f, 0.0f), 0.0f); float dot = DotProd(vs, vf); return AngAxis( cross, acos( dot ) ); } return mr; }
/** * @brief * Constructor */ PLSceneNode::PLSceneNode(PLSceneContainer *pContainer, IGameNode *pIGameNode, const String &sName, EType nType, const String &sClassName) : m_pContainer(pContainer), m_pIGameNode(pIGameNode), m_sName(sName), m_nType(nType), m_sClassName(sClassName), m_sFlags(""), m_vPos(0.0f, 0.0f, 0.0f), m_vRot(0.0f, 0.0f, 0.0f), m_vScale(1.0f, 1.0f, 1.0f), m_nIsRotationFlipped(-1) { // Check some universal flags INode *pMaxNode = GetMaxNode(); if (pMaxNode) { // If this is not a container... TSTR sString; // Check whether the default PixelLight class is changed if (pMaxNode->GetUserPropString(_T("Class"), sString)) { m_sClassName = sString; // Erase all '"' int i = m_sClassName.IndexOf("\""); while (i >= 0) { m_sClassName.Delete(i, 1); i = m_sClassName.IndexOf("\""); } } // Is this 3ds Max node frozen? if (pMaxNode->IsFrozen()) AddFlag("Frozen"); // Is this 3ds Max node invisible? if (pMaxNode->IsHidden() || !pMaxNode->Renderable()) AddFlag("Invisible"); // Is this 3ds Max node excluded from lighting? INodeGIProperties *pINodeGIProperties = static_cast<INodeGIProperties*>(pMaxNode->GetInterface(NODEGIPROPERTIES_INTERFACE)); if (pINodeGIProperties && pINodeGIProperties->GIGetIsExcluded()) AddFlag("NoLighting"); { // Get the world space bounding box of the scene node. Because this is not 'trival' we're using // the sample code from "3dsMaxSDK.chm" (3ds Max SDK) -> "The Pipeline and the INode TM Methods" // to get it working correctly. ::Object *pMaxObject = pMaxNode->GetObjectRef(); if (pMaxObject) { TimeValue t = 0; Matrix3 mat; // The Object TM // Determine if the object is in world space or object space // so we can get the correct TM. We can check this by getting // the Object TM after the world space modifiers have been // applied. It the matrix returned is the identity matrix the // points of the object have been transformed into world space. if (pMaxNode->GetObjTMAfterWSM(t).IsIdentity()) { // It's in world space, so put it back into object // space. We can do this by computing the inverse // of the matrix returned before any world space // modifiers were applied. mat = Inverse(pMaxNode->GetObjTMBeforeWSM(t)); } else { // It's in object space, get the Object TM mat = pMaxNode->GetObjectTM(t); } // Get the bound box, and affect it by just the scaling portion pMaxObject->GetDeformBBox(t, m_cBoundingBox, &mat); } } // We really need to flip the coordinates to OpenGL style m_cBoundingBox.pmin = PLTools::Convert3dsMaxVectorToOpenGLVector(m_cBoundingBox.pmin); m_cBoundingBox.pmax = PLTools::Convert3dsMaxVectorToOpenGLVector(m_cBoundingBox.pmax); // Validate minimum/maximum - I already had situations with incorrect values causing problems! PLTools::ValidateMinimumMaximum(m_cBoundingBox); } // Get the position, rotation and scale GetPosRotScale(m_vPos, m_vRot, m_vScale); }
Point3 WindField::Force( TimeValue t,const Point3 &pos, const Point3 &vel,int index) { if (!obj) return Point3(0.0f, 0.0f, 0.0f); float strength, decay, turb; obj->pblock2->GetValue(PB_DECAY,t,decay,fValid); obj->pblock2->GetValue(PB_TURBULENCE,t,turb,FOREVER); if (decay <0.0f) decay = 0.0f; if (!fValid.InInterval(t) || type==FORCE_SPHERICAL || decay!=0.0f) { fValid = FOREVER; if (!tmValid.InInterval(t)) { tmValid = FOREVER; tm = node->GetObjectTM(t,&tmValid); } fValid &= tmValid; obj->pblock2->GetValue(PB_STRENGTH,t,strength,fValid); if (type==FORCE_PLANAR) { force = Normalize(tm.GetRow(2)); if (decay!=0.0f) { float dist = (float)fabs(DotProd(force,pos-tm.GetTrans())); strength *= (float)exp(-decay*dist); } force *= strength * 0.0001f * forceScaleFactor; } else { float dist; force = pos-tm.GetTrans(); dist = Length(force); if (dist!=0.0f) force /= dist; if (decay!=0.0f) { strength *= (float)exp(-decay*dist); } force *= strength * 0.0001f * forceScaleFactor; } } if (turb!=0.0f) { float freq, scale; Point3 tf, pt = pos-tm.GetTrans(), p; obj->pblock2->GetValue(PB_FREQUENCY,t,freq,FOREVER); obj->pblock2->GetValue(PB_SCALE,t,scale,FOREVER); freq *= 0.01f; turb *= 0.0001f * forceScaleFactor; p = pt; p.x = freq * float(t); tf.x = RTurbulence(p,scale); p = pt; p.y = freq * float(t); tf.y = RTurbulence(p,scale); p = pt; p.z = freq * float(t); tf.z = RTurbulence(p,scale); return force + (turb*tf); } else { return force; } }
BOOL TrackMouseCallBack::point_on_obj(ViewExp& vpt, IPoint2 m, Point3& pt, Point3 &norm) { if ( ! vpt.IsAlive() ) { // why are we here DbgAssert(!_T("Invalid viewport!")); return FALSE; } // computes the normal ray at the point of intersection Ray ray, world_ray; float at, best_dist = 0.0f; TimeValue t = MAXScript_time(); Object *obj = NULL; Matrix3 obtm, iobtm; Point3 testNorm; BOOL found_hit = FALSE; vl->face_num_val = vl->face_bary = &undefined; hit_node = NULL; // Calculate a ray from the mouse point vpt.MapScreenToWorldRay(float(m.x), float(m.y), world_ray); for( int i=(nodeTab.Count()-1); i>=0; i-- ) { // Get the object from the node INode* node = nodeTab[i]; ObjectState os = node->EvalWorldState(t); obj = os.obj; // Back transform the ray into object space. obtm = node->GetObjectTM(t); iobtm = Inverse(obtm); ray.p = iobtm * world_ray.p; ray.dir = VectorTransform(iobtm, world_ray.dir); // See if we hit the object if (obj->IsSubClassOf(triObjectClassID)) { TriObject* tobj = (TriObject*)obj; DWORD fi; Point3 bary; if (tobj->mesh.IntersectRay(ray, at, testNorm, fi, bary) && ((!found_hit) || (at<=best_dist)) ) { // Calculate the hit point and transform everything back into world space. // record the face index and bary coord best_dist = at; pt = ray.p + ray.dir * at; pt = pt * obtm; norm = Normalize(VectorTransform(obtm, testNorm)); vl->face_num_val = Integer::intern(fi + 1); vl->face_bary = new Point3Value(bary); hit_node = node; found_hit = TRUE; } } else if (obj->IntersectRay(t, ray, at, testNorm) && ((!found_hit) || (at<=best_dist)) ) { // Calculate the hit point and transform everything back into world space. best_dist = at; pt = ray.p + ray.dir * at; pt = pt * obtm; norm = Normalize(VectorTransform(obtm, testNorm)); hit_node = node; found_hit = TRUE; } } if( found_hit ) return TRUE; // Failed to find a hit on any node, look at the Normal Align Vector for the first node if ((obj!=NULL) && obj->NormalAlignVector(t, pt, testNorm)) // See if a default NA vector is provided { pt = pt * obtm; norm = Normalize(VectorTransform(obtm, testNorm)); return TRUE; } else return FALSE; }
void ParticleMesherObject::BuildMesh(TimeValue t) { //check if render time //get node //mkae ivalid interesect with the int isRendering = 0; ivalid = FOREVER; TimeValue offset; float foffset; Interval iv; pblock2->GetValue(particlemesher_time, 0, foffset, iv); foffset = -foffset; pblock2->GetValue(particlemesher_radius, 0, radius, iv); foffset *= GetTicksPerFrame(); offset = (TimeValue) foffset; // pblock2->GetValue(particlemesher_time, 0, foffset, iv); if ((lastTime == t) ) { ivalid.Set(t,t); return; } pblock2->GetValue(particlemesher_rendertimeonly, 0, isRendering, ivalid); isRendering = !isRendering; if ((isRendering) || (TestAFlag(A_RENDER))) { INode *node=NULL; pblock2->GetValue(particlemesher_pick, 0, node, ivalid); BOOL reevalGroup = FALSE; if ((node != NULL) && (node->IsGroupHead()) ) { for (int ch=0;ch<node->NumberOfChildren();ch++) { INode *cnode= node->GetChildNode(ch); Interval iv; Matrix3 tm=cnode->GetObjectTM(t,&iv); if (cnode->IsGroupMember()) { reevalGroup = TRUE; for (int groupCount = 0; groupCount < pblock2->Count(particlemesher_extranodes); groupCount++) { INode *extraNode = pblock2->GetINode(particlemesher_extranodes,t,ch); if (cnode == extraNode) { reevalGroup = FALSE; groupCount = pblock2->Count(particlemesher_extranodes); } } if (reevalGroup) ch=node->NumberOfChildren(); } } if (reevalGroup) { tmList.ZeroCount(); pblock2->ZeroCount(particlemesher_extranodes); for (int ch=0;ch<node->NumberOfChildren();ch++) { INode *cnode= node->GetChildNode(ch); Interval iv; Matrix3 tm=cnode->GetObjectTM(t,&iv); if (cnode->IsGroupMember()) { pblock2->Append(particlemesher_extranodes,1,&cnode); tmList.Append(1,&tm); } } } } if (node) { if ( (node->IsGroupHead()) && (pblock2->Count(particlemesher_extranodes)!=0)) { int ct = 0; Matrix3 ident(1), inverseTm(1); mesh.setNumVerts(0); mesh.setNumFaces(0); for (int ch=0;ch<pblock2->Count(particlemesher_extranodes);ch++) { INode *cnode = pblock2->GetINode(particlemesher_extranodes,t,ch); if (cnode) { Object *pobj = cnode->EvalWorldState(t+offset).obj; if ( (pobj->SuperClassID() == GEOMOBJECT_CLASS_ID) || (pobj->SuperClassID() == SHAPE_CLASS_ID) ) { BOOL needDel; NullView nullView; Mesh *msh = ((GeomObject*)pobj)->GetRenderMesh(t+offset,cnode,nullView,needDel); Mesh tmsh = *msh; ivalid &= pobj->ObjectValidity(t+offset); Matrix3 tm(1); if (ch < tmList.Count()) tm = tmList[ch]; for (int v = 0; v < msh->numVerts; v++) { tmsh.verts[v] = tmsh.verts[v] * tm; } if (tmsh.numVerts != 0) { if (ct ==0) { mesh = tmsh; } else mesh = mesh + tmsh; ct++; } if (needDel) delete msh; } } } mesh.InvalidateTopologyCache(); } else { // Object *tobj = node->GetObjectRef(); // macroRecorder->FunctionCall(_T("time"), 1, 0, mr_int, t); // macroRecorder->EmitScript(); ObjectState os = node->EvalWorldState(t+offset); IParticleObjectExt* epobj; epobj = (IParticleObjectExt*) os.obj->GetInterface(PARTICLEOBJECTEXT_INTERFACE); if (os.obj->IsParticleSystem() && epobj) { if (epobj) { BOOL useAllPFEvents; pblock2->GetValue(particlemesher_useallpf,0,useAllPFEvents,FOREVER); pfNodes.ZeroCount(); INode *basenode=NULL; pblock2->GetValue(particlemesher_pick, 0, basenode, ivalid); tmList.ZeroCount(); if (useAllPFEvents) { MyEnumProc dep; os.obj->DoEnumDependents(&dep); for (int i = 0; i < dep.Nodes.Count(); i++) { Interval valid; INode *node = dep.Nodes[i]; Object *obj = node->GetObjectRef(); if (ParticleGroupInterface(obj) != nullptr) { pfNodes.Append(1,&node); Matrix3 tm = node->GetNodeTM(t+offset); tmList.Append(1,&tm); } } } else { int ct = pblock2->Count(particlemesher_pfeventlist); for (int i = 0; i < ct; i++) { INode *node; pblock2->GetValue(particlemesher_pfeventlist,t,node,FOREVER,i); if (node) { Object *obj = node->GetObjectRef(); if (ParticleGroupInterface(obj) != nullptr) { pfNodes.Append(1,&node); Matrix3 tm(1);// = basenode->GetNodeTM(t+offset); Matrix3 ntm = node->GetObjectTM(t+offset); tm = ntm; tmList.Append(1,&ntm); } } } } mesh.setNumVerts(0); mesh.setNumFaces(0); int ct = 0; for (int ch=0;ch< pfNodes.Count();ch++) { INode *cnode = pfNodes[ch]; if (cnode) { Object *pobj = cnode->EvalWorldState(t+offset).obj; if ( (pobj->SuperClassID() == GEOMOBJECT_CLASS_ID) || (pobj->SuperClassID() == SHAPE_CLASS_ID) ) { BOOL needDel; NullView nullView; Mesh *msh = ((GeomObject*)pobj)->GetRenderMesh(t+offset,cnode,nullView,needDel); Mesh tmsh = *msh; ivalid &= pobj->ObjectValidity(t+offset); Matrix3 tm(1); if (ch < tmList.Count()) tm = tmList[ch]; for (int v = 0; v < msh->numVerts; v++) { tmsh.verts[v] = tmsh.verts[v] * tm; } if (tmsh.numVerts != 0) { if (ct ==0) { mesh = tmsh; } else { Mesh tempMesh = mesh; CombineMeshes(mesh, tempMesh, tmsh); } ct++; } if (needDel) delete msh; } } } mesh.InvalidateTopologyCache(); } } else if ( (os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID) || (os.obj->SuperClassID() == SHAPE_CLASS_ID) ) { BOOL needDel; NullView nullView; Mesh *msh = ((GeomObject*)os.obj)->GetRenderMesh(t+offset,node,nullView,needDel); ivalid &= os.obj->ObjectValidity(t); if (msh) { mesh = *msh; mesh.InvalidateTopologyCache(); if (needDel) delete msh; } } } lastTime = t; } else { //build proxy mesh if (node == NULL) { mesh.setNumVerts(5); mesh.setNumFaces(8); mesh.setNumMaps(2); mesh.setNumMapVerts(0, 0); mesh.setNumMapVerts(1, 0); mesh.setNumMapFaces(0, 0); mesh.setNumMapFaces(1, 0); mesh.setVert(0, Point3(-radius,-radius, 0.0f)); mesh.setVert(1, Point3( radius,-radius, 0.0f)); mesh.setVert(2, Point3( radius, radius, 0.0f)); mesh.setVert(3, Point3(-radius, radius, 0.0f)); // mesh.setVert(4, Point3(0.0f, 0.0f, 0.0f)); mesh.setVert(4, Point3(0.0f, 0.0f, radius)); mesh.faces[0].setEdgeVisFlags(1,0,1); mesh.faces[0].setSmGroup(1); mesh.faces[0].setVerts(0,1,3); mesh.faces[1].setEdgeVisFlags(1,1,0); mesh.faces[1].setSmGroup(1); mesh.faces[1].setVerts(1,2,3); mesh.faces[2].setEdgeVisFlags(1,1,1); mesh.faces[2].setSmGroup(1); mesh.faces[2].setVerts(0,4,1); mesh.faces[3].setEdgeVisFlags(1,1,1); mesh.faces[3].setSmGroup(1); mesh.faces[3].setVerts(1,4,0); mesh.faces[4].setEdgeVisFlags(1,1,1); mesh.faces[4].setSmGroup(1); mesh.faces[4].setVerts(2,4,3); mesh.faces[5].setEdgeVisFlags(1,1,1); mesh.faces[5].setSmGroup(1); mesh.faces[5].setVerts(3,4,2); mesh.faces[6].setEdgeVisFlags(1,0,1); mesh.faces[6].setSmGroup(1); mesh.faces[6].setVerts(3,1,0); mesh.faces[7].setEdgeVisFlags(1,1,0); mesh.faces[7].setSmGroup(1); mesh.faces[7].setVerts(3,2,1); } } } else { //build proxy mesh //build proxy mesh INode *node=NULL; pblock2->GetValue(particlemesher_pick, 0, node, ivalid); // if (node == NULL) { mesh.setNumVerts(5); mesh.setNumFaces(8); mesh.setNumMaps(2); mesh.setNumMapVerts(0, 0); mesh.setNumMapVerts(1, 0); mesh.setNumMapFaces(0, 0); mesh.setNumMapFaces(1, 0); mesh.setVert(0, Point3(-radius,-radius, 0.0f)); mesh.setVert(1, Point3( radius,-radius, 0.0f)); mesh.setVert(2, Point3( radius, radius, 0.0f)); mesh.setVert(3, Point3(-radius, radius, 0.0f)); // mesh.setVert(4, Point3(0.0f, 0.0f, 0.0f)); mesh.setVert(4, Point3(0.0f, 0.0f, radius)); mesh.faces[0].setEdgeVisFlags(1,0,1); mesh.faces[0].setSmGroup(1); mesh.faces[0].setVerts(0,1,3); mesh.faces[1].setEdgeVisFlags(1,1,0); mesh.faces[1].setSmGroup(1); mesh.faces[1].setVerts(1,2,3); mesh.faces[2].setEdgeVisFlags(1,1,1); mesh.faces[2].setSmGroup(1); mesh.faces[2].setVerts(0,4,1); mesh.faces[3].setEdgeVisFlags(1,1,1); mesh.faces[3].setSmGroup(1); mesh.faces[3].setVerts(1,4,0); mesh.faces[4].setEdgeVisFlags(1,1,1); mesh.faces[4].setSmGroup(1); mesh.faces[4].setVerts(2,4,3); mesh.faces[5].setEdgeVisFlags(1,1,1); mesh.faces[5].setSmGroup(1); mesh.faces[5].setVerts(3,4,2); mesh.faces[6].setEdgeVisFlags(1,0,1); mesh.faces[6].setSmGroup(1); mesh.faces[6].setVerts(3,1,0); mesh.faces[7].setEdgeVisFlags(1,1,0); mesh.faces[7].setSmGroup(1); mesh.faces[7].setVerts(3,2,1); } } mesh.InvalidateTopologyCache(); }
void BlobMesh::BuildMesh(TimeValue t) { //TODO: Implement the code to build the mesh representation of the object // using its parameter settings at the time passed. The plug-in should // use the data member mesh to store the built mesh. int numberOfNodes = pblock2->Count(pb_nodelist); float size, tension, renderCoarseness, viewCoarseness,coarseness; // Interval valid; pblock2->GetValue(pb_size,t,size,ivalid); pblock2->GetValue(pb_tension,t,tension,ivalid); if (tension < 0.011f) tension = 0.011f; if (tension > 1.0f) tension = 1.0f; pblock2->GetValue(pb_render,t,renderCoarseness,ivalid); pblock2->GetValue(pb_viewport,t,viewCoarseness,ivalid); BOOL autoCoarseness; pblock2->GetValue(pb_relativecoarseness,t,autoCoarseness,ivalid); BOOL largeDataSetOpt; pblock2->GetValue(pb_oldmetaballmethod,t,largeDataSetOpt,ivalid); BOOL useSoftSel; float minSize; pblock2->GetValue(pb_usesoftsel,t,useSoftSel,ivalid); pblock2->GetValue(pb_minsize,t,minSize,ivalid); if (inRender) coarseness = renderCoarseness; else coarseness = viewCoarseness; if (autoCoarseness) coarseness = size/coarseness; Tab<INode *> pfList; BOOL useAllPFEvents; pblock2->GetValue(pb_useallpf,0,useAllPFEvents,FOREVER); int pfCt = pblock2->Count(pb_pfeventlist); BOOL offInView; pblock2->GetValue(pb_offinview,0,offInView,FOREVER); for (int i = 0; i < pfCt; i++) { INode* node = NULL; pblock2->GetValue(pb_pfeventlist,0,node,FOREVER,i); if (node) pfList.Append(1,&node); } float thres = 0.6f; //need to get our tm if (selfNode == NULL) { MyEnumProc dep(true); DoEnumDependents(&dep); if (dep.Nodes.Count() > 0) selfNode = dep.Nodes[0]; } Matrix3 baseTM(1); if (selfNode != NULL) baseTM = Inverse(selfNode->GetObjectTM(t)); //loop throght the nodes if (!inRender) { if (offInView) numberOfNodes = 0; } std::vector<SphereData> data; data.reserve(500); for (int i = 0; i < numberOfNodes; i++) { INode* node = NULL; pblock2->GetValue(pb_nodelist,t,node,ivalid,i); if (node) { //get the nodes tm Matrix3 objectTM = node->GetObjectTM(t); Matrix3 toLocalSpace = objectTM*baseTM; ObjectState tos = node->EvalWorldState(t,TRUE); if (tos.obj->IsParticleSystem()) { SimpleParticle* pobj = NULL; IParticleObjectExt* epobj = NULL; pobj = static_cast<SimpleParticle*>( tos.obj->GetInterface(I_SIMPLEPARTICLEOBJ) ); if (pobj) { pobj->UpdateParticles(t, node); int count = pobj->parts.Count(); data.reserve(data.size() + count); float closest = 999999999.9f; SphereData d; for (int pid = 0; pid < count; pid++) { TimeValue age = pobj->ParticleAge(t,pid); TimeValue life = pobj->ParticleLife(t,pid); if (age != -1) { float psize = pobj->ParticleSize(t,pid); Point3 curval = pobj->parts.points[pid]; d.center = curval * baseTM; d.radius = psize; d.oradius = psize; d.rsquare = psize * psize; d.tover4 = tension * d.rsquare *d.rsquare ; data.push_back(d); } } } else { epobj = (IParticleObjectExt*) tos.obj->GetInterface(PARTICLEOBJECTEXT_INTERFACE); if (epobj) { epobj->UpdateParticles(node, t); int count = epobj->NumParticles(); data.reserve(data.size() + count); for (int pid = 0; pid < count; pid++) { TimeValue age = epobj->GetParticleAgeByIndex(pid); if (age!=-1) { INode *node = epobj->GetParticleGroup(pid); Point3 *curval = epobj->GetParticlePositionByIndex(pid); BOOL useParticle = TRUE; if (!useAllPFEvents) { useParticle = FALSE; for (int k = 0; k < pfList.Count(); k++) { if (node == pfList[k]) { useParticle = TRUE; k = pfList.Count(); } } } if ((curval) && (useParticle)) { float scale = epobj->GetParticleScaleByIndex(pid) ; float psize = scale; SphereData d; d.center = *curval*baseTM; d.radius = psize; d.oradius = psize; d.rsquare = psize * psize; d.tover4 = tension * d.rsquare *d.rsquare ; data.push_back(d); } } } } } } else if (tos.obj->IsShapeObject()) { PolyShape shape; ShapeObject *pathOb = (ShapeObject*)tos.obj; pathOb->MakePolyShape(t, shape); // first find out how many points there are: size_t num_points = 0; for (int i = 0; i < shape.numLines; i++) { PolyLine& line = shape.lines[i]; num_points += line.numPts; } data.reserve(data.size() + num_points); for (int i = 0; i < shape.numLines; i++) { PolyLine line = shape.lines[i]; for (int j = 0; j < line.numPts; j++) { SphereData d; float tsize = size; d.center = line.pts[j].p*toLocalSpace; d.radius = tsize; d.oradius = tsize; d.rsquare = tsize * tsize; d.tover4 = tension * d.rsquare *d.rsquare ; data.push_back(d); } } } else if (tos.obj->SuperClassID()==GEOMOBJECT_CLASS_ID) { SphereData d; BOOL converted = FALSE; TriObject *triObj = NULL; if(tos.obj->IsSubClassOf(triObjectClassID)) { triObj = (TriObject *)tos.obj; } // If it can convert to a TriObject, do it else if(tos.obj->CanConvertToType(triObjectClassID)) { triObj = (TriObject *)tos.obj->ConvertToType(t, triObjectClassID); converted = TRUE; } if (triObj != NULL) { Mesh* mesh = &triObj->GetMesh(); if (mesh) { int vcount = mesh->getNumVerts(); float *vsw = mesh->getVSelectionWeights (); BitArray vsel = mesh->VertSel(); data.reserve(data.size() + vcount); for (int j = 0; j < vcount; j++) { float tsize = size; if (useSoftSel) { tsize = 0.0f; if (vsw) { float v = 0.0f; if (vsel[j]) v = 1.0f; else { if (vsw) v = vsw[j]; } if (v == 0.0f) tsize = 0.0f; else { tsize = minSize + (size -minSize)*v; } } else { float v = 0.0f; if (vsel[j]) v = 1.0f; tsize = minSize + (size -minSize)*v; } } if (tsize != 0.0f) { d.center = mesh->getVert(j)*toLocalSpace; d.radius = tsize; d.oradius = tsize; d.rsquare = tsize * tsize; d.tover4 = tension * d.rsquare *d.rsquare ; data.push_back(d); } } } if (converted) triObj->DeleteThis(); } } else { SphereData d; d.center = Point3(0.0f,0.0f,0.0f)*toLocalSpace; d.radius = size; d.oradius = size; d.rsquare = size * size; d.tover4 = tension * d.rsquare *d.rsquare ; data.push_back(d); } } } if ((data.size() == 0) && (numberOfNodes==0)) { data.resize(1); data[0].center = Point3(0.0f,0.0f,0.0f); data[0].radius = size; data[0].oradius = size; data[0].rsquare = size * size; data[0].tover4 = tension * data[0].rsquare *data[0].rsquare ; } if (data.size() > 0) { int iRes = 1; if (!largeDataSetOpt) { MetaParticle oldBlob; iRes = oldBlob.CreatePodMetas(&data[0],(int)data.size(),&mesh,thres,coarseness); } else { MetaParticleFast blob; iRes = blob.CreatePodMetas(&data[0],(int)data.size(),&mesh,thres,coarseness); } // An out of memory error is the only reason iRes would be zero in either case if( (iRes == 0) && GetCOREInterface() ) GetCOREInterface()->DisplayTempPrompt(GetString(IDS_NOT_ENOUGH_MEM), 5000 ); } else { mesh.setNumFaces(0); mesh.setNumVerts(0); } mesh.InvalidateTopologyCache(); ivalid.Set(t,t); }
void HoudiniEngineMesh::BuildMesh(TimeValue t) { if ( buildingMesh ) return; bool conv_unit_i = pblock2->GetInt(pb_conv_unit_i) != 0; bool conv_unit_o = pblock2->GetInt(pb_conv_unit_o) != 0; bool time_update = pblock2->GetInt(pb_updatetime, t) ? true : false; bool bypass = pblock2->GetInt(pb_bypass, t) ? true : false; if (bypass) reCook = true; hapi::Engine* engine = hapi::Engine::instance(); buildingMesh = true; if (!engine || !engine->isInitialize() || bypass) { util::BuildLogoMesh(mesh); mesh.InvalidateTopologyCache(); buildingMesh = false; return; } bool new_loading = LoadAsset(); if ( assetId >= 0 ) { INode* selfNode = GetINode(); Matrix3 baseTM(1); if (selfNode != nullptr) baseTM = Inverse(selfNode->GetObjectTM(t)); float hapi_time; float max_time = TicksToSec(t); bool cook = UpdateParameters(t) | new_loading | reCook; HAPI_GetTime(hapi::Engine::instance()->session(), &hapi_time); if ( time_update && hapi_time != max_time ) { HAPI_SetTime(hapi::Engine::instance()->session(), TicksToSec(t)); cook = true; } // Cooking if ( cook ) { hapi::Asset asset(assetId); if ( asset.isValid() ) { try { asset.cook(); int status; int wait_count = 0; int start_progress = 10; bool progressBar = false; do { Sleep(2); HAPI_GetStatus(hapi::Engine::instance()->session(), HAPI_STATUS_COOK_STATE, &status); if ( start_progress == wait_count ) { StartProgress(); progressBar = true; } if ( progressBar ) { int ccount; int tcount; HAPI_GetCookingCurrentCount(hapi::Engine::instance()->session(), &ccount); HAPI_GetCookingTotalCount(hapi::Engine::instance()->session(), &tcount); int progress = (int)(((float)ccount / (float)tcount) * 100.f); UpdateProgress( progress ); } wait_count ++; } while ( status > HAPI_STATE_MAX_READY_STATE ); if ( progressBar ) { EndProgress(); } if ( status != HAPI_STATE_READY ) { // Cooking Error cook = false; } } catch( hapi::Failure& e) { // Cooking Error e.lastErrorMessage(); cook = false; } } else cook = false; } // dont need check cook flag, will check hasGeoChanged flag // if ( cook ) { int verts = mesh.getNumVerts(); double scl = conv_unit_o ? GetRelativeScale( UNITS_METERS, 1, GetUSDefaultUnit(), 1 ) : 1.0; util::BuildMeshFromCookResult( mesh, assetId, (float)scl, new_loading || (scl != outScale) || reCook ); outScale = (float)scl; if (mesh.getNumVerts() && verts != mesh.getNumVerts()) { mesh.InvalidateTopologyCache(); } } } if ( !mesh.getNumVerts() ) { util::BuildLogoMesh( mesh); mesh.InvalidateTopologyCache(); } ivalid.Set(t,t); buildingMesh = false; reCook = false; }
Point3 PtForceField::Force(TimeValue t,const Point3 &pos, const Point3 &vel,int index) { Point3 ApplyAt,OutForce,zdir; fValid= FOREVER; if (!tmValid.InInterval(t)) { tmValid=FOREVER; tm=node->GetObjectTM(t,&tmValid); invtm=Inverse(tm); } ApplyAt=tm.GetTrans(); fValid&=tmValid; TimeValue t1,t2; if (obj == NULL) return Point3(0.0f,0.0f,0.0f); //667105 watje xrefs can change the base object type through proxies which will cause this to be null or the user can copy a new object type over ours obj->pblock->GetValue(PB_ONTIME,t,t1,fValid); obj->pblock->GetValue(PB_OFFTIME,t,t2,fValid); if ((t>=t1)&&(t<=t2)) { float BaseF; zdir=tm.GetRow(2); obj->pblock->GetValue(PB_STRENGTH,t,BaseF,fValid); //assume N int tpf=GetTicksPerFrame(); //int tpf2=tpf*tpf; int tps=TIME_TICKSPERSEC; int tps2=tps*tps; //int FToTick=(int)((float)tps/(float)GetFrameRate()); BaseF/=(float)tps2;//convert to kg-m/t2 BaseF*=100.0f; //convert to kg-cm/t2 int UnitsVal; obj->pblock->GetValue(PB_UNITS,t,UnitsVal,fValid); if (UnitsVal!=0) BaseF*=4.4591f; //lbf is this much larger than N=kg-m/s2 TimeValue tage=t-t1; TimeValue Per1,Per2; obj->pblock->GetValue(PB_TIMEPER1,t,Per1,fValid); obj->pblock->GetValue(PB_TIMEPER2,t,Per2,fValid); float scalefactor=1.0f; int sintoggle; obj->pblock->GetValue(PB_ENABLESINES,t,sintoggle,fValid); if (sintoggle) { if (Per1>0) { float phase1,amp1; float relage1=(float)tage/(float)Per1; obj->pblock->GetValue(PB_PHASPER1,t,phase1,fValid); obj->pblock->GetValue(PB_AMP1,t,amp1,fValid); scalefactor+=0.01f*amp1*(float)sin(TWOPI*relage1+phase1); } if (Per2>0) { float phase2,amp2; float relage2=(float)tage/(float)Per2; obj->pblock->GetValue(PB_PHASPER2,t,phase2,fValid); obj->pblock->GetValue(PB_AMP2,t,amp2,fValid); scalefactor+=0.01f*amp2*(float)sin(TWOPI*relage2+phase2); } } int feedback; obj->pblock->GetValue(PB_FEEDBACKON,t,feedback,fValid); float scalefactorg=1.0f; if (feedback) { float targetspeed,loopgain; obj->pblock->GetValue(PB_TARGETVEL,t,targetspeed,fValid); targetspeed/=(float)tpf; obj->pblock->GetValue(PB_CONTROLGAIN,t,loopgain,fValid); float diffspeed=targetspeed-DotProd(vel,zdir); //scalefactorg*=diffspeed*loopgain/(float)tpf; scalefactorg*=diffspeed*loopgain/100.0f; int revon; obj->pblock->GetValue(PB_REVERSIBLE,t,revon,fValid); if ((!revon)&&(scalefactorg<0.0f)) scalefactorg=0.0f; } OutForce=zdir*BaseF*scalefactor*scalefactorg; int rangeon; obj->pblock->GetValue(PB_RANGEON,t,rangeon,fValid); if (rangeon) { float maxrange; obj->pblock->GetValue(PB_RANGEVAL,t,maxrange,fValid); float distance=Length(ApplyAt-pos); if (distance<maxrange) { if (maxrange>FLOAT_EPSILON) OutForce*=(1.0f-distance/maxrange); else OutForce=Zero; } else OutForce=Zero; } } else OutForce=Zero; return OutForce*6.25e-03f; }
void BombMod::ModifyObject( TimeValue t, ModContext &mc, ObjectState *os, INode *node) { BombObject *bobj = GetWSMObject(t); if (bobj && nodeRef && (bobj->ClassID() == Class_ID(BOMB_OBJECT_CLASS_ID,0))) { assert(os->obj->IsSubClassOf(triObjectClassID)); TriObject *triOb = (TriObject *)os->obj; Interval valid = FOREVER; if (os->GetTM()) { Matrix3 tm = *(os->GetTM()); for (int i=0; i<triOb->GetMesh().getNumVerts(); i++) { triOb->GetMesh().verts[i] = triOb->GetMesh().verts[i] * tm; } os->obj->UpdateValidity(GEOM_CHAN_NUM,os->tmValid()); os->SetTM(NULL,FOREVER); } if (waitPostLoad) { valid.SetInstant(t); triOb->UpdateValidity(GEOM_CHAN_NUM,valid); triOb->UpdateValidity(TOPO_CHAN_NUM,valid); return; } Matrix3 tm; TimeValue det = bobj->GetDetonation(t,valid); float strength = bobj->GetStrength(t,valid) * STRENGTH_CONSTANT; float grav = bobj->GetGravity(t,valid); float chaos = bobj->GetChaos(t,valid); float chaosBase = (float(1)-chaos/2); float rotSpeed = bobj->GetSpin(t,valid) * TWOPI/float(TIME_TICKSPERSEC); int minClust = bobj->GetMinFrag(t,valid), maxClust = bobj->GetMaxFrag(t,valid); if (minClust<1) minClust = 1; if (maxClust<1) maxClust = 1; int clustVar = maxClust-minClust+1; float falloff = bobj->GetFalloff(t,valid); int useFalloff = bobj->GetFalloffOn(t,valid); int seed = bobj->GetSeed(t,valid); //tm = nodeRef->GetNodeTM(t,&valid); tm = nodeRef->GetObjectTM(t,&valid); if (t<det) { valid.Set(TIME_NegInfinity,det-1); triOb->UpdateValidity(GEOM_CHAN_NUM,valid); triOb->UpdateValidity(TOPO_CHAN_NUM,valid); triOb->PointsWereChanged(); return; } float dt = float(t-det); valid.SetInstant(t); int n0=0,n1=1,n2=2,nv; Point3 v, p0, p1, g(0.0f,0.0f,grav*GRAVITY_CONSTANT); Tab<Point3> l_newVerts ; Face *f = triOb->GetMesh().faces; float dot; Mesh &mesh = triOb->GetMesh(); // First, segment the faces. srand((unsigned int)seed); Tab<DWORD> vclust; Tab<DWORD> vmap; Tab<Point3> nverts; vclust.SetCount(mesh.getNumVerts()); vmap.SetCount(mesh.getNumVerts()); int numClust = 0; if (minClust==1 && maxClust==1) { // Simple case... 1 face per cluster nv = triOb->GetMesh().getNumFaces() * 3; l_newVerts.SetCount(nv); vclust.SetCount(nv); for (int i=0; i<nv; i++) { vclust[i] = i/3; } for (int i=0,j=0; i<mesh.getNumFaces(); i++) { l_newVerts[j] = mesh.verts[mesh.faces[i].v[0]]; l_newVerts[j+1] = mesh.verts[mesh.faces[i].v[1]]; l_newVerts[j+2] = mesh.verts[mesh.faces[i].v[2]]; mesh.faces[i].v[0] = j; mesh.faces[i].v[1] = j+1; mesh.faces[i].v[2] = j+2; j += 3; } numClust = triOb->GetMesh().getNumFaces(); } else { // More complex case... clusters are randomely sized for (int i=0; i<mesh.getNumVerts(); i++) { vclust[i] = UNDEFINED; vmap[i] = i; } int cnum = 0; for (int i=0; i<mesh.getNumFaces(); ) { int csize = int(Rand1()*float(clustVar)+float(minClust)); if (i+csize>mesh.getNumFaces()) { csize = mesh.getNumFaces()-i; } // Make sure each face in the cluster has at least 2 verts in common with another face. BOOL verified = FALSE; while (!verified) { verified = TRUE; if (csize<2) break; for (int j=0; j<csize; j++) { BOOL match = FALSE; for (int k=0; k<csize; k++) { if (k==j) continue; int common = 0; for (int i1=0; i1<3; i1++) { for (int i2=0; i2<3; i2++) { if (mesh.faces[i+j].v[i1]== mesh.faces[i+k].v[i2]) common++; } } if (common>=2) { match = TRUE; break; } } if (!match) { csize = j; verified = FALSE; // Have to check again break; } } } if (csize==0) csize = 1; // Clear the vert map for (int j=0; j<mesh.getNumVerts(); j++) vmap[j] = UNDEFINED; // Go through the cluster and remap verts. for (int j=0;j<csize; j++) { for (int k=0; k<3; k++) { if (vclust[mesh.faces[i+j].v[k]]==UNDEFINED) { vclust[mesh.faces[i+j].v[k]] = cnum; } else if (vclust[mesh.faces[i+j].v[k]]!=(DWORD)cnum) { if (vmap[mesh.faces[i+j].v[k]]==UNDEFINED) { vclust.Append(1,(DWORD*)&cnum,50); nverts.Append(1,&mesh.verts[mesh.faces[i+j].v[k]],50); mesh.faces[i+j].v[k] = vmap[mesh.faces[i+j].v[k]] = mesh.getNumVerts()+nverts.Count()-1; } else { mesh.faces[i+j].v[k] = vmap[mesh.faces[i+j].v[k]]; } } } } cnum++; numClust++; i += csize; } nv = mesh.getNumVerts() + nverts.Count(); l_newVerts.SetCount(nv); int i; for (i=0; i<mesh.getNumVerts(); i++) l_newVerts[i] = mesh.verts[i]; for ( ; i<mesh.getNumVerts()+nverts.Count(); i++) l_newVerts[i] = nverts[i-mesh.getNumVerts()]; } // Find the center of all clusters Tab<Point3> clustCent; Tab<DWORD> clustCounts; clustCent.SetCount(numClust); clustCounts.SetCount(numClust); for (int i=0; i<numClust; i++) { clustCent[i] = Point3(0,0,0); clustCounts[i] = 0; } for (int i=0; i<nv; i++) { if (vclust[i]==UNDEFINED) continue; clustCent[vclust[i]] += l_newVerts[i]; clustCounts[vclust[i]]++; } // Build transformations for all clusters Tab<Matrix3> mats; mats.SetCount(numClust); srand((unsigned int)seed); for (int i=0; i<numClust; i++) { if (clustCounts[i]) clustCent[i] /= float(clustCounts[i]); v = clustCent[i] - tm.GetTrans(); float u = 1.0f; if (useFalloff) { u = 1.0f - Length(v)/falloff; if (u<0.0f) u = 0.0f; } dot = DotProd(v,v); if (dot==0.0f) dot = 0.000001f; v = v / dot * strength * u; v.x *= chaosBase + chaos * Rand1(); v.y *= chaosBase + chaos * Rand1(); v.z *= chaosBase + chaos * Rand1(); p1 = v*dt + 0.5f*g*dt*dt; // projectile motion // Set rotation Point3 axis; axis.x = -1.0f + 2.0f*Rand1(); axis.y = -1.0f + 2.0f*Rand1(); axis.z = -1.0f + 2.0f*Rand1(); axis = Normalize(axis); float angle = dt*rotSpeed*(chaosBase + chaos * Rand1())*u; Quat q = QFromAngAxis(angle, axis); q.MakeMatrix(mats[i]); mats[i].PreTranslate(-clustCent[i]); mats[i].Translate(clustCent[i]+p1); } // Now transform the clusters for (int i=0; i<nv; i++) { if (vclust[i]==UNDEFINED) continue; l_newVerts[i] = l_newVerts[i] * mats[vclust[i]]; } triOb->UpdateValidity(GEOM_CHAN_NUM,valid); triOb->UpdateValidity(TOPO_CHAN_NUM,valid); triOb->PointsWereChanged(); triOb->GetMesh().setNumVerts(nv,FALSE,TRUE); //assign the new vertices to the mesh for ( int i=0; i < nv; i++) triOb->GetMesh().setVert( i,l_newVerts[i]); } }
void SWrapMod::ModifyObject( TimeValue t, ModContext &mc, ObjectState *os, INode *node) { SWrapObject *obj = (SWrapObject *)GetWSMObject(t); INode *pnode; TriObject *towrapOb=NULL;Object *pobj=NULL; if (obj) pnode=obj->custnode; if (obj && nodeRef && pnode) { Interval valid = FOREVER; if (!obj->cmValid.InInterval(t)) { pobj = pnode->EvalWorldState(t).obj; obj->cmValid=pobj->ObjectValidity(t); Matrix3 tm=pnode->GetObjectTM(t,&(obj->cmValid)); TriObject *wrapOb=IsUseable(pobj,t); if (wrapOb) { if (obj->cmesh) delete obj->cmesh; obj->cmesh=new Mesh; obj->cmesh->DeepCopy(&wrapOb->GetMesh(), PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_TOPO|TM_CHANNEL); for (int ic=0;ic<obj->cmesh->getNumVerts();ic++) obj->cmesh->verts[ic]=obj->cmesh->verts[ic]*tm; GetVFLst(obj->cmesh,&obj->vnorms,&obj->fnorms); if (wrapOb!=pobj) wrapOb->DeleteThis(); } } if (!obj->cmesh) return; if ((obj->cmesh->getNumVerts()==0)||(obj->cmesh->getNumFaces()==0)) return; // Matrix3 invtm=Inverse(obj->tm); valid=obj->cmValid; Matrix3 ctm; ctm = nodeRef->GetNodeTM(t,&valid); Ray ray; Point3 v=-ctm.GetRow(2); // Matrix3 nooff=invtm;nooff.NoTrans(); ray.dir=v;//*nooff; int selverts; float kdef,standoff; obj->pblock->GetValue(PB_USESELVERTS,t,selverts,valid); obj->pblock->GetValue(PB_KIDEFAULT,t,kdef,valid); obj->pblock->GetValue(PB_STANDOFF,t,standoff,valid); BezierShape stowrapOb; int found=0; Matrix3 towtm(1); if (os->GetTM()) towtm=*(os->GetTM()); Matrix3 invtowtm=Inverse(towtm); Point3 vert; Class_ID cid=os->obj->ClassID(),es=EDITABLE_SURF_CLASS_ID,efp=FITPOINT_PLANE_CLASS_ID,ecv=EDITABLE_CVCURVE_CLASS_ID,ecfp=EDITABLE_FPCURVE_CLASS_ID; if (((cid==EDITABLE_SURF_CLASS_ID)||(cid==FITPOINT_PLANE_CLASS_ID))||((cid==EDITABLE_CVCURVE_CLASS_ID)||(cid==EDITABLE_FPCURVE_CLASS_ID))) { Object* nurbobj=os->obj; int num=nurbobj->NumPoints(); for (int i=0;i<num;i++) { vert=DoIntersect(nurbobj->GetPoint(i)*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); nurbobj->SetPoint(i,(vert*invtowtm)); } } #ifndef NO_PATCHES else if (os->obj->IsSubClassOf(patchObjectClassID)) { PatchObject* patchob=(PatchObject *)os->obj; PatchMesh *pm=&(patchob->patch); int nv=pm->getNumVerts(); BitArray sel = pm->VertSel(); for (int i=0;i<nv;i++) { if (!selverts||sel[i]) { vert=DoIntersect(pm->getVert(i).p*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); vert=vert*invtowtm; pm->setVert(i,vert); } } /* pm->buildLinkages(); pm->computeInteriors(); pm->InvalidateGeomCache();*/ } #endif // NO_PATCHES else if (towrapOb=IsUseable(os->obj,t)) { Point3 tvector; float dist; float *vssel = NULL; if (selverts) vssel = towrapOb->GetMesh().getVSelectionWeights(); for (int i=0;i<towrapOb->GetMesh().getNumVerts();i++) { if ((!selverts)||(towrapOb->GetMesh().vertSel[i]) ||(vssel&&vssel[i])) { vert = DoIntersect(towrapOb->GetMesh().verts[i]*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); vert = vert*invtowtm; if (vssel&&vssel[i]) { tvector = vert - towrapOb->GetMesh().verts[i]; dist = Length(tvector); if ((float)fabs(dist) > EPSILON) tvector = tvector/dist; else tvector = Zero; vert = towrapOb->GetMesh().verts[i] + dist*vssel[i]*tvector; } towrapOb->GetMesh().verts[i] = vert; } } if (towrapOb!=os->obj) towrapOb->DeleteThis(); } else if((os->obj->IsSubClassOf(splineShapeClassID))||(os->obj->CanConvertToType(splineShapeClassID))) { SplineShape *attSplShape = (SplineShape *)os->obj->ConvertToType(t,splineShapeClassID); if (attSplShape) { stowrapOb=attSplShape->shape; for (int poly=0; poly<stowrapOb.splineCount; ++poly) { Spline3D *spline = stowrapOb.GetSpline(poly); int verts = spline->Verts(); int knots = spline->KnotCount(); BitArray sel = stowrapOb.VertexTempSel(poly); Point3 cknot,cknot2; { for(int k=0; k<knots; ++k) { int vert = k * 3 + 1; if (!selverts||sel[vert]) { cknot=DoIntersect(spline->GetKnotPoint(k)*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); attSplShape->shape.SetVert(poly,vert,cknot*invtowtm); if (found) { int knotType = spline->GetKnotType(k); if(knotType & KTYPE_BEZIER) { cknot2= DoIntersect(spline->GetInVec(k)*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); attSplShape->shape.SetVert(poly,vert-1,(found?cknot2:cknot)*invtowtm); cknot2= DoIntersect(spline->GetOutVec(k)*towtm,ray,obj->cmesh,kdef,standoff,&found,v,obj->vnorms,obj->fnorms); attSplShape->shape.SetVert(poly,vert+1,(found?cknot2:cknot)*invtowtm); } } } } } } if (attSplShape!=os->obj) attSplShape->DeleteThis(); } } // os->obj->PointsWereChanged(); os->obj->UpdateValidity(GEOM_CHAN_NUM,valid); // NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } }