Notifications
Clear all
[Closed] SDK GeomObject crash on Render ??
Feb 16, 2016 10:03 am
Hi, I’ve just started up a new Object plugin with the GeomObject as its Class. I’ve put in the basic things so it creates a small mesh etc. But whenever I try and render, it crashes ?? Is there anything important I need to add to get it to render ? Its a pretty much a brand new project so not sure what I’m missing. Thanks.
1 Reply
Feb 16, 2016 10:03 am
Right, I have tried making a new project and using mrGeomShaderObject from the HowTo as a base for my plugin, and I’m still can’t render. I also found out, it also crashes if I try and access the Object Properties. I will post the full code below and hopefully someone will be able to tell me what I am doing wrong.
maxProject1.h
#include "3dsmaxsdk_preinclude.h"
#include "resource.h"
#include <max.h>
#include <simpobj.h>
#include <istdplug.h>
#include <iparamb2.h>
#include <iparamm2.h>
#include <maxtypes.h>
//SIMPLE TYPE
#include <mouseman.h>
#include <triobj.h>
#include "Graphics/IDisplayManager.h"
extern TCHAR *GetString(int id);
extern HINSTANCE hInstance;
#define maxProject1_CLASS_ID Class_ID(0x37c4f9f4, 0x76925481)
#define PBLOCK_REF 0
class maxProject1 : public GeomObject
{
public:
//Constructor/Destructor
maxProject1();
virtual ~maxProject1();
virtual void DeleteThis() { delete this; }
void SetBox(const Box3& box);
void InvalidateMesh();
void SetCreating(bool creating);
void UpdateMesh(TimeValue t);
// From BaseObject
virtual CreateMouseCallBack* GetCreateMouseCallBack();
virtual int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags);
virtual int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt);
virtual void Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt);
//TODO: Return the name that will appear in the history browser (modifier stack)
virtual const TCHAR *GetObjectName() { return GetString(IDS_CLASS_NAME); }
virtual void GetWorldBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
virtual void GetLocalBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
virtual void GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel );
//TODO: Return the default name of the node when it is created.
virtual void InitNodeName(TSTR& s) { s = GetString(IDS_CLASS_NAME); }
// From Object
virtual BOOL HasUVW();
virtual void SetGenUVW(BOOL sw);
virtual int CanConvertToType(Class_ID obtype);
virtual Object* ConvertToType(TimeValue t, Class_ID obtype);
virtual void GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist);
virtual int IntersectRay(TimeValue t, Ray& ray, float& at, Point3& norm);
//TODO: Evaluate the object and return the ObjectState
virtual ObjectState Eval(TimeValue /*t*/) { return ObjectState(this); };
//TODO: Return the validity interval of the object as a whole
virtual Interval ObjectValidity(TimeValue /*t*/) { return FOREVER; }
// From Animatable
virtual void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev);
virtual void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
// From GeomObject
virtual Mesh* GetRenderMesh(TimeValue t, INode *inode, View& view, BOOL& needDelete);
// Loading/Saving
virtual IOResult Load(ILoad *iload);
virtual IOResult Save(ISave *isave);
//From Animatable
virtual Class_ID ClassID() {return maxProject1_CLASS_ID;}
virtual SClass_ID SuperClassID() { return GEOMOBJECT_CLASS_ID; }
virtual void GetClassName(TSTR& s) {s = GetString(IDS_CLASS_NAME);}
virtual RefTargetHandle Clone( RemapDir &remap );
virtual RefResult NotifyRefChanged(const Interval& changeInt, RefTargetHandle hTarget,
PartID& partID, RefMessage message, BOOL propagate);
virtual int NumSubs() { return 1; }
virtual TSTR SubAnimName(int /*i*/) { return GetString(IDS_PARAMS); }
virtual Animatable* SubAnim(int /*i*/) { return pblock; }
// TODO: Maintain the number or references here
virtual int NumRefs() { return 1; }
virtual RefTargetHandle GetReference(int i);
virtual int NumParamBlocks() { return 1; } // return number of ParamBlocks in this instance
virtual IParamBlock2* GetParamBlock(int /*i*/) { return pblock; } // return i'th ParamBlock
virtual IParamBlock2* GetParamBlockByID(BlockID id) { return (pblock->ID() == id) ? pblock : NULL; } // return id'd ParamBlock
// local methods
BOOL GetSuspendSnap(){return suspendSnap;}
void SetSuspendSnap(BOOL iSuspendSnap){suspendSnap = iSuspendSnap;}
// from IDisplay
unsigned long GetObjectDisplayRequirement() const;
bool PrepareDisplay(
const MaxSDK::Graphics::UpdateDisplayContext& prepareDisplayContext);
bool UpdatePerNodeItems(
const MaxSDK::Graphics::UpdateDisplayContext& updateDisplayContext,
MaxSDK::Graphics::UpdateNodeContext& nodeContext,
MaxSDK::Graphics::IRenderItemContainer& targetRenderItemContainer);
protected:
virtual void SetReference(int i, RefTargetHandle rtarg);
private:
// The bounding box of the object (used to generate the mesh)
Box3 m_box;
// Parameter block
IParamBlock2* pblock; // ref 0
BOOL suspendSnap; // A flag for setting snapping on/off
void BuildMesh(TimeValue t);
Mesh m_mesh;
Interval m_meshValid;
// In the creation process?
bool m_creating;
};
inline void maxProject1::SetBox(const Box3& box) {
m_box = box;
}
inline void maxProject1::InvalidateMesh() {
m_meshValid.SetEmpty();
}
inline void maxProject1::SetCreating(bool creating) {
m_creating = creating;
}
maxProject1.cpp
#include "maxProject1.h"
#define DD (.01f)
namespace {
void MakeQuad(Face *f, int a, int b , int c , int d, int sg) {
f[0].setVerts( a, b, c);
f[0].setSmGroup(sg);
f[0].setEdgeVisFlags(1,1,0);
f[1].setVerts( c, d, a);
f[1].setSmGroup(sg);
f[1].setEdgeVisFlags(1,1,0);
}
}
class maxProject1ClassDesc : public ClassDesc2
{
public:
virtual int IsPublic() { return TRUE; }
virtual void* Create(BOOL /*loading = FALSE*/) { return new maxProject1(); }
virtual const TCHAR * ClassName() { return GetString(IDS_CLASS_NAME); }
virtual SClass_ID SuperClassID() { return GEOMOBJECT_CLASS_ID; }
virtual Class_ID ClassID() { return maxProject1_CLASS_ID; }
virtual const TCHAR* Category() { return GetString(IDS_CATEGORY); }
virtual const TCHAR* InternalName() { return _T("maxProject1"); } // returns fixed parsable name (scripter-visible name)
virtual HINSTANCE HInstance() { return hInstance; } // returns owning module handle
};
ClassDesc2* GetmaxProject1Desc() {
static maxProject1ClassDesc maxProject1Desc;
return &maxProject1Desc;
}
enum { maxproject1_params };
//TODO: Add enums for various parameters
enum {
pb_spin,
};
static ParamBlockDesc2 maxproject1_param_blk ( maxproject1_params, _T("params"), 0, GetmaxProject1Desc(),
P_AUTO_CONSTRUCT + P_AUTO_UI, PBLOCK_REF,
//rollout
IDD_PANEL, IDS_PARAMS, 0, 0, NULL,
// params
pb_spin, _T("spin"), TYPE_FLOAT, P_ANIMATABLE, IDS_SPIN,
p_default, 0.1f,
p_range, 0.0f,1000.0f,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_EDIT, IDC_SPIN, 0.01f,
p_end,
p_end
);
//--- maxProject1 -------------------------------------------------------
maxProject1::maxProject1()
: pblock(nullptr)
{
GetmaxProject1Desc()->MakeAutoParamBlocks(this);
}
maxProject1::~maxProject1()
{
}
IOResult maxProject1::Load(ILoad* /*iload*/)
{
#pragma message(TODO("Add code to allow plugin to load its data"))
return IO_OK;
}
IOResult maxProject1::Save(ISave* /*isave*/)
{
#pragma message(TODO("Add code to allow plugin to save its data"))
return IO_OK;
}
void maxProject1::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev)
{
GetmaxProject1Desc()->BeginEditParams(ip, this, flags, prev);
}
void maxProject1::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
{
#pragma message(TODO("Save plugin parameter values into class variables, if they are not hosted in ParamBlocks."))
GetmaxProject1Desc()->EndEditParams(ip, this, flags, next);
}
//From Object
BOOL maxProject1::HasUVW()
{
#pragma message(TODO("Return whether the object has UVW coordinates or not"))
return TRUE;
}
void maxProject1::SetGenUVW(BOOL sw)
{
if (sw==HasUVW()) return;
#pragma message(TODO("TODO: Set the plugin's internal value to sw"))
}
//Class for interactive creation of the object using the mouse
class maxProject1CreateCallBack : public CreateMouseCallBack {
IPoint2 sp0; //First point in screen coordinates
maxProject1 *ob; //Pointer to the object
Point3 p0; //First point in world coordinates
public:
int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat);
void SetObj(maxProject1 *obj) {ob = obj;}
};
int maxProject1CreateCallBack::proc(ViewExp *vpt,int msg, int point, int /*flags*/, IPoint2 m, Matrix3& mat )
{
if ( ! vpt || ! vpt->IsAlive() )
{
// why are we here
DbgAssert(!_T("Doing proc() on invalid viewport!"));
return FALSE;
}
DbgAssert(ob != NULL);
// Stole this code from DummyObjectCreateCallBack::proc(), see src\core\dummy.cpp
switch(msg) {
case MOUSE_FREEMOVE:
#ifdef _OSNAP
vpt->SnapPreview(m, m, NULL, SNAP_IN_3D);
#endif
break;
case MOUSE_POINT:
case MOUSE_MOVE:
switch(point) {
case 0:
ob->SetBox(Box3(Point3(-DD, -DD, -DD), Point3(DD, DD, DD)));
ob->SetCreating(true);
#ifdef _3D_CREATE
p0 = vpt->SnapPoint(m, m, NULL, SNAP_IN_3D);
#else
m_point = vpt->SnapPoint(m, m, NULL, SNAP_IN_PLANE);
#endif
mat.SetTrans(p0);
sp0 = m;
ob->InvalidateMesh();
break;
case 1:
{
float l;
#ifdef _OSNAP
l = fabsf(vpt->SnapLength(vpt->GetCPDisp(p0, Point3(0,0,1), sp0, m, TRUE)));
#else
l = fabsf(vpt->SnapLength(vpt->GetCPDisp(m_point, Point3(0,0,1), m_snapPoint, m)));
#endif
ob->SetBox(Box3(Point3(-l, -l, -l), Point3(l, l, l)));
ob->InvalidateMesh();
if(msg == MOUSE_POINT) {
ob->SetCreating(false);
if(Length(m - sp0) < 4)
return CREATE_ABORT;
else
return CREATE_STOP;
}
}
break;
}
break;
case MOUSE_ABORT:
ob->SetCreating(false);
return CREATE_ABORT;
}
return CREATE_CONTINUE;
}
static maxProject1CreateCallBack maxProject1CreateCB;
//From BaseObject
CreateMouseCallBack* maxProject1::GetCreateMouseCallBack()
{
maxProject1CreateCB.SetObj(this);
return(&maxProject1CreateCB);
}
void maxProject1::UpdateMesh(TimeValue t) {
if(!m_meshValid.InInterval(t)) {
BuildMesh(t);
}
}
void maxProject1::BuildMesh(TimeValue t) {
// Stole this from DummyObject::BuildMesh()
int nverts = 8;
int nfaces = 12;
Point3 va = m_box.pmin;
Point3 vb = m_box.pmax;
if(m_box.IsEmpty()) {
va = Point3(-DD, -DD, -DD);
vb = Point3( DD, DD, DD);
}
m_mesh.setNumVerts(nverts);
m_mesh.setNumFaces(nfaces);
m_mesh.setVert(0, Point3( va.x, va.y, va.z));
m_mesh.setVert(1, Point3( vb.x, va.y, va.z));
m_mesh.setVert(2, Point3( va.x, vb.y, va.z));
m_mesh.setVert(3, Point3( vb.x, vb.y, va.z));
m_mesh.setVert(4, Point3( va.x, va.y, vb.z));
m_mesh.setVert(5, Point3( vb.x, va.y, vb.z));
m_mesh.setVert(6, Point3( va.x, vb.y, vb.z));
m_mesh.setVert(7, Point3( vb.x, vb.y, vb.z));
MakeQuad(&(m_mesh.faces[ 0]), 0,2,3,1, 1);
MakeQuad(&(m_mesh.faces[ 2]), 2,0,4,6, 2);
MakeQuad(&(m_mesh.faces[ 4]), 3,2,6,7, 4);
MakeQuad(&(m_mesh.faces[ 6]), 1,3,7,5, 8);
MakeQuad(&(m_mesh.faces[ 8]), 0,1,5,4, 16);
MakeQuad(&(m_mesh.faces[10]), 4,5,7,6, 32);
m_mesh.InvalidateGeomCache();
m_mesh.EnableEdgeList(1);
m_meshValid.SetInfinite();
}
int maxProject1::Display(TimeValue t, INode* inode, ViewExp* vpt, int flags)
{
if ( ! vpt || ! vpt->IsAlive() )
{
// why are we here
DbgAssert(!_T("Doing Display() on invalid viewport!"));
return FALSE;
}
if (MaxSDK::Graphics::IsRetainedModeEnabled())
{
return 0;
}
GraphicsWindow *gw = vpt->getGW();
DWORD rlim = gw->getRndLimits();
Matrix3 mat = inode->GetObjectTM(t);
gw->setTransform(mat);
UpdateMesh(t);
m_mesh.render(gw, inode->Mtls(), NULL, COMP_ALL, inode->NumMtls());
gw->setRndLimits(rlim);
return 0;
}
unsigned long maxProject1::GetObjectDisplayRequirement() const
{
return 0;
}
bool maxProject1::PrepareDisplay(
const MaxSDK::Graphics::UpdateDisplayContext& prepareDisplayContext)
{
using namespace MaxSDK::Graphics;
// create a mesh to display (leave it in cache)
UpdateMesh(prepareDisplayContext.GetDisplayTime());
IMeshDisplay2* pMeshDisplay = static_cast<IMeshDisplay2*>(m_mesh.GetInterface(IMesh_DISPLAY2_INTERFACE_ID));
if (NULL == pMeshDisplay)
{
return false;
}
GenerateMeshRenderItemsContext generateRenderItemsContext;
generateRenderItemsContext.GenerateDefaultContext(prepareDisplayContext);
pMeshDisplay->PrepareDisplay(generateRenderItemsContext);
return true;
}
bool maxProject1::UpdatePerNodeItems(
const MaxSDK::Graphics::UpdateDisplayContext& updateDisplayContext,
MaxSDK::Graphics::UpdateNodeContext& nodeContext,
MaxSDK::Graphics::IRenderItemContainer& targetRenderItemContainer)
{
using namespace MaxSDK::Graphics;
IMeshDisplay2* pMeshDisplay = static_cast<IMeshDisplay2*>(m_mesh.GetInterface(IMesh_DISPLAY2_INTERFACE_ID));
if (NULL == pMeshDisplay)
{
return false;
}
// create a mesh to display (leave it in cache)
UpdateMesh(updateDisplayContext.GetDisplayTime());
GenerateMeshRenderItemsContext generateRenderItemsContext;
generateRenderItemsContext.GenerateDefaultContext(updateDisplayContext);
generateRenderItemsContext.RemoveInvisibleMeshElementDescriptions(nodeContext.GetRenderNode());
pMeshDisplay->GetRenderItems(generateRenderItemsContext,nodeContext,targetRenderItemContainer);
return true;
}
int maxProject1::HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt)
{
if (MaxSDK::Graphics::IsHardwareHitTesting(vpt))
{
return 0;
}
if ( ! vpt || ! vpt->IsAlive() )
{
// why are we here
DbgAssert(!_T("Doing Display() on invalid viewport!"));
return FALSE;
}
GraphicsWindow* gw = vpt->getGW();
DWORD rlim = gw->getRndLimits();
HitRegion hitRegion;
MakeHitRegion(hitRegion, type, crossing, 4, p);
Matrix3 tm = inode->GetObjectTM(t);
gw->setTransform(tm);
UpdateMesh(t);
int res = m_mesh.select(gw, inode->Mtls(), &hitRegion, (flags & HIT_ABORTONHIT), inode->NumMtls());
gw->setRndLimits(rlim);
return res;
}
void maxProject1::Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt)
{
if ( ! vpt || ! vpt->IsAlive() )
{
// why are we here
DbgAssert(!_T("Doing Snap() on invalid viewport!"));
return;
}
// Don't snap while creating
if(m_creating)
return;
Matrix3 tm = inode->GetObjectTM(t);
GraphicsWindow *gw = vpt->getGW();
gw->setTransform(tm);
UpdateMesh(t);
m_mesh.snap( gw, snap, p, tm );
}
void maxProject1::GetWorldBoundBox(TimeValue t, INode* inode, ViewExp* vpt, Box3& box )
{
#pragma message(TODO("Return the world space bounding box of the object"))
Matrix3 mat = inode->GetObjectTM(t);
UpdateMesh(t);
box = m_mesh.getBoundingBox();
box = box * mat;
}
void maxProject1::GetLocalBoundBox(TimeValue t, INode* inode, ViewExp* /*vpt*/, Box3& box)
{
#pragma message(TODO("Return the local space bounding box of the object"))
UpdateMesh(t);
box = m_mesh.getBoundingBox();
}
void maxProject1::GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel)
{
#pragma message(TODO("Compute the bounding box in the objects local coordinates or the optional space defined by tm."))
UpdateMesh(t);
box = m_mesh.getBoundingBox(tm);
}
void maxProject1::SetReference( int i, RefTargetHandle rtarg )
{
if (i == PBLOCK_REF)
{
pblock=(IParamBlock2*)rtarg;
}
}
RefTargetHandle maxProject1::GetReference( int i)
{
if (i == PBLOCK_REF)
{
return pblock;
}
return nullptr;
}
//From ReferenceMaker
RefResult maxProject1::NotifyRefChanged(const Interval& changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message, BOOL propagate)
{
return REF_SUCCEED;
}
Mesh* maxProject1::GetRenderMesh(TimeValue t, INode *inode, View &view, BOOL& needDelete)
{
#pragma message(TODO("Return the mesh representation of the object used by the renderer"))
needDelete = FALSE;
UpdateMesh(t);
return &m_mesh;
}
Object* maxProject1::ConvertToType(TimeValue /*t*/, Class_ID /*obtype*/)
{
#pragma message(TODO("If the plugin can convert to a nurbs surface then check to see whether obtype == EDITABLE_SURF_CLASS_ID and convert the object to nurbs surface and return the object"))
return NULL;
}
int maxProject1::CanConvertToType(Class_ID obtype)
{
#pragma message(TODO("Check for any additional types the plugin supports"))
if (obtype == defObjectClassID || obtype == triObjectClassID)
{
return 1;
}
else
{
return 0;
}
}
// From Object
int maxProject1::IntersectRay( TimeValue t, Ray& ray, float& at, Point3& norm)
{
#pragma message(TODO("Return TRUE after you implement this method"))
UpdateMesh(t);
return m_mesh.IntersectRay(ray, at, norm);
}
void maxProject1::GetCollapseTypes(Tab<Class_ID>& clist, Tab<TSTR*>& nlist)
{
Object::GetCollapseTypes(clist, nlist);
#pragma message(TODO("Append any any other collapse type the plugin supports"))
}
// From ReferenceTarget
RefTargetHandle maxProject1::Clone(RemapDir& remap)
{
maxProject1* newob = new maxProject1();
#pragma message(TODO("Make a copy all the data and also clone all the references"))
newob->ReplaceReference(0,remap.CloneRef(pblock));
newob->m_box = m_box;
newob->m_mesh = m_mesh;
newob->m_meshValid = m_meshValid;
newob->m_creating = m_creating;
BaseClone(this, newob, remap);
return(newob);
}