from __future__ import print_function from renpy.display.matrix cimport Matrix from libc.stdlib cimport malloc, free from libc.string cimport memcpy from libc.math cimport copysign DEF MAX_POINTS = 128 DEF X = 0 DEF Y = 1 DEF Z = 2 DEF W = 3 DEF TX = 4 DEF TY = 5 cdef class Polygon: def __init__(Polygon self, int stride, int points, data): """ Allocates a new Polygon. `stride` The number of floats per vertex. This should be at least 3, for the default aPosition vec3. `points` The number of vertices in the polygon that space is allocated for. If `data` is given, this is also the number of points in the polygon. `data` If not None, an iterable of length stride * points, that gives the vertex data for each of the points. """ cdef int i self.stride = stride self.data = malloc(sizeof(float) * points * stride) if data is None: self.points = 0 else: self.points = points for 0 <= i < stride * points: self.data[i] = data[i] def __dealloc__(Polygon self): free(self.data) cpdef Polygon copy(Polygon self): """ Returns a copy of this polygon. """ cdef Polygon rv = Polygon(self.stride, self.points, None) rv.points = self.points memcpy(rv.data, self.data, sizeof(float) * self.stride * self.points) return rv cpdef void offset_inplace(Polygon self, double x, double y, double z): cdef float *p = self.data cdef int i for 0 <= i < self.points: p[X] += x p[Y] += y p[Z] += z p += self.stride cpdef Polygon offset(Polygon self, double x, double y, double z): cdef Polygon rv = self.copy() rv.offset_inplace(x, y, z) return rv cpdef void perspective_divide_inplace(Polygon self): """ Performs a perspective divide on each point in this polygon, in place. """ cdef float *p = self.data cdef int i for 0 <= i < self.points: if p[W]: p[X] /= p[W] p[Y] /= p[W] p[Z] /= p[W] p += self.stride cpdef Polygon perspective_divide(Polygon self): """ Returns a perspective divided copy of this polygon. """ cdef Polygon rv = self.copy() rv.perspective_divide_inplace() return rv cpdef void multiply_matrix_inplace(Polygon self, Matrix m): """ Multiplies each point's position with the matrix `m`, in place. """ cdef int i cdef float *p = self.data for 0 <= i < self.points: m.transform4(p, p+1, p+2, p+3, p[0], p[1], p[2], p[3]) p += self.stride cpdef Polygon multiply_matrix(Polygon self, Matrix m): """ Multiplies each point's position with the matrix `m`, in place, and returns a copy. """ cdef Polygon rv = self.copy() rv.multiply_matrix_inplace(m) return rv def __mul__(self, other): if isinstance(other, Matrix): return self.multiply_matrix(other) return NotImplemented def __rmul__(self, other): if isinstance(other, Matrix): return self.multiply_matrix(other) return NotImplemented cpdef Polygon intersect(Polygon self, Polygon other): """ Returns a new polygon that is the intersection of the current polygon and the other polygon. """ rv = intersect(other, self, self.stride + other.stride - 4, True) if rv is None: return None # We only need to interpolate OP if it has something other than # position data. if other.stride > 4: barycentric(other, rv, self.stride - 4) barycentric(self, rv, 0) return rv def __repr__(self): rv = " malloc(self.points * self.stride * sizeof(float)) i = 0 for p in self.polygons: memcpy(&self.data[i], p.data, p.points * self.stride * sizeof(float)) i += p.points * self.stride if p.points != points: points = 0 self.polygon_points = points return self.data + offset cpdef Mesh copy(Mesh self): """ Returns a copy of this Mesh. """ rv = Mesh() rv.stride = self.stride rv.points = self.points rv.polygons = [ i.copy() for i in self.polygons ] rv.attributes = self.attributes return rv cpdef void offset_inplace(Mesh self, double x, double y, double z): """ Offsets each polygon in the mesh, in place. """ cdef Polygon p for p in self.polygons: p.offset_inplace(x, y, z) cpdef Mesh offset(self, double x, double y, double z): """ Returns a copy of the mesh with each polygon offset. """ cdef Mesh rv = self.copy() rv.offset_inplace(x, y, z) return rv cpdef void perspective_divide_inplace(Mesh self): """ Performs a perspective divide on this mesh, inplace. """ cdef Polygon p for p in self.polygons: p.perspective_divide_inplace() cpdef Mesh perspective_divide(Mesh self): """ Returns a copy of this mesh that has been perspective divided. """ cdef Mesh rv = self.copy() rv.perspective_divide_inplace() return rv cpdef void multiply_matrix_inplace(Mesh self, Matrix matrix): """ Multiplies the position data in this Mesh by the matrix, in place. """ cdef Polygon p for p in self.polygons: p.multiply_matrix_inplace(matrix) cpdef Mesh multiply_matrix(Mesh self, Matrix matrix): """ Returns a copy of this Mesh with the position data multipled by Matrix. """ cdef Mesh rv = self.copy() rv.multiply_matrix_inplace(matrix) return rv def __mul__(self, other): if isinstance(other, Matrix): return self.multiply_matrix(other) return NotImplemented def __rmul__(self, other): if isinstance(other, Matrix): return self.multiply_matrix(other) return NotImplemented cpdef Mesh intersect(Mesh self, Mesh other): """ Intersects this mesh with another mesh, and returns a new mesh that is the result. The resulting mesh has z- and w-coordinates from this mesh. Attributes that are present in this mesh are taken from this mesh, otherwise the attributes from the other mesh are used. """ rv = Mesh() rv.stride = self.stride + other.stride - 4 rv.attributes = { k : v + self.stride - 4 for k, v in other.attributes.iteritems() } rv.attributes.update(self.attributes) cdef Polygon op cdef Polygon sp cdef Polygon p for op in other.polygons: for sp in self.polygons: p = intersect(op, sp, rv.stride, True) if p is None: continue # We only need to interpolate OP if it has something other than # position data. if op.stride > 4: barycentric(op, p, self.stride - 4) barycentric(sp, p, 0) rv.polygons.append(p) rv.points += p.points return rv cpdef Mesh crop(Mesh self, Polygon op): """ This uses the polygon `op` to crop this mesh. This may or may not return a new Mesh. (If op contains all of self, it will return self.) """ same = True cdef Polygon sp cdef Polygon p cdef list polygons = [ ] cdef int points = 0 for sp in self.polygons: p = intersect(op, sp, self.stride, False) if p is None: same = False continue if p is not sp: same = False barycentric(sp, p, 0) polygons.append(p) points += p.points if same: return self rv = Mesh() rv.stride = self.stride rv.attributes = self.attributes rv.polygons = polygons rv.points = points return rv def __repr__(self): rv = "