Hi @d_schmidt,
thank you for reaching out to us. And thank you for pointing out this false information in the C++ SDK documentation. In fact you cannot return a BaseObject
in ObjectData::GetContour
, instead you should return an empty spline. I added a modified double circle example at the end to demonstrate that approach.
About your code, there are also two other things that stand out to me. I was actually surprised that static_cast<SplineObject*>(BaseObject::Alloc(Onull))
did compile for you, because there is no direct implicit or explicit conversion from BaseObject
to SplineObject
. @m_adam then pointed out that this probably does compile because you can convert via BaseList2D
and the type symbols to a SplineObject
. But this cast is probably still the reason why Cinema 4D does crash for you, since this casting probably produces garbage. There is also the fact that you allocate bp
and leave it behind in the case when CIRCLEOBJECT_RAD > 500.0
.
I have added the documentation issue to our task list and it will be reflected in an upcoming release.
I hope this helps and cheers,
Ferdinand
The relevant bits:
static SplineObject* GenerateCircle(Float rad)
{
#define TANG 0.415
// Our little radius cutoff value.
#define MAX_RADIUS 500.0
// If the passed radius exceeds our MAX_RADIUS, bail and return an
// empty spline.
if (rad > MAX_RADIUS) {
SplineObject* dummy = SplineObject::Alloc(0, SPLINETYPE::LINEAR);
dummy->Message(MSG_UPDATE);
return dummy;
}
...
SplineObject* DoubleCircleData::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
{
BaseContainer* bc = op->GetDataInstance();
if (!bc)
return nullptr;
// This will now return an empty spline when CIRCLEOBJECT_RAD > MAX_RADIUS
SplineObject* bp = GenerateCircle(bc->GetFloat(CIRCLEOBJECT_RAD));
...
The whole file:
// spline example
#include "c4d.h"
#include "c4d_symbols.h"
#include "odoublecircle.h"
#include "main.h"
class DoubleCircleData : public ObjectData
{
INSTANCEOF(DoubleCircleData, ObjectData)
public:
virtual Bool Init(GeListNode* node);
virtual Bool Message (GeListNode* node, Int32 type, void* data);
virtual Int32 GetHandleCount(BaseObject* op);
virtual void GetHandle(BaseObject* op, Int32 i, HandleInfo& info);
virtual void SetHandle(BaseObject* op, Int32 i, Vector p, const HandleInfo& info);
virtual SplineObject* GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt);
virtual Bool GetDEnabling(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_ENABLE flags, const BaseContainer* itemdesc);
static NodeData* Alloc() { return NewObjClear(DoubleCircleData); }
};
Bool DoubleCircleData::Message(GeListNode* node, Int32 type, void* data)
{
if (type == MSG_MENUPREPARE)
{
BaseDocument* doc = (BaseDocument*)data;
((BaseObject*)node)->GetDataInstance()->SetInt32(PRIM_PLANE, doc->GetSplinePlane());
}
return true;
}
Bool DoubleCircleData::Init(GeListNode* node)
{
BaseObject* op = (BaseObject*)node;
BaseContainer* data = op->GetDataInstance();
if (!data)
return false;
data->SetFloat(CIRCLEOBJECT_RAD, 200.0);
data->SetInt32(PRIM_PLANE, 0);
data->SetBool(PRIM_REVERSE, false);
data->SetInt32(SPLINEOBJECT_INTERPOLATION, SPLINEOBJECT_INTERPOLATION_ADAPTIVE);
data->SetInt32(SPLINEOBJECT_SUB, 8);
data->SetFloat(SPLINEOBJECT_ANGLE, DegToRad(5.0));
data->SetFloat(SPLINEOBJECT_MAXIMUMLENGTH, 5.0);
return true;
}
static Vector SwapPoint(const Vector& p, Int32 plane)
{
switch (plane)
{
case 1: return Vector(-p.z, p.y, p.x); break;
case 2: return Vector(p.x, -p.z, p.y); break;
}
return p;
}
Int32 DoubleCircleData::GetHandleCount(BaseObject* op)
{
return 1;
}
void DoubleCircleData::GetHandle(BaseObject* op, Int32 i, HandleInfo& info)
{
BaseContainer* data = op->GetDataInstance();
if (!data)
return;
Float rad = data->GetFloat(CIRCLEOBJECT_RAD);
Int32 plane = data->GetInt32(PRIM_PLANE);
info.position = SwapPoint(Vector(rad, 0.0, 0.0), plane);
info.direction = !SwapPoint(Vector(1.0, 0.0, 0.0), plane);
info.type = HANDLECONSTRAINTTYPE::LINEAR;
}
void DoubleCircleData::SetHandle(BaseObject* op, Int32 i, Vector p, const HandleInfo& info)
{
BaseContainer* data = op->GetDataInstance();
if (!data)
return;
Float val = Dot(p, info.direction);
data->SetFloat(CIRCLEOBJECT_RAD, ClampValue(val, 0.0_f, (Float) MAXRANGE));
}
// --- Modifications start here. ---
static SplineObject* GenerateCircle(Float rad)
{
#define TANG 0.415
// Our little radius cutoff value.
#define MAX_RADIUS 500.0
// If the passed radius exceeds our MAX_RADIUS, bail and return an
// empty spline.
if (rad > MAX_RADIUS) {
SplineObject* dummy = SplineObject::Alloc(0, SPLINETYPE::LINEAR);
dummy->Message(MSG_UPDATE);
return dummy;
}
// --- Modifications end here ---
Float sn, cs;
Int32 i, sub = 4;
SplineObject* op = SplineObject::Alloc(sub * 2, SPLINETYPE::BEZIER);
if (!op || !op->MakeVariableTag(Tsegment, 2))
{
blDelete(op); return nullptr;
}
op->GetDataInstance()->SetBool(SPLINEOBJECT_CLOSED, true);
Vector* padr = op->GetPointW();
Tangent* hadr = op->GetTangentW();
Segment* sadr = op->GetSegmentW();
if (sadr)
{
sadr[0].closed = true;
sadr[0].cnt = sub;
sadr[1].closed = true;
sadr[1].cnt = sub;
}
if (hadr && padr)
{
for (i = 0; i < sub; i++)
{
SinCos(2.0 * PI * i / Float(sub), sn, cs);
padr[i] = Vector(cs * rad, sn * rad, 0.0);
hadr[i].vl = Vector(sn * rad * TANG, -cs * rad * TANG, 0.0);
hadr[i].vr = -hadr[i].vl;
padr[i + sub] = Vector(cs * rad, sn * rad, 0.0) * 0.5;
hadr[i + sub].vl = Vector(sn * rad * TANG, -cs * rad * TANG, 0.0) * 0.5;
hadr[i + sub].vr = -hadr[i + sub].vl;
}
}
op->Message(MSG_UPDATE);
return op;
}
static void OrientObject(SplineObject* op, Int32 plane, Bool reverse)
{
Vector* padr = ToPoint(op)->GetPointW();
Tangent* hadr = ToSpline(op)->GetTangentW(), h;
Int32 pcnt = ToPoint(op)->GetPointCount(), i;
if (!hadr && ToSpline(op)->GetTangentCount())
return;
if (!padr && ToPoint(op)->GetPointCount())
return;
if (plane >= 1)
{
switch (plane)
{
case 1: // ZY
for (i = 0; i < pcnt; i++)
{
padr[i] = Vector(-padr[i].z, padr[i].y, padr[i].x);
if (!hadr)
continue;
hadr[i].vl = Vector(-hadr[i].vl.z, hadr[i].vl.y, hadr[i].vl.x);
hadr[i].vr = Vector(-hadr[i].vr.z, hadr[i].vr.y, hadr[i].vr.x);
}
break;
case 2: // XZ
for (i = 0; i < pcnt; i++)
{
padr[i] = Vector(padr[i].x, -padr[i].z, padr[i].y);
if (!hadr)
continue;
hadr[i].vl = Vector(hadr[i].vl.x, -hadr[i].vl.z, hadr[i].vl.y);
hadr[i].vr = Vector(hadr[i].vr.x, -hadr[i].vr.z, hadr[i].vr.y);
}
break;
}
}
if (reverse)
{
Vector p;
Int32 to = pcnt / 2;
if (pcnt % 2)
to++;
for (i = 0; i < to; i++)
{
p = padr[i]; padr[i] = padr[pcnt - 1 - i]; padr[pcnt - 1 - i] = p;
if (!hadr)
continue;
h = hadr[i];
hadr[i].vl = hadr[pcnt - 1 - i].vr;
hadr[i].vr = hadr[pcnt - 1 - i].vl;
hadr[pcnt - 1 - i].vl = h.vr;
hadr[pcnt - 1 - i].vr = h.vl;
}
}
op->Message(MSG_UPDATE);
}
SplineObject* DoubleCircleData::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
{
BaseContainer* bc = op->GetDataInstance();
if (!bc)
return nullptr;
// This will now return an empty spline when CIRCLEOBJECT_RAD > MAX_RADIUS
SplineObject* bp = GenerateCircle(bc->GetFloat(CIRCLEOBJECT_RAD));
if (!bp)
return nullptr;
BaseContainer* bb = bp->GetDataInstance();
bb->SetInt32(SPLINEOBJECT_INTERPOLATION, bc->GetInt32(SPLINEOBJECT_INTERPOLATION));
bb->SetInt32(SPLINEOBJECT_SUB, bc->GetInt32(SPLINEOBJECT_SUB));
bb->SetFloat(SPLINEOBJECT_ANGLE, bc->GetFloat(SPLINEOBJECT_ANGLE));
bb->SetFloat(SPLINEOBJECT_MAXIMUMLENGTH, bc->GetFloat(SPLINEOBJECT_MAXIMUMLENGTH));
OrientObject(bp, bc->GetInt32(PRIM_PLANE), bc->GetBool(PRIM_REVERSE));
return bp;
}
Bool DoubleCircleData::GetDEnabling(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_ENABLE flags, const BaseContainer* itemdesc)
{
Int32 inter;
BaseContainer* data = ((BaseObject*)node)->GetDataInstance();
if (!data)
return false;
switch (id[0].id)
{
case SPLINEOBJECT_SUB:
inter = data->GetInt32(SPLINEOBJECT_INTERPOLATION);
return inter == SPLINEOBJECT_INTERPOLATION_NATURAL || inter == SPLINEOBJECT_INTERPOLATION_UNIFORM;
case SPLINEOBJECT_ANGLE:
inter = data->GetInt32(SPLINEOBJECT_INTERPOLATION);
return inter == SPLINEOBJECT_INTERPOLATION_ADAPTIVE || inter == SPLINEOBJECT_INTERPOLATION_SUBDIV;
case SPLINEOBJECT_MAXIMUMLENGTH:
return data->GetInt32(SPLINEOBJECT_INTERPOLATION) == SPLINEOBJECT_INTERPOLATION_SUBDIV;
}
return true;
}
// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_CIRCLEOBJECT 1001154
Bool RegisterCircle()
{
return RegisterObjectPlugin(ID_CIRCLEOBJECT, GeLoadString(IDS_CIRCLE), OBJECT_GENERATOR | OBJECT_ISSPLINE, DoubleCircleData::Alloc, "Odoublecircle"_s, AutoBitmap("circle.tif"_s), 0);
}