Hello,
I am working on a C++ plugin that allows a user to manipulate surfaces in real time utilizing a polyhedral mesh. I am attempting to have the user manipulate a Gumball object to help them move vertices on the mesh. The issue I am having is that the surfaces that I generate with the dynamic draw method end up clipped at certain angles. My guess is this has something to do with the clipping planes, but I am not sure what that would be. The following image shows the surface with no clipping:
From a different angle, the same surface looks like this:
Here is the relevant code that performs the drawing:
VertexGumballGetXForm.hpp
class VertexGumballGetXForm : public CRhinoGetXform
{
public:
VertexGumballGetXForm(MeshSurfaces* a_MeshSurfaces, const CRhinoGetObject& a_VertObjects, int a_NumThreads);
~VertexGumballGetXForm() = default;
CRhinoGet::result moveGumball();
void updateMeshSurfaces();
// m_GumballDC Wrapper Methods
void Disable();
void Enable(unsigned int a_DocSerialNumber);
void EnableGumballDraw(bool a_Enable);
const ON_Xform PreTransform();
void setBaseGumball();
void updateGumball();
// Overrides
BOOL CalculateTransform(CRhinoViewport& vp, const ON_3dPoint& pt, ON_Xform& xform);
void OnMouseDown(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p);
void OnMouseMove(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p);
void DynamicDraw(CRhinoDisplayPipeline& dp, const ON_3dPoint& pt);
private:
// Initial data
const CRhinoCommandContext& m_Context;
MeshSurfaces* m_MeshSurfaces;
MeshType m_Mesh;
ON_Mesh m_OnMesh; //TODO: This probably is not needed anymore
std::vector<ON_3dPoint> m_InitialVertPositions;
std::vector<int> m_ModifiedVertIndices;
std::vector<VertexHandle> m_ModifiedVertHandles;
std::vector<struct Handle> m_ModifiedNurbsHandles;
// Used for dynamic draw updates
bool m_Draw;
bool m_Hidden;
ON_Mesh m_UpdateMesh;
ON_Mesh m_StaticMesh;
std::vector<ON_3dPoint> m_UpdatedVertPositions;
ON_3dVector m_TranslationDirection;
std::vector<NurbsSurfaceToHandlePair> m_UpdatedNurbs;
NurbsSurfaceToHandlePairBuilder* m_NurbsSurfaceToHandlePairBuilder;
CRhinoGumballDisplayConduit* m_GumballDC;
CRhinoGumball* m_Gumball;
void initializeVertData(const CRhinoGetObject& a_VertObjects);
void initializeUpdateMesh();
void initializeGumballDC(const CRhinoGetObject& a_VertObjects);
void initializeGumball(const CRhinoGetObject& a_VertObjects);
void updateUpdatedNurbs();
void initializeModifiedNurbsHandles();
void hide();
void hideSurfacePatches();
void hideMeshObject();
void show();
void showSurfacePatches();
void showMeshObject();
};
VertexGumballGetXForm.cpp
VertexGumballGetXForm::VertexGumballGetXForm(MeshSurfaces* a_MeshSurfaces, const CRhinoGetObject& a_VertObjects, int a_NumThreads) :
m_MeshSurfaces(a_MeshSurfaces),
m_Mesh(MeshType(*a_MeshSurfaces->getMesh())),
m_OnMesh(ON_Mesh(*a_MeshSurfaces->getOnMesh())),
m_Context(a_MeshSurfaces->getContext()),
m_NurbsSurfaceToHandlePairBuilder(new NurbsSurfaceToHandlePairBuilder(a_NumThreads)),
m_Draw(false),
m_Hidden(false),
m_TranslationDirection(ON_3dVector(0, 0, 0))
{
initializeVertData(a_VertObjects);
initializeGumball(a_VertObjects);
initializeGumballDC(a_VertObjects);
updateUpdatedNurbs(); //TODO: Find better way to get Handles?
initializeModifiedNurbsHandles();
//SetBasePoint(m_InitialVertPositions.at(0)); // Sets base point to first vertex (Note: at least one vertex will be selected)
//DrawLineFromPoint(m_InitialVertPositions.at(0), true);
this->AppendObjects(a_VertObjects);
}
/*
* Input: A CRhinoGetObject containing data for vertices selected by user
*
* Initializes m_ModifiedVertIndices, m_ModifiedVertHandles, m_InitialVertPositions,
* and m_UpdatedVertPositions
*/
void VertexGumballGetXForm::initializeVertData(const CRhinoGetObject& a_VertObjects)
{
int t_NumVerts = a_VertObjects.ObjectCount();
for (int i = 0; i < t_NumVerts; ++i)
{
// Grab vertex index
CRhinoObjRef t_VertRef = a_VertObjects.Object(i);
int t_VertIndex = t_VertRef.GeometryComponentIndex().m_index;
m_ModifiedVertIndices.push_back(t_VertIndex);
// Grab vertex handle
m_ModifiedVertHandles.push_back(VertexHandle(m_ModifiedVertIndices.at(i)));
// Grab initial vertex position
ON_3dPoint t_VertPosition = m_MeshSurfaces->getOnMesh()->m_dV[t_VertIndex];
m_InitialVertPositions.push_back(t_VertPosition);
m_UpdatedVertPositions.push_back(t_VertPosition);
}
}
void VertexGumballGetXForm::initializeGumballDC(const CRhinoGetObject& a_VertObjects)
{
m_GumballDC = new CRhinoGumballDisplayConduit();
}
void VertexGumballGetXForm::initializeGumball(const CRhinoGetObject& a_VertObjects)
{
ON_BoundingBox t_BBox = getBoundingBox(a_VertObjects);
m_Gumball = new CRhinoGumball();
m_Gumball->SetFromBoundingBox(t_BBox);
// Turn off rotation
m_Gumball->m_appearance.m_bEnableXRotate = false;
m_Gumball->m_appearance.m_bEnableYRotate = false;
m_Gumball->m_appearance.m_bEnableZRotate = false;
// Turn off scale
m_Gumball->m_appearance.m_bEnableXScale = false;
m_Gumball->m_appearance.m_bEnableYScale = false;
m_Gumball->m_appearance.m_bEnableZScale = false;
}
/*
* Updates the collection of ON_NurbsSurface to new positions
* based on changes made in the mesh
*/
void VertexGumballGetXForm::updateUpdatedNurbs()
{
// Calculate new points
m_UpdatedVertPositions = m_InitialVertPositions;
for (size_t i = 0; i < m_UpdatedVertPositions.size(); ++i)
{
ON_3dPoint t_NewPt = m_UpdatedVertPositions.at(i);
t_NewPt.x = t_NewPt.x + m_TranslationDirection.x;
t_NewPt.y = t_NewPt.y + m_TranslationDirection.y;
t_NewPt.z = t_NewPt.z + m_TranslationDirection.z;
m_UpdatedVertPositions.at(i) = t_NewPt;
}
//OnMesh
for (size_t i = 0; i < m_ModifiedVertIndices.size(); ++i)
{
int t_VertIndex = m_ModifiedVertIndices.at(i);
ON_3dPoint t_NewVert = m_UpdatedVertPositions.at(i);
m_OnMesh.SetVertex(t_VertIndex, t_NewVert);
}
//Need to invalidate things so they may be recalculated
m_OnMesh.InvalidateVertexBoundingBox();
m_OnMesh.InvalidateVertexNormalBoundingBox();
m_OnMesh.InvalidateCurvatureStats();
m_OnMesh.m_FN.SetCount(0);
m_OnMesh.m_N.SetCount(0);
m_OnMesh.ComputeFaceNormals();
m_OnMesh.ComputeVertexNormals();
m_OnMesh.SetClosed(-1);
//Mesh
for (size_t i = 0; i < m_UpdatedVertPositions.size(); ++i)
{
// New vertex position
OpenMesh::Vec3d t_NewVert((m_UpdatedVertPositions.at(i).x),
(m_UpdatedVertPositions.at(i).y),
(m_UpdatedVertPositions.at(i).z));
m_Mesh.set_point(m_ModifiedVertHandles.at(i), t_NewVert);
}
m_UpdatedNurbs = m_NurbsSurfaceToHandlePairBuilder->buildNurbsSurfaceToHandlePairs(m_Mesh, m_ModifiedVertHandles);
}
void VertexGumballGetXForm::initializeModifiedNurbsHandles()
{
std::set<struct Handle> t_Handles;
for (NurbsSurfaceToHandlePair t_Pair : m_UpdatedNurbs)
{
t_Handles.emplace(t_Pair.m_Handle);
}
m_ModifiedNurbsHandles = std::vector<struct Handle>(t_Handles.begin(), t_Handles.end());
}
CRhinoGet::result VertexGumballGetXForm::moveGumball()
{
if (m_GumballDC == 0)
return CRhinoGet::cancel;
if (!m_GumballDC->PreTransform().IsIdentity())
{
m_bHaveXform = TRUE;
m_xform = m_GumballDC->PreTransform();
}
SetBasePoint(m_GumballDC->BaseGumball().m_frame.m_plane.Origin(), false);
const_cast<CRhinoXformObjectList&>(ObjectList()).EnableDisplayFeedback(true);
if (!m_xform.IsIdentity())
const_cast<CRhinoXformObjectList&>(ObjectList()).UpdateDisplayFeedbackXform(m_xform);
SetGetPointCursor(RhinoApp().m_default_cursor);
CRhinoGet::result t_Result = GetPoint(0, true);
const_cast<CRhinoXformObjectList&>(ObjectList()).EnableDisplayFeedback(false);
return t_Result;
}
/*
* Called after the user confirms the new locations of the selected vertices.
*/
void VertexGumballGetXForm::updateMeshSurfaces()
{
if (m_Hidden)
{
show();
}
m_MeshSurfaces->update(m_UpdatedNurbs, m_Mesh, m_ModifiedVertHandles, m_OnMesh);
}
void VertexGumballGetXForm::show()
{
showSurfacePatches();
showMeshObject();
m_Hidden = false;
}
void VertexGumballGetXForm::showSurfacePatches()
{
m_MeshSurfaces->showSurfacePatches(m_ModifiedNurbsHandles);
}
void VertexGumballGetXForm::showMeshObject()
{
m_MeshSurfaces->showMeshObject();
}
void VertexGumballGetXForm::Disable()
{
m_GumballDC->Disable();
}
void VertexGumballGetXForm::Enable(unsigned int a_DocSerialNumber)
{
m_GumballDC->Enable(a_DocSerialNumber);
}
void VertexGumballGetXForm::EnableGumballDraw(bool a_Enable)
{
m_GumballDC->EnableGumballDraw(a_Enable);
}
const ON_Xform VertexGumballGetXForm::PreTransform()
{
return m_GumballDC->PreTransform();
}
void VertexGumballGetXForm::setBaseGumball()
{
m_GumballDC->SetBaseGumball(*m_Gumball);
}
void VertexGumballGetXForm::updateGumball()
{
if (!m_GumballDC->m_drag_settings.m_bRelocateGumball)
{
ON_Xform xform = m_GumballDC->TotalTransform();
m_GumballDC->SetPreTransform(xform);
}
// update location of base gumball
CRhinoGumballFrame t_GumballDCFrame = m_GumballDC->Gumball().m_frame;
CRhinoGumballFrame t_GumballFrame = m_Gumball->m_frame;
t_GumballFrame.m_plane = t_GumballDCFrame.m_plane;
t_GumballFrame.m_scale_grip_distance = t_GumballDCFrame.m_scale_grip_distance;
m_Gumball->m_frame = t_GumballFrame;
}
/*
* Input: CRhinoViewport: Rhino Viewport
* ON_3dPoint: The 3d location of the mouse pointer
* ON_Xform: The resulting Xform is stored here
*
* Output: BOOL: TRUE for Xform != IdentityTransform
*/
BOOL VertexGumballGetXForm::CalculateTransform(CRhinoViewport& vp, const ON_3dPoint& pt, ON_Xform& xform)
{
if (m_GumballDC == 0)
return FALSE;
if (m_GumballDC->m_drag_settings.m_bRelocateGumball)
xform = m_GumballDC->PreTransform();
else
xform = m_GumballDC->TotalTransform();
m_TranslationDirection = ON_3dVector(xform.m_xform[0][3], xform.m_xform[1][3], xform.m_xform[2][3]);
return xform.IsValid() ? TRUE : FALSE;
//m_TranslationDirection = pt - m_basepoint;
//if (m_TranslationDirection.IsTiny())
// xform = ON_Xform::IdentityTransformation;
//else
// xform = ON_Xform::TranslationTransformation(m_TranslationDirection);
//return (xform.IsValid()) ? TRUE : FALSE;
}
void VertexGumballGetXForm::OnMouseDown(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p)
{
if (p == 0 || m_GumballDC == 0 || m_GumballDC->m_pick_result.m_gumball_mode != gb_mode_nothing)
return;
m_GumballDC->m_pick_result.SetToDefaultPickResult();
CRhinoPickContext t_PickContext;
t_PickContext.m_view = vp.ParentView();
t_PickContext.m_pick_style = CRhinoPickContext::point_pick;
if (vp.SetClippingRegionTransformation(p->x, p->y, t_PickContext.m_pick_region))
{
vp.VP().GetFrustumLine(p->x, p->y, t_PickContext.m_pick_line);
t_PickContext.UpdateClippingPlanes();
m_GumballDC->PickGumball(t_PickContext, this);
}
}
/*
* Create Nurbs Surfaces each time the mouse pointer is moved
*/
void VertexGumballGetXForm::OnMouseMove(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p)
{
if (p == 0 || m_GumballDC == 0 || m_GumballDC->m_pick_result.m_gumball_mode == gb_mode_nothing)
return;
m_GumballDC->CheckShiftAndControlKeys();
ON_Line t_WorldLine;
if (!vp.VP().GetFrustumLine(p->x, p->y, t_WorldLine))
t_WorldLine = ON_Line(ON_UNSET_POINT, ON_UNSET_POINT);
bool t_Success = m_GumballDC->UpdateGumball(pt, t_WorldLine);
if (t_Success)
{
ON_Xform t_Xform;
m_Draw = false;
if (CalculateTransform(vp, pt, t_Xform))
{
updateUpdatedNurbs();
m_Draw = true;
}
CRhinoGetXform::OnMouseMove(vp, flags, pt, p);
}
}
/*
* Draw the Mesh and Nurbs Surfaces (with Zebras) to the viewport
*/
void VertexGumballGetXForm::DynamicDraw(CRhinoDisplayPipeline& dp, const ON_3dPoint& pt)
{
if (m_Draw)
{
if (!m_Hidden)
{
hide();
}
//TODO: Draw only modified portion of Mesh
// Draw Mesh
dp.DrawMesh(m_OnMesh, true, false, nullptr);
// Draw Surfaces
const CRhinoZebraAnalysisSettings& t_ZebraSettings = RhinoApp().AppSettings().ZebraAnalysisSettings();
const ON_MeshParameters& t_MeshParameters = m_Context.Document()->Properties().AnalysisMeshSettings();
const ON_Color& t_Color = RhinoApp().AppSettings().SelectedObjectColor();
for (NurbsSurfaceToHandlePair t_Current : m_UpdatedNurbs)
{
ON_Brep* t_Brep = t_Current.m_NurbsSurface.BrepForm();
dp.DrawZebraPreview(t_Brep, t_ZebraSettings, t_MeshParameters, t_Color, nullptr);
delete t_Brep;
}
// const CDisplayPipelineAttributes* attrs = dp.DisplayAttrs();
// CDisplayPipelineMaterial dpm = *attrs->m_pMaterial;
// dp.SetupDisplayMaterial(dpm, m_Context.m_doc);
// dp.SetupDisplayMaterial(dpm, &m_doc, nullptr);
// //int wireframe_density = 5;
// for (size_t i = 0; i < patches.size(); i++)
// {
// ON_Brep* b = patches.at(i).BrepForm();
// dp.DrawShadedBrep(b, &dpm, nullptr);
// delete b;
// }
}
CRhinoGetXform::DynamicDraw(dp, pt);
}
/*
* Hides the Document Surfaces and Mesh before the user moves the vertices.
*/
void VertexGumballGetXForm::hide()
{
//TODO: hide unaffected patches (except for first ring around)?
hideSurfacePatches();
hideMeshObject();
m_Hidden = true;
}
void VertexGumballGetXForm::hideSurfacePatches()
{
m_MeshSurfaces->hideSurfacePatches(m_ModifiedNurbsHandles);
}
void VertexGumballGetXForm::hideMeshObject()
{
m_MeshSurfaces->hideMeshObject();
}
In the above code, I made use of the example code offered by Rhino on using CRhinoGetXform for dynamic draw as well as the ones for Gumball display.
I appreciate any and all help in trying to solve this issue.