瀏覽代碼

Particle Editor - Show Variation in Base Graph

This changes the base graph to show the variation when editing particle effects. It's pretty awesome.
Peter Robinson 3 年之前
父節點
當前提交
d0c54b4a4b

+ 1 - 0
editor/AssetAdmin/ParticleEditor/AssetParticleGraphTool.cs

@@ -152,6 +152,7 @@ function AssetParticleGraphEmitterTool::initEmitter(%this)
 	};
 	ThemeManager.setProfile(%this.variGraph, "labelProfile");
 	%this.toolGrid.add(%this.variGraph);
+	%this.baseGraph.setVarianceGraph(%this.variGraph);
 
 	%this.lifeGraph = new GuiControl()
 	{

+ 5 - 0
editor/AssetAdmin/ParticleEditor/AssetParticleGraphUnit.cs

@@ -270,3 +270,8 @@ function AssetParticleGraphUnit::timeMoveForward(%this)
 	%this.timeController.moveUp();
 	%this.refreshCamera();
 }
+
+function AssetParticleGraphUnit::setVarianceGraph(%this, %variGraph)
+{
+	%this.graph.setVariationGraphInspector(%variGraph.graph);
+}

+ 11 - 0
engine/source/gui/buttons/guiButtonCtrl.cc

@@ -62,6 +62,17 @@ void GuiButtonCtrl::initPersistFields()
 	addField("easeTimeFillColorSL", TypeS32, Offset(mEaseTimeFillColorSL, GuiButtonCtrl));
 }
 
+void GuiButtonCtrl::setActive(bool value)
+{
+	Parent::setActive(value);
+
+	if (!value)
+	{
+		mDepressed = false;
+		mMouseOver = false;
+	}
+}
+
 void GuiButtonCtrl::acceleratorKeyPress(U32)
 {
 	if (!mActive)

+ 2 - 0
engine/source/gui/buttons/guiButtonCtrl.h

@@ -56,6 +56,8 @@ public:
 	void acceleratorKeyPress(U32 index);
 	void acceleratorKeyRelease(U32 index);
 
+	virtual void setActive(bool value);
+
 	virtual void onTouchDown(const GuiEvent &);
 	virtual void onTouchUp(const GuiEvent &);
 	virtual void onRightMouseUp(const GuiEvent &);

+ 218 - 12
engine/source/gui/editor/guiParticleGraphInspector.cc

@@ -44,6 +44,7 @@ GuiParticleGraphInspector::GuiParticleGraphInspector()
 	mTargetAsset = NULL;
 	mTargetField = StringTable->insert("QuantityScale");
 	mEmitterIndex = 0;
+	mVariationInspector = NULL;
 	mMinX = 0;
 	mMinXLabel = StringTable->insert("0");
 	mMaxX = 1;
@@ -114,7 +115,7 @@ void GuiParticleGraphInspector::setDisplayLabels(const char* labelX, const char*
 
 ParticleAssetField* GuiParticleGraphInspector::getTargetField()
 {
-	ParticleAssetFieldCollection &collection = mTargetAsset->getParticleFields();
+	ParticleAssetFieldCollection& collection = mTargetAsset->getParticleFields();
 	ParticleAssetField* field = collection.findField(mTargetField);
 
 	if (field == NULL)
@@ -125,12 +126,12 @@ ParticleAssetField* GuiParticleGraphInspector::getTargetField()
 		}
 
 		ParticleAssetEmitter* emitter = mTargetAsset->getEmitter(mEmitterIndex);
-		ParticleAssetFieldCollection &emitterCollection = emitter->getParticleFields();
+		ParticleAssetFieldCollection& emitterCollection = emitter->getParticleFields();
 		field = emitterCollection.findField(mTargetField);
 	}
 
 	AssertFatal(field != NULL, "GuiParticleGraphInspector::getTargetField() - Unable to find the requested field.");
-	
+
 	return field;
 }
 
@@ -405,23 +406,27 @@ void GuiParticleGraphInspector::calculatePoints(const RectI &contentRect)
 		time = key.mTime;
 	}
 
-	F32 width = mMaxX - mMinX;
-	F32 height = mMaxY - mMinY;
 	Point2I p;
 	for (U32 i = 0; i < count; i++)
 	{
 		ParticleAssetField::DataKey key = field->getDataKey(i);
-
-		F32 ratioX = (key.mTime - mMinX) / width;
-		F32 ratioY = (key.mValue - mMinY) / height;
-
-		p = Point2I(contentRect.point.x + (contentRect.extent.x * ratioX), contentRect.point.y + (contentRect.extent.y * (1 - ratioY)));
-
+		p = convertToRenderPoint(contentRect, key.mTime, key.mValue);
 		mPointList.push_back(GraphPoint(p, key.mTime, key.mValue, i));
 	}
 	mDirty = false;
 }
 
+Point2I GuiParticleGraphInspector::convertToRenderPoint(const RectI& contentRect, F32 time, F32 value)
+{
+	F32 width = mMaxX - mMinX;
+	F32 height = mMaxY - mMinY;
+
+	F32 ratioX = (time - mMinX) / width;
+	F32 ratioY = (value - mMinY) / height;
+
+	return Point2I(contentRect.point.x + (contentRect.extent.x * ratioX), contentRect.point.y + (contentRect.extent.y * (1 - ratioY)));
+}
+
 void GuiParticleGraphInspector::renderPoints(const RectI &contentRect, const ColorI &lineColor)
 {
 	if (mTargetAsset)
@@ -439,7 +444,15 @@ void GuiParticleGraphInspector::renderPoints(const RectI &contentRect, const Col
 			cursorPt = root->getCursorPos();
 		}
 
-		//Render the lines first
+		//Render variation
+		if (mVariationInspector != NULL)
+		{
+			ColorI variColor = ColorI(lineColor);
+			variColor.alpha /= 2;
+			renderVariation(contentRect, variColor);
+		}
+
+		//Render the lines
 		Point2I p1, p2;
 		U32 count = mPointList.size();
 		for(U32 i = 1; i < count; i++)
@@ -460,6 +473,76 @@ void GuiParticleGraphInspector::renderPoints(const RectI &contentRect, const Col
 	}
 }
 
+void GuiParticleGraphInspector::renderVariation(const RectI& contentRect, const ColorI& color)
+{
+	Vector<GraphPoint>* variPointList = mVariationInspector->getRenderPoints();
+
+	S32 vPen = 0;
+	S32 bPen = 0;
+	Point2I up1, down1, up2, down2;
+	while (vPen < variPointList->size() || bPen < mPointList.size())
+	{
+		GraphPoint& vari = variPointList->at(getMin(vPen, variPointList->size() - 1));
+		GraphPoint& base = mPointList.at(getMin(bPen, mPointList.size() - 1));
+
+		if (vPen == 0 && bPen == 0)
+		{
+			up1 = convertToRenderPoint(contentRect, 0, base.mValue + vari.mValue);
+			down1 = convertToRenderPoint(contentRect, 0, base.mValue - vari.mValue);
+			vPen = 1;
+			bPen = 1;
+			continue;
+		}
+
+		if (vPen >= variPointList->size() || bPen >= mPointList.size())
+		{
+			F32 time = vPen >= variPointList->size() ? base.mTime : vari.mTime;
+			up2 = convertToRenderPoint(contentRect, time, base.mValue + vari.mValue);
+			down2 = convertToRenderPoint(contentRect, time, base.mValue - vari.mValue);
+			vPen++;
+			bPen++;
+		}
+		else if (vari.mTime == base.mTime)
+		{
+			up2 = convertToRenderPoint(contentRect, base.mTime, base.mValue + vari.mValue);
+			down2 = convertToRenderPoint(contentRect, base.mTime, base.mValue - vari.mValue);
+			vPen++;
+			bPen++;
+		}
+		else if (vari.mTime < base.mTime)
+		{
+			GraphPoint& oldBase = mPointList.at(bPen - 1);
+			F32 timeDeltaB = base.mTime - oldBase.mTime;
+			F32 timeDeltaV = vari.mTime - oldBase.mTime;
+			F32 ratio = timeDeltaV / timeDeltaB;
+			F32 baseValue = oldBase.mValue + ((base.mValue - oldBase.mValue) * ratio);
+			up2 = convertToRenderPoint(contentRect, vari.mTime, baseValue + vari.mValue);
+			down2 = convertToRenderPoint(contentRect, vari.mTime, baseValue - vari.mValue);
+			vPen++;
+		}
+		else if (vari.mTime > base.mTime)
+		{
+			GraphPoint& oldVari = variPointList->at(vPen - 1);
+			F32 timeDeltaB = base.mTime - oldVari.mTime;
+			F32 timeDeltaV = vari.mTime - oldVari.mTime;
+			F32 ratio = timeDeltaB / timeDeltaV;
+			F32 variValue = oldVari.mValue + ((vari.mValue - oldVari.mValue) * ratio);
+			up2 = convertToRenderPoint(contentRect, base.mTime, base.mValue + variValue);
+			down2 = convertToRenderPoint(contentRect, base.mTime, base.mValue - variValue);
+			bPen++;
+		}
+		renderQuad(contentRect, up1, up2, down1, down2, color);
+		up1 = up2;
+		down1 = down2;
+	}
+	up2 = Point2I(contentRect.point.x + contentRect.extent.x, up1.y);
+	down2 = Point2I(contentRect.point.x + contentRect.extent.x, down1.y);
+	if (up1.x < up2.x)
+	{
+		renderQuad(contentRect, up1, up2, down1, down2, color);
+	}
+}
+
 void GuiParticleGraphInspector::renderDot(const RectI &contentRect, const Point2I &point, const Point2I &cursorPt, bool isSelected)
 {
 	if(point.x >= contentRect.point.x && point.x <= contentRect.point.x + contentRect.extent.x && point.y >= contentRect.point.y && point.y <= contentRect.point.y + contentRect.extent.y)
@@ -496,4 +579,127 @@ void GuiParticleGraphInspector::renderLine(const RectI &contentRect, const Point
 	{
 		dglDrawLine(p1, p2, ColorI(lineColor));
 	}
+}
+
+//Points are leftTop, rightTop, leftBottom, rightBottom
+void GuiParticleGraphInspector::renderQuad(const RectI& contentRect, const Point2I& point1, const Point2I& point2, const Point2I& point3, const Point2I& point4, const ColorI& quadColor)
+{
+	RectI area = RectI(point1.x, getMin(point1.y, point2.y), point2.x - point1.x, point1.y < point2.y ? getMax(point3.y, point4.y) - point1.y : getMax(point3.y, point4.y) - point2.y);
+	if (!contentRect.overlaps(area))
+	{
+		//Nothing to draw here...
+		return;
+	}
+
+	if (point1.y > point4.y || point2.y > point3.y)
+	{
+		Point2I point5 = Point2I(mRound((point1.x + point2.x) / 2), mRound((point1.y + point2.y) / 2));
+		Point2I point6 = Point2I(mRound((point3.x + point4.x) / 2), mRound((point3.y + point4.y) / 2));
+		renderQuad(contentRect, point1, point5, point3, point6, quadColor);
+		renderQuad(contentRect, point5, point2, point6, point4, quadColor);
+		return;
+	}
+
+	RectClipper clipper = RectClipper(contentRect);
+
+	Point2I topStart;
+	Point2I topEnd;
+	bool hasTop = clipper.clipLine(point1, point2, topStart, topEnd);
+
+	Point2I bottomStart;
+	Point2I bottomEnd;
+	bool hasBottom = clipper.clipLine(point3, point4, bottomStart, bottomEnd);
+
+	Point2I leftStart;
+	Point2I leftEnd;
+	bool hasLeft = clipper.clipLine(point1, point3, leftStart, leftEnd);
+
+	Point2I rightStart;
+	Point2I rightEnd;
+	bool hasRight = clipper.clipLine(point2, point4, rightStart, rightEnd);
+
+	//Replace left and right if they're missing
+	if (!hasLeft)
+	{
+		leftStart.x = leftEnd.x = contentRect.point.x;
+		leftStart.y = hasTop ? topStart.y : contentRect.point.y;
+		leftEnd.y = hasBottom ? bottomStart.y : contentRect.point.y + contentRect.extent.y;
+	}
+	if (!hasRight)
+	{
+		rightStart.x = rightEnd.x = contentRect.point.x + contentRect.extent.x - 1;
+		rightStart.y = hasTop ? topEnd.y : contentRect.point.y;
+		rightEnd.y = hasBottom ? bottomEnd.y : contentRect.point.y + contentRect.extent.y;
+	}
+
+	S32 leftEdge = leftStart.x;
+	S32 rightEdge = rightStart.x;
+
+	//Middle Section
+	S32 y = getMax(leftStart.y, rightStart.y);
+	S32 h = getMin(leftEnd.y - y, rightEnd.y - y);
+	RectI fillRect = RectI(leftStart.x, y, rightStart.x - leftStart.x, h);
+	dglDrawRectFill(fillRect, quadColor);
+
+	//Top Section
+	if (hasTop && topStart.y != topEnd.y)
+	{
+		if (leftEdge != topStart.x && topStart.y < topEnd.y)
+		{
+			RectI rect = RectI(leftEdge, topStart.y, topStart.x - leftEdge, topEnd.y - topStart.y);
+			dglDrawRectFill(rect, quadColor);
+
+			Point2I p = Point2I(topStart.x, topEnd.y);
+			dglDrawTriangleFill(topStart, p, topEnd, quadColor);
+		}
+		else if (rightEdge != topEnd.x && topStart.y > topEnd.y)
+		{
+			RectI rect = RectI(topEnd.x, topEnd.y, rightEdge - topEnd.x, topStart.y - topEnd.y);
+			dglDrawRectFill(rect, quadColor);
+
+			Point2I p = Point2I(topEnd.x, topStart.y);
+			dglDrawTriangleFill(topStart, p, topEnd, quadColor);
+		}
+		else if (topStart.y > topEnd.y)
+		{
+			Point2I p = Point2I(topEnd.x, topStart.y);
+			dglDrawTriangleFill(topStart, p, topEnd, quadColor);
+		}
+		else if (topStart.y < topEnd.y)
+		{
+			Point2I p = Point2I(topStart.x, topEnd.y);
+			dglDrawTriangleFill(topStart, p, topEnd, quadColor);
+		}
+	}
+
+	//Bottom Section
+	if (hasBottom && bottomStart.y != bottomEnd.y)
+	{
+		if (leftEdge != bottomStart.x && bottomStart.y > bottomEnd.y)
+		{
+			RectI rect = RectI(leftEdge, bottomEnd.y, bottomStart.x - leftEdge, bottomStart.y - bottomEnd.y);
+			dglDrawRectFill(rect, quadColor);
+
+			Point2I p = Point2I(bottomStart.x, bottomEnd.y);
+			dglDrawTriangleFill(bottomEnd, p, bottomStart, quadColor);
+		}
+		else if (rightEdge != bottomEnd.x && bottomStart.y < bottomEnd.y)
+		{
+			RectI rect = RectI(bottomEnd.x, bottomStart.y, rightEdge - bottomEnd.x, bottomEnd.y - bottomStart.y);
+			dglDrawRectFill(rect, quadColor);
+
+			Point2I p = Point2I(bottomEnd.x, bottomStart.y);
+			dglDrawTriangleFill(bottomStart, bottomEnd, p, quadColor);
+		}
+		else if (bottomStart.y < bottomEnd.y)
+		{
+			Point2I p = Point2I(bottomEnd.x, bottomStart.y);
+			dglDrawTriangleFill(bottomStart, bottomEnd, p, quadColor);
+		}
+		else if (bottomStart.y > bottomEnd.y)
+		{
+			Point2I p = Point2I(bottomStart.x, bottomEnd.y);
+			dglDrawTriangleFill(bottomStart, bottomEnd, p, quadColor);
+		}
+	}
 }

+ 7 - 0
engine/source/gui/editor/guiParticleGraphInspector.h

@@ -44,6 +44,8 @@ private:
    RectI mGridRect;
    Point2I mCalculationOffset;
 
+   GuiParticleGraphInspector* mVariationInspector;
+
 public:
 	struct GraphPoint
 	{
@@ -69,6 +71,7 @@ public:
    virtual void setDisplayField(const char* fieldName, U16 index);
    virtual void setDisplayArea(StringTableEntry minX, StringTableEntry minY, StringTableEntry maxX, StringTableEntry maxY);
    virtual void setDisplayLabels(const char* labelX, const char* labelY);
+   virtual void setVariationGraphInspector(GuiParticleGraphInspector* object) { mVariationInspector = object; }
 
    virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
    virtual void setControlProfile(GuiControlProfile *prof);
@@ -78,6 +81,7 @@ public:
    virtual void onTouchDragged(const GuiEvent &event);
 
    void onRender(Point2I offset, const RectI &updateRect);
+   Vector<GraphPoint>* getRenderPoints() { if (mDirty) { RectI rect = RectI(0, 0, 1, 1); calculatePoints(rect); mDirty = true; } return &mPointList; }
 
 protected:
 	U32 findHitGraphPoint(const Point2I &point);
@@ -85,11 +89,14 @@ protected:
 	F32 getGraphTime(const F32 x);
 
 	void calculatePoints(const RectI &contentRect);
+	Point2I convertToRenderPoint(const RectI& contentRect, F32 time, F32 value);
 	void renderLabels(const RectI &contentRect, const ColorI &labelColor);
 	void renderGrid(const RectI &contentRect, const ColorI &gridColor);
 	void renderPoints(const RectI &contentRect, const ColorI &lineColor);
+	void renderVariation(const RectI &contentRect, const ColorI& color);
 	void renderDot(const RectI &contentRect, const Point2I &point, const Point2I &cursorPt, bool isSelected);
 	void renderLine(const RectI &contentRect, const Point2I &point1, const Point2I &point2, const ColorI &lineColor);
+	void renderQuad(const RectI& contentRect, const Point2I& point1, const Point2I& point2, const Point2I& point3, const Point2I& point4, const ColorI& quadColor);
 
 	ParticleAssetField* getTargetField();
 };

+ 17 - 0
engine/source/gui/editor/guiParticleGraphInspector_ScriptBinding.h

@@ -57,6 +57,23 @@ ConsoleMethodWithDocs(GuiParticleGraphInspector, setDisplayField, ConsoleVoid, 3
 	}
 }
 
+/*! Sets the graph inspector to use to show variance.
+	@param Inspector The GuiParticleGraphInspector that is tracking the variation.
+	@return No return value.
+*/
+ConsoleMethodWithDocs(GuiParticleGraphInspector, setVariationGraphInspector, ConsoleVoid, 3, 3, "(inspector)")
+{
+	GuiParticleGraphInspector* inspector = dynamic_cast<GuiParticleGraphInspector*>(Sim::findObject(argv[2]));
+	if (!inspector)
+	{
+		if (dAtoi(argv[2]) > 0)
+			Con::warnf("%s::setVariationGraphInspector(): Object is not a GuiParticleGraphInspector: %s", argv[0], argv[2]);
+
+		return;
+	}
+	object->setVariationGraphInspector(inspector);
+}
+
 /*! Sets the area that will be displayed on the graph.
 	@param Area Four space-deliminated values representing left, bottom, right, top.
 	@return No return value.

+ 1 - 1
engine/source/gui/guiControl.h

@@ -351,7 +351,7 @@ public:
 
     /// Sets the status of this control as active and responding or inactive
     /// @param   value   True if this is active
-    void setActive(bool value);
+    virtual void setActive(bool value);
     bool isActive() { return mActive; } ///< Returns true if this control is active
 
     bool isAwake() { return mAwake; } ///< Returns true if this control is awake