bool SplineData::UpdateNode(INode *node) { if (mReset) { mReset = false; return AddNode(node); } else { TimeValue t = GetCOREInterface()->GetTime(); Interval valid; Matrix3 tm = node->GetObjectTM(t,&valid); ObjectState nos = node->EvalWorldState(t); //make sure it is a shape if (nos.obj->IsShapeObject()) { ShapeObject *pathOb = (ShapeObject*)nos.obj; pathOb->MakePolyShape(t, mShapeCache,32,FALSE); mShapeCache.Transform(tm); int numberOfCurves = pathOb->NumberOfCurves(); for (int i = 0; i < numberOfCurves; i++) { //just rebuild our cache info mSplineElementData[i]->ResetBoundingBox(); mSplineElementData[i]->SetLineCache(&mShapeCache.lines[i]); mSplineElementData[i]->ComputePlanarUp(mPlanarMapUp); if (pathOb->CurveClosed(t,i)) mSplineElementData[i]->SetClosed(true); else mSplineElementData[i]->SetClosed(false); mSplineElementData[i]->Select(FALSE); } //now rebuild our derived cross sections RecomputeCrossSections(); if (mActiveSpline >= numberOfCurves) mActiveSpline = 0; mSplineElementData[mActiveSpline]->Select(TRUE); return true; } } return false; }
bool SplineData::AddNode(INode *node) { //see if the node is a shape object if (node != NULL) { TimeValue t = GetCOREInterface()->GetTime(); Interval valid; Matrix3 tm = node->GetObjectTM(t,&valid); ObjectState nos = node->EvalWorldState(t); if (nos.obj->IsShapeObject()) { ShapeObject *pathOb = (ShapeObject*)nos.obj; int numberOfCurves = pathOb->NumberOfCurves(); //get number of splines if (numberOfCurves != 0) { pathOb->MakePolyShape(t, mShapeCache,32,FALSE); mShapeCache.Transform(tm); mSplineElementData.SetCount(numberOfCurves); for (int i = 0; i < numberOfCurves; i++) mSplineElementData[i] = NULL; for (int i = 0; i < numberOfCurves; i++) { Box3 bounds; bounds.Init(); //sample the shape object int numberOfCurves = pathOb->NumberOfPieces(t, i); if (numberOfCurves != 0) { mSplineElementData[i] = new SplineElementData(); mSplineElementData[i]->ResetBoundingBox(); mSplineElementData[i]->SetLineCache(&mShapeCache.lines[i]); mSplineElementData[i]->ComputePlanarUp(mPlanarMapUp); mSplineElementData[i]->SetNumCurves(numberOfCurves); mSplineElementData[i]->SetNumberOfCrossSections(numberOfCurves+1); if (pathOb->CurveClosed(t,i)) mSplineElementData[i]->SetClosed(true); else mSplineElementData[i]->SetClosed(false); float curveLength = pathOb->LengthOfCurve(t,i); float runningLength = 0.0f; Quat q; q.Identity(); mSplineElementData[i]->SetCrossSection(0,Point3(0.0f,0.0f,0.0f),Point2(20.0f,20.0f),q); for (int j = 0; j < numberOfCurves; j++) { float u = runningLength/curveLength; mSplineElementData[i]->SetCrossSection(j,Point3(0.0f,0.0f,u),Point2(20.0f,20.0f),q); Point3 lastPoint = pathOb->InterpPiece3D(t,i,j,0.0f,PARAM_NORMALIZED); bounds += lastPoint; float runningU = 0.0f; float inc = 1.0f/(float)(mSampleRate-1); for (int k = 1; k < mSampleRate; k++) { runningU += inc; Point3 p = pathOb->InterpPiece3D(t,i,j,runningU,PARAM_NORMALIZED); runningLength += Length(p-lastPoint); lastPoint = p; bounds += lastPoint; } } mSplineElementData[i]->SetCrossSection(numberOfCurves,Point3(0.0f,0.0f,1.0f),Point2(20.0f,20.0f),q); mSplineElementData[i]->Select(FALSE); } } if (mActiveSpline >= numberOfCurves) mActiveSpline = 0; mSplineElementData[mActiveSpline]->Select(TRUE); //build our bounding box RecomputeCrossSections(); return true; } } } return false; }
bool AlembicCurves::Save(double time, bool bLastFrame) { ESS_PROFILE_FUNC(); //TimeValue ticks = GET_MAX_INTERFACE()->GetTime(); TimeValue ticks = GetTimeValueFromFrame(time); Object *obj = mMaxNode->EvalWorldState(ticks).obj; if(mNumSamples == 0){ bForever = CheckIfObjIsValidForever(obj, ticks); } else{ bool bNewForever = CheckIfObjIsValidForever(obj, ticks); if(bForever && bNewForever != bForever){ ESS_LOG_INFO( "bForever has changed" ); } } SaveMetaData(mMaxNode, this); // check if the spline is animated if(mNumSamples > 0) { if(bForever) { return true; } } AbcG::OCurvesSchema::Sample curvesSample; std::vector<AbcA::int32_t> nbVertices; std::vector<Point3> vertices; std::vector<float> knotVector; std::vector<Abc::uint16_t> orders; if(obj->ClassID() == EDITABLE_SURF_CLASS_ID){ NURBSSet nurbsSet; BOOL success = GetNURBSSet(obj, ticks, nurbsSet, TRUE); AbcG::CurvePeriodicity cPeriod = AbcG::kNonPeriodic; AbcG::CurveType cType = AbcG::kCubic; AbcG::BasisType cBasis = AbcG::kNoBasis; int n = nurbsSet.GetNumObjects(); for(int i=0; i<n; i++){ NURBSObject* pObject = nurbsSet.GetNURBSObject((int)i); //NURBSType type = pObject->GetType(); if(!pObject){ continue; } if( pObject->GetKind() == kNURBSCurve ){ NURBSCurve* pNurbsCurve = (NURBSCurve*)pObject; int degree; int numCVs; NURBSCVTab cvs; int numKnots; NURBSKnotTab knots; pNurbsCurve->GetNURBSData(ticks, degree, numCVs, cvs, numKnots, knots); orders.push_back(degree+1); const int cvsCount = cvs.Count(); const int knotCount = knots.Count(); for(int j=0; j<cvs.Count(); j++){ NURBSControlVertex cv = cvs[j]; double x, y, z; cv.GetPosition(ticks, x, y, z); vertices.push_back( Point3((float)x, (float)y, (float)z) ); } nbVertices.push_back(cvsCount); //skip the first and last entry because Maya and XSI use this format for(int j=1; j<knots.Count()-1; j++){ knotVector.push_back((float)knots[j]); } if(i == 0){ if(pNurbsCurve->IsClosed()){ cPeriod = AbcG::kPeriodic; } } else{ if(pNurbsCurve->IsClosed()){ if(cPeriod != AbcG::kPeriodic){ ESS_LOG_WARNING("Mixed curve wrap types not supported."); } } else{ if(cPeriod != AbcG::kNonPeriodic){ ESS_LOG_WARNING("Mixed curve wrap types not supported."); } } } } } curvesSample.setType(cType); curvesSample.setWrap(cPeriod); curvesSample.setBasis(cBasis); } else { BezierShape beziershape; PolyShape polyShape; bool bBezier = false; // Get a pointer to the spline shpae ShapeObject *pShapeObject = NULL; if (obj->IsShapeObject()) { pShapeObject = reinterpret_cast<ShapeObject *>(obj); } else { return false; } // Determine if we are a bezier shape if (pShapeObject->CanMakeBezier()) { pShapeObject->MakeBezier(ticks, beziershape); bBezier = true; } else { pShapeObject->MakePolyShape(ticks, polyShape); bBezier = false; } // Get the control points //std::vector<Point3> inTangents; //std::vector<Point3> outTangents; if (bBezier) { int oldVerticesCount = (int)vertices.size(); for (int i = 0; i < beziershape.SplineCount(); i += 1) { Spline3D *pSpline = beziershape.GetSpline(i); int knots = pSpline->KnotCount(); for(int ix = 0; ix < knots; ++ix) { Point3 in = pSpline->GetInVec(ix); Point3 p = pSpline->GetKnotPoint(ix); Point3 out = pSpline->GetOutVec(ix); vertices.push_back( p ); //inTangents.push_back( in ); //outTangents.push_back( out ); } int nNumVerticesAdded = (int)vertices.size() - oldVerticesCount; nbVertices.push_back( nNumVerticesAdded ); oldVerticesCount = (int)vertices.size(); } } else { for (int i = 0; i < polyShape.numLines; i += 1) { PolyLine &refLine = polyShape.lines[i]; nbVertices.push_back(refLine.numPts); for (int j = 0; j < refLine.numPts; j += 1) { Point3 p = refLine.pts[j].p; vertices.push_back(p); } } } // set the type + wrapping curvesSample.setType(bBezier ? AbcG::kCubic : AbcG::kLinear); curvesSample.setWrap(pShapeObject->CurveClosed(ticks, 0) ? AbcG::kPeriodic : AbcG::kNonPeriodic); curvesSample.setBasis(AbcG::kNoBasis); } if(nbVertices.size() == 0 || vertices.size() == 0){ ESS_LOG_WARNING("No curve data to export."); return false; } const int vertCount = (int)vertices.size(); // prepare the bounding box Abc::Box3d bbox; // allocate the points and normals std::vector<Abc::V3f> posVec(vertCount); Matrix3 wm = mMaxNode->GetObjTMAfterWSM(ticks); for(int i=0;i<vertCount;i++) { posVec[i] = ConvertMaxPointToAlembicPoint(vertices[i] ); bbox.extendBy(posVec[i]); // Set the archive bounding box if (mJob) { Point3 worldMaxPoint = wm * vertices[i]; Abc::V3f alembicWorldPoint = ConvertMaxPointToAlembicPoint(worldMaxPoint); mJob->GetArchiveBBox().extendBy(alembicWorldPoint); } } if(knotVector.size() > 0 && orders.size() > 0){ if(!mKnotVectorProperty.valid()){ mKnotVectorProperty = Abc::OFloatArrayProperty(mCurvesSchema.getArbGeomParams(), ".knot_vector", mCurvesSchema.getMetaData(), mJob->GetAnimatedTs() ); } mKnotVectorProperty.set(Abc::FloatArraySample(knotVector)); if(!mOrdersProperty.valid()){ mOrdersProperty = Abc::OUInt16ArrayProperty(mCurvesSchema.getArbGeomParams(), ".orders", mCurvesSchema.getMetaData(), mJob->GetAnimatedTs() ); } mOrdersProperty.set(Abc::UInt16ArraySample(orders)); } // store the bbox curvesSample.setSelfBounds(bbox); mCurvesSchema.getChildBoundsProperty().set(bbox); Abc::Int32ArraySample nbVerticesSample(&nbVertices.front(),nbVertices.size()); curvesSample.setCurvesNumVertices(nbVerticesSample); // allocate for the points and normals Abc::P3fArraySample posSample(&posVec.front(),posVec.size()); curvesSample.setPositions(posSample); mCurvesSchema.set(curvesSample); mNumSamples++; return true; }
//[-------------------------------------------------------] //[ Private virtual PLSceneNode functions ] //[-------------------------------------------------------] void PLSceneSpline::WriteToFile(XmlElement &cSceneElement, const String &sApplicationDrive, const String &sApplicationDir) { // Do NOT save it as scene node, it's just a 'resource' // Get path filename const String sFilename = sApplicationDrive + sApplicationDir + PLTools::GetResourceFilename(PLTools::ResourcePath, GetName() + ".path"); // Get the IGame spline object of the given IGame node IGameObject *pIGameObject = GetIGameNode()->GetIGameObject(); if (pIGameObject) { // Check the type of the IGame object if (pIGameObject->GetIGameType() == IGameObject::IGAME_SPLINE && pIGameObject->InitializeData()) { IGameSpline &cIGameSpline = *static_cast<IGameSpline*>(pIGameObject); if (cIGameSpline.GetNumberOfSplines () > 0) { // We only support spline 0 IGameSpline3D *pIGameSpline3D = cIGameSpline.GetIGameSpline3D(0); if (pIGameSpline3D) { // Get the local transform matrix GMatrix mTransform = cIGameSpline.GetIGameObjectTM(); // Get the 3ds Max shape object ShapeObject *pMaxShapeObject = static_cast<ShapeObject*>(pIGameObject->GetMaxObject()->ConvertToType(TIME_PosInfinity, Class_ID(GENERIC_SHAPE_CLASS_ID, 0))); if (pMaxShapeObject != nullptr && pMaxShapeObject->NumberOfCurves() == cIGameSpline.GetNumberOfSplines()) { // Create XML document XmlDocument cDocument; // Add declaration XmlDeclaration *pDeclaration = new XmlDeclaration("1.0", "ISO-8859-1", ""); cDocument.LinkEndChild(*pDeclaration); // Add path XmlElement *pPathElement = new XmlElement("Path"); // Setup attributes pPathElement->SetAttribute("Version", "1"); pPathElement->SetAttribute("Closed", String::Format("%d", pMaxShapeObject->CurveClosed(0, 0))); // Add all nodes for (int nKnot=0; nKnot<pIGameSpline3D->GetIGameKnotCount(); nKnot++) { IGameKnot *pIGameKnot = pIGameSpline3D->GetIGameKnot(nKnot); if (pIGameKnot) { // Get knot point in object space (although it is not documented it looks like object space...) Point3 cPoint = pIGameKnot->GetKnotPoint(); // We really need to flip the coordinates to OpenGL style, IGame is not doing this automatically... cPoint = PLTools::Convert3dsMaxVectorToOpenGLVector(cPoint); // Transform to world space cPoint = cPoint*mTransform; // If there's a parent container, make the position of this scene node relative to it PLSceneContainer *pContainer = GetContainer(); if (pContainer) cPoint -= pContainer->GetWorldSpaceCenter(); // Add node XmlElement *pNodeElement = new XmlElement("Node"); pNodeElement->SetAttribute("Name", String::Format("%d", nKnot)); pNodeElement->SetAttribute("Position", String::Format("%f %f %f", cPoint.x, cPoint.y, cPoint.z)); // Link general element pPathElement->LinkEndChild(*pNodeElement); } } // Link material element cDocument.LinkEndChild(*pPathElement); // Save settings if (cDocument.Save(sFilename)) g_pLog->LogFLine(PLLog::Hint, "Created '%s'", sFilename.GetASCII()); else g_pLog->LogFLine(PLLog::Error, "Can't create '%s'!", sFilename.GetASCII()); } } } } else { g_pLog->LogFLine(PLLog::Error, "%s: IGame object is no known spline object!", GetIGameNode()->GetName()); } // Release the IGame object GetIGameNode()->ReleaseIGameObject(); } else { g_pLog->LogFLine(PLLog::Error, "%s: IGame node has no IGame object!", GetIGameNode()->GetName()); } }