forked from blurstudio/TwistSpline
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'upstream/master'
- Loading branch information
Showing
6 changed files
with
521 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
#include "drawOverride.h" | ||
|
||
TwistSplineGeometryOverride::TwistSplineGeometryOverride(const MObject& obj) | ||
: MPxGeometryOverride(obj), mTwistSplineNode(nullptr) { | ||
MStatus status; | ||
MFnDependencyNode dependNode(obj, &status); | ||
if (status != MStatus::kSuccess) return; | ||
|
||
MPxNode* twistSplineNode = dependNode.userNode(&status); | ||
if (status != MStatus::kSuccess) twistSplineNode = nullptr; | ||
|
||
mTwistSplineNode = dynamic_cast<TwistSplineNode*>(twistSplineNode); | ||
} | ||
|
||
TwistSplineGeometryOverride::~TwistSplineGeometryOverride() {} | ||
|
||
void TwistSplineGeometryOverride::updateDG() { | ||
/* | ||
Pull (evaluate) all attributes in TwistSplineNodeNode node we will be | ||
using. Here is the list of attributes we are pulling : | ||
- geometryChanging: Needed by requiresGeometryUpdate() to check if we | ||
needs to update the vertex and index buffer; | ||
- debugDisplay: Needed by addUIDrawables() to draw debug mode; | ||
- debugScale: Needed by addUIDrawables() to draw debug mode scale; | ||
- outputSpline: Needed by addUIDrawables() to draw the main spline; | ||
It is very important that all the attributes pulled from this method are | ||
cached (with Technique 1) otherwise Evaluation Cache will not work. In | ||
fact, this method is not needed by EM Evaluation modes. | ||
*/ | ||
mTwistSplineNode->updateRenderAttributes(); | ||
} | ||
|
||
bool TwistSplineGeometryOverride::requiresUpdateRenderItems( | ||
const MDagPath& path) const { | ||
/* | ||
Override this function if you need a more complicated animated-material | ||
behavior For example, you will need to change this to `return true` if | ||
object's associated shader is changing at every frame. (This could be | ||
very expensive). | ||
Note 1: this method have to return 'false' in most case, otherwise | ||
VP2 caching may not work. | ||
Note 2: for rendering simple animated-material like animated-color or | ||
animated-texture-uv check FootPrintGeometryOverrideAnimatedMaterial | ||
example. | ||
*/ | ||
return false; | ||
} | ||
|
||
//--------------------------------------------------------------------------- | ||
//--------------------------------------------------------------------------- | ||
// Geometry update and VP2 cache implementations | ||
//--------------------------------------------------------------------------- | ||
//--------------------------------------------------------------------------- | ||
|
||
/* | ||
Return true when the aOutputSpline changes, which requires us to re-generate | ||
the geometry. | ||
Note: this method must return the exact same value in a cache-restoration | ||
frame as it was in the corresponding cache-store frame. | ||
*/ | ||
bool TwistSplineGeometryOverride::requiresGeometryUpdate() const { | ||
/* | ||
Checking the "TwistSplineNodeNode::geometryChanging" attribute if any | ||
attribute affecting the geometry is changing, | ||
"TwistSplineNodeNode::geometryChanging" should be affected and dirtied. | ||
Also check TwistSplineNodeNodometry(). Warning: this method may be | ||
called outside of regular { update() : cleanUp() } scope. Thus, we must | ||
invoke node-local evaluation to ensure the correctness. | ||
*/ | ||
return mTwistSplineNode->isGeometryChanging(); | ||
} | ||
|
||
// Generate the geometry(vertex / index) from TwistSplineNodeNode's parameter | ||
// data | ||
// [[ensure : requiresGeometryUpdate() == false]] | ||
void TwistSplineGeometryOverride::populateGeometry( | ||
const MGeometryRequirements& requirements, | ||
const MRenderItemList& renderItems, MGeometry& data) { | ||
// This call will ensure the post-condition that requiresGeometryUpdate() is | ||
// false | ||
mTwistSplineNode->updatingGeometry(); | ||
} | ||
|
||
void TwistSplineGeometryOverride::addUIDrawables( | ||
const MDagPath& path, MHWRender::MUIDrawManager& drawManager, | ||
const MHWRender::MFrameContext& frameContext) { | ||
// Retrieve the spline data to draw elements; | ||
auto splineData = mTwistSplineNode->getSplineData(); | ||
auto splinePoints = splineData->getPoints(); | ||
|
||
bool debugDraw; | ||
double debugScale; | ||
mTwistSplineNode->getDebugDraw(debugDraw, debugScale); | ||
|
||
MFnDependencyNode node(path.node()); | ||
bool draw2D = false; // ALWAYS false | ||
|
||
drawManager.beginDrawable(MUIDrawManager::kSelectable); | ||
|
||
// Get correct color based on the state of object, e.g. active or | ||
// dormant. | ||
MHWRender::DisplayStatus displayStatus = | ||
MHWRender::MGeometryUtilities::displayStatus(path); | ||
MColor color = MHWRender::MGeometryUtilities::wireframeColor(path); | ||
drawManager.setColor(color); | ||
drawManager.lineStrip(splinePoints, draw2D); | ||
|
||
if (debugDraw) { | ||
MPointArray tangents; | ||
MVectorArray tans = splineData->getTangents(); | ||
tangents.setLength(tans.length() * 2); | ||
for (size_t i = 0; i < tans.length(); ++i) { | ||
MPoint& spi = splinePoints[i]; | ||
tangents[2 * i] = spi; | ||
tangents[(2 * i) + 1] = debugScale * tans[i] + spi; | ||
} | ||
drawManager.setColor(MColor(.5, 0, 0)); | ||
drawManager.lineList(tangents, draw2D); | ||
|
||
MPointArray normals; | ||
MVectorArray norms = splineData->getNormals(); | ||
normals.setLength(norms.length() * 2); | ||
for (size_t i = 0; i < norms.length(); ++i) { | ||
MPoint& spi = splinePoints[i]; | ||
MVector& nn = norms[i]; | ||
normals[2 * i] = spi; | ||
normals[(2 * i) + 1] = debugScale * nn + spi; | ||
} | ||
drawManager.setColor(MColor(0, .5, 0)); | ||
drawManager.lineList(normals, draw2D); | ||
|
||
MPointArray binormals; | ||
MVectorArray binorms = splineData->getBinormals(); | ||
binormals.setLength(binorms.length() * 2); | ||
for (size_t i = 0; i < binorms.length(); ++i) { | ||
MPoint& spi = splinePoints[i]; | ||
binormals[2 * i] = spi; | ||
binormals[(2 * i) + 1] = debugScale * binorms[i] + spi; | ||
} | ||
drawManager.setColor(MColor(0, 0, .5)); | ||
drawManager.lineList(binormals, draw2D); | ||
} | ||
drawManager.endDrawable(); | ||
} | ||
|
||
//--------------------------------------------------------------------------- | ||
//--------------------------------------------------------------------------- | ||
// Debug functions | ||
//--------------------------------------------------------------------------- | ||
//--------------------------------------------------------------------------- | ||
|
||
// Return true if internal tracing is desired. | ||
bool TwistSplineGeometryOverride::traceCallSequence() const { | ||
/* | ||
Tracing will look something like the following when in shaded mode (on | ||
selection change): | ||
- TwistSplineGeometryOverride: Geometry override DG update: twistSpline1 | ||
- TwistSplineGeometryOverride: Start geometry override render item | ||
update: |transform1|twistSpline1 | ||
- TwistSplineGeometryOverride: - Call API to update render items | ||
- TwistSplineGeometryOverride: End geometry override render item update: | ||
|transform1|twistSpline1 | ||
- TwistSplineGeometryOverride: End geometry override clean up: | ||
twistSpline1 | ||
This is based on the existing stream and indexing dirty flags being used | ||
which attempts to minimize the amount of render item, vertex buffer and | ||
indexing update. | ||
*/ | ||
return false; | ||
} | ||
|
||
inline void TwistSplineGeometryOverride::handleTraceMessage( | ||
const MString& message) const { | ||
MGlobal::displayInfo(gPluginNodeMessagePrefix + message); | ||
|
||
// Some simple custom message formatting. | ||
fputs(gPluginNodeMessagePrefix, stderr); | ||
fputs(message.asChar(), stderr); | ||
fputs("\n", stderr); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
#ifndef DRAW_OVERRIDE_H | ||
#define DRAW_OVERRIDE_H | ||
|
||
#include <maya/M3dView.h> | ||
#include <maya/MDrawRegistry.h> | ||
#include <maya/MHWGeometry.h> | ||
#include <maya/MHWGeometryUtilities.h> | ||
#include <maya/MPxGeometryOverride.h> | ||
#include <maya/MShaderManager.h> | ||
#include <maya/MSharedPtr.h> | ||
#include <maya/MUserData.h> | ||
#include <maya/MViewport2Renderer.h> | ||
|
||
#include "twistSplineNode.h" | ||
|
||
static const MString WireframeItemName = "twistSplineWires"; | ||
|
||
/* | ||
Customized rendering logic of TwistSplineNodeNode | ||
This class is responsible for generating the geometry (vertex buffer) from | ||
the procedure geometry. Beside rendering, the functions for generating | ||
geometry is also a crucial part in Viewport Caching (in contrast with | ||
Evaluation Caching). | ||
*/ | ||
class TwistSplineGeometryOverride : public MPxGeometryOverride { | ||
public: | ||
static MPxGeometryOverride* Creator(const MObject& obj) { | ||
return new TwistSplineGeometryOverride(obj); | ||
} | ||
~TwistSplineGeometryOverride() override; | ||
|
||
// Support configuration functions : | ||
MHWRender::DrawAPI supportedDrawAPIs() const override { | ||
return (MHWRender::DrawAPI::kOpenGL | MHWRender::DrawAPI::kDirectX11 | | ||
MHWRender::DrawAPI::kOpenGLCoreProfile); | ||
} | ||
bool supportsEvaluationManagerParallelUpdate() const override { | ||
return true; | ||
} | ||
/* | ||
Supporting Viewport Caching (VP2 Custom Caching) | ||
Viewport Cache for customize rendering nodes is caching the geometry | ||
data generated by MPxGeometryOverride::populateGeometry(). Like the | ||
graphics API, the geometry data is usually expressed in three different | ||
objects: | ||
- Vertex Buffer : Viewport Cache | ||
- Index Buffer : No cache, must be time-independent | ||
- Uniform Buffer : Evaluation Cache | ||
Currently, *only* Vertex Buffer can be cached in Viewport Cache and all | ||
the data in Uniform Buffer (shader parameters such as Color), must be | ||
stored in Evaluation Cache. | ||
Viewport Caching is helpful if you are generating the geometry data in | ||
CPU. For GPU generated geometry, the benefit is limited. To make the | ||
Viewport caching works properly the following constraints must be meet: | ||
- requiresUpdateRenderItems(path) always return 'false' in | ||
background-dg-context. | ||
- requiresGeometryUpdate() always return 'true' when restored from | ||
cache (if geometry is animated). | ||
- populateGeometry() should be context-safe | ||
*/ | ||
bool supportsVP2CustomCaching() const override { return true; } | ||
|
||
/* | ||
Interaction with TwistSplineNodeNode | ||
- This is the only method where you can call MPlug::getValue() or | ||
Mdatablock::inputValue() | ||
- This method can be empty (in EM modes), if you have setup the node | ||
correctly with Technique 1. | ||
*/ | ||
void updateDG() override; | ||
// For the best practice, this method should contain no status data | ||
void cleanUp() override{}; | ||
|
||
// Render item functions, only involved in foreground rendering, should | ||
// not affect VP2 Caching at all | ||
bool requiresUpdateRenderItems(const MDagPath& path) const override; | ||
void updateRenderItems(const MDagPath& path, | ||
MRenderItemList& list) override{}; | ||
bool hasUIDrawables() const override { return true; }; | ||
void addUIDrawables(const MDagPath& path, MUIDrawManager& drawManager, | ||
const MFrameContext& frameContext) override; | ||
|
||
// Geometry update functions, major entry for support VP2 Custom Caching | ||
bool requiresGeometryUpdate() const override; | ||
bool isIndexingDirty(const MRenderItem& item) override { | ||
return false; | ||
} // Viewport Caching does not support index buffer change (animated | ||
// topology) | ||
void populateGeometry(const MGeometryRequirements& requirements, | ||
const MRenderItemList& renderItems, | ||
MGeometry& data) override; | ||
|
||
// Debugging functions | ||
bool traceCallSequence() const override; | ||
void handleTraceMessage(const MString& message) const override; | ||
|
||
private: | ||
TwistSplineGeometryOverride(const MObject& obj); | ||
/* | ||
To ensure the plugin works well with Viewport and Evaluation Caching | ||
we recommend to use a state-less MpxGeometryOverride: | ||
- Store all time-dependent data on the attributes of corresponding Maya | ||
node, instead of a data member in MpxGeometryOverride. | ||
- updateDG() pulls (evaluates) all the time-dependent data on node by | ||
"MDataBlock::inputValue()", but *not* store them in a data member of | ||
MPxGeometryOverride | ||
- Always access the time-dependent data by "MDataBlock::outputValue()" | ||
in the renderer | ||
Backgrounds of the recommendation: | ||
In this plugin example, we have 3 different pipeline stages involved: | ||
1. Node Evaluation | ||
2. Geometry Update (MPxGeometryOverride invocation) | ||
3. Rendering (Shader-pre-draw-callback invocation) | ||
With (Evaluation or Viewport) Caching, some surprising mixture of | ||
stages happen concurrently: | ||
- Foreground thread : | ||
MPxGeometryOverride -> Shader Callback -> (Rendering on frame 10) | ||
- Background thread : | ||
Node Evaluation -> MPxGeometryOverride -> (Viewport cache fill on frame 20) | ||
Accessing the same data directly cross stage boundaries can cause | ||
incorrect result or even crash Maya. Thus we must use different storage | ||
for each data in each DG context (frame). And a depend node's attribute | ||
are always isolated for different context. | ||
Beside, within the MPxGeometryOverride invocation stage, it is not | ||
always safe to access its data member : During Viewport Cache | ||
Restoration, requiresGeometryUpdate() is the only method get called. | ||
(No updateDG() call) Thus the data must be stored in the node. | ||
*/ | ||
TwistSplineNode* mTwistSplineNode; // The node we are rendering | ||
}; | ||
|
||
#endif |
Oops, something went wrong.