Notifications
Clear all

[Closed] SDK GeomObject crash on Render ??

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

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);
  }