I’m using the RopeNode class to model a flat, fluidly-sinuating object, and for the most part it works pretty well for this, I feel! However, when it turns at all acutely–even if not hugely so–I find that it produces little breaks and overlaps along its length–i.e. places where gaps show, or where its transparent rendering is more-opaque than intended due (presumably) to the geometry overlapping itself.
A few excerpts are attached–the effect is fairly slight when seen statically like this, but it’s more apparent in motion, I find.
My initialisation of the RopeNode looks something like this:
self.ropeNode = RopeNode("fox rope")
self.curve = NurbsCurveEvaluator()
self.ropeNode.setCurve(self.curve)
self.ropeNode.setNormalMode(RopeNode.NM_none)
self.ropeNode.setRenderMode(RopeNode.RM_tape)
self.ropeNode.setThickness(3)
self.ropeNode.setTubeUp(Vec3(0, 0, 1))
self.ropeNode.setUvDirection(True)
self.ropeNode.setUvMode(RopeNode.UV_distance)
self.ropeNode.setUvScale(1/7)
self.curve.reset(7)
self.ropeNP = NodePath(self.ropeNode)
self.ropeNP.setZ(2)
self.ropeNP.reparentTo(render)
self.ropeNode.setBounds(OmniBoundingVolume())
self.ropeNode.setFinal(True)
Then, in an update-method that should be called once per frame, I maintain a “position-list”, adding and removing points based on the object’s distance from the last point.
And finally, in that same update-method, I set the zeroth curve-vertex to have current position of the object, and then iterate over the position-list and set the subsequent vertices of the curve to have the stored positions. (This via calls to “setVertex”.)
Is there anything that might be done about this? Or perhaps a better approach to modelling the object in question?
rdb
June 23, 2022, 8:43am
2
Can you reduce this to a simple sample and post it on GitHub?
Sure! You should find that in the issue below:
opened 09:18AM - 23 Jun 22 UTC
## Description
A moving rope-node, with the "tape" render-mode applied, can pro… duce small overlaps and breaks along its course. This is most especially the case when it turns acutely, I find.
## Steps to Reproduce
The following program demonstrates the effect, on my machine at least. To test, simply run the program--you should see a flat rope-node moving in a circle of varying diameter, and triangular breaks and overlaps appearing. (The latter made visible via transparency and a lack of depth-testing)
```python
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Vec3, Vec4, NodePath, RopeNode, NurbsCurveEvaluator
import math
from panda3d import __version__ as pandaVersion
print (pandaVersion)
import sys
print (sys.version)
class Game(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# A black backdrop to better show the effect
self.win.setClearColor(Vec4(0, 0, 0, 1))
# Our rope and its curve!
self.ropeNode = RopeNode("rope")
self.curve = NurbsCurveEvaluator()
self.ropeNode.setCurve(self.curve)
# Make the rope a flat surface, and set its width
self.ropeNode.setRenderMode(RopeNode.RM_tape)
self.ropeNode.setThickness(3)
# I'm not sure that these are required, but I'll leave them in just in case
self.ropeNode.setNormalMode(RopeNode.NM_none)
self.ropeNode.setTubeUp(Vec3(0, 0, 1))
# Set set up the curve and our number of points
self.curve.reset(7)
# Create and arrange a NodePath for our rope
self.ropeNP = NodePath(self.ropeNode)
self.ropeNP.setY(25)
self.ropeNP.setP(90)
self.ropeNP.reparentTo(render)
# This makes the rope semi-transparent and prevents depth-testing from interfering, as in the original case
self.ropeNP.setAlphaScale(0.5)
self.ropeNP.setTransparency(True)
self.ropeNP.setDepthTest(False)
# For the purposes of this demonstrative program, we'll just have the rope spin around in a circle.
# These next things, then, are elements that control the points of the curve:
# a list of points, a speed-value, and a circle-size (which changes)
self.positionList = []
self.angle = 0
self.speed = 5
self.circleSize = 5
self.maxCircleSize = 5
self.minCircleSize = 1
self.circleSizeDir = 1
# And finally, an update task in which to update our curve
self.updateTask = self.taskMgr.add(self.update, "update task")
def update(self, task):
dt = self.clock.getDt()
# Vary the size of the circle of the rope's movement, to show different aspects of the effect produced
self.circleSize += self.circleSizeDir * 1.5 * dt
if self.circleSizeDir > 0 and self.circleSize > self.maxCircleSize:
self.circleSize = self.maxCircleSize
self.circleSizeDir = -1
elif self.circleSizeDir < 0 and self.circleSize < self.minCircleSize:
self.circleSize = self.minCircleSize
self.circleSizeDir = 1
# Accumulate a circular angle, and from that calculate the current position on the circle
self.angle += self.speed * dt
pos = Vec3(math.sin(self.angle) * self.circleSize, math.cos(self.angle) * self.circleSize, 0)
# Update the list of points:
numPoints = len(self.positionList)
if numPoints == 0:
# If there are no points yet, just add the current one
self.positionList.append(pos)
else:
# Otherwise, if the current point is sufficiently far from the last, add it
lastPt = self.positionList[-1]
diff = pos - lastPt
lastSegmentDist = diff.length()
testDist = 1.5
if lastSegmentDist > testDist:
self.positionList.append(pos)
# Prevent the list from holding too many points
while len(self.positionList) > 8:
self.positionList.pop(0)
# And finally, update the curve!
# The first vertex is always the current point, and subsequent vertices
# take their positions from the point-list
self.curve.setVertex(0, pos)
for index, pt in enumerate(self.positionList[-1:-(len(self.positionList)-1):-1]):
self.curve.setVertex(index+1, pt)
return task.cont
app = Game()
app.run()
```

## Environment
* Operating system: Ubuntu Linux 18.04.6
* System architecture: 64-bit
* Panda3D version: 1.10.11
* Installation method: pip, I believe.
* Python version (if using Python): 3.6.9
* Compiler (if using C++): N/A