Group Details Private

Global Moderators

Forum wide moderators

  • RE: BaseContainer Sort broken?

    Hi @mp5gosu,

    I can ask, but I doubt that this will added, because it is ultimately something that can be "fixed" relatively easily from the user side (see end of posting for example). The only meaningful explanation I can give is that BaseContainer.Sort was implemented for a very specific reason. BaseContainer is not really meant to be a replacement for a sortable collection data type.

    Cheers,
    Ferdinand

    """A little workaround for the somewhat odd BaseContainer sorting behavior.
    """
    
    import c4d
    
    def cleanSort(bc):
        """A little helper for BaseContainer.Sort().
        """
        # Sort the container.
        bc.Sort()
        # Get out when the IDs 0 and 1 are not of type string.
        if (bc.GetType(0) != c4d.DA_STRING or
            bc.GetType(1) != c4d.DA_STRING):
            return bc
        # Save the value of ID 1
        valIdOne = bc[1]
        # Remove the ID 1 and reinsert it in the "right" place.
        if bc.GetIndexId(0) == 1 and bc.RemoveIndex(0):
            bc.InsDataAfter(1, valIdOne, bc.GetDataPointer(0))
        return bc
    
    def main():
        """
        """
        data = {3: "d", 1: "b", 2: "c", 0:"a"}
        bc = c4d.BaseContainer()
        for eid, value in data.items():
            bc.SetString(eid, value)
    
        print ("Before:")
        for eid, value in bc:
            print(f"id: {eid}, value:{value}")
        print ("")
    
        bc = cleanSort(bc)
    
        print ("After:")
        for eid, value in bc:
            print(f"id: {eid}, value:{value}")
    
    
    if __name__=='__main__':
        main()
    
    posted in Cinema 4D Development
  • RE: Errors when recompiling R20 plugin to R21

    Hi @Boony2000, thanks for reaching out to us.

    From R20 to R21, as expectable, changes took place in the API which led to adapt R20 examples to be compiled against the newer API. I recommend having a look at Changes in R21 in our documentation and adapt your code or the demo examples consequently if you copied them straightly from R20.

    I'm also a bit puzzled by your last reply where you stated that errors were all fixed but you confirm that warnings are still around. Can you clarify what warnings look like? Can you also provide a little bit of context in your code where the warning it's likely to take place?

    Thanks, Riccardo

    posted in Cinema 4D Development
  • RE: Is there a Resource Editor for Version 23

    Hi Thomas, I'm sad to confirm that at the moment there is no updated version of ResEdit for R23.

    Cheers, Riccardo

    posted in Cinema 4D Development
  • RE: BaseContainer Sort broken?

    Hi @mp5gosu,

    thank you for reaching out to us and thank you for pointing out this behaviour. However, this is not a bug, but an intentional feature which is linked to the fact the sub-menu titles will be stored under the ID 1 in a BaseContainer. Because of that the method will not be fixed and it is also very unlikely that we will update the documentation, as explaining the reason for this behaviour would require revealing more of the non-public API than we are currently comfortable with.

    However, since this is an intentional behaviour, you can rely on the fact that this will only happen with the ID 1. I.e., when you place your BaseContainer IDs in the range [1, +MAX_INT32], the sort method should work like intended.

    Thank you for your understanding,
    Ferdinand

    posted in Cinema 4D Development
  • RE: Updating F-Curves in Timeline on EditSlider Drag

    @blastframe said in Updating F-Curves in Timeline on EditSlider Drag:

    What's c4d.EVMSG_FCURVECHANGE for then?

    After a quick check (sorry, I should have checked yesterday for a more complete answer) literally nothing. It's used nowhere.

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: SceneHook and object caches

    Hi @C4DS,

    sorry, I was probably a bit cryptic when I talked about a "shift in design". The reason why I was so cryptic is that such design decisions formally fall out of the scope of support (see Forum Guidelines: Scope of Support). It is not because we are lazy, but such decisions often come with a certain vagueness and uncertainty which makes is either very hard or impossible to give reliable answers. So please take my following ideas with a grain of salt.

    I would look at it from the standpoint of reducing complexity. The major problems seems to be the ambiguity of caches. One way to circumvent this could be moving from a SceneHook to a ObjectData plugin. The idea would be to write a thin wrapper around Cinema's SDS object. In GVO you could just collapse all the input objects into a single polygon object and then just return that collapsed object as a child of a SDS-object. In Draw you could then just evaluate this very simple cache of your object (the input would already be taken care of due to the collapsing), i.e. you could directly draw your vertex-to-vertex relations. This would of course come with a considerable amount of overhead due to the collapsing.

    Another approach could be to try to introduce tags as markers which will allow the user to guide your plugin what is considered to be the original. I cannot say you anymore than this without doing it myself (which falls out of the scope of support). The small insight would be here that for humans that decision what is the "original" might often be trivial.

    For the SDS/subdivision vertex-to-vertex relations: My first instinct was that Cinema's subdivisions do not change anything about the index order of the source vertices (i.e., the vertices object that is going to be subdivided) due to the unnecessary overhead that would be introduced by inserting the new topological connected points in the index neighborhood of an "old" vertex. A quick test seems to confirm that, both for the SDS object as well as the "subdivide" command. If you want more clarity on this subject, I would have to ask you to open a new topic on that, so that we can keep topics in a single subject order.

    7c914f63-2a58-4ef9-bed3-9811749d5d50-image.png
    The selected vertices are all of index 8, the object in the middle was the source for the objects on the left and right.

    Thank you for your understanding and cheers,
    Ferdinand

    posted in Cinema 4D Development
  • RE: CCurve.FindPrevUnmuted & CCurve.FindNextUnmuted

    Hi @blastframe,

    thank you for reaching out to us. And again you are right, the documentation is here unfortunately also not the greatest. As a somewhat immediate fix, you could do this:

    prevKey = curveY.FindPrevUnmuted(keyDict["nidx"] - 1)
    nextKey = curveY.FindNextUnmuted(keyDict["nidx"] + 1)
    

    I.e., you cannot include in the key index of the key you want to search from, because the logic here will be otherwise: "Well, the next/prev unmuted key to the passed key is the key itself, since it is a unmuted one). Which is being reflected in the argument description:

    index (int) – The key index to start the search from: 0 <= idx < GetKeyCount()

    but does not mesh very well with the method name. We are also going to fix also the documentation on this one (here including C++). And just for clarity, you can always iterate over the keys of a CCurve, i.e., just access them by their index:

    for eid in range(curveY.GetKeyCount()):
        key = curveY.GetKey(eid)
        print(eid, key)
    

    I am sorry, I understand your CCurve documentation experience was probably not the greatest, but we try to improve.

    Thank you for your understanding,
    Ferdinand

    posted in Cinema 4D Development
  • RE: Updating F-Curves in Timeline on EditSlider Drag

    hi @blastframe

    You should use EVMSG_CHANGE instead of EVMSG_FCURVECHANGE

    c4d.GeSyncMessage(c4d.EVMSG_CHANGE)
    

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: Collect System Information/Configuration

    hi,

    you can have a look at our manual about MachineInterface you will find all the machine information under those attributs

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: Custom FieldList is not reevaluated on field change

    hi,

    this doesn't seem to be the right way to do it. If the fields object isn't a child of your effector, this will not work.

    Our DropEffector example wasn't working with fields (only falloff) so I've updated it.
    I'm not sure where you are adding your code so i'm not sure why it's not working.

    Here's the updated version of the drop effector. Could you try to compile it on your side and tell me if it's working or not. If not, how to reproduce the issue :)

    // this example demonstrates how to implement a more complex direct control effector
    // and utilize falloff and strength directly
    // the effector drops the particles to a surface
    
    #include "c4d.h"
    #include "c4d_symbols.h"
    #include "lib_collider.h"
    #include "c4d_baseeffectorplugin.h"
    #include "c4d_falloffplugin.h"
    #include "oedrop.h"
    #include "main.h"
    
    struct DropEffectorData
    {
    	BaseObject* target;
    	Int32				mode;
    	Float				maxdist;
    	Matrix			genmg, igenmg;
    	Matrix			targmg, itargmg;
    };
    
    class DropEffector : public EffectorData
    {
    public:
    	DropEffectorData				 ed;
    	AutoAlloc<GeRayCollider> rcol;
    	GeRayColResult					 rcolres;
    
    	virtual Bool InitEffector(GeListNode* node);
    
    	virtual void InitPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread);
    	virtual void ModifyPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread);
    
    	static NodeData* Alloc() { return NewObjClear(DropEffector); }
    };
    
    Bool DropEffector::InitEffector(GeListNode* node)
    {
    	if (!rcol || !node)
    		return false;
    
    	BaseObject* op = (BaseObject*)node;
    	if (!op)
    		return false;
    
    	BaseContainer* bc = op->GetDataInstance();
    	if (!bc)
    		return false;
    
    	bc->SetFloat(DROPEFFECTOR_DISTANCE, 1000.0);
    
    	return true;
    }
    
    void DropEffector::InitPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread)
    {
    	BaseContainer* bc = op->GetDataInstance();
    	if (!bc)
    		return;
    
    	if (!rcol)
    		return;
    
    	ed.mode = bc->GetInt32(DROPEFFECTOR_MODE);
    	ed.maxdist = bc->GetFloat(DROPEFFECTOR_DISTANCE);
    	ed.target	 = bc->GetObjectLink(DROPEFFECTOR_TARGET, doc);
    	if (!ed.target)
    		return;
    
    	ed.targmg	 = ed.target->GetMg();
    	ed.itargmg = ~ed.targmg;
    	ed.genmg	= gen->GetMg();
    	ed.igenmg = ~ed.genmg;
    
    	// Add a dependency so that the effector will update if the target changes
    	AddEffectorDependence(ed.target);
    
    	// Can't init raycollider or the target isn't polygonal, then skip
    	if (!rcol->Init(ed.target))
    		ed.target = nullptr;
    	else if (!ed.target->IsInstanceOf(Opolygon))
    		ed.target = nullptr;
    }
    
    void DropEffector::ModifyPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread)
    {
    	if (!ed.target || !rcol || data->strength == 0.0)
    		return;
    
    	C4D_Falloff* falloff = GetFalloff();
    	FieldOutput* fieldSample = nullptr;
    
    	if (!falloff)
    	{
    		fieldSample = CalcFields(doc, gen, md, FIELDSAMPLE_FLAG::VALUE);
    		if (!fieldSample)
    			return;
    	}
    
    	Int32	 i = 0;
    	Float	 fall	 = 0.0;
    	Vector off	 = Vector(0.0);
    	Vector ray_p = Vector(0.0), ray_dir = Vector(0.0);
    	Vector targ_off = Vector(0.0), targ_hpb = Vector(0.0);
    
    	MDArray<Int32>	flag_array = md->GetLongArray(MODATA_FLAGS);
    	MDArray<Matrix> mat_array	 = md->GetMatrixArray(MODATA_MATRIX);
    	MDArray<Float>	weight_array = md->GetRealArray(MODATA_WEIGHT);
    
    	if (!mat_array)
    		return;
    
    	Int32 mdcount = (Int32)md->GetCount();
    	for (i = 0; i < mdcount; i++)
    	{
    		// If the particle isn't visible, don't calculate
    		if (!(flag_array[i] & MOGENFLAG_CLONE_ON) || (flag_array[i] & MOGENFLAG_DISABLE))
    			continue;
    
    		// Multiply into global space
    		off = mat_array[i].off;
    		off = ed.genmg * off;
    
    		// Sample the falloff
    		if (falloff)
    		{
    			falloff->Sample(off, &fall, true, weight_array[i]);
    		}
    		else
    		{
    			fall = fieldSample->_value[i];
    		}
    		if (fall == 0.0)
    			continue;
    
    		// Set up the ray for the collision
    		ray_p = ed.itargmg * off;
    		switch (ed.mode)
    		{
    			default:
    			case DROPEFFECTOR_MODE_PNORMAL:		ray_dir = ed.genmg.sqmat * mat_array[i].sqmat.v3; break;
    			case DROPEFFECTOR_MODE_NNORMAL:		ray_dir = -(ed.genmg.sqmat * mat_array[i].sqmat.v3); break;
    			case DROPEFFECTOR_MODE_AXIS:			ray_dir = (ed.targmg.off - off); break;
    			case DROPEFFECTOR_MODE_SELFAXIS:	ray_dir = (ed.genmg.off - off);	break;
    			case DROPEFFECTOR_MODE_PX:				ray_dir = Vector(1.0, 0.0, 0.0); break;
    			case DROPEFFECTOR_MODE_PY:				ray_dir = Vector(0.0, 1.0, 0.0); break;
    			case DROPEFFECTOR_MODE_PZ:				ray_dir = Vector(0.0, 0.0, 1.0); break;
    			case DROPEFFECTOR_MODE_NX:				ray_dir = Vector(-1.0, 0.0, 0.0);	break;
    			case DROPEFFECTOR_MODE_NY:				ray_dir = Vector(0.0, -1.0, 0.0);	break;
    			case DROPEFFECTOR_MODE_NZ:				ray_dir = Vector(0.0, 0.0, -1.0);	break;
    		}
    		ray_dir = ed.itargmg.sqmat * ray_dir;
    
    		// Calculate an intersection
    		if (rcol->Intersect(ray_p, !ray_dir, ed.maxdist, false))
    		{
    			if (rcol->GetNearestIntersection(&rcolres))
    			{
    				fall *= data->strength;
    
    				targ_off = Blend(mat_array[i].off, ed.igenmg * (ed.targmg * rcolres.hitpos), fall);
    				targ_hpb = VectorToHPB(ed.igenmg.sqmat * (ed.targmg.sqmat * rcolres.s_normal));
    
    				mat_array[i] = HPBToMatrix(Blend(MatrixToHPB(mat_array[i], ROTATIONORDER::DEFAULT), targ_hpb, fall), ROTATIONORDER::DEFAULT);
    				mat_array[i].off = targ_off;
    			}
    		}
    	}
    }
    
    // be sure to use a unique ID obtained from www.plugincafe.com
    #define ID_DROPEFFECTOR 1019571
    
    Bool RegisterDropEffector()
    {
    	return RegisterEffectorPlugin(ID_DROPEFFECTOR, GeLoadString(IDS_DROPEFFECTOR), OBJECT_CALL_ADDEXECUTION, DropEffector::Alloc, "oedrop"_s, AutoBitmap("dropeffector.tif"_s), 0);
    }
    
    

    Cheers,
    Manuel

    posted in Cinema 4D Development