def __init__(self): ShowBase.__init__(self) PanditorDisableMouseFunc() camera.setPos(0.0, 0.0, 50.0) camera.lookAt(0.0) PanditorEnableMouseFunc() # 1) create GeomVertexData frmt = GeomVertexFormat.getV3n3cp() vdata = GeomVertexData('triangle', frmt, Geom.UHDynamic) # 2) create Writers/Rewriters (must all be created before any readers and readers are one-pass-temporary) vertex = GeomVertexRewriter(vdata, 'vertex') normal = GeomVertexRewriter(vdata, 'normal') color = GeomVertexRewriter(vdata, 'color') zUp = Vec3(0, 0, 1) wt = Vec4(1.0, 1.0, 1.0, 1.0) gr = Vec4(0.5, 0.5, 0.5, 1.0) # 3) write each column on the vertex data object (for speed, have a different writer for each column) def addPoint(x, y, z): vertex.addData3f(x, y, z) normal.addData3f(zUp) color.addData4f(wt) addPoint(0.0, 0.0, 0.0) addPoint(5.0, 0.0, 0.0) addPoint(0.0, 5.0, 0.0) addPoint(5.0, 5.0, 0.0) # 4) create a primitive and add the vertices via index (not truely associated with the actual vertex table, yet) tris = GeomTriangles(Geom.UHDynamic) tris.addVertices(0, 1, 2) tris.closePrimitive( ) # exception thrown if verts added != 3, other types inform Panda how many verts/primitive tris.addVertices(2, 1, 3) print "vdataPoints", vdata.getArrays()[0] # 5.1) (adding to scene) create a Geom and add primitives of like base-type i.e. triangles and triangle strips geom = Geom(vdata) geom.addPrimitive(tris) # 5.2) create a GeomNode to hold the Geom(s) and add the Geom(s) gn = GeomNode('gnode') gn.addGeom(geom) # 5.3) attache the node to the scene gnNodePath = render.attachNewNode(gn) geomPts = Geom(vdata) pts = GeomPoints(Geom.UHStatic) pts.addVertices(0, 1, 2) pts.closePrimitive() geomPts.addPrimitive(pts) pointsNode = GeomNode('points_node') pointsNode.addGeom(geomPts) pointsNP = render.attachNewNode(pointsNode) pointsNP.setZ(0.5) render.ls()
def __call__(self, mesh): vertex_writer = GeomVertexRewriter(mesh.data, "vertex") while not vertex_writer.isAtEnd(): vertex = vertex_writer.getData4() vertex_writer.setData4(self.m.xform(vertex)) return mesh
def drawBody(self, pos, quat, radius=1, keepDrawing=True, numVertices=16): """ this draws the body of the tree. This draws a ring of vertices and connects the rings with triangles to from the body. the keepDrawing parameter tells the function whether or not we're at an end if the vertices before were an end, don't draw branches to it """ vdata = self.bodydata circleGeom = Geom(vdata) vertWriter = GeomVertexWriter(vdata, "vertex") normalWriter = GeomVertexWriter(vdata, "normal") texReWriter = GeomVertexRewriter(vdata, "texcoord") startRow = vdata.getNumRows() vertWriter.setRow(startRow) normalWriter.setRow(startRow) sCoord = 0 if (startRow != 0): texReWriter.setRow(startRow - numVertices) sCoord = texReWriter.getData2f().getX() + 1 draw = (startRow - numVertices) in self.drawFlags if not draw: sCoord -= 1 drawIndex = startRow texReWriter.setRow(startRow) angleSlice = 2 * math.pi / numVertices currAngle = 0 perp1 = quat.getRight() perp2 = quat.getForward() #vertex information is written here for i in xrange(numVertices + 1): #doubles the last vertex to fix UV seam adjCircle = pos + (perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle)) * radius normal = perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle) normalWriter.addData3f(normal) vertWriter.addData3f(adjCircle) texReWriter.addData2f(1.0 * i / numVertices, sCoord) if keepDrawing: self.drawFlags.add(drawIndex) drawIndex += 1 currAngle += angleSlice draw = (startRow - numVertices) in self.drawFlags #we cant draw quads directly so we use Tristrips if (startRow != 0) and draw: lines = GeomTristrips(Geom.UHStatic) for i in xrange(numVertices + 1): lines.addVertex(i + startRow) lines.addVertex(i + startRow - numVertices - 1) lines.addVertex(startRow) lines.addVertex(startRow - numVertices) lines.closePrimitive() #lines.decompose() circleGeom.addPrimitive(lines) circleGeomNode = GeomNode("Debug") circleGeomNode.addGeom(circleGeom) self.numPrimitives += numVertices * 2 self.bodies.attachNewNode(circleGeomNode)
def flood(self, task=None): # Animate Water Surface step_np1 = self.water_physic() vertex = GeomVertexRewriter(self.water_vdata, 'vertex') normal = GeomVertexRewriter(self.water_vdata, 'normal') for j in range(0, self.n_points, self.details): for i in range(0, self.n_points, self.details): # Flood if j != 0 and i != 0 and j != self.n_points - self.details and \ i != self.n_points - self.details: self.wz[j][i] = step_np1[j // self.details][i // self.details] else: self.wz[j][i] = self.H # borders condition v = vertex.getData3f() vertex.setData3f(v[0], v[1], self.wz[j][i]) n = np.array([v[0], v[1], self.wz[j][i]]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) # Extend Water Borders vertex = GeomVertexRewriter(self.water_border_vdata, 'vertex') normal = GeomVertexRewriter(self.water_border_vdata, 'normal') for i in range(0, 8, 2): v = vertex.getData3f() vertex.setData3f(v[0], v[1], self.H) n = np.array([v[0], v[1], self.H]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) v = vertex.getData3f() vertex.setData3f(v[0], v[1], 0) n = np.array([v[0], v[1], 1e-12]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) # animate level if self.flooding == True and self.flush == False and self.H < self.n_points: self.H += self.dt elif self.flush == True and self.H > 1: self.H -= self.dt # handle last puddles #self.handle_last_puddles() if task: return task.cont
def update_geom(self, points, colors, sizes): geom = self.instance.children[0].node().modify_geom(0) vdata = geom.modify_vertex_data() vdata.unclean_set_num_rows(len(points)) vwriter = GeomVertexRewriter(vdata, InternalName.get_vertex()) colorwriter = GeomVertexWriter(vdata, InternalName.get_color()) sizewriter = GeomVertexWriter(vdata, InternalName.get_size()) for (point, color, size) in zip(points, colors, sizes): vwriter.addData3f(*point) colorwriter.addData4f(*color) sizewriter.addData1f(size)
def __call__(self, mesh): vertex_writer = GeomVertexRewriter(mesh.data, "vertex") while not vertex_writer.isAtEnd(): vertex = vertex_writer.getData4() vertex_writer.setData4(( vertex.x * self.v.x, vertex.y * self.v.y, vertex.z * self.v.z, vertex.w * self.v.w, )) return mesh
def wave(self, task): # Compute physic step_np1 = self.water_physic() self.H += 0.1 * (np.mean(step_np1) - self.H) # render wave vertex = GeomVertexRewriter(self.water_vdata, 'vertex') normal = GeomVertexRewriter(self.water_vdata, 'normal') for j in range(0, self.n_points, self.details): for i in range(0, self.n_points, self.details): self.wz[j][i] = step_np1[j // self.details][i // self.details] v = vertex.getData3f() if j != 0 and i != 0 and j != self.n_points - self.details and \ i != self.n_points - self.details: vertex.setData3f(v[0], v[1], self.wz[j][i]) n = np.array([v[0], v[1], self.wz[j][i]]) else: vertex.setData3f(v[0], v[1], self.H) n = np.array([v[0], v[1], self.H]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) # Extend Water Borders vertex = GeomVertexRewriter(self.water_border_vdata, 'vertex') normal = GeomVertexRewriter(self.water_border_vdata, 'normal') for i in range(0, 8, 2): v = vertex.getData3f() vertex.setData3f(v[0], v[1], self.H) n = np.array([v[0], v[1], self.H]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) v = vertex.getData3f() vertex.setData3f(v[0], v[1], 0) n = np.array([v[0], v[1], 1e-12]) norm = n / np.linalg.norm(n) normal.setData3f(norm[0], norm[1], norm[2]) # handle last puddles self.handle_last_puddles() return task.cont
def update_geom(self): geom = self.node.modify_geom(0) vdata = geom.modify_vertex_data() vwriter = GeomVertexRewriter(vdata, InternalName.get_vertex()) #TODO: refactor with above code !!! delta = self.body.parent.get_local_position() if self.orbit.is_periodic(): epoch = self.context.time.time_full - self.orbit.period step = self.orbit.period / (self.nbOfPoints - 1) else: #TODO: Properly calculate orbit start and end time epoch = self.orbit.get_time_of_perihelion() - self.orbit.period * 5.0 step = self.orbit.period * 10.0 / (self.nbOfPoints - 1) for i in range(self.nbOfPoints): time = epoch + step * i pos = self.orbit.get_position_at(time) - delta vwriter.setData3f(*pos)
def rain(self, task): # animate level self.flood() # Animate Rain speed = 1.0 vertex = GeomVertexRewriter(self.rain_vdata, 'vertex') color = GeomVertexWriter(self.rain_vdata, 'color') moving = np.random.choice([0, 1], size=(self.n_points, self.n_points), p=[999 / 1000, 1. / 1000]) moved = 0 for j in range(self.n_points): for i in range(self.n_points): # rain v = vertex.getData3f() # start falling if v[2] == self.n_points: if moving[j][i] == 1 and self.raining == True: self.rz[j][i] -= speed vertex.setData3f(v[0], v[1], self.rz[j][i]) color.setData4f(0.3, 0.3, 1, 1) moved += 1 else: vertex.setData3f(v[0], v[1], v[2]) color.setData4f(0.3, 0.3, 1, 0) # keep falling elif self.rz[j][i] > self.lz[j][i]: if self.rz[j][i] - speed > self.lz[j][i]: self.rz[j][i] -= speed else: self.rz[j][i] = self.lz[j][i] vertex.setData3f(v[0], v[1], self.rz[j][i]) if self.rz[j][i] > self.wz[j][i]: color.setData4f(0.3, 0.3, 1, 1) else: color.setData4f(0.3, 0.3, 1, 0) moved += 1 # stop falling else: # handle rolling drops v = list(map(int, v)) moved += self.rolling_drops(v, vertex, color, moved, i, j) if moved == 0: self.flooding = False return task.done return task.cont
def Car(carAppearance): newVisualCar = loader.loadModel("models/f1car") geomNodeCollection = newVisualCar.findAllMatches('**/+GeomNode') for nodePath in geomNodeCollection: geomNode = nodePath.node() for i in range(geomNode.getNumGeoms()): geom = geomNode.modifyGeom(i) state = geomNode.getGeomState(i) vdata = geom.modifyVertexData() color = GeomVertexRewriter(vdata, 'color') while not color.isAtEnd(): c = color.getData4f() r, g, b, a = c #f r==1.0 and g==0.0 and b==0.0: # g=1.0 # b=1.0 if r == 0.0 and g == 1.0 and b == 0.0: r = carAppearance[0] g = carAppearance[1] b = carAppearance[2] elif r == 1.0 and g == 0.0 and b == 0.0: r = carAppearance[3] g = carAppearance[4] b = carAppearance[5] elif r == 1.0 and g == 1.0 and b == 1.0: r = carAppearance[6] g = carAppearance[7] b = carAppearance[8] elif r == 1.0 and g == 1.0 and b == 0: r = carAppearance[9] g = carAppearance[10] b = carAppearance[11] #print "c = %s" % (repr(c)) #print('r='+str(r)+' g='+str(g)+' b='+str(b)) color.setData4f(r, g, b, 1.0) return newVisualCar
def __init__(self, vertexName='ConstrainedDelaunayTriangles', vertexFormat=GeomVertexFormat.getV3(), usage = Geom.UHDynamic, onVertexCreationCallback = None, universalZ = 0.0): self._vertexData = GeomVertexData(vertexName, vertexFormat, Geom.UHDynamic) self._geomTriangles = GeomTriangles(usage) self._geomTrianglesHoles = GeomTriangles(usage) self._vertexRewriter = GeomVertexRewriter(self._vertexData, 'vertex') # user cannot have control of a writer if onVertexCreationCallback is None: onVertexCreationCallback = lambda x, y, z: None # something to call without checking existence later self._vertexCallback = onVertexCreationCallback self._universalZ = universalZ self.__holes = [[]] self.__polygon = [] inf = float('inf') negInf = float('-inf') self.bounds = { 'minX': inf, 'maxX': negInf, 'minY': inf, 'maxY': negInf, } self.lastStaticVertexIndex = -1
def drawBody(nodePath, vdata, pos, vecList, radius=1, keepDrawing=True, numVertices=8): circleGeom = Geom(vdata) vertWriter = GeomVertexWriter(vdata, "vertex") colorWriter = GeomVertexWriter(vdata, "color") normalWriter = GeomVertexWriter(vdata, "normal") drawReWriter = GeomVertexRewriter(vdata, "drawFlag") texReWriter = GeomVertexRewriter(vdata, "texcoord") startRow = vdata.getNumRows() vertWriter.setRow(startRow) colorWriter.setRow(startRow) normalWriter.setRow(startRow) sCoord = 0 if (startRow != 0): texReWriter.setRow(startRow - numVertices) sCoord = texReWriter.getData2f().getX() + 1 drawReWriter.setRow(startRow - numVertices) if (drawReWriter.getData1f() == False): sCoord -= 1 drawReWriter.setRow(startRow) texReWriter.setRow(startRow) angleSlice = 2 * math.pi / numVertices currAngle = 0 #axisAdj=LMatrix4.rotateMat(45, axis)*LMatrix4.scaleMat(radius)*LMatrix4.translateMat(pos) perp1 = vecList[1] perp2 = vecList[2] # vertex information is written here for i in range(numVertices): adjCircle = pos + \ (perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle)) * \ radius normal = perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle) normalWriter.addData3f(normal) vertWriter.addData3f(adjCircle) texReWriter.addData2f(sCoord, (i + 0.001) / (numVertices - 1)) colorWriter.addData4f(0.5, 0.5, 0.5, 1) drawReWriter.addData1f(keepDrawing) currAngle += angleSlice if startRow == 0: return drawReader = GeomVertexReader(vdata, "drawFlag") drawReader.setRow(startRow - numVertices) # we cant draw quads directly so we use Tristrips if drawReader.getData1i() != 0: lines = GeomTristrips(Geom.UHStatic) half = int(numVertices * 0.5) for i in range(numVertices): lines.addVertex(i + startRow) if i < half: lines.addVertex(i + startRow - half) else: lines.addVertex(i + startRow - half - numVertices) lines.addVertex(startRow) lines.addVertex(startRow - half) lines.closePrimitive() lines.decompose() circleGeom.addPrimitive(lines) circleGeomNode = GeomNode("Debug") circleGeomNode.addGeom(circleGeom) # I accidentally made the front-face face inwards. Make reverse makes the tree render properly and # should cause any surprises to any poor programmer that tries to use # this code circleGeomNode.setAttrib(CullFaceAttrib.makeReverse(), 1) global numPrimitives numPrimitives += numVertices * 2 nodePath.attachNewNode(circleGeomNode)
def drawBody(self, pos, quat, radius=1, UVcoord=(1, 1), numVertices=_polySize): # if isRoot: # self.bodydata = GeomVertexData("body vertices", GeomVertexFormat.getV3n3t2(), Geom.UHStatic) vdata = self.bodydata circleGeom = Geom( vdata ) # this was originally a copy of all previous geom in vdata... vertWriter = GeomVertexWriter(vdata, "vertex") #colorWriter = GeomVertexWriter(vdata, "color") normalWriter = GeomVertexWriter(vdata, "normal") # drawReWriter = GeomVertexRewriter(vdata, "drawFlag") texReWriter = GeomVertexRewriter(vdata, "texcoord") startRow = vdata.getNumRows() vertWriter.setRow(startRow) #colorWriter.setRow(startRow) normalWriter.setRow(startRow) texReWriter.setRow(startRow) #axisAdj=Mat4.rotateMat(45, axis)*Mat4.scaleMat(radius)*Mat4.translateMat(pos) perp1 = quat.getRight() perp2 = quat.getForward() #TODO: PROPERLY IMPLEMENT RADIAL NOISE #vertex information is written here angleSlice = 2 * pi / numVertices currAngle = 0 for i in xrange(numVertices + 1): adjCircle = pos + (perp1 * cos(currAngle) + perp2 * sin(currAngle)) * radius * ( .5 + bNodeRadNoise * random.random()) normal = perp1 * cos(currAngle) + perp2 * sin(currAngle) normalWriter.addData3f(normal) vertWriter.addData3f(adjCircle) texReWriter.addData2f( float(UVcoord[0] * i) / numVertices, UVcoord[1]) # UV SCALE HERE! #colorWriter.addData4f(0.5, 0.5, 0.5, 1) currAngle += angleSlice #we cant draw quads directly so we use Tristrips if (startRow != 0): lines = GeomTristrips(Geom.UHStatic) for i in xrange(numVertices + 1): lines.addVertex(i + startRow) lines.addVertex(i + startRow - numVertices - 1) lines.addVertex(startRow) lines.addVertex(startRow - numVertices) lines.closePrimitive() #lines.decompose() circleGeom.addPrimitive(lines) circleGeomNode = GeomNode("Debug") circleGeomNode.addGeom(circleGeom) self.numPrimitives += numVertices * 2 self.bodies.attachNewNode(circleGeomNode) return circleGeomNode
def movePmTo(self, dest_index): geom = self.geomPath.node().modifyGeom(0) vertdata = geom.modifyVertexData() prim = geom.modifyPrimitive(0) indexdata = prim.modifyVertices() indexrewriter = GeomVertexRewriter(indexdata) indexrewriter.setColumn(0) nextTriangleIndex = indexdata.getNumRows() vertwriter = GeomVertexWriter(vertdata, 'vertex') numverts = vertdata.getNumRows() vertwriter.setRow(numverts) normalwriter = GeomVertexWriter(vertdata, 'normal') normalwriter.setRow(numverts) uvwriter = GeomVertexWriter(vertdata, 'texcoord') uvwriter.setRow(numverts) while self.pm_index < dest_index: for op_index in range(len(self.pm_refinements[self.pm_index])): vals = self.pm_refinements[self.pm_index][op_index] op = vals[0] if op == PM_OP.TRIANGLE_ADDITION: indexrewriter.setRow(nextTriangleIndex) nextTriangleIndex += 3 indexrewriter.addData1i(vals[1]) indexrewriter.addData1i(vals[2]) indexrewriter.addData1i(vals[3]) elif op == PM_OP.INDEX_UPDATE: #TODO: ugly workaround for p3d 1.7 bug, change to below for 1.8 indexreader = GeomVertexReader(indexdata) indexreader.setColumn(0) indexreader.setRow(vals[1]) oldval = indexreader.getData1i() del indexreader #indexrewriter.setRow(vals[1]) #oldval = indexrewriter.getData1i() indexrewriter.setRow(vals[1]) indexrewriter.setData1i(vals[2]) self.pm_refinements[self.pm_index][op_index] = (op, vals[1], oldval) elif op == PM_OP.VERTEX_ADDITION: numverts += 1 vertwriter.addData3f(vals[1], vals[2], vals[3]) normalwriter.addData3f(vals[4], vals[5], vals[6]) uvwriter.addData2f(vals[7], vals[8]) self.pm_index += 1 while self.pm_index > dest_index: self.pm_index -= 1 for op_index in range(len(self.pm_refinements[self.pm_index])): vals = self.pm_refinements[self.pm_index][op_index] op = vals[0] if op == PM_OP.TRIANGLE_ADDITION: nextTriangleIndex -= 3 elif op == PM_OP.INDEX_UPDATE: #TODO: ugly workaround for p3d 1.7 bug, change to below for 1.8 indexreader = GeomVertexReader(indexdata) indexreader.setColumn(0) indexreader.setRow(vals[1]) oldval = indexreader.getData1i() del indexreader #indexrewriter.setRow(vals[1]) #oldval = indexrewriter.getData1i() indexrewriter.setRow(vals[1]) indexrewriter.setData1i(vals[2]) self.pm_refinements[self.pm_index][op_index] = (op, vals[1], oldval) elif op == PM_OP.VERTEX_ADDITION: numverts -= 1 if nextTriangleIndex < indexdata.getNumRows(): indexdata.setNumRows(nextTriangleIndex) if numverts < vertdata.getNumRows(): vertdata.setNumRows(numverts)
def __init__(self, objinit, cdtype="triangle", mass=.3, restitution=0, allowdeactivation=False, allowccd=True, friction=.2, dynamic=True, name="rbd"): """ :param objinit: could be itself (copy), or an instance of collision model :param type: triangle or convex :param mass: :param restitution: bounce parameter :param friction: :param dynamic: only applicable to triangle type, if an object does not move with force, it is not dynamic :param name: author: weiwei date: 20190626, 20201119 """ super().__init__(name) if isinstance(objinit, gm.GeometricModel): if objinit._objtrm is None: raise ValueError("Only applicable to models with a trimesh!") self.com = objinit.objtrm.center_mass self.setMass(mass) self.setRestitution(restitution) self.setFriction(friction) self.setLinearDamping(.3) self.setAngularDamping(.3) if allowdeactivation: self.setDeactivationEnabled(True) self.setLinearSleepThreshold(0.001) self.setAngularSleepThreshold(0.001) else: self.setDeactivationEnabled(False) if allowccd: # continuous collision detection self.setCcdMotionThreshold(1e-6) self.setCcdSweptSphereRadius(0.0005) gnd = objinit.objpdnp.getChild(0).find("+GeomNode") geom = copy.deepcopy(gnd.node().getGeom(0)) vdata = geom.modifyVertexData() vertrewritter = GeomVertexRewriter(vdata, 'vertex') while not vertrewritter.isAtEnd(): # shift local coordinate to geom to correctly update dynamic changes v = vertrewritter.getData3f() vertrewritter.setData3f(v[0] - self.com[0], v[1] - self.com[1], v[2] - self.com[2]) geomtf = gnd.getTransform() if cdtype is "triangle": geombmesh = BulletTriangleMesh() geombmesh.addGeom(geom) bulletshape = BulletTriangleMeshShape(geombmesh, dynamic=dynamic) bulletshape.setMargin(1e-6) self.addShape(bulletshape, geomtf) elif cdtype is "convex": bulletshape = BulletConvexHullShape() # TODO: compute a convex hull? bulletshape.addGeom(geom, geomtf) bulletshape.setMargin(1e-6) self.addShape(bulletshape, geomtf) else: raise NotImplementedError pdmat4 = geomtf.getMat() pdv3 = pdmat4.xformPoint(Vec3(self.com[0], self.com[1], self.com[2])) homomat = dh.pdmat4_to_npmat4(pdmat4) pos = dh.pdv3_to_npv3(pdv3) homomat[:3, 3] = pos # update center to com self.setTransform(TransformState.makeMat(dh.npmat4_to_pdmat4(homomat))) elif isinstance(objinit, BDBody): self.com = objinit.com.copy() self.setMass(objinit.getMass()) self.setRestitution(objinit.restitution) self.setFriction(objinit.friction) self.setLinearDamping(.3) self.setAngularDamping(.3) if allowdeactivation: self.setDeactivationEnabled(True) self.setLinearSleepThreshold(0.001) self.setAngularSleepThreshold(0.001) else: self.setDeactivationEnabled(False) if allowccd: self.setCcdMotionThreshold(1e-6) self.setCcdSweptSphereRadius(0.0005) self.setTransform(TransformState.makeMat(dh.npmat4_to_pdmat4(objinit.gethomomat()))) self.addShape(objinit.getShape(0), objinit.getShapeTransform(0))
def draw_water_mesh(self): _format = GeomVertexFormat.get_v3n3cp() self.water_vdata = GeomVertexData('water', _format, Geom.UHDynamic) self.water_vdata.setNumRows((self.n_points // self.details)**2) vertex = GeomVertexWriter(self.water_vdata, 'vertex') normal = GeomVertexWriter(self.water_vdata, 'normal') color = GeomVertexWriter(self.water_vdata, 'color') for j in range(0, self.n_points, self.details): for i in range(0, self.n_points, self.details): if j == self.n_points - self.details: j = self.n_points - 1 if i == self.n_points - self.details: i = self.n_points - 1 # Water Vertices vertex.addData3f(self.x[j][i], self.y[j][i], self.wz[j][i]) # Water Color color.addData4f(0.3, 0.3, 1, 0.8) # water Normals n = np.array([self.x[j][i], self.y[j][i], self.wz[j][i]]) norm = n / np.linalg.norm(n) normal.addData3f(norm[0], norm[1], norm[2]) # Water Primitive prim = GeomTriangles(Geom.UHDynamic) for j in range(self.n_points // self.details): for i in range(self.n_points // self.details): if j != (self.n_points // self.details) - 1 and i != ( self.n_points // self.details) - 1: prim.add_vertices( j * (self.n_points // self.details) + i, j * (self.n_points // self.details) + (i + 1), (j + 1) * (self.n_points // self.details) + i) prim.add_vertices( j * (self.n_points // self.details) + (i + 1), (j + 1) * (self.n_points // self.details) + (i + 1), (j + 1) * (self.n_points // self.details) + i) geom = Geom(self.water_vdata) prim.closePrimitive() geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) water_nodePath = render.attachNewNode(node) water_nodePath.setTransparency(TransparencyAttrib.MAlpha) water_nodePath.setAntialias(AntialiasAttrib.MAuto) water_nodePath.setPos(-50, -50, 0) # Border self.water_border_vdata = GeomVertexData('water_border', _format, Geom.UHDynamic) self.water_border_vdata.setNumRows(8) vertex = GeomVertexWriter(self.water_border_vdata, 'vertex') normal = GeomVertexRewriter(self.water_border_vdata, 'normal') color = GeomVertexWriter(self.water_border_vdata, 'color') for i in [0, 99]: for j in range(1, -1, -1): # Borders Vertices vertex.addData3f(i, 0, j) # Borders Colors color.addData4f(0.3, 0.3, 1, 0.8) # Borders Normals n = np.array([i, 0, 1e-12 if j == 0 else 1]) norm = n / np.linalg.norm(n) normal.addData3f(norm[0], norm[1], norm[2]) for i in [99, 0]: for j in range(1, -1, -1): vertex.addData3f(i, 99, j) color.addData4f(0.3, 0.3, 1, 0.8) n = np.array([i, 99, 1e-12 if j == 0 else 1]) norm = n / np.linalg.norm(n) normal.addData3f(norm[0], norm[1], norm[2]) # Borders Primitive prim = GeomTriangles(Geom.UHDynamic) for i in range(0, 8, 2): prim.add_vertices(i, i+1 if i+1 < 8 else i+1-8, \ i+2 if i+2 < 8 else i+2-8) prim.add_vertices(i+2 if i+2 < 8 else i+2-8, \ i+1 if i+1 < 8 else i+1-8, i+3 if i+3 < 8 else i+3-8) geom = Geom(self.water_border_vdata) geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) water_border_nodePath = render.attachNewNode(node) water_border_nodePath.setTransparency(TransparencyAttrib.MAlpha) water_border_nodePath.setAntialias(AntialiasAttrib.MAuto) water_border_nodePath.setPos(-50, -50, 0)
def __init__(self): ShowBase.__init__(self) winProps = WindowProperties() winProps.setTitle("Triangle and SimpleCircle Unittest") # base.win.requestProperties(winProps) (same as below e.g. self == base) self.win.requestProperties(winProps) zUp = Vec3(0.0, 0.0, 1.0) wt = Vec4(1.0, 1.0, 1.0, 1.0) def addToVertex(x, y, z): normal.addData3f(zUp) color.addData4f(wt) # BLOG post about these bullet points then delete them # 1) create GeomVertexData (inside the triangulator's constructor) frmt = GeomVertexFormat.getV3n3cp() triangulator = ConstrainedDelaunayTriangulator(vertexFormat = frmt, onVertexCreationCallback = addToVertex) vdata = triangulator.getGeomVertexData() # 2) create Writers/Rewriters (must all be created before any readers and readers are one-pass-temporary) normal = GeomVertexRewriter(vdata, 'normal') # DOC 'vertex' is the only prohibited column for the user to use color = GeomVertexRewriter(vdata, 'color') # 3) write each column on the vertex data object (for speed, have a different writer for each column) # DOC 1.DT) create triangulator # DOC 2.DT) add vertices (before calling triangulate) # ############# NOT ANGLE OPTIMAL BELOW ##################### # triangulator.addVertexToPolygon(5.0, 0.0, 0.0) # triangulator.addVertexToPolygon(0.0, 0.0, 0.0) # triangulator.addVertexToPolygon(1.5, 2.5, 0.0) # triangulator.addVertexToPolygon(0.0, 5.0, 0.0) # triangulator.addVertexToPolygon(5.0, 5.0, 0.0) # ############# NOT ANGLE OPTIMAL ABOVE ##################### triangulator.addVertexToPolygon(5.0, 0.0, 0.0) triangulator.addVertexToPolygon(6.5, 6.5, 0.0) # triangulator.addVertexToPolygon(1.5, 2.5, 0.0) triangulator.addVertexToPolygon(0.0, 0.0, 0.0) triangulator.addVertexToPolygon(0.0, 5.0, 0.0) triangulator.addVertexToPolygon(5.0, 5.0, 0.0) # DOC 3.DT) add hole vertices (before calling triangulate) # DOC 4.DT) call triangulate triangulator.triangulate(makeDelaunay=True) # ######################## REMOVE ################################### # ######################## REMOVE ################################### assert triangulator.isTriangulated() adjLst = triangulator.getAdjacencyList() foundInvalidReference = [] foundMissingReference = [] # so I can explicitly state all index references are correct for t in adjLst: # check that references with no neighbor haven't missed an edge noneEdges = [] if t._neighbor0 is None: noneEdges.append(t.edgeIndices0) elif t._neighbor1 is None: noneEdges.append(t.edgeIndices1) elif t._neighbor2 is None: noneEdges.append(t.edgeIndices2) for tri_ in adjLst: for edge_ in noneEdges: # the edge that should hold the reference is on t. The one that should get referenced is on tri_ missedCount = 0 if edge_[0] in tri_.edgeIndices0 and edge_[1] in tri_.edgeIndices0 and tri_ != t: missedCount += 1 if edge_[0] in tri_.edgeIndices1 and edge_[1] in tri_.edgeIndices1 and tri_ != t: missedCount += 1 if edge_[0] in tri_.edgeIndices2 and edge_[1] in tri_.edgeIndices2 and tri_ != t: missedCount += 1 if missedCount == 1: foundMissingReference.extend((t, tri_)) notify.warning( "!MISSED REFERENCE TO NEIGHBOR\nreferrer: {} ptIndices: {} neighbors: {}\n".format( t.index, t.getPointIndices(), t.getNeighbors(), ) + "missed: {} ptIndices: {} neighbors: {}".format( tri_.index, tri_.getPointIndices(), tri_.getNeighbors(), )) elif missedCount > 1: foundMissingReference.extend((t, tri_)) notify.warning( "!EXTRANEOUS & MISSED SHARED EDGES\nreferrer: {} ptIndices: {} neighbors: {}\n".format( t.index, t.getPointIndices(), t.getNeighbors(), ) + "missed: {} ptIndices: {} neighbors: {}".format( tri_.index, tri_.getPointIndices(), tri_.getNeighbors(), )) # check that neighbor relations point to correct triangles for n in t.getNeighbors(includeEmpties=False): neighbor = adjLst[n] otherInds = neighbor.getPointIndices() if neighbor.index == t._neighbor0: edge = t.edgeIndices0 elif neighbor.index == t._neighbor1: edge = t.edgeIndices1 elif neighbor.index == t._neighbor2: edge = t.edgeIndices2 if edge[0] not in otherInds and edge[1] not in otherInds: foundInvalidReference.extend((t, neighbor)) notify.warning( "!INVALID REFERENCE TO NEIGHBOR\nreferrer: {} indices: {} neighbors: {}".format( t.index, t.getPointIndices(), t.getNeighbors(), ) + "\nreferee: {} indices: {} neighbors: {}".format( neighbor.index, neighbor.getPointIndices(), neighbor.getNeighbors() )) if not foundMissingReference: notify.warning("No error missing reference in neighbors.") else: notify.warning("!!!ERROR missing reference in neighbor references.") if not foundInvalidReference: notify.warning("No error found in neighbors that were referenced.") else: notify.warning("!!!ERROR found in neighbors that were referenced.") foundPointInsideCircle = False trianglesWithInvalidPoint = set() for t in adjLst: circle_ = t.getCircumcircle() # cycle through triangles checking each point against each circle.center for e in adjLst: p0, p1, p2 = e.getPoints() if circle_.radius - (p0 - circle_.center).length() > EPSILON: foundPointInsideCircle = True notify.warning( "!point in circumcircle point {0} circle {1}\ntriangle1: {2}\ntriangle2: {3}".format( p0, circle_, t, e )) trianglesWithInvalidPoint |= set((t.index, )) if circle_.radius - (p1 - circle_.center).length() > EPSILON: foundPointInsideCircle = True notify.warning( "!point in circumcircle point {0} circle {1}\ntriangle1: {2}\ntriangle2: {3}".format( p1, circle_, t, e )) trianglesWithInvalidPoint |= set((t.index, )) if circle_.radius - (p2 - circle_.center).length() > EPSILON: foundPointInsideCircle = True notify.warning( "!point in circumcircle point {0} circle {1}\ntriangle1: {2}\ntriangle2: {3}".format( p2, circle_, t, e )) trianglesWithInvalidPoint |= set((t.index, )) if not foundPointInsideCircle: notify.warning("No point found inside circumcircle.") else: notify.warning("!!!ERROR found point inside circumcircle. Triangles: {}".format(trianglesWithInvalidPoint)) # TODO test edges that reference no neighbor # triangles = triangulator.getGeomTriangles() # print "Triangulated:" # for tri in triangleList: # print "\t{0}".format(tri) # 4) create a primitive and add the vertices via index (not truly associated with the actual vertex table, yet) # tris = GeomTriangles(Geom.UHDynamic) # t1 = Triangle(0, 1, 2, vdata, tris, vertex) # t2 = Triangle(2, 1, 3, vdata, tris, vertex) # c1 = t1.getCircumcircle() # t1AsEnum = t1.asPointsEnum() # r0 = (t1AsEnum.point0 - c1.center).length() # r1 = (t1AsEnum.point1 - c1.center).length() # r2 = (t1AsEnum.point2 - c1.center).length() # assert abs(r0 - r2) < utilities.EPSILON and abs(r0 - r1) < utilities.EPSILON # t2AsEnum = t2.asPointsEnum() # c2 = t2.getCircumcircle() # r0 = (t2AsEnum.point0 - c2.center).length() # r1 = (t2AsEnum.point1 - c2.center).length() # r2 = (t2AsEnum.point2 - c2.center).length() # assert abs(r0 - r2) < utilities.EPSILON and abs(r0 - r1) < utilities.EPSILON # assert t1.getAngleDeg0() == 90.0 # assert t1.getAngleDeg1() == t1.getAngleDeg2() # # oldInd0 = t1.pointIndex0 # oldInd1 = t1.pointIndex1 # oldInd2 = t1.pointIndex2 # t1.pointIndex0 = t1.pointIndex1 # t1.pointIndex1 = oldInd0 # assert t1.pointIndex0 == oldInd1 # assert t1.pointIndex1 == oldInd0 # assert t1.pointIndex0 != t1.pointIndex1 # t1.reverse() # assert t1.pointIndex1 == oldInd2 # gn = triangulator.getGeomNode('triangles') gnNodePath = render.attachNewNode(gn) # setup a wire frame wireNP = render.attachNewNode('wire') wireNP.setPos(0.0, 0.0, .1) wireNP.setColor(0.1, 0.1, 0.1, 1) wireNP.setRenderMode(RenderModeAttrib.MWireframe, .5, 0) gnNodePath.instanceTo(wireNP) # # # test and draw intersections and circles # pt1 = Point3(0.0, 5.0, 0.0) # pt2 = Point3(1.0, 5.0, 0.0) # intersection = t2.getIntersectionsWithCircumcircle(pt1, pt2) # circle = t2.getCircumcircle() # cuts = 128 # border = circle.getBorder(cuts, closed=True) # assert len(border) == cuts or (len(border) == cuts + 1 and border[0] == border[len(border) - 1]) # n = len(border) # xMid = yMid = 0 # for p in border: # xMid += p.x # yMid += p.y # mid = Point3(xMid / n, yMid / n, border[0].z) # assert mid.almostEqual(circle.center, 0.06) # assert t2.isLeftWinding() # assert t1.containsPoint(c1.center) != t1.containsPoint(c1.center, includeEdges=False) # # circleSegs = LineSegs("circleLines") # circleSegs.setColor(1.0, 0.0, 0.0, 1.0) # for p in border: # circleSegs.drawTo(*p) # circleNode = circleSegs.create(False) # circleNP = render.attachNewNode(circleNode) # circleNP.setZ(-5) # # originSpot = LineSegs("intersection") # originSpot.setColor(1.0, 0.0, 0.0, 1.0) # originSpot.setThickness(10) # for p in intersection: # originSpot.drawTo(p) # spotNode = originSpot.create(False) # spotNP = render.attachNewNode(spotNode) # circleNP.setZ(-0.75) # fix the camera rot/pos PHF.PanditorDisableMouseFunc() camera.setPos(0.0, 0.0, 50.0) camera.lookAt(Point3(0.0)) # 2.5, 2.5, 0.0)) PHF.PanditorEnableMouseFunc() # print "isLeftWinding()", triangulator.isLeftWinding() # TODO port the triangle-indices node func drawTriangleIndices(...) indsNp = ConstrainedDelaunayTriangulator.drawTriangleIndices(triangulator.getTriangleList()) indsNp.setPos(0.0, 0.0, 0.3)