Getting Deformed Object Final State [SOLVED]



  • On 07/10/2014 at 12:19, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   14, 15 
    Platform:      
    Language(s) :     C++  ;

    ---------
    I would like to get the final state of an animated deformed object, as with a displacer modifier.

    The code I am using to get the current state of an object (myGeometry) is:

    		BaseObject* myGeometry; 
    		myGeometry= static_cast<BaseObject*>(myLink->GetClone(COPYFLAGS_0, NULL));
    		ModelingCommandData mcd;
    		mcd.op = myGeometry;
    		mcd.mode = MODELINGCOMMANDMODE_ALL;
    		mcd.doc = doc;
    		SendModelingCommand(MCOMMAND_CURRENTSTATETOOBJECT, mcd);
    		myGeometry= static_cast<BaseObject*>(mcd.result->GetIndex(0)); 
    

    I am then projecting some points over myGeometry using GeRayCollider.
    The problem is that the deformation is not considered, and the projection happens on the original undeformed geometry.

    Any idea on how to get the final state of the geometry after the application of any (animated) modifiers?

    Thanks!



  • On 07/10/2014 at 13:26, xxxxxxxx wrote:

    I use the following code to bake my generated object to a mesh inside my GetVirtualObjects function.  I think the key was using a temporary document to do the SendModellingCommand in order for it to work.  Not sure if it`ll work in your case but here it is:

    //// CURRENT_STATE_TO_OBJECT ///////////////////////////////////////////////
    AutoAlloc<BaseDocument>tempdoc;
    tdoc->InsertObject(myObject, FALSE, FALSE);
    ModelingCommandData cd;
    cd.doc = tempdoc;
    cd.op = myObject;
    if (SendModelingCommand(MCOMMAND_CURRENTSTATETOOBJECT, cd))
    {
    	GePrint("returning SMC");
    	return static_cast<BaseObject*>(cd.result->GetIndex(0));
    }
    else
    {
    	GePrint("returning non-SMC");
    	return myObject;
    }
    //// CURRENT_STATE_TO_OBJECT ///////////////////////////////////////////////
    


  • On 07/10/2014 at 13:33, xxxxxxxx wrote:

    Edit: Maybe your problem is that you are cloning your reference object beforehand.
    However, it should contain the deformations, this seems strange. The context it is used in might be important here (how is myLink for example defined? Is there an error already before you actually call CSTO? You want to check this..), so you may need to provide a bit more information.

    Here is a function I use (nearly the same as Eclektriks) :

        
        
        static BaseObject* GetDeformedObject(BaseObject* obj, BaseDocument* doc)
        { 
        	if (!obj) return nullptr;
        	ModelingCommandData cd; 
        	cd.doc = doc; 
        	cd.op = obj; 
        	if (!SendModelingCommand(MCOMMAND_CURRENTSTATETOOBJECT,cd)) return nullptr;
        	return static_cast<BaseObject*>(cd.result->GetIndex(0));
        }
    

    It's fine to pass the current document as long as it is not forbidden or unsafe to use the document you have. Then you may want to create a temporary document as Eclectrik but usually it's not necessary if your situation allows you to modify the current scene.



  • On 07/10/2014 at 22:03, xxxxxxxx wrote:

    Thanks for both your replies Eclectrik and Katachi.

    Inserting into a temporary document did work with the Displacer deformer, so that was part of the solution to my problem. The other part now is making it work with an ANIMATED displacer.
    I am inserting a NOISE shader and increasing the animation speed. The displacer is considered only on the first frame and ignoring the animation.

    Any idea on how to make it calculate on ever frame? I am already checking the dirty state of the object with GetDirty(DIRTYFLAGS_MATRIX | DIRTYFLAGS_CACHE | DIRTYFLAGS_DATA | DIRTYFLAGS_SELECT).
    Maybe other flags are needed?



  • On 08/10/2014 at 00:25, xxxxxxxx wrote:

    Ah, if you are talking about animated deformation it is slightly more involved. It wouldn't simply be baked into the mesh, what you will have to do is look into caching the deformation, normally to PLA ( look that up to get an understanding of how it works ).

    It might seem counterintuitive at first or an extra step but caching the deforms has the added bonus that they run faster.



  • On 08/10/2014 at 18:45, xxxxxxxx wrote:

    To elaborate more: You take your base mesh, add a PLA animation track to it, and add keys, frame by frame, by animating (using BaseDocument::SetTime()) the original animated mesh and storing the deformed vertex array at each frame into each key.  If I have a chance, I can dig up code for doing this.  As mentioned, it is a bit more involved.



  • On 09/10/2014 at 12:22, xxxxxxxx wrote:

    Thanks Robert. I would love to take a look at the code.
    One thing that comes to mind though, is how can I get a BaseObject from the stored vertex arrays to use later in a GeRayCollider operation.

    On another note, I think that ultimately this whole approach will not work as intended, as the projection will produce different results on every frame, even if the animated deformation is considered. 
    I come from a Softimage background, where ICE (visual programming language, slightly similar to Xpresso) provides a feature called LOCATIONS, which are attributes on an object that are automatically interpolated when the object is deformed.  
    I think that if MAXON  adds the concept of Geometry Locations for polygonal objects, it will provide for a lot of (in my humble opinon) missing functionality.

    I would still like to know how I can get a BaseObject out of a point array if you have the time.



  • On 10/10/2014 at 02:43, xxxxxxxx wrote:

    You wont get a baseobject out of a point array as such, what you'll want to do is run your ray collision function on the final deformed geometry, probably on a frame by frame basis. If you want an average result from the raycollision then do an average of the results. I'm not familiar with ICE having never used it but Locations sounds like a pretty straightforward thing to achieve - ie tracking the position of a point on a deformed mesh - nothing groundbreaking in that..



  • On 10/10/2014 at 06:27, xxxxxxxx wrote:

    Here is a classic example of using Locations in ICE: suppose you have a vector point (or array of points) in 3D space, you can get the closest location to that point on any polygonal object (similar to GeRayCollider, but works by closest location and not directional projection). From that location, you can retrieve point position, polygon position, normals, and these retrieved attributes are still automatically retrieved when the target geometry is deformed.

    Does something similar to Get Closest Location (from a 3D point, not from mouse coordinates) exist in the Cinema 4D SDK? If it did, it would surely make my life easier.



  • On 15/10/2014 at 05:26, xxxxxxxx wrote:

    Hi Salozo sorry for the delay in replying..

    As far as i know there`s not a built in function for doing this, but the tools are all there.  I did something similar ( selecting polygons based on distance from another object ) and using the same approach i would tackle your problem as follows:

    Loop through all of the points in your Point Array.  For each point, grab the absolute/world position, then loop through all of the points/polys of your target Object.  And for each point/poly, do a simple calculation to work out the distance between the two world positions, and store a pointer to the closest one in the loop.  You can also implement a threshold ( which is what i did ) which says if distance is greater than a certain value then ignore..  Once you have stored your pointers you can grab normals or anything you need from the object using those pointers.  Hey presto.

    Anyway thats the semantics of it.  And as i said nothing groundbreaking and pretty straightforward.  If there WAS a GetClosetLocation within the API that is what itd be doing anyway.  You just got to figure out the code you need within the API to do it.  I`ll dig up my version shortly as an example..



  • On 15/10/2014 at 06:31, xxxxxxxx wrote:

    Heres the code that loops an array of points ( its actually an array that stores the positions of clones from a cloner object ) and checks the distance between the point and each polygon in my object.  It then selects the polys that meet the conditions and stores the selection to a baseselect.  There are a couple of conditions in this loop you`ll not need ( thisDist which is part of the larger function ) but eh core is there as an example.  The code is probably not perfect as it was one of the first things i did with the C4D api and C++ but here it is:

    	Real thisDist;
      
    	for (i=0; i<polyCount; i++)
    	{
    		CPolygon pPoly = obj->GetPolygonR()[i];
    			
    		Vector polyPos = poly_center(obj, &pPoly);
    		polyPos *= obj->GetMg();  // Translates poly position to absolute/world
    		
    		for (m=0; m<positionCount; m++)
    		{
    			
    			Real vecLen = positionArray[m].Scale().GetLength();  // theres a reason for this, i think it was to check that there is a valid matrix stored.
      
    			if (LONG(vecLen) == LONG(1))
    			{
    				thisDist = maxDist;
    			}
    			else
    			{
    				thisDist = VectorMax(positionArray[m].Scale())+maxDist;
    			}
      
    			Vector posIt = polyPos - positionArray[m].off;
    			Real polyDist = posIt.GetLength();
    			if (polyDist <= thisDist)
    			{
    				if ((deSel->IsSelected(i)) || (!clipBool))
    				{
    					sel->Select(i);
    				}
    			}
    		}
    	}
      
    //// Poly Center function i store from someone else on here, sorry i can`t remember who, it calculates the position of the center of a polygon /////
      
    Vector poly_center(PolygonObject* op, CPolygon* polygon)
    {
    	//# Determine whether you passed an index or a Polygon object
    	/*
    	if (!isinstance(polygon, CPolygon))
    	{
    		polygon = op->GetPolygonR()[polygon];
    	}
    	*/
    	std::vector<long> indices;
    	indices.push_back(polygon->a);
    	indices.push_back(polygon->b);
    	indices.push_back(polygon->c);
    		
    	//# Determine whether the polygon is a triangle or a quadrangle
    		
    	if (polygon->c != polygon->d)
    	{
    		indices.push_back(polygon->d);
    	}
    	//# Gather a list of the points' Vectors
    	
    	std::vector<Vector> vectors;
      
    	for (int i=0; i<int(indices.size()); i++)
    	{
      
    		Vector pnt = op->GetPointR()[indices[i]];
    		vectors.push_back(pnt);
    		//vectors = map(lambda i: op->GetPoint(i), indices);
    	}
    		
    	//# Compute the average of the Vectors
    	
    	Vector sum;
    	for(std::vector<Vector>::iterator j=vectors.begin(); j<vectors.end(); j++)
    	{
    		sum += *j;
    	}
      
    	return sum / vectors.size();
    }
    

Log in to reply