def create3D(self, x=0, y=0, w=0, h=0, near=1.0, far=800.0, aspect=60.0, depth=24): if w <= 0 or h <= 0: w = self.max_width h = self.max_height self.win_width = w self.win_height = h self.near = near self.far = far self.left = x self.top = y self.right = x + w self.bottom = y + h self.create_display(x, y, w, h, depth) #Setup perspective view opengles.glMatrixMode(GL_PROJECTION) Utility.load_identity() hht = near * math.tan(math.radians(aspect / 2.0)) hwd = hht * w / h opengles.glFrustumf(c_float(-hwd), c_float(hwd), c_float(-hht), c_float(hht), c_float(near), c_float(far)) opengles.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) opengles.glMatrixMode(GL_MODELVIEW) Utility.load_identity()
def perspective(self, w, h, zoom, near=1, far=500): opengles.glMatrixMode(GL_PROJECTION) Utility.load_identity() hht = near * math.tan(math.radians(45.0)) hwd = hht * w / h opengles.glFrustumf(c_float(-hwd), c_float(hwd), c_float(-hht), c_float(hht), c_float(near), c_float(far)) opengles.glMatrixMode(GL_MODELVIEW)
def string(font, string, x, y, z, rot, sclx, scly): opengles.glNormalPointer(GL_BYTE, 0, RECT_NORMALS) Utility.texture_min_mag() opengles.glEnableClientState(GL_TEXTURE_COORD_ARRAY) opengles.glBindTexture(GL_TEXTURE_2D,font.tex) opengles.glEnable(GL_TEXTURE_2D) opengles.glDisable(GL_DEPTH_TEST) opengles.glDisable(GL_CULL_FACE) opengles.glEnable(GL_BLEND) opengles.glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) mtrx =(c_float * 16)() opengles.glGetFloatv(GL_MODELVIEW_MATRIX,ctypes.byref(mtrx)) Utility.translatef(x, y, z) Utility.rotatef(rot, 0, 0, 1) Utility.scalef(sclx, scly, 1) for c in range(0,len(string)): v = ord(string[c])-32 w, h, texc, verts = font.chr[v] if v > 0: opengles.glVertexPointer(3, GL_FLOAT, 0,verts) opengles.glTexCoordPointer(2, GL_FLOAT,0,texc) opengles.glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, RECT_TRIANGLES) Utility.translatef(w, 0, 0) opengles.glLoadMatrixf(mtrx) opengles.glDisable(GL_TEXTURE_2D) opengles.glDisable(GL_BLEND) opengles.glEnable(GL_DEPTH_TEST) opengles.glEnable(GL_CULL_FACE)
def __init__( self, camera=None, light=None, radius=1, slices=12, sides=12, hemi=0.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, invert=False, ): """uses standard constructor for Shape extra Keyword arguments: *radius* radius of sphere *slices* number of latitude edges *hemi* if set to 0.5 it will only construct the top half of sphere *sides* number of sides for Shape._lathe() to use *invert* normals will face inwards, Texture will need flip=True """ super(Sphere, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating sphere ...") path = [] # extra points added at poles to reduce distortion (mainly normals) st = ((math.pi - 0.002) * (1.0 - hemi)) / slices path.append((0.0, radius)) for r in range(slices + 1): x, y = Utility.from_polar_rad(r * st + 0.001, radius) path.append((y, x)) x, y = Utility.from_polar_rad(r * st + 0.002, radius) path.append((y, x)) if invert: path.reverse() self.radius = radius self.slices = slices self.hemi = hemi self.ttype = GL_TRIANGLES self.buf = [] self.buf.append(self._lathe(path, sides))
def __init__(self, camera=None, light=None, radius=1, slices=12, sides=12, hemi=0.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, invert=False): """uses standard constructor for Shape extra Keyword arguments: *radius* radius of sphere *slices* number of latitude edges *hemi* if set to 0.5 it will only construct the top half of sphere *sides* number of sides for Shape._lathe() to use *invert* normals will face inwards, Texture will need flip=True """ super(Sphere, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating sphere ...") path = [] #extra points added at poles to reduce distortion (mainly normals) st = ((math.pi - 0.002) * (1.0 - hemi)) / slices path.append((0.0, radius)) for r in range(slices + 1): x, y = Utility.from_polar_rad(r * st + 0.001, radius) path.append((y, x)) x, y = Utility.from_polar_rad(r * st + 0.002, radius) path.append((y, x)) if invert: path.reverse() self.radius = radius self.slices = slices self.hemi = hemi self.ttype = GL_TRIANGLES self.buf = [] self.buf.append(self._lathe(path, sides))
def calc_normals(self): normals = np.zeros((len(self.array_buffer), 3), dtype="float32") #empty array rights size fv = self.array_buffer[self.element_array_buffer,0:3] #expand faces with x,y,z values for each vertex #cross product of two edges of triangles self.element_normals = np.cross(fv[:,1] - fv[:,0], fv[:,2] - fv[:,0]) self.element_normals = Utility.normalize_v3(self.element_normals) normals[self.element_array_buffer[:,0]] += self.element_normals #add up all normal vectors for a vertex normals[self.element_array_buffer[:,1]] += self.element_normals normals[self.element_array_buffer[:,2]] += self.element_normals return Utility.normalize_v3(normals)
def calc_normals(self): normals = np.zeros((len(self.array_buffer), 3), dtype="float32") #empty array rights size fv = self.array_buffer[self.element_array_buffer,0:3] #expand faces with x,y,z values for each vertex #cross product of two edges of triangles fn = np.cross(fv[:,1] - fv[:,0], fv[:,2] - fv[:,0]) fn = Utility.normalize_v3(fn) normals[self.element_array_buffer[:,0]] += fn #add up all normal vectors for a vertex normals[self.element_array_buffer[:,1]] += fn normals[self.element_array_buffer[:,2]] += fn return Utility.normalize_v3(normals)
def position(self, x, y, z, w=1): """ move or rotate the light and change its type x,y,z -- location or direction components w -- type 0 for directional, 1 for point (default 1) """ mtrx = (ctypes.c_float * 16)() opengles.glGetFloatv(GL_MODELVIEW_MATRIX, ctypes.byref(mtrx)) Utility.load_identity() self.xyz = c_floats((x, y, z , w)) opengles.glLightfv(self.no, GL_POSITION, self.xyz) opengles.glLoadMatrixf(mtrx)
def orthographic(self, left, right, bottom, top, zoom=1, near=-1, far=10): opengles.glMatrixMode(GL_PROJECTION) Utility.load_identity() # opengles.glOrthof(c_float(-10), c_float(10), c_float(10), c_float(-10.0), c_float(near), c_float(far)) opengles.glOrthof( c_float(left / zoom), c_float(right / zoom), c_float(bottom / zoom), c_float(top / zoom), c_float(near), c_float(far), ) opengles.glMatrixMode(GL_MODELVIEW) Utility.load_identity()
def __init__(self,radius=2.0, thickness=0.5, ringrots=6, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): super(Torus,self).__init__(name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating Torus ..." path = [] st = (math.pi * 2)/ringrots for r in range(ringrots + 1): x, y = Utility.from_polar_rad(r * st, thickness) path.append((radius + y, x)) # TODO: why the reversal? self.radius = radius self.thickness = thickness self.ringrots = ringrots self.sides = sides self.ttype = GL_TRIANGLES results = self.lathe(path) self.vertices = c_floats(results[0]) self.normals = c_floats(results[1]) self.indices = c_shorts(results[2]) self.tex_coords = c_floats(results[3]) self.ssize = results[4]
def __init__(self, camera=None, light=None, radius=1, slices=12, sides=12, hemi=0.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* radius of sphere *slices* number of latitude edges *hemi* if set to 0.5 it will only construct the top half of sphere *sides* number of sides for Shape._lathe() to use """ super(Sphere, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating sphere ..." path = [] st = (math.pi * (1.0 - hemi)) / slices for r in range(slices + 1): x, y = Utility.from_polar_rad(r * st, radius) path.append((y, x)) # TODO: why is the reversal here? self.radius = radius self.slices = slices self.hemi = hemi self.ttype = GL_TRIANGLES self.buf = [] self.buf.append(self._lathe(path, sides))
def __init__(self, camera=None, light=None, radius=2.0, thickness=0.5, ringrots=6, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Major radius of torus *thickness* Minor radius, section through one side of torus *ringrots* Sides around minor radius circle *sides* Number of sides for Shape._lathe() to use """ super(Torus,self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating Torus ..." path = [] st = (math.pi * 2)/ringrots for r in range(ringrots + 1): x, y = Utility.from_polar_rad(r * st, thickness) path.append((radius + y, x)) # TODO: why the reversal? self.radius = radius self.thickness = thickness self.ringrots = ringrots self.ttype = GL_TRIANGLES self.buf = [] self.buf.append(self._lathe(path, sides))
def __init__(self, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): super(Disk, self).__init__(name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating disk ..." self.verts = [] self.norms = [] self.inds = [] self.texcoords = [] self.ttype = GL_TRIANGLES self.sides = sides st = math.pi / slices self.add_vertex(x, y, z, 0, 1, 0, 0.5, 0.5) for r in range(sides+1): ca, sa = Utility.from_polar_rad(r * st) self.add_vertex(x + radius * sa, y, z + radius * ca, 0, 1, 0, sa * 0.5 + 0.5, ca * 0.5 + 0.5) # TODO: why the reversal? for r in range(sides): self.add_tri(0, r + 1, r + 2) self.vertices = c_floats(self.verts); self.indices = c_shorts(self.inds); self.normals = c_floats(self.norms); self.tex_coords = c_floats(self.texcoords); self.ssize = sides * 3
def __init__(self, radius=1, slices=12, sides=12, hemi=0.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): super(Sphere,self).__init__(name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating sphere ..." path = [] st = (math.pi * (1.0 - hemi)) / slices for r in range(slices + 1): x, y = Utility.from_polar_rad(r * st, radius) path.append((y, x)) # TODO: why is the reversal here? self.radius = radius self.slices = slices self.sides = sides self.hemi = hemi self.ttype = GL_TRIANGLES results = self.lathe(path) self.vertices = c_floats(results[0]) self.normals = c_floats(results[1]) self.indices = c_shorts(results[2]) self.tex_coords = c_floats(results[3]) self.ssize = results[4]
def __init__(self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(Disk, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating disk ...") self.verts = [] self.norms = [] self.inds = [] self.texcoords = [] self.ttype = GL_TRIANGLES self.sides = sides st = 2 * pi / sides for j in range(-1, 1): self.verts.append((0.0, -0.1 * j, 0.0)) self.norms.append((0.0, -j, 0.0)) self.texcoords.append((0.5, 0.5)) for r in range(sides + 1): ca, sa = Utility.from_polar_rad(r * st) self.verts.append((radius * sa, 0.0, radius * ca)) self.norms.append((0.0, -j - 0.1 * j, 0.0)) self.texcoords.append((sa * 0.5 + 0.5, ca * 0.5 + 0.5)) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): self.inds.append((v0, r + v1, r + v2)) self.but = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def _draw(verts, tex, x, y, w, h, r, z): opengles.glNormalPointer(GL_BYTE, 0, RECT_NORMALS); opengles.glVertexPointer(3, GL_BYTE, 0, verts); Utility.load_identity() Utility.translatef(x, y, z) Utility.scalef(w, h, 1) if r: Utility.rotatef(r, 0, 0, 1) with Texture.Loader(tex,RECT_TEX_COORDS,GL_BYTE): opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, RECT_TRIANGLES)
def __init__( self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, ): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(Disk, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating disk ..." self.verts = [] self.norms = [] self.inds = [] self.texcoords = [] self.ttype = GL_TRIANGLES self.sides = sides st = 2 * pi / sides for j in range(-1, 1): self._add_vertex((0.0, -0.1 * j, 0.0), (0.0, -j, 0.0), (0.5, 0.5)) for r in range(sides + 1): ca, sa = Utility.from_polar_rad(r * st) self._add_vertex( (radius * sa, 0.0, radius * ca), (0.0, -j - 0.1 * j, 0.0), (sa * 0.5 + 0.5, ca * 0.5 + 0.5) ) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): self._add_tri((v0, r + v1, r + v2)) self.but = [] self.buf.append(Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def rotate(self, rx, ry, rz): if rz: Utility.rotatef(rz, 0, 0, 1) if rx: Utility.rotatef(rx, 1, 0, 0) if ry: Utility.rotatef(ry, 0, 1, 0)
def __init__(self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(RoundCorner, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) verts = [] norms = [] inds = [] texcoords = [] self.sides = sides st = (pi / 2) / sides for j in range(-1, 1): verts.append((0.0, -0.1 * j, 0.0)) norms.append((0.0, -j, 0.0)) texcoords.append((0.5, 0.5)) for r in range(0, sides + 1): ca, sa = Utility.from_polar_rad(r * st) verts.append((radius * sa, 0.0, radius * ca)) norms.append((0.0, -j - 0.1 * j, 0.0)) texcoords.append((sa * 0.5 + 0.5, ca * 0.5 + 0.5)) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): inds.append((v0, r + v1, r + v2)) self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def __init__(self, camera=None, light=None, radius=2.0, thickness=0.5, ringrots=6, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Major radius of torus *thickness* Minor radius, section through one side of torus *ringrots* Sides around minor radius circle *sides* Number of sides for Shape._lathe() to use """ super(Torus, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating Torus ..." path = [] st = (math.pi * 2) / ringrots for r in range(ringrots + 1): x, y = Utility.from_polar_rad(r * st, thickness) path.append((radius + y, x)) # TODO: why the reversal? self.radius = radius self.thickness = thickness self.ringrots = ringrots self.ttype = GL_TRIANGLES self.buf = [] self.buf.append(self._lathe(path, sides))
def create2D(self, x=0, y=0, w=0, h=0, depth=24, near=-1.0, far=100.0): if w <= 0 or h <= 0: w = self.max_width h = self.max_height self.win_width = w self.win_height = h self.near = near self.far = far self.left = x self.top = y self.right = x + w self.bottom = y + h self.create_display(x, y, w, h, depth) opengles.glMatrixMode(GL_PROJECTION) Utility.load_identity() opengles.glOrthof(c_float(0), c_float(w), c_float(0), c_float(h), c_float(-1), c_float(500)) opengles.glMatrixMode(GL_MODELVIEW) Utility.load_identity()
def bounce_collision(self, otherball): """work out resultant velocities using 17th.C phsyics""" # relative positions dx = self.unif[0] - otherball.unif[0] dy = self.unif[1] - otherball.unif[1] rd = self.radius + otherball.radius # check sign of a.b to see if converging dotP = Utility.dotproduct(dx, dy, 0, (self.vx - otherball.vx), (self.vy - otherball.vy), 0) if dx * dx + dy * dy <= rd * rd and dotP < 0: R = otherball.mass / self.mass #ratio of masses """Glancing angle for equating angular momentum before and after collision. Three more simultaneous equations for x and y components of momentum and kinetic energy give: """ if dy: D = dx / dy delta2y = 2 * (D * self.vx + self.vy - D * otherball.vx - otherball.vy) / ( (1 + D * D) * (R + 1)) delta2x = D * delta2y delta1y = -1 * R * delta2y delta1x = -1 * R * D * delta2y elif dx: # Same code as above with x and y reversed. D = dy / dx delta2x = 2 * (D * self.vy + self.vx - D * otherball.vy - otherball.vx) / ( (1 + D * D) * (R + 1)) delta2y = D * delta2x delta1x = -1 * R * delta2x delta1y = -1 * R * D * delta2x else: delta1x = delta1y = delta2x = delta2y = 0 self.vx += delta1x self.vy += delta1y otherball.vx += delta2x otherball.vy += delta2y
def __init__(self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(Disk, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating disk ...") verts = [] norms = [] inds = [] texcoords = [] self.sides = sides st = 2 * pi / sides for j in range(-1, 1): verts.append((0.0, -0.1*j, 0.0)) norms.append((0.0, -j, 0.0)) texcoords.append((0.5, 0.5)) for r in range(sides+1): ca, sa = Utility.from_polar_rad(r * st) verts.append((radius * sa, 0.0, radius * ca)) norms.append((0.0, -j - 0.1*j, 0.0)) texcoords.append((sa * 0.5 + 0.5, ca * 0.5 + 0.5)) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): inds.append((v0, r + v1, r + v2)) self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def _lathe(self, path, sides=12, rise=0.0, loops=1.0): """Returns a Buffer object by rotating the points defined in path. Arguments: *path* An array of points [(x0, y0), (x1, y1) ...] to rotate around the y axis. Keyword arguments: *sides* Number of sides to divide each rotation into. *rise* Amount to increment the path y values for each rotation (ie helix) *loops* Number of times to rotate the path by 360 (ie helix). """ self.sides = sides s = len(path) rl = int(self.sides * loops) pn = 0 pp = 0 tcx = 1.0 / self.sides pr = (pi / self.sides) * 2.0 rdiv = rise / rl # Find length of the path path_len = 0.0 for p in range(1, s): path_len += ((path[p][0] - path[p-1][0])**2 + (path[p][1] - path[p-1][1])**2)**0.5 verts = [] norms = [] idx = [] tex_coords = [] opx = path[0][0] opy = path[0][1] tcy = 0.0 for p in range(s): px = path[p][0] * 1.0 py = path[p][1] * 1.0 if p > 0: tcy += ((path[p][0] - path[p-1][0])**2 + (path[p][1] - path[p-1][1])**2)**0.5 / path_len # Normalized 2D vector between path points if p == 0: ipx, ipy = path[1][0] * 1.0, path[1][1] * 1.0 else: ipx, ipy = px, py dx, dy = Utility.vec_normal(Utility.vec_sub((ipx, ipy), (opx, opy))) for r in range (0, rl + 1): sinr = sin(pr * r) cosr = cos(pr * r) verts.append((px * sinr, py, px * cosr)) norms.append((-sinr * dy, dx, -cosr * dy)) tex_coords.append((1.0 - tcx * r, tcy)) py += rdiv if p < s - 1: pn += (rl + 1) for r in range(rl): idx.append((pp + r + 1, pp + r, pn + r)) idx.append((pn + r, pn + r + 1, pp + r + 1)) pp += (rl + 1) opx = px opy = py return Buffer(self, verts, tex_coords, idx, norms)
def identity(self): Utility.load_identity() self.mc = 0
def scale(self, sx, sy, sz): # TODO: get rid of this. Utility.scalef(sx, sy, sz)
def __init__(self, shape, pts, texcoords, faces, normals=None, smooth=True): """Generate a vertex buffer to hold data and indices. If no normals are provided then these are generated. Arguments: *shape* Shape object that this Buffer is a child of *pts* array of vertices tuples i.e. [(x0,y0,z0), (x1,y1,z1),...] *texcoords* array of texture (uv) coordinates tuples i.e. [(u0,v0), (u1,v1),...] *faces* array of indices (of pts array) defining triangles i.e. [(a0,b0,c0), (a1,b1,c1),...] Keyword arguments: *normals* array of vector component tuples defining normals at each vertex i.e. [(x0,y0,z0), (x1,y1,z1),...] *smooth* if calculating normals then average normals for all faces meeting at this vertex, otherwise just use first (for speed). """ super(Buffer, self).__init__() # Uniform variables all in one array! self.unib = (c_float * 9)(0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.0) """ pass to shader array of vec3 uniform variables: ===== ============================ ==== == vec3 description python ----- ---------------------------- ------- index from to ===== ============================ ==== == 0 ntile, shiny, blend 0 2 1 material 3 5 2 umult, vmult (only 2 used) 6 7 ===== ============================ ==== == """ self.shape = shape if not normals: LOGGER.debug("Calculating normals ...") normals = [[] for p in pts] # Calculate normals. for f in faces: a, b, c = f[0:3] ab = Utility.vec_sub(pts[a], pts[b]) bc = Utility.vec_sub(pts[a], pts[c]) n = tuple(Utility.vec_normal(Utility.vec_cross(ab, bc))) for x in f[0:3]: normals[x].append(n) for i, n in enumerate(normals): if n: if smooth: norms = [sum(v[k] for v in n) for k in range(3)] else: # This should be slightly faster for large shapes norms = [n[0][k] for k in range(3)] normals[i] = tuple(Utility.vec_normal(norms)) else: normals[i] = 0, 0, 0.01 # keep a copy for speeding up the collision testing of ElevationMap self.vertices = pts self.normals = normals self.tex_coords = texcoords self.indices = faces self.material = (0.5, 0.5, 0.5, 1.0) # Pack points,normals and texcoords into tuples and convert to ctype floats. points = [p + n + t for p, n, t in zip(pts, normals, texcoords)] self.array_buffer = c_floats(list(itertools.chain(*points))) self.ntris = len(faces) points = [f[0:3] for f in faces] self.element_array_buffer = c_shorts(list(itertools.chain(*points)))
mymodel = Model("models/Triceratops/Triceratops.egg",texs,"Triceratops", 0,-1,0, -90,0,0, .005,.005,.005) # Fetch key presses mykeys = Keyboard() # mastrix and rotate variables rot=0 #create a light mylight = Light(0,1,1,1,"",10,10,0) mylight.on() while 1: display.clear() Utility.load_identity() Utility.translatef(0,0, -40) Utility.rotatef(rot, 0, 1, 0) rot += 3 mymodel.draw() k = mykeys.read() if k >-1: if k==112: display.screenshot("Triceratops.jpg") elif k==27: mykeys.close() texs.deleteAll() display.destroy() break else:
def draw(self,tex, x, y, z): mtrx = (ctypes.c_float*16)() opengles.glGetFloatv(GL_MODELVIEW_MATRIX,ctypes.byref(mtrx)) Utility.translatef(-x, -y, -z) Utility.texture_min_mag(); opengles.glVertexPointer(3, GL_FLOAT, 0, self.vertices) opengles.glNormalPointer(GL_FLOAT, 0, self.normals) opengles.glEnableClientState(GL_TEXTURE_COORD_ARRAY) opengles.glDisable(GL_LIGHTING) opengles.glEnable(GL_TEXTURE_2D) if self.maptype=="FACES": opengles.glTexCoordPointer(2, GL_FLOAT, 0, self.tex_faces) opengles.glBindTexture(GL_TEXTURE_2D,tex[0].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indtop) Utility.texture_min_mag() opengles.glBindTexture(GL_TEXTURE_2D,tex[1].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indleft) Utility.texture_min_mag() opengles.glBindTexture(GL_TEXTURE_2D,tex[2].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indfront) Utility.texture_min_mag() opengles.glBindTexture(GL_TEXTURE_2D,tex[3].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indright) Utility.texture_min_mag() opengles.glBindTexture(GL_TEXTURE_2D,tex[4].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indback) if tex[5] >0: Utility.texture_min_mag() opengles.glBindTexture(GL_TEXTURE_2D,tex[5].tex) opengles.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT , self.indbot) else: #load view matrix opengles.glTexCoordPointer(2, GL_FLOAT, 0, self.tex_coords) opengles.glBindTexture(GL_TEXTURE_2D,tex.tex) opengles.glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT , self.indices) opengles.glEnable(GL_LIGHTING) opengles.glDisable(GL_TEXTURE_2D) #restore to previous matrix opengles.glLoadMatrixf(mtrx)
def clashTest(self, px, py, pz, rad): """Works out if an object at a given location and radius will overlap with the map surface. Returns four values: * boolean whether there is a clash * x, y, z components of the normal vector * the amount of overlap at the x,z location Arguments: *px, py, pz* Location of object to test in world coordinates. *rad* Radius of object to test. """ radSq = rad**2 # adjust for map not set at origin px -= self.unif[0] py -= self.unif[1] pz -= self.unif[2] ht = self.height / 255 halfw = self.width / 2.0 halfd = self.depth / 2.0 dx = self.width / self.ix dz = self.depth / self.iy # work out x and z ranges to check, x0 etc correspond with vertex indices in grid x0 = int(math.floor((halfw + px - rad) / dx + 0.5)) - 1 if x0 < 0: x0 = 0 x1 = int(math.floor((halfw + px + rad) / dx + 0.5)) + 1 if x1 > self.ix - 1: x1 = self.ix - 1 z0 = int(math.floor((halfd + pz - rad) / dz + 0.5)) - 1 if z0 < 0: z0 = 0 z1 = int(math.floor((halfd + pz + rad) / dz + 0.5)) + 1 if z1 > self.iy - 1: z1 = self.iy - 1 # go through grid around px, pz minDist, minLoc = 1000000, (0, 0) for i in xrange(x0 + 1, x1): for j in xrange(z0 + 1, z1): # use the locations stored in the one dimensional vertices matrix #generated in __init__. 3 values for each location p = j * self.ix + i # pointer to the start of xyz for i,j in the vertices array p1 = j * self.ix + i - 1 # pointer to the start of xyz for i-1,j p2 = (j - 1 ) * self.ix + i # pointer to the start of xyz for i, j-1 vertp = self.buf[0].vertices[p] normp = self.buf[0].normals[p] # work out distance squared from this vertex to the point distSq = (px - vertp[0])**2 + (py - vertp[1])**2 + ( pz - vertp[2])**2 if distSq < minDist: # this vertex is nearest so keep a record minDist = distSq minLoc = (i, j) #now find the distance between the point and the plane perpendicular #to the normal at this vertex pDist = Utility.dotproduct((px - vertp[0]), (py - vertp[1]), (pz - vertp[2]), -normp[0], -normp[1], -normp[2]) #and the position where the normal from point crosses the plane xIsect = px - normp[0] * pDist zIsect = pz - normp[2] * pDist #if the intersection point is in this rectangle then the x,z values #will lie between edges if xIsect > self.buf[0].vertices[p1][0] and \ xIsect < self.buf[0].vertices[p][0] and \ zIsect > self.buf[0].vertices[p2][2] and \ zIsect < self.buf[0].vertices[p][2]: pDistSq = pDist**2 # finally if the perpendicular distance is less than the nearest so far #keep a record if pDistSq < minDist: minDist = pDistSq minLoc = (i, j) gLevel = self.calcHeight( px, pz) #check it hasn't tunnelled through by going fast if gLevel > (py - rad): minDist = py - gLevel minLoc = (int((x0 + x1) / 2), int((z0 + z1) / 2)) if minDist <= radSq: #i.e. near enough to clash so return normal p = minLoc[1] * self.ix + minLoc[0] normp = self.buf[0].normals[p] if minDist < 0: jump = rad - minDist else: jump = 0 return (True, normp[0], normp[1], normp[2], jump) else: return (False, 0, 0, 0, 0)
def create(x=None, y=None, w=None, h=None, near=None, far=None, fov=DEFAULT_FOV, depth=DEFAULT_DEPTH, background=None, tk=False, window_title='', window_parent=None, mouse=False, frames_per_second=None): """ Creates a pi3d Display. *x* Left x coordinate of the display. If None, defaults to the x coordinate of the tkwindow parent, if any. *y* Top y coordinate of the display. If None, defaults to the y coordinate of the tkwindow parent, if any. *w* Width of the display. If None, full the width of the screen. *h* Height of the display. If None, full the height of the screen. *near* This will be used for the default instance of Camera *near* plane *far* This will be used for the default instance of Camera *far* plane *fov* Used to define the Camera lens field of view *depth* The bit depth of the display - must be 8, 16 or 24. *background* r,g,b,alpha (opacity) *tk* Do we use the tk windowing system? *window_title* A window title for tk windows only. *window_parent* An optional tk parent window. *mouse* Automatically create a Mouse. *frames_per_second* Maximum frames per second to render (None means "free running"). """ if tk: from pi3d.util import TkWin if not (w and h): # TODO: how do we do full-screen in tk? #LOGGER.error("Can't compute default window size when using tk") #raise Exception # ... just force full screen - TK will automatically fit itself into the screen w = 1920 h = 1180 tkwin = TkWin.TkWin(window_parent, window_title, w, h) tkwin.update() if x is None: x = tkwin.winx if y is None: y = tkwin.winy else: tkwin = None x = x or 0 y = y or 0 display = Display(tkwin) if (w or 0) <= 0: w = display.max_width - 2 * x if w <= 0: w = display.max_width if (h or 0) <= 0: h = display.max_height - 2 * y if h <= 0: h = display.max_height LOGGER.debug('Display size is w=%d, h=%d', w, h) display.frames_per_second = frames_per_second if near is None: near = DEFAULT_NEAR if far is None: far = DEFAULT_FAR display.width = w display.height = h display.near = near display.far = far display.fov = fov display.left = x display.top = y display.right = x + w display.bottom = y + h display.opengl.create_display(x, y, w, h, depth) display.mouse = None if mouse: from pi3d.Mouse import Mouse display.mouse = Mouse(width=w, height=h) display.mouse.start() # This code now replaced by camera 'lens' """opengles.glMatrixMode(GL_PROJECTION) Utility.load_identity() if is_3d: hht = near * math.tan(math.radians(aspect / 2.0)) hwd = hht * w / h opengles.glFrustumf(c_float(-hwd), c_float(hwd), c_float(-hht), c_float(hht), c_float(near), c_float(far)) opengles.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) else: opengles.glOrthof(c_float(0), c_float(w), c_float(0), c_float(h), c_float(near), c_float(far)) """ opengles.glMatrixMode(GL_MODELVIEW) Utility.load_identity() if background: display.set_background(*background) return display
def _lathe(self, path, sides=12, rise=0.0, loops=1.0): """Returns a Buffer object by rotating the points defined in path. Arguments: *path* An array of points [(x0, y0), (x1, y1) ...] to rotate around the y axis. Keyword arguments: *sides* Number of sides to divide each rotation into. *rise* Amount to increment the path y values for each rotation (ie helix) *loops* Number of times to rotate the path by 360 (ie helix). """ self.sides = sides s = len(path) rl = int(self.sides * loops) pn = 0 pp = 0 tcx = 1.0 / self.sides pr = (pi / self.sides) * 2.0 rdiv = rise / rl # Find length of the path path_len = 0.0 for p in range(1, s): path_len += ((path[p][0] - path[p - 1][0])**2 + (path[p][1] - path[p - 1][1])**2)**0.5 verts = [] norms = [] idx = [] tex_coords = [] opx = path[0][0] opy = path[0][1] tcy = 0.0 for p in range(s): px = path[p][0] * 1.0 py = path[p][1] * 1.0 if p > 0: tcy += ((path[p][0] - path[p - 1][0])**2 + (path[p][1] - path[p - 1][1])**2)**0.5 / path_len # Normalized 2D vector between path points if p == 0: ipx, ipy = path[1][0] * 1.0, path[1][1] * 1.0 else: ipx, ipy = px, py dx, dy = Utility.vec_normal(Utility.vec_sub((ipx, ipy), (opx, opy))) for r in range(0, rl + 1): sinr = sin(pr * r) cosr = cos(pr * r) verts.append((px * sinr, py, px * cosr)) norms.append((-sinr * dy, dx, -cosr * dy)) tex_coords.append((1.0 - tcx * r, tcy)) py += rdiv if p < s - 1: pn += (rl + 1) for r in range(rl): idx.append((pp + r + 1, pp + r, pn + r)) idx.append((pn + r, pn + r + 1, pp + r + 1)) pp += (rl + 1) opx = px opy = py return Buffer(self, verts, tex_coords, idx, norms)
def __init__(self, shape, pts, texcoords, faces, normals=None, smooth=True): """Generate a vertex buffer to hold data and indices. If no normals are provided then these are generated. Arguments: *shape* Shape object that this Buffer is a child of *pts* array of vertices tuples i.e. [(x0,y0,z0), (x1,y1,z1),...] *texcoords* array of texture (uv) coordinates tuples i.e. [(u0,v0), (u1,v1),...] *faces* array of indices (of pts array) defining triangles i.e. [(a0,b0,c0), (a1,b1,c1),...] Keyword arguments: *normals* array of vector component tuples defining normals at each vertex i.e. [(x0,y0,z0), (x1,y1,z1),...] *smooth* if calculating normals then average normals for all faces meeting at this vertex, otherwise just use first (for speed). """ super(Buffer, self).__init__() # Uniform variables all in one array! self.unib = (c_float * 12)(0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0) """ pass to shader array of vec3 uniform variables: ===== ============================ ==== == vec3 description python ----- ---------------------------- ------- index from to ===== ============================ ==== == 0 ntile, shiny, blend 0 2 1 material 3 5 2 umult, vmult (only 2 used) 6 7 3 u_off, v_off (only 2 used) 9 10 ===== ============================ ==== == """ self.shape = shape if not normals: LOGGER.debug("Calculating normals ...") normals = [[] for p in pts] # Calculate normals. for f in faces: a, b, c = f[0:3] ab = Utility.vec_sub(pts[a], pts[b]) bc = Utility.vec_sub(pts[a], pts[c]) n = tuple(Utility.vec_normal(Utility.vec_cross(ab, bc))) for x in f[0:3]: normals[x].append(n) for i, n in enumerate(normals): if n: if smooth: norms = [sum(v[k] for v in n) for k in range(3)] else: # This should be slightly faster for large shapes norms = [n[0][k] for k in range(3)] normals[i] = tuple(Utility.vec_normal(norms)) else: normals[i] = 0, 0, 0.01 # keep a copy for speeding up the collision testing of ElevationMap self.vertices = pts self.normals = normals self.tex_coords = texcoords self.indices = faces self.material = (0.5, 0.5, 0.5, 1.0) # Pack points,normals and texcoords into tuples and convert to ctype floats. points = [p + n + t for p, n, t in zip(pts, normals, texcoords)] self.array_buffer = c_floats(list(itertools.chain(*points))) self.ntris = len(faces) points = [f[0:3] for f in faces] self.element_array_buffer = c_shorts(list(itertools.chain(*points)))
def hit(self, otherball): """Used for pre-checking ball positions.""" dx = (self.unif[0] + self.vx) - (otherball.unif[0] + otherball.vx) dy = (self.unif[1] + self.vy) - (otherball.unif[1] + otherball.vy) rd = self.radius + otherball.radius return Utility.sqsum(dx, dy) <= (rd * rd)
def _lathe(self, path, sides=12, rise=0.0, loops=1.0): """Returns a Buffer object by rotating the points defined in path. Arguments: *path* An array of points [(x0, y0), (x1, y1) ...] to rotate around the y axis. Keyword arguments: *sides* Number of sides to divide each rotation into. *rise* Amount to increment the path y values for each rotation (ie helix) *loops* Number of times to rotate the path by 360 (ie helix). """ self.sides = sides s = len(path) rl = int(self.sides * loops) pn = 0 pp = 0 tcx = 1.0 / self.sides pr = (pi / self.sides) * 2.0 rdiv = rise / rl # Find largest and smallest y of the path used for stretching the texture miny = path[0][1] maxy = path[s-1][1] for p in range(s): if path[p][1] < miny: miny = path[p][1] if path[p][1] > maxy: maxy = path[p][1] verts = [] norms = [] idx = [] tex_coords = [] opx = path[0][0] opy = path[0][1] for p in range(s): px = path[p][0] * 1.0 py = path[p][1] * 1.0 tcy = 1.0 - ((py - miny) / (maxy - miny)) # Normalized 2D vector between path points dx, dy = Utility.vec_normal(Utility.vec_sub((px, py), (opx, opy))) for r in range (0, rl): sinr = sin(pr * r) cosr = cos(pr * r) verts.append((px * sinr, py, px * cosr)) norms.append((-sinr * dy, dx, -cosr * dy)) tex_coords.append((1.0 - tcx * r, tcy)) py += rdiv # Last path profile (tidies texture coords). verts.append((0, py, px)) norms.append((0, dx, -dy)) tex_coords.append((0, tcy)) if p < s - 1: pn += (rl + 1) for r in range(rl): idx.append((pp + r + 1, pp + r, pn + r)) idx.append((pn + r, pn + r + 1, pp + r + 1)) pp += (rl + 1) opx = px opy = py return Buffer(self, verts, tex_coords, idx, norms)
def __init__(self, shape, pts, texcoords, faces, normals=None, smooth=True): """Generate a vertex buffer to hold data and indices. If no normals are provided then these are generated. Arguments: *shape* Shape object that this Buffer is a child of *pts* array of vertices tuples i.e. [(x0,y0,z0), (x1,y1,z1),...] *texcoords* array of texture (uv) coordinates tuples i.e. [(u0,v0), (u1,v1),...] *faces* array of indices (of pts array) defining triangles i.e. [(a0,b0,c0), (a1,b1,c1),...] Keyword arguments: *normals* array of vector component tuples defining normals at each vertex i.e. [(x0,y0,z0), (x1,y1,z1),...] *smooth* if calculating normals then average normals for all faces meeting at this vertex, otherwise just use first (for speed). """ super(Buffer, self).__init__() # Uniform variables all in one array! self.unib = (c_float * 12)(0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0) """ pass to shader array of vec3 uniform variables: ===== ============================ ==== == vec3 description python ----- ---------------------------- ------- index from to ===== ============================ ==== == 0 ntile, shiny, blend 0 2 1 material 3 5 2 umult, vmult, point_size 6 8 3 u_off, v_off (only 2 used) 9 10 ===== ============================ ==== == """ #self.shape = shape self.textures = [] pts = np.array(pts) texcoords = np.array(texcoords) faces = np.array(faces) if normals == None: #i.e. normals will only be generated if explictly None LOGGER.debug('Calculating normals ...') normals = np.zeros(pts.shape, dtype=pts.dtype) #empty array rights size fv = pts[faces] #expand faces with x,y,z values for each vertex #cross product of two edges of triangles fn = np.cross(fv[:][:][:,1] - fv[:][:][:,0], fv[:][:][:,2] - fv[:][:][:,0]) fn = Utility.normalize_v3(fn) normals[faces[:,0]] += fn #add up all normal vectors for a vertex normals[faces[:,1]] += fn normals[faces[:,2]] += fn Utility.normalize_v3(normals) else: normals = np.array(normals) # keep a copy for speeding up the collision testing of ElevationMap self.vertices = pts self.normals = normals self.tex_coords = texcoords self.indices = faces self.material = (0.5, 0.5, 0.5, 1.0) # Pack points,normals and texcoords into tuples and convert to ctype floats. n_verts = len(pts) if len(texcoords) != n_verts: if len(normals) != n_verts: self.N_BYTES = 12 # only use pts self.array_buffer = c_floats(pts.reshape(-1).tolist()) else: self.N_BYTES = 24 # use pts and normals self.array_buffer = c_floats(np.concatenate((pts, normals), axis=1).reshape(-1).tolist()) else: self.N_BYTES = 32 # use all three NB doesn't check that normals are there self.array_buffer = c_floats(np.concatenate((pts, normals, texcoords), axis=1).reshape(-1).tolist()) self.ntris = len(faces) self.element_array_buffer = c_shorts(faces.reshape(-1))
def _lathe(self, path, sides=12, rise=0.0, loops=1.0): """Returns a Buffer object by rotating the points defined in path. Arguments: *path* An array of points [(x0, y0), (x1, y1) ...] to rotate around the y axis. Keyword arguments: *sides* Number of sides to divide each rotation into. *rise* Amount to increment the path y values for each rotation (ie helix) *loops* Number of times to rotate the path by 360 (ie helix). """ self.sides = sides s = len(path) rl = int(self.sides * loops) pn = 0 pp = 0 tcx = 1.0 / self.sides pr = (pi / self.sides) * 2.0 rdiv = rise / rl # Find largest and smallest y of the path used for stretching the texture miny = path[0][1] maxy = path[s - 1][1] for p in range(s): if path[p][1] < miny: miny = path[p][1] if path[p][1] > maxy: maxy = path[p][1] verts = [] norms = [] idx = [] tex_coords = [] opx = path[0][0] opy = path[0][1] for p in range(s): px = path[p][0] * 1.0 py = path[p][1] * 1.0 tcy = 1.0 - ((py - miny) / (maxy - miny)) # Normalized 2D vector between path points dx, dy = Utility.vec_normal(Utility.vec_sub((px, py), (opx, opy))) for r in range(0, rl): sinr = sin(pr * r) cosr = cos(pr * r) verts.append((px * sinr, py, px * cosr)) norms.append((-sinr * dy, dx, -cosr * dy)) tex_coords.append((1.0 - tcx * r, tcy)) py += rdiv # Last path profile (tidies texture coords). verts.append((0, py, px)) norms.append((0, dx, -dy)) tex_coords.append((0, tcy)) if p < s - 1: pn += (rl + 1) for r in range(rl): idx.append((pp + r + 1, pp + r, pn + r)) idx.append((pn + r, pn + r + 1, pp + r + 1)) pp += (rl + 1) opx = px opy = py return Buffer(self, verts, tex_coords, idx, norms)
def translate(self,x,y,z): # TODO: get rid of this. Utility.translatef(x, y, z)
def __init__(self, shape, pts, texcoords, faces, normals=None, smooth=True): """Generate a vertex buffer to hold data and indices. If no normals are provided then these are generated. Arguments: *shape* Shape object that this Buffer is a child of *pts* array of vertices tuples i.e. [(x0,y0,z0), (x1,y1,z1),...] *texcoords* array of texture (uv) coordinates tuples i.e. [(u0,v0), (u1,v1),...] *faces* array of indices (of pts array) defining triangles i.e. [(a0,b0,c0), (a1,b1,c1),...] Keyword arguments: *normals* array of vector component tuples defining normals at each vertex i.e. [(x0,y0,z0), (x1,y1,z1),...] *smooth* if calculating normals then average normals for all faces meeting at this vertex, otherwise just use first (for speed). """ super(Buffer, self).__init__() # Uniform variables all in one array! self.unib = (c_float * 12)(0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0) """ pass to shader array of vec3 uniform variables: ===== ============================ ==== == vec3 description python ----- ---------------------------- ------- index from to ===== ============================ ==== == 0 ntile, shiny, blend 0 2 1 material 3 5 2 umult, vmult, point_size 6 8 3 u_off, v_off (only 2 used) 9 10 ===== ============================ ==== == """ #self.shape = shape self.textures = [] pts = np.array(pts, dtype=float) texcoords = np.array(texcoords, dtype=float) faces = np.array(faces, dtype=int) if normals == None: #i.e. normals will only be generated if explictly None LOGGER.debug('Calculating normals ...') normals = np.zeros(pts.shape, dtype=float) #empty array rights size fv = pts[faces] #expand faces with x,y,z values for each vertex #cross product of two edges of triangles fn = np.cross(fv[:, 1] - fv[:, 0], fv[:, 2] - fv[:, 0]) fn = Utility.normalize_v3(fn) normals[faces[:, 0]] += fn #add up all normal vectors for a vertex normals[faces[:, 1]] += fn normals[faces[:, 2]] += fn normals = Utility.normalize_v3(normals) else: normals = np.array(normals) # keep a copy for speeding up the collision testing of ElevationMap self.vertices = pts self.normals = normals self.tex_coords = texcoords self.indices = faces self.material = (0.5, 0.5, 0.5, 1.0) self.__pack_data()
def clashTest(self, px, py, pz, rad): """Works out if an object at a given location and radius will overlap with the map surface. Returns four values: * boolean whether there is a clash * x, y, z components of the normal vector * the amount of overlap at the x,z location Arguments: *px, py, pz* Location of object to test in world coordinates. *rad* Radius of object to test. """ radSq = rad**2 # adjust for map not set at origin px -= self.unif[0] py -= self.unif[1] pz -= self.unif[2] ht = self.height/255 halfw = self.width/2.0 halfd = self.depth/2.0 dx = self.width/self.ix dz = self.depth/self.iy # work out x and z ranges to check, x0 etc correspond with vertex indices in grid x0 = int(math.floor((halfw + px - rad)/dx + 0.5)) - 1 if x0 < 0: x0 = 0 x1 = int(math.floor((halfw + px + rad)/dx + 0.5)) + 1 if x1 > self.ix-1: x1 = self.ix-1 z0 = int(math.floor((halfd + pz - rad)/dz + 0.5)) - 1 if z0 < 0: z0 = 0 z1 = int(math.floor((halfd + pz + rad)/dz + 0.5)) + 1 if z1 > self.iy-1: z1 = self.iy-1 # go through grid around px, pz minDist, minLoc = 1000000, (0, 0) for i in xrange(x0+1, x1): for j in xrange(z0+1, z1): # use the locations stored in the one dimensional vertices matrix #generated in __init__. 3 values for each location p = j*self.ix + i # pointer to the start of xyz for i,j in the vertices array p1 = j*self.ix + i - 1 # pointer to the start of xyz for i-1,j p2 = (j-1)*self.ix + i # pointer to the start of xyz for i, j-1 vertp = self.buf[0].vertices[p] normp = self.buf[0].normals[p] # work out distance squared from this vertex to the point distSq = (px - vertp[0])**2 + (py - vertp[1])**2 + (pz - vertp[2])**2 if distSq < minDist: # this vertex is nearest so keep a record minDist = distSq minLoc = (i, j) #now find the distance between the point and the plane perpendicular #to the normal at this vertex pDist = Utility.dotproduct((px - vertp[0]), (py - vertp[1]), (pz - vertp[2]), -normp[0], -normp[1], -normp[2]) #and the position where the normal from point crosses the plane xIsect = px - normp[0]*pDist zIsect = pz - normp[2]*pDist #if the intersection point is in this rectangle then the x,z values #will lie between edges if xIsect > self.buf[0].vertices[p1][0] and \ xIsect < self.buf[0].vertices[p][0] and \ zIsect > self.buf[0].vertices[p2][2] and \ zIsect < self.buf[0].vertices[p][2]: pDistSq = pDist**2 # finally if the perpendicular distance is less than the nearest so far #keep a record if pDistSq < minDist: minDist = pDistSq minLoc = (i,j) gLevel = self.calcHeight(px, pz) #check it hasn't tunnelled through by going fast if gLevel > (py-rad): minDist = py - gLevel minLoc = (int((x0+x1)/2), int((z0+z1)/2)) if minDist <= radSq: #i.e. near enough to clash so return normal p = minLoc[1]*self.ix + minLoc[0] normp = self.buf[0].normals[p] if minDist < 0: jump = rad - minDist else: jump = 0 return(True, normp[0], normp[1], normp[2], jump) else: return (False, 0, 0, 0, 0)