From 801307e06992c27cb07c867c3d0d2a3a367f1a7d Mon Sep 17 00:00:00 2001 From: David Vierra Date: Sun, 4 Jan 2015 12:46:40 -1000 Subject: [PATCH] Store cooked models as structs instead of list of tuples - now the important loops are all white (C code) but it leaks memory --- src/mcedit2/rendering/blockmodels.pxd | 20 +++++++ src/mcedit2/rendering/blockmodels.pyx | 49 +++++++++++++-- src/mcedit2/rendering/modelmesh.pyx | 86 +++++++++++++++++---------- 3 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 src/mcedit2/rendering/blockmodels.pxd diff --git a/src/mcedit2/rendering/blockmodels.pxd b/src/mcedit2/rendering/blockmodels.pxd new file mode 100644 index 0000000..8f8f508 --- /dev/null +++ b/src/mcedit2/rendering/blockmodels.pxd @@ -0,0 +1,20 @@ + +cdef struct ModelQuad: + float[24] xyzuvc + char[4] cullface # isCulled, dx, dy, dz + +cdef struct ModelQuadList: + int count + ModelQuad *quads + +cdef class BlockModels: + cdef object resourceLoader + cdef object blocktypes + cdef object modelBlockJsons + cdef object modelStateJsons + cdef object modelQuads + cdef object _textureNames + cdef public object firstTextures + cdef object cookedModels + cdef ModelQuadList cookedModelsByID[4096][16] + cdef object cooked diff --git a/src/mcedit2/rendering/blockmodels.pyx b/src/mcedit2/rendering/blockmodels.pyx index b477f71..4451411 100644 --- a/src/mcedit2/rendering/blockmodels.pyx +++ b/src/mcedit2/rendering/blockmodels.pyx @@ -16,10 +16,21 @@ from array import array from mceditlib import faces from mceditlib.geometry import Vector, FloatBox +from libc.stdlib cimport malloc, free +from libc.string cimport memset + log = logging.getLogger(__name__) +cdef struct ModelQuad: + float[24] xyzuvc + char[4] cullface # isCulled, dx, dy, dz + +cdef struct ModelQuadList: + int count + ModelQuad *quads + +cdef class BlockModels(object): -class BlockModels(object): def _getBlockModel(self, modelName): model = self.modelBlockJsons.get(modelName) if model is None: @@ -53,7 +64,8 @@ class BlockModels(object): self._textureNames = set() self.firstTextures = {} # first texture found for each block - used for icons (xxx) self.cookedModels = {} # nameAndState -> list[(xyzuvc, cullface)] - self.cookedModelsByID = numpy.zeros((256*16, 16), dtype=list) # (id, meta) -> list[(xyzuvc, cullface)] + #self.cookedModelsByID = numpy.zeros((256*16, 16), dtype=list) # (id, meta) -> list[(xyzuvc, cullface)] + memset(self.cookedModelsByID, 0, sizeof(self.cookedModelsByID)) self.cooked = False for i, block in enumerate(blocktypes): @@ -266,17 +278,44 @@ class BlockModels(object): xyzuvc.view('uint8')[:, 20:] = faceShades[face] else: xyzuvc.view('uint8')[:, 20:] = 0xff - xyzuvc.shape = 1, 4, 6 # add the first dimension to stack along in modelmesh.pyx + xyzuvc.shape = 24 # flatten to store in ModelQuad.xyzuvc cookedQuads.append((xyzuvc, cullface)) cookedModels[nameAndState] = cookedQuads ID, meta = self.blocktypes.IDsByState[nameAndState] - self.cookedModelsByID[ID, meta] = cookedQuads - + self.storeQuads(cookedQuads, ID, meta) + self.cookedModels = cookedModels self.cooked = True + def storeQuads(self, list cookedQuads, unsigned short ID, unsigned char meta): + cdef ModelQuadList modelQuads + modelQuads.count = len(cookedQuads) + cdef void * quads = malloc(modelQuads.count * sizeof(ModelQuad)) + modelQuads.quads = quads + cdef float[:] xyzuvc, quadxyzuvc + cdef int i + for i in range(modelQuads.count): + xyzuvc, cullface = cookedQuads[i] + quadxyzuvc = modelQuads.quads[i].xyzuvc + quadxyzuvc[:] = xyzuvc[:] + if cullface: + modelQuads.quads[i].cullface[0] = 1 + dx, dy, dz = cullface.vector + modelQuads.quads[i].cullface[1] = dx + modelQuads.quads[i].cullface[2] = dy + modelQuads.quads[i].cullface[3] = dz + else: + modelQuads.quads[i].cullface[0] = 0 + modelQuads.quads[i].cullface[1] = 0 + modelQuads.quads[i].cullface[2] = 0 + modelQuads.quads[i].cullface[3] = 0 + + + + self.cookedModelsByID[ID][meta] = modelQuads + def rotateVertices(self, rotation, variantXrot, variantYrot, variantZrot, xyzuvc): if rotation is not None: origin = rotation["origin"] diff --git a/src/mcedit2/rendering/modelmesh.pyx b/src/mcedit2/rendering/modelmesh.pyx index d25dc2b..7a02050 100644 --- a/src/mcedit2/rendering/modelmesh.pyx +++ b/src/mcedit2/rendering/modelmesh.pyx @@ -10,6 +10,10 @@ cimport numpy from mcedit2.rendering import renderstates from mcedit2.rendering.vertexarraybuffer import VertexArrayBuffer +from mcedit2.rendering cimport blockmodels + +from libc.stdlib cimport malloc, realloc, free +from libc.string cimport memcpy log = logging.getLogger(__name__) @@ -32,12 +36,9 @@ class BlockModelMesh(object): cdef numpy.ndarray[numpy.uint8_t, ndim=3] data cdef numpy.ndarray[numpy.uint8_t, ndim=1] renderType cdef numpy.ndarray[numpy.uint8_t, ndim=1] opaqueCube + cdef blockmodels.BlockModels blockModels - chunk = self.sectionUpdate.chunkUpdate.chunk cdef short cy = self.sectionUpdate.cy - section = chunk.getSection(cy) - if section is None: - return atlas = self.sectionUpdate.chunkUpdate.updateTask.textureAtlas blockModels = atlas.blockModels @@ -45,28 +46,33 @@ class BlockModelMesh(object): blocktypes = self.sectionUpdate.blocktypes areaBlocks = self.sectionUpdate.areaBlocks - data = section.Data + data = self.sectionUpdate.Data renderType = self.sectionUpdate.renderType opaqueCube = blocktypes.opaqueCube - faceQuadVerts = [] + #faceQuadVerts = [] cdef unsigned short y, z, x, ID, meta cdef short dx, dy, dz, cdef unsigned short nx, ny, nz, nID - cdef numpy.ndarray verts - cdef list quads - cdef tuple quad + cdef blockmodels.ModelQuadList quads + cdef blockmodels.ModelQuad quad - cdef numpy.ndarray[numpy.uint16_t, ndim=1] coords = numpy.zeros(3, dtype=numpy.uint16) - cdef numpy.ndarray[list, ndim=2] cookedModelsByID = blockModels.cookedModelsByID + cdef unsigned short rx, ry, rz + cdef unsigned int buffer_ptr = 0 + cdef unsigned int buffer_size = 256 + cdef float * vertexBuffer = malloc(buffer_size * sizeof(float) * 24) + cdef float * xyzuvc + cdef numpy.ndarray vabuffer + if vertexBuffer == NULL: + return for y in range(1, 17): - coords[1] = y - 1 + (cy << 4) + ry = y - 1 + (cy << 4) for z in range(1, 17): - coords[2] = z - 1 + rz = z - 1 for x in range(1, 17): - coords[0] = x - 1 + rx = x - 1 ID = areaBlocks[y, z, x] if ID == 0: continue @@ -74,27 +80,47 @@ class BlockModelMesh(object): if renderType[ID] != 3: # only model blocks for now continue - quads = cookedModelsByID[ID, meta] - if quads is None: + quads = blockModels.cookedModelsByID[ID][meta] + if quads.count == 0: continue - for xyzuvc, cullface in quads: - if cullface is not None: - dx, dy, dz = cullface.vector - nx = x + dx - ny = y + dy - nz = z + dz + for i in range(quads.count): + quad = quads.quads[i] + if quad.cullface[0]: + nx = x + quad.cullface[1] + ny = y + quad.cullface[2] + nz = z + quad.cullface[3] nID = areaBlocks[ny, nz, nx] if opaqueCube[nID]: continue - verts = numpy.array(xyzuvc) - verts[..., :3] += coords - faceQuadVerts.append(verts) - # log.info("Block %s:\nVertices: %s", (x-1, y-1, z-1), verts) + xyzuvc = vertexBuffer + buffer_ptr * 24 + memcpy(xyzuvc, quad.xyzuvc, sizeof(float) * 24) - # raise SystemExit - if len(faceQuadVerts): - vertexArray = VertexArrayBuffer(len(faceQuadVerts), lights=False) - vertexArray.buffer[..., :6] = numpy.vstack(faceQuadVerts) + #(verts).shape = 1, 4, 6 + #verts[..., :3] += coords + xyzuvc[0] += rx + xyzuvc[1] += ry + xyzuvc[2] += rz + xyzuvc[6] += rx + xyzuvc[7] += ry + xyzuvc[8] += rz + xyzuvc[12] += rx + xyzuvc[13] += ry + xyzuvc[14] += rz + xyzuvc[18] += rx + xyzuvc[19] += ry + xyzuvc[20] += rz + buffer_ptr += 1 + if buffer_ptr >= buffer_size: + buffer_size *= 2 + vertexBuffer = realloc(vertexBuffer, buffer_size * sizeof(float) * 24) + #buffer.resize((buffer_size, 24)) + #faceQuadVerts.append(verts) + + if buffer_ptr: # now buffer size + vertexArray = VertexArrayBuffer(buffer_ptr, lights=False) + vabuffer = vertexArray.buffer + memcpy(vabuffer.data, vertexBuffer, buffer_ptr * sizeof(float) * 24) self.vertexArrays = [vertexArray] + free(vertexBuffer)