"""Copyright (c) 2010-2012 David Rio Vierra
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
"""
renderer.py
What is going on in this file?
Here is an attempt to show the relationships between classes and
their responsibilities
MCRenderer:
has "position", "origin", optionally "viewFrustum"
Loads chunks near position+origin, draws chunks offset by origin
Calls visible on viewFrustum to exclude chunks
(+) ChunkRenderer
Has "chunkPosition", "invalidLayers", "lists"
One per chunk and detail level.
Creates display lists from BlockRenderers
(*) BlockRenderer
Has "vertexArrays"
One per block type, plus one for low detail and one for Entity
BlockRender documentation
Each Block renderer renders a particular block types or entities.
The block renderer that is chosen to draw that block type(by ID)
is the block renderer class that is lowest in the list within the
makeRenderstates method. Each blockRenderer is assigned a materialIndex
and the blockMaterial parameter indicates what material index each block
in the chunk is therefore what block renderer is used to render it.
Vertex arrays are arrays of vertices(groups of six elements) and
every group of 4 vertices is a quad that will be drawn.
Before the vertex arrays will be drawn `.ravel()` will be called
(flattened to one dimension arrays).
The vertex arrays will draw quads and each vertex elements
with the foramt:
0:3 index - xyz values
3:5 index - st(texture coordinates) values
5 index - rgba(colour) value
Note: each element of rgba value is a uint8 type(the 4 colour
elements makes up 32 bits) to view/change the values use
`.view('uint8')` to change the view of the array into uint8 type.
To implement a block renderer either makeVertices or makeFaceVertices
needs to be implemented. The base class BlockRenderer implements
makeVertices in terms of makeFaceVertices, by iterating over the different
face directions.
The makeVertices function is called on the block renderer to gat a
list of vertexArrays that will draw the blocks for a 16x16x16 chunk.
parameters:
all parameters are in xzy order
facingBlockIndices:
list of 6, (16, 16, 16) numpy boolean arrays
each array corresponds to the blocks within the chunk that
has it face exposed in that direction. The direction is the
index into the list defined by the constants in pymclevel/faces.py
This is used to only draw exposed faces
blocks:
(16, 16, 16) numpy array of the id of blocks in the chunk
blockMaterials:
(16, 16, 16) numpy array of the material index of each block in the chunk
each material refers to a different block renderer to get the
material index for this block renderer `self.materialIndex`
blockData:
(16, 16, 16) numpy array of the metadata value of each block
in the chunk
areaBlockLights:
(18, 18, 18) numpy array of light value(max of block light and
skylight) of the chunk and 1 block 'border' aroun it.
texMap:
function that takes id, data value and directions
and returns texture coordinates
returns a list of vertex arrays in the form of float32 numpy arrays.
For this chunk.
The makeFaceVertices gets an vertexArray for a particular face.
parameters:
all parameters are in xzy order
direction:
the face defined by constants in pymclevel/faces.py
materialIndices:
list of (x, z, y) indices of blocks in this chunks that
is of this material(in blocktypes).
exposedFaceIndices:
list of (x, z, y) indices of blocks that has an exposed face
in the direction `direction`.
blocks:
(16, 16, 16) numpy array of the id of blocks in the chunk
blockData:
(16, 16, 16) numpy array of the metadata value of each block
in the chunk
blockLights:
(16, 16, 16) numpy array of light values(max of block light and
skylight) of the blocks in the chunk chunk.
facingBlockLight:
(16, 16, 16) numpy array of light values(max of block light and
skylight) of the blocks just in front of the face.
i.e.
if direction = pymclevel.faces.FaceXDecreasing
facingBlockLight[1, 0, 0] refers to the light level
at position (0, 0, 0) within the chunk.
texMap:
function that takes id, data value and directions
and returns texture coordinates
returns a list of vertex arrays in the form of float32 numpy arrays.
Fields
blocktypes / getBlocktypes(mats)
list of block ids the block renderer handles
detailLevels
what detail level the renderer render at
layer
what layer is this block renderer in
renderstate
the render state this block renderer uses
Models:
There are also several functions that make it easy to translate
json models to block renderer.
makeVertexTemplatesFromJsonModel:
creates a template from information that is in json models
rotateTemplate:
rotate templates. This is equivalent to the rotation in block states files.
makeVerticesFromModel:
creates function based on templates to be used for makeVertices function in block renderer.
Helper functions:
self.MaterialIndices(blockMaterial):
Given blockMaterial(parameter in makeVertices) it return a list of
(x, z, y) indices of blocks in the chunk that are of this block renderer
material(blocktypes).
self.makeTemplate(direction, blockIndices):
get a vertex array filled with default values for face `direction`
and for the block relating to `blockIndices`
makeVertexTemplates(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):
returns a numpy array with dimensions (6, 4, 6) filled with values to create
a vertex array for a cube.
For Entities:
renderer's for entities are similar to blocks but:
- they extend EntityRendererGeneric class
- they are added to the list in calcFacesForChunkRenderer method
- makeChunkVertices(chunk) where chunk is a chunk object
is called rather than makeVertices
there is also a helper method _computeVertices(positions, colors, offset, chunkPosition):
parameters:
positions
locations of entity
colors
colors of entity boxes
offset
whether to offset the box
chunkPosition
chunk position of the chunk
creates a vertex array that draws entity boxes
"""
from collections import defaultdict, deque
from datetime import datetime, timedelta
from depths import DepthOffset
from glutils import gl, Texture
from albow.resource import _2478aq_heot
import logging
import numpy
from OpenGL import GL
import pymclevel
from pymclevel import MCEDIT_DEFS, MCEDIT_IDS
from pymclevel.materials import alphaMaterials
import sys
from config import config
# import time
[docs]def chunkMarkers(chunkSet):
""" Returns a mapping { size: [position, ...] } for different powers of 2
as size.
"""
sizedChunks = defaultdict(list)
size = 1
def all4(cx, cz):
cx &= ~size
cz &= ~size
return [(cx, cz), (cx + size, cz), (cx + size, cz + size), (cx, cz + size)]
# lastsize = 6
size = 1
while True:
nextsize = size << 1
chunkSet = set(chunkSet)
while len(chunkSet):
cx, cz = chunkSet.pop()
chunkSet.add((cx, cz))
o = all4(cx, cz)
others = set(o).intersection(chunkSet)
if len(others) == 4:
sizedChunks[nextsize].append(o[0])
for c in others:
chunkSet.discard(c)
else:
for c in others:
sizedChunks[size].append(c)
chunkSet.discard(c)
if len(sizedChunks[nextsize]):
chunkSet = set(sizedChunks[nextsize])
sizedChunks[nextsize] = []
size <<= 1
else:
break
return sizedChunks
[docs]class ChunkRenderer(object):
maxlod = 2
minlod = 0
def __init__(self, renderer, chunkPosition):
self.renderer = renderer
self.blockRenderers = []
self.detailLevel = 0
self.invalidLayers = set(Layer.AllLayers)
self.chunkPosition = chunkPosition
self.bufferSize = 0
self.renderstateLists = None
@property
def visibleLayers(self):
return self.renderer.visibleLayers
[docs] def forgetDisplayLists(self, states=None):
if self.renderstateLists is not None:
# print "Discarded {0}, gained {1} bytes".format(self.chunkPosition,self.bufferSize)
for k in states or self.renderstateLists.iterkeys():
a = self.renderstateLists.get(k, [])
# print a
for i in a:
gl.glDeleteLists(i, 1)
if states:
del self.renderstateLists[states]
else:
self.renderstateLists = None
self.needsRedisplay = True
self.renderer.discardMasterList()
[docs] def debugDraw(self):
for blockRenderer in self.blockRenderers:
blockRenderer.drawArrays(self.chunkPosition, False)
[docs] def makeDisplayLists(self):
if not self.needsRedisplay:
return
self.forgetDisplayLists()
if not self.blockRenderers:
return
lists = defaultdict(list)
showRedraw = self.renderer.showRedraw
if not (showRedraw and self.needsBlockRedraw):
GL.glEnableClientState(GL.GL_COLOR_ARRAY)
renderers = self.blockRenderers
for blockRenderer in renderers:
if self.detailLevel not in blockRenderer.detailLevels:
continue
if blockRenderer.layer not in self.visibleLayers:
continue
l = blockRenderer.makeArrayList(self.chunkPosition, self.needsBlockRedraw and showRedraw)
lists[blockRenderer.renderstate].append(l)
if not (showRedraw and self.needsBlockRedraw):
GL.glDisableClientState(GL.GL_COLOR_ARRAY)
self.needsRedisplay = False
self.renderstateLists = lists
@property
def needsBlockRedraw(self):
return Layer.Blocks in self.invalidLayers
[docs] def invalidate(self, layers=None):
if layers is None:
layers = Layer.AllLayers
if layers:
layers = set(layers)
self.invalidLayers.update(layers)
blockRenderers = [br for br in self.blockRenderers
if br.layer is Layer.Blocks
or br.layer not in layers]
if len(blockRenderers) < len(self.blockRenderers):
self.forgetDisplayLists()
self.blockRenderers = blockRenderers
if self.renderer.showRedraw and Layer.Blocks in layers:
self.needsRedisplay = True
[docs] def calcFaces(self):
minlod = self.renderer.detailLevelForChunk(self.chunkPosition)
minlod = min(minlod, self.maxlod)
if self.detailLevel != minlod:
self.forgetDisplayLists()
self.detailLevel = minlod
self.invalidLayers.add(Layer.Blocks)
# discard the standard detail renderers
if minlod > 0:
blockRenderers = []
for br in self.blockRenderers:
if br.detailLevels != (0,):
blockRenderers.append(br)
self.blockRenderers = blockRenderers
if self.renderer.chunkCalculator:
for _ in self.renderer.chunkCalculator.calcFacesForChunkRenderer(self):
yield
else:
raise StopIteration
[docs] def vertexArraysDone(self):
bufferSize = 0
for br in self.blockRenderers:
bufferSize += br.bufferSize()
if self.renderer.alpha != 0xff:
br.setAlpha(self.renderer.alpha)
self.bufferSize = bufferSize
self.invalidLayers = set()
self.needsRedisplay = True
self.renderer.invalidateMasterList()
needsRedisplay = False
@property
def done(self):
return len(self.invalidLayers) == 0
_XYZ = numpy.s_[..., 0:3]
_ST = numpy.s_[..., 3:5]
_XYZST = numpy.s_[..., :5]
_RGBA = numpy.s_[..., 20:24]
_RGB = numpy.s_[..., 20:23]
_A = numpy.s_[..., 23]
[docs]def makeVertexTemplatesFromJsonModel(fromVertices, toVertices, uv):
"""
This is similar to makeVertexTemplates but is a more convenient
when reading off of the json model files.
:param fromVertices: from
:param toVertices: to
:param uv: keywords uv map
:return: template for a cube
"""
xmin = fromVertices[0] / 16.
xmax = toVertices[0] / 16.
ymin = fromVertices[1] / 16.
ymax = toVertices[1] / 16.
zmin = fromVertices[2] / 16.
zmax = toVertices[2] / 16.
return numpy.array([
# FaceXIncreasing:
[[xmax, ymin, zmax, uv["east"][0], uv["east"][3], 0x0b],
[xmax, ymin, zmin, uv["east"][2], uv["east"][3], 0x0b],
[xmax, ymax, zmin, uv["east"][2], uv["east"][1], 0x0b],
[xmax, ymax, zmax, uv["east"][0], uv["east"][1], 0x0b],
],
# FaceXDecreasing:
[[xmin, ymin, zmin, uv["west"][0], uv["west"][3], 0x0b],
[xmin, ymin, zmax, uv["west"][2], uv["west"][3], 0x0b],
[xmin, ymax, zmax, uv["west"][2], uv["west"][1], 0x0b],
[xmin, ymax, zmin, uv["west"][0], uv["west"][1], 0x0b]],
# FaceYIncreasing:
[[xmin, ymax, zmin, uv["up"][0], uv["up"][1], 0x11], # ne
[xmin, ymax, zmax, uv["up"][0], uv["up"][3], 0x11], # nw
[xmax, ymax, zmax, uv["up"][2], uv["up"][3], 0x11], # sw
[xmax, ymax, zmin, uv["up"][2], uv["up"][1], 0x11]], # se
# FaceYDecreasing:
[[xmin, ymin, zmin, uv["down"][0], uv["down"][3], 0x08],
[xmax, ymin, zmin, uv["down"][2], uv["down"][3], 0x08],
[xmax, ymin, zmax, uv["down"][2], uv["down"][1], 0x08],
[xmin, ymin, zmax, uv["down"][0], uv["down"][1], 0x08]],
# FaceZIncreasing:
[[xmin, ymin, zmax, uv["south"][0], uv["south"][3], 0x0d],
[xmax, ymin, zmax, uv["south"][2], uv["south"][3], 0x0d],
[xmax, ymax, zmax, uv["south"][2], uv["south"][1], 0x0d],
[xmin, ymax, zmax, uv["south"][0], uv["south"][1], 0x0d]],
# FaceZDecreasing:
[[xmax, ymin, zmin, uv["north"][0], uv["north"][3], 0x0d],
[xmin, ymin, zmin, uv["north"][2], uv["north"][3], 0x0d],
[xmin, ymax, zmin, uv["north"][2], uv["north"][1], 0x0d],
[xmax, ymax, zmin, uv["north"][0], uv["north"][1], 0x0d],
],
])
[docs]def rotateTemplate(template, x=0, y=0):
"""
Rotate template around x-axis and then around
y-axis. Both angles must to multiples of 90.
TODO: Add ability for multiples of 45
"""
template = template.copy()
for _ in range(0, x, 90):
# y -> -z and z -> y
template[..., (1, 2)] = template[..., (2, 1)]
template[..., 2] -= 0.5
template[..., 2] *= -1
template[..., 2] += 0.5
for _ in range(0, y, 90):
# z -> -x and x -> z
template[..., (0, 2)] = template[..., (2, 0)]
template[..., 0] -= 0.5
template[..., 0] *= -1
template[..., 0] += 0.5
return template
[docs]def makeVerticesFromModel(templates, dataMask=0, debug=False, id=""):
"""
Returns a function that creates vertex arrays.
This produces vertex arrays based on the passed
templates. This doesn't cull any faces based on
if they are exposed.
:param templates: list of templates to draw
:param dataMask: mask to mask the data
"""
if type(templates) is list:
templates = numpy.array(templates)
if templates.shape == (6, 4, 6):
templates = numpy.array([templates])
if len(templates.shape) == 4:
templates = templates[numpy.newaxis, ...]
elements = templates.shape[0]
def makeVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
mask = self.getMaterialIndices(blockMaterials)
blockIndices = mask.nonzero()
yield
data = blockData[mask]
data &= dataMask
self.vertexArrays = []
if debug:
print "=== " + id + " ==="
print "Elements: " + str(elements)
print "Data: " + str(data)
print "Block Mask: " + str(blockData[mask])
print "Supplied Mask: " + str(dataMask)
for i in range(elements):
vertexArray = numpy.zeros((len(blockIndices[0]), 6, 4, 6), dtype='float32')
for indicies in range(3):
dimension = (0, 2, 1)[indicies]
vertexArray[..., indicies] = blockIndices[dimension][:, numpy.newaxis,
numpy.newaxis] # xxx swap z with y using ^
vertexArray[..., 0:5] += templates[i, data][..., 0:5]
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices] & 15)[..., numpy.newaxis, :]
vertexArray.view('uint8')[_RGB] = templates[i, data][..., 5][..., numpy.newaxis]
vertexArray.view('uint8')[_A] = 0xFF
vertexArray.view('uint8')[_RGB] *= areaBlockLights[1:-1, 1:-1, 1:-1][blockIndices][
..., numpy.newaxis, numpy.newaxis, numpy.newaxis]
vertexArray.shape = (vertexArray.shape[0] * 6, 4, 6)
yield
self.vertexArrays.append(vertexArray)
return makeVertices
[docs]def makeVertexTemplates(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):
return numpy.array([
# FaceXIncreasing:
[[xmax, ymin, zmax, (zmin * 16), 16 - (ymin * 16), 0x0b],
[xmax, ymin, zmin, (zmax * 16), 16 - (ymin * 16), 0x0b],
[xmax, ymax, zmin, (zmax * 16), 16 - (ymax * 16), 0x0b],
[xmax, ymax, zmax, (zmin * 16), 16 - (ymax * 16), 0x0b],
],
# FaceXDecreasing:
[[xmin, ymin, zmin, (zmin * 16), 16 - (ymin * 16), 0x0b],
[xmin, ymin, zmax, (zmax * 16), 16 - (ymin * 16), 0x0b],
[xmin, ymax, zmax, (zmax * 16), 16 - (ymax * 16), 0x0b],
[xmin, ymax, zmin, (zmin * 16), 16 - (ymax * 16), 0x0b]],
# FaceYIncreasing:
[[xmin, ymax, zmin, xmin * 16, 16 - (zmax * 16), 0x11], # ne
[xmin, ymax, zmax, xmin * 16, 16 - (zmin * 16), 0x11], # nw
[xmax, ymax, zmax, xmax * 16, 16 - (zmin * 16), 0x11], # sw
[xmax, ymax, zmin, xmax * 16, 16 - (zmax * 16), 0x11]], # se
# FaceYDecreasing:
[[xmin, ymin, zmin, xmin * 16, 16 - (zmax * 16), 0x08],
[xmax, ymin, zmin, xmax * 16, 16 - (zmax * 16), 0x08],
[xmax, ymin, zmax, xmax * 16, 16 - (zmin * 16), 0x08],
[xmin, ymin, zmax, xmin * 16, 16 - (zmin * 16), 0x08]],
# FaceZIncreasing:
[[xmin, ymin, zmax, xmin * 16, 16 - (ymin * 16), 0x0d],
[xmax, ymin, zmax, xmax * 16, 16 - (ymin * 16), 0x0d],
[xmax, ymax, zmax, xmax * 16, 16 - (ymax * 16), 0x0d],
[xmin, ymax, zmax, xmin * 16, 16 - (ymax * 16), 0x0d]],
# FaceZDecreasing:
[[xmax, ymin, zmin, xmin * 16, 16 - (ymin * 16), 0x0d],
[xmin, ymin, zmin, xmax * 16, 16 - (ymin * 16), 0x0d],
[xmin, ymax, zmin, xmax * 16, 16 - (ymax * 16), 0x0d],
[xmax, ymax, zmin, xmin * 16, 16 - (ymax * 16), 0x0d],
],
])
elementByteLength = 24
[docs]def createPrecomputedVertices():
height = 16
precomputedVertices = [numpy.zeros(shape=(16, 16, height, 4, 6), # x,y,z,s,t,rg, ba
dtype='float32') for d in faceVertexTemplates]
xArray = numpy.arange(16)[:, numpy.newaxis, numpy.newaxis, numpy.newaxis]
zArray = numpy.arange(16)[numpy.newaxis, :, numpy.newaxis, numpy.newaxis]
yArray = numpy.arange(height)[numpy.newaxis, numpy.newaxis, :, numpy.newaxis]
for dir in range(len(faceVertexTemplates)):
precomputedVertices[dir][_XYZ][..., 0] = xArray
precomputedVertices[dir][_XYZ][..., 1] = yArray
precomputedVertices[dir][_XYZ][..., 2] = zArray
precomputedVertices[dir][_XYZ] += faceVertexTemplates[dir][..., 0:3] # xyz
precomputedVertices[dir][_ST] = faceVertexTemplates[dir][..., 3:5] # s
precomputedVertices[dir].view('uint8')[_RGB] = faceVertexTemplates[dir][..., 5, numpy.newaxis]
precomputedVertices[dir].view('uint8')[_A] = 0xff
return precomputedVertices
faceVertexTemplates = makeVertexTemplates()
[docs]class ChunkCalculator(object):
cachedTemplate = None
cachedTemplateHeight = 0
whiteLight = numpy.array([[[15] * 16] * 16] * 16, numpy.uint8)
precomputedVertices = createPrecomputedVertices()
def __init__(self, level):
self.level = level
self.makeRenderstates(level.materials)
# del xArray, zArray, yArray
self.nullVertices = numpy.zeros((0,) * len(self.precomputedVertices[0].shape),
dtype=self.precomputedVertices[0].dtype)
config.settings.fastLeaves.addObserver(self)
config.settings.roughGraphics.addObserver(self)
[docs] class renderstatePlain(object):
@classmethod
@classmethod
[docs] class renderstateVines(object):
@classmethod
[docs] def bind(cls):
GL.glDisable(GL.GL_CULL_FACE)
GL.glEnable(GL.GL_ALPHA_TEST)
@classmethod
[docs] def release(cls):
GL.glEnable(GL.GL_CULL_FACE)
GL.glDisable(GL.GL_ALPHA_TEST)
[docs] class renderstateLowDetail(object):
@classmethod
[docs] def bind(cls):
GL.glDisable(GL.GL_CULL_FACE)
GL.glDisable(GL.GL_TEXTURE_2D)
@classmethod
[docs] def release(cls):
GL.glEnable(GL.GL_CULL_FACE)
GL.glEnable(GL.GL_TEXTURE_2D)
[docs] class renderstateAlphaTest(object):
@classmethod
[docs] def bind(cls):
GL.glEnable(GL.GL_ALPHA_TEST)
@classmethod
[docs] def release(cls):
GL.glDisable(GL.GL_ALPHA_TEST)
class _renderstateAlphaBlend(object):
@classmethod
def bind(cls):
GL.glEnable(GL.GL_BLEND)
@classmethod
def release(cls):
GL.glDisable(GL.GL_BLEND)
[docs] class renderstateWater(_renderstateAlphaBlend):
pass
[docs] class renderstateIce(_renderstateAlphaBlend):
pass
[docs] class renderstateEntity(object):
@classmethod
[docs] def bind(cls):
GL.glDisable(GL.GL_DEPTH_TEST)
# GL.glDisable(GL.GL_CULL_FACE)
GL.glDisable(GL.GL_TEXTURE_2D)
GL.glEnable(GL.GL_BLEND)
@classmethod
[docs] def release(cls):
GL.glEnable(GL.GL_DEPTH_TEST)
# GL.glEnable(GL.GL_CULL_FACE)
GL.glEnable(GL.GL_TEXTURE_2D)
GL.glDisable(GL.GL_BLEND)
renderstates = (
renderstatePlain,
renderstateVines,
renderstateLowDetail,
renderstateAlphaTest,
renderstateIce,
renderstateWater,
renderstateEntity,
)
[docs] def makeRenderstates(self, materials):
self.blockRendererClasses = [
GenericBlockRenderer,
LeafBlockRenderer,
PlantBlockRenderer,
TorchBlockRenderer,
WaterBlockRenderer,
SlabBlockRenderer,
]
if materials.name in ("Alpha", "Pocket"):
self.blockRendererClasses += [
RailBlockRenderer,
LadderBlockRenderer,
SnowBlockRenderer,
CarpetBlockRenderer,
CactusBlockRenderer,
PaneBlockRenderer,
CakeBlockRenderer,
DaylightBlockRenderer,
StandingSignRenderer,
WallSignBlockRenderer,
LeverBlockRenderer,
BedBlockRenderer,
EnchantingBlockRenderer,
RedstoneBlockRenderer,
IceBlockRenderer,
DoorRenderer,
ButtonRenderer,
TrapDoorRenderer,
FenceBlockRenderer,
FenceGateBlockRenderer,
StairBlockRenderer,
RepeaterBlockRenderer,
VineBlockRenderer,
PlateBlockRenderer,
EndRodRenderer,
# button, floor plate, door -> 1-cube features
# lever, sign, wall sign, stairs -> 2-cube features
# fence
# portal
]
self.materialMap = materialMap = numpy.zeros((pymclevel.materials.id_limit,), 'uint8')
materialMap[1:] = 1 # generic blocks
materialCount = 2
for br in self.blockRendererClasses[1:]: # skip generic blocks
materialMap[br.getBlocktypes(materials)] = materialCount
br.materialIndex = materialCount
materialCount += 1
self.exposedMaterialMap = numpy.array(materialMap)
self.addTransparentMaterials(self.exposedMaterialMap, materialCount)
[docs] def addTransparentMaterials_old(self, mats, materialCount):
transparentMaterials = [
alphaMaterials.Glass,
alphaMaterials.StructureVoid,
alphaMaterials.GlassPane,
alphaMaterials.IronBars,
alphaMaterials.MonsterSpawner,
alphaMaterials.Vines,
alphaMaterials.Fire,
alphaMaterials.Trapdoor,
alphaMaterials.Lever,
alphaMaterials.BrewingStand,
alphaMaterials.Anvil,
alphaMaterials.Barrier,
alphaMaterials.StainedGlass,
alphaMaterials.Hopper,
alphaMaterials.Cauldron,
alphaMaterials.WoodenDoor,
alphaMaterials.IronDoor,
alphaMaterials.AcaciaDoor,
alphaMaterials.JungleDoor,
alphaMaterials.IronTrapdoor,
alphaMaterials.Button,
alphaMaterials.WoodenButton,
alphaMaterials.FenceGate,
alphaMaterials.SpruceFenceGate,
alphaMaterials.BirchFenceGate,
alphaMaterials.JungleFenceGate,
alphaMaterials.DarkOakFenceGate,
alphaMaterials.AcaciaFenceGate,
alphaMaterials.Sign,
alphaMaterials.StructureVoid
]
for b in transparentMaterials:
mats[b.ID] = materialCount
materialCount += 1
[docs] def addTransparentMaterials_new(self, mats, materialCount):
transparentMaterials = []
logging.debug("renderer::ChunkCalculator: Dynamically adding transparent materials.")
for b in self.level.materials:
if hasattr(b, 'yaml'):
if b.yaml.get('opacity', 1) < 1:
logging.debug("Adding '%s'"%b)
transparentMaterials.append(b)
logging.debug("renderer::ChunkCalculator: Transparent materials added: %s"%len(transparentMaterials))
for b in transparentMaterials:
mats[b.ID] = materialCount
materialCount += 1
# if __builtins__.get('mcenf_addTransparentMaterials', False):
# logging.info("Using new ChunkCalculator.addTransparentMaterials")
# addTransparentMaterials = addTransparentMaterials_new
# else:
# addTransparentMaterials = addTransparentMaterials_old
addTransparentMaterials = addTransparentMaterials_new
# don't show boundaries between dirt,grass,sand,gravel,or stone.
# This hiddenOreMaterial definition shall be delayed after the level is loaded, in order to get the exact ones from the game versionned data.
hiddenOreMaterials = numpy.arange(pymclevel.materials.id_limit, dtype='uint16')
stoneid = alphaMaterials.Stone.ID
hiddenOreMaterials[alphaMaterials.Dirt.ID] = stoneid
hiddenOreMaterials[alphaMaterials.Grass.ID] = stoneid
hiddenOreMaterials[alphaMaterials.Sand.ID] = stoneid
hiddenOreMaterials[alphaMaterials.Gravel.ID] = stoneid
hiddenOreMaterials[alphaMaterials.Netherrack.ID] = stoneid
roughMaterials = numpy.ones((pymclevel.materials.id_limit,), dtype='uint8')
roughMaterials[0] = 0
# Do not pre-load transparent materials, since it is game version dependent.
# addTransparentMaterials(None, roughMaterials, 2)
[docs] def calcFacesForChunkRenderer(self, cr):
if 0 == len(cr.invalidLayers):
# layers = set(br.layer for br in cr.blockRenderers)
# assert set() == cr.visibleLayers.difference(layers)
return
lod = cr.detailLevel
cx, cz = cr.chunkPosition
level = cr.renderer.level
try:
chunk = level.getChunk(cx, cz)
except Exception, e:
if "Session lock lost" in e.message:
yield
return
logging.warn(u"Error reading chunk: %s", e)
yield
return
yield
brs = []
classes = [
TileEntityRenderer,
MonsterRenderer,
ItemRenderer,
TileTicksRenderer,
TerrainPopulatedRenderer,
ChunkBorderRenderer,
LowDetailBlockRenderer,
OverheadBlockRenderer,
]
existingBlockRenderers = dict(((type(b), b) for b in cr.blockRenderers))
for blockRendererClass in classes:
if cr.detailLevel not in blockRendererClass.detailLevels:
continue
if blockRendererClass.layer not in cr.visibleLayers:
continue
if blockRendererClass.layer not in cr.invalidLayers:
if blockRendererClass in existingBlockRenderers:
brs.append(existingBlockRenderers[blockRendererClass])
continue
br = blockRendererClass(self)
br.detailLevel = cr.detailLevel
for _ in br.makeChunkVertices(chunk):
yield
brs.append(br)
blockRenderers = []
# Recalculate high detail blocks if needed, otherwise retain the high detail renderers
if lod == 0 and Layer.Blocks in cr.invalidLayers:
for _ in self.calcHighDetailFaces(cr, blockRenderers):
yield
else:
blockRenderers.extend(br for br in cr.blockRenderers if type(br) not in classes)
# Add the layer renderers
blockRenderers.extend(brs)
cr.blockRenderers = blockRenderers
cr.vertexArraysDone()
raise StopIteration
@staticmethod
[docs] def getNeighboringChunks(chunk):
cx, cz = chunk.chunkPosition
level = chunk.world
neighboringChunks = {}
for dir, dx, dz in ((pymclevel.faces.FaceXDecreasing, -1, 0),
(pymclevel.faces.FaceXIncreasing, 1, 0),
(pymclevel.faces.FaceZDecreasing, 0, -1),
(pymclevel.faces.FaceZIncreasing, 0, 1)):
if not level.containsChunk(cx + dx, cz + dz):
neighboringChunks[dir] = pymclevel.infiniteworld.ZeroChunk(level.Height)
else:
# if not level.chunkIsLoaded(cx+dx,cz+dz):
# raise StopIteration
try:
neighboringChunks[dir] = level.getChunk(cx + dx, cz + dz)
except (EnvironmentError, pymclevel.mclevelbase.ChunkNotPresent, pymclevel.mclevelbase.ChunkMalformed):
neighboringChunks[dir] = pymclevel.infiniteworld.ZeroChunk(level.Height)
return neighboringChunks
@staticmethod
[docs] def getAreaBlocks(chunk, neighboringChunks):
chunkWidth, chunkLength, chunkHeight = chunk.Blocks.shape
areaBlocks = numpy.zeros((chunkWidth + 2, chunkLength + 2, chunkHeight + 2), numpy.uint16)
areaBlocks[1:-1, 1:-1, 1:-1] = chunk.Blocks
areaBlocks[:1, 1:-1, 1:-1] = neighboringChunks[pymclevel.faces.FaceXDecreasing].Blocks[-1:, :chunkLength,
:chunkHeight]
areaBlocks[-1:, 1:-1, 1:-1] = neighboringChunks[pymclevel.faces.FaceXIncreasing].Blocks[:1, :chunkLength,
:chunkHeight]
areaBlocks[1:-1, :1, 1:-1] = neighboringChunks[pymclevel.faces.FaceZDecreasing].Blocks[:chunkWidth, -1:,
:chunkHeight]
areaBlocks[1:-1, -1:, 1:-1] = neighboringChunks[pymclevel.faces.FaceZIncreasing].Blocks[:chunkWidth, :1,
:chunkHeight]
return areaBlocks
@staticmethod
[docs] def getFacingBlockIndices(areaBlocks, areaBlockMats):
facingBlockIndices = [None] * 6
exposedFacesX = (areaBlockMats[:-1, 1:-1, 1:-1] != areaBlockMats[1:, 1:-1, 1:-1])
facingBlockIndices[pymclevel.faces.FaceXDecreasing] = exposedFacesX[:-1]
facingBlockIndices[pymclevel.faces.FaceXIncreasing] = exposedFacesX[1:]
exposedFacesZ = (areaBlockMats[1:-1, :-1, 1:-1] != areaBlockMats[1:-1, 1:, 1:-1])
facingBlockIndices[pymclevel.faces.FaceZDecreasing] = exposedFacesZ[:, :-1]
facingBlockIndices[pymclevel.faces.FaceZIncreasing] = exposedFacesZ[:, 1:]
exposedFacesY = (areaBlockMats[1:-1, 1:-1, :-1] != areaBlockMats[1:-1, 1:-1, 1:])
facingBlockIndices[pymclevel.faces.FaceYDecreasing] = exposedFacesY[:, :, :-1]
facingBlockIndices[pymclevel.faces.FaceYIncreasing] = exposedFacesY[:, :, 1:]
return facingBlockIndices
[docs] def getAreaBlockLights(self, chunk, neighboringChunks):
chunkWidth, chunkLength, chunkHeight = chunk.Blocks.shape
lights = chunk.BlockLight
skyLight = chunk.SkyLight
finalLight = self.whiteLight
if lights is not None:
finalLight = lights
if skyLight is not None:
finalLight = numpy.maximum(skyLight, lights)
areaBlockLights = numpy.ones((chunkWidth + 2, chunkLength + 2, chunkHeight + 2), numpy.uint8)
areaBlockLights[:] = 15
areaBlockLights[1:-1, 1:-1, 1:-1] = finalLight
nc = neighboringChunks[pymclevel.faces.FaceXDecreasing]
numpy.maximum(nc.SkyLight[-1:, :chunkLength, :chunkHeight],
nc.BlockLight[-1:, :chunkLength, :chunkHeight],
areaBlockLights[0:1, 1:-1, 1:-1])
nc = neighboringChunks[pymclevel.faces.FaceXIncreasing]
numpy.maximum(nc.SkyLight[:1, :chunkLength, :chunkHeight],
nc.BlockLight[:1, :chunkLength, :chunkHeight],
areaBlockLights[-1:, 1:-1, 1:-1])
nc = neighboringChunks[pymclevel.faces.FaceZDecreasing]
numpy.maximum(nc.SkyLight[:chunkWidth, -1:, :chunkHeight],
nc.BlockLight[:chunkWidth, -1:, :chunkHeight],
areaBlockLights[1:-1, 0:1, 1:-1])
nc = neighboringChunks[pymclevel.faces.FaceZIncreasing]
numpy.maximum(nc.SkyLight[:chunkWidth, :1, :chunkHeight],
nc.BlockLight[:chunkWidth, :1, :chunkHeight],
areaBlockLights[1:-1, -1:, 1:-1])
minimumLight = 4
# areaBlockLights[areaBlockLights<minimumLight]=minimumLight
numpy.clip(areaBlockLights, minimumLight, 16, areaBlockLights)
return areaBlockLights
[docs] def calcHighDetailFaces(self, cr,
blockRenderers): # ForChunk(self, chunkPosition = (0,0), level = None, alpha = 1.0):
""" calculate the geometry for a chunk renderer from its blockMats, data,
and lighting array. fills in the cr's blockRenderers with verts
for each block facing and material"""
# chunkBlocks and chunkLights shall be indexed [x,z,y] to follow infdev's convention
cx, cz = cr.chunkPosition
level = cr.renderer.level
chunk = level.getChunk(cx, cz)
neighboringChunks = self.getNeighboringChunks(chunk)
areaBlocks = self.getAreaBlocks(chunk, neighboringChunks)
yield
areaBlockLights = self.getAreaBlockLights(chunk, neighboringChunks)
yield
slabs = areaBlocks == alphaMaterials.StoneSlab.ID #If someone could combine these, that would be great.
if slabs.any():
areaBlockLights[slabs] = areaBlockLights[:, :, 1:][slabs[:, :, :-1]]
yield
woodSlabs = areaBlocks == alphaMaterials.OakWoodSlab.ID
if woodSlabs.any():
areaBlockLights[woodSlabs] = areaBlockLights[:, :, 1:][woodSlabs[:, :, :-1]]
yield
redSlabs = areaBlocks == alphaMaterials.RedSandstoneSlab.ID
if redSlabs.any():
areaBlockLights[redSlabs] = areaBlockLights[:, :, 1:][redSlabs[:, :, :-1]]
yield
showHiddenOres = cr.renderer.showHiddenOres
if showHiddenOres:
facingMats = self.hiddenOreMaterials[areaBlocks]
else:
facingMats = self.exposedMaterialMap[areaBlocks]
yield
if self.roughGraphics:
areaBlockMats = self.roughMaterials[areaBlocks]
else:
areaBlockMats = self.materialMap[areaBlocks]
facingBlockIndices = self.getFacingBlockIndices(areaBlocks, facingMats)
yield
for _ in self.computeGeometry(chunk, areaBlockMats, facingBlockIndices, areaBlockLights, cr, blockRenderers):
yield
[docs] def computeGeometry(self, chunk, areaBlockMats, facingBlockIndices, areaBlockLights, chunkRenderer, blockRenderers):
blocks, blockData = chunk.Blocks, chunk.Data
blockData &= 0xf
blockMaterials = areaBlockMats[1:-1, 1:-1, 1:-1]
if self.roughGraphics:
blockMaterials.clip(0, 1, blockMaterials)
else:
# Special case for doors
#
# Each part of a door itself does not have all of the information required
# to render, as direction/whether its open is on the lower part and the hinge
# side is on the upper part. So here we combine the metadata of the bottom part
# with the top to form 0-32 metadata(which would be used in door renderer).
#
copied = False
for door in DoorRenderer.blocktypes:
doors = blocks == door
if doors.any():
if not copied:
# copy if required but only once
blockData = blockData.copy()
copied = True
# only accept lower part one block below upper part
valid = doors[:, :, :-1] & doors[:, :, 1:] & (blockData[:, :, :-1] < 8) & (blockData[:, :, 1:] >= 8)
mask = valid.nonzero()
upper_mask = (mask[0], mask[1], mask[2]+1)
blockData[mask] += (blockData[upper_mask] - 8) * 16
blockData[upper_mask] = blockData[mask] + 8
sx = sz = slice(0, 16)
asx = asz = slice(0, 18)
for y in range(0, chunk.world.Height, 16):
sy = slice(y, y + 16)
asy = slice(y, y + 18)
for _ in self.computeCubeGeometry(
y,
blockRenderers,
blocks[sx, sz, sy],
blockData[sx, sz, sy],
chunk.materials,
blockMaterials[sx, sz, sy],
[f[sx, sz, sy] for f in facingBlockIndices],
areaBlockLights[asx, asz, asy],
chunkRenderer):
yield
[docs] def computeCubeGeometry(self, y, blockRenderers, blocks, blockData, materials, blockMaterials, facingBlockIndices,
areaBlockLights, chunkRenderer):
materialCounts = numpy.bincount(blockMaterials.ravel())
def texMap(blocks, blockData=0, direction=slice(None)):
return materials.blockTextures[blocks, blockData, direction] # xxx slow
for blockRendererClass in self.blockRendererClasses:
mi = blockRendererClass.materialIndex
if mi >= len(materialCounts) or materialCounts[mi] == 0:
continue
blockRenderer = blockRendererClass(self)
blockRenderer.y = y
blockRenderer.materials = materials
for _ in blockRenderer.makeVertices(facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights,
texMap):
yield
blockRenderers.append(blockRenderer)
yield
[docs] def makeTemplate(self, direction, blockIndices):
return self.precomputedVertices[direction][blockIndices]
[docs]class Layer:
Blocks = "Blocks"
Entities = "Entities"
Monsters = "Monsters"
Items = "Items"
TileEntities = "TileEntities"
TileTicks = "TileTicks"
TerrainPopulated = "TerrainPopulated"
ChunkBorder = "ChunkBorder"
AllLayers = (Blocks, Entities, Monsters, Items, TileEntities, TileTicks, TerrainPopulated, ChunkBorder)
[docs]class BlockRenderer(object):
# vertexArrays = None
detailLevels = (0,)
layer = Layer.Blocks
directionOffsets = {
pymclevel.faces.FaceXDecreasing: numpy.s_[:-2, 1:-1, 1:-1],
pymclevel.faces.FaceXIncreasing: numpy.s_[2:, 1:-1, 1:-1],
pymclevel.faces.FaceYDecreasing: numpy.s_[1:-1, 1:-1, :-2],
pymclevel.faces.FaceYIncreasing: numpy.s_[1:-1, 1:-1, 2:],
pymclevel.faces.FaceZDecreasing: numpy.s_[1:-1, :-2, 1:-1],
pymclevel.faces.FaceZIncreasing: numpy.s_[1:-1, 2:, 1:-1],
}
renderstate = ChunkCalculator.renderstateAlphaTest
used = False
def __init__(self, cc):
self.makeTemplate = cc.makeTemplate
self.chunkCalculator = cc
self.vertexArrays = []
pass
@classmethod
[docs] def getBlocktypes(cls, mats):
return cls.blocktypes
[docs] def setAlpha(self, alpha):
"alpha is an unsigned byte value"
for a in self.vertexArrays:
a.view('uint8')[_RGBA][..., 3] = alpha
[docs] def bufferSize(self):
return sum(a.size for a in self.vertexArrays) * 4
[docs] def getMaterialIndices(self, blockMaterials):
return blockMaterials == self.materialIndex
[docs] def makeVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
arrays = []
materialIndices = self.getMaterialIndices(blockMaterials)
yield
blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
vertexArray = self.makeFaceVertices(direction, materialIndices, exposedFaceIndices, blocks, blockData,
blockLight, facingBlockLight, texMap)
yield
if len(vertexArray):
arrays.append(vertexArray)
self.vertexArrays = arrays
[docs] def makeArrayList(self, chunkPosition, showRedraw):
l = gl.glGenLists(1)
GL.glNewList(l, GL.GL_COMPILE)
self.drawArrays(chunkPosition, showRedraw)
GL.glEndList()
return l
[docs] def drawArrays(self, chunkPosition, showRedraw):
cx, cz = chunkPosition
y = 0
if hasattr(self, 'y'):
y = self.y
with gl.glPushMatrix(GL.GL_MODELVIEW):
GL.glTranslate(cx << 4, y, cz << 4)
if showRedraw:
GL.glColor(1.0, 0.25, 0.25, 1.0)
self.drawVertices()
[docs] def drawVertices(self):
if self.vertexArrays:
for buf in self.vertexArrays:
self.drawFaceVertices(buf)
[docs] def drawFaceVertices(self, buf):
if 0 == len(buf):
return
stride = elementByteLength
GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
[docs]class EntityRendererGeneric(BlockRenderer):
renderstate = ChunkCalculator.renderstateEntity
detailLevels = (0, 1, 2)
[docs] def drawFaceVertices(self, buf):
if 0 == len(buf):
return
stride = elementByteLength
GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
GL.glDepthMask(False)
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
GL.glLineWidth(2.0)
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
GL.glPolygonOffset(DepthOffset.TerrainWire, DepthOffset.TerrainWire)
with gl.glEnable(GL.GL_POLYGON_OFFSET_FILL, GL.GL_DEPTH_TEST):
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glDepthMask(True)
@staticmethod
def _computeVertices(positions, colors, offset=False, chunkPosition=(0, 0)):
cx, cz = chunkPosition
x = cx << 4
z = cz << 4
vertexArray = numpy.zeros(shape=(len(positions), 6, 4, 6), dtype='float32')
if len(positions):
positions = numpy.array(positions)
positions[:, (0, 2)] -= (x, z)
if offset:
positions -= 0.5
vertexArray.view('uint8')[_RGBA] = colors
vertexArray[_XYZ] = positions[:, numpy.newaxis, numpy.newaxis, :]
vertexArray[_XYZ] += faceVertexTemplates[_XYZ]
vertexArray.shape = (len(positions) * 6, 4, 6)
return vertexArray
[docs]class TileEntityRenderer(EntityRendererGeneric):
layer = Layer.TileEntities
[docs] def makeChunkVertices(self, chunk):
tilePositions = []
for i, ent in enumerate(chunk.TileEntities):
if i % 10 == 0:
yield
if 'x' not in ent:
continue
tilePositions.append(pymclevel.TileEntity.pos(ent))
tiles = self._computeVertices(tilePositions, (0xff, 0xff, 0x33, 0x44), chunkPosition=chunk.chunkPosition)
yield
self.vertexArrays = [tiles]
[docs]class BaseEntityRenderer(EntityRendererGeneric):
pass
[docs]class MonsterRenderer(BaseEntityRenderer):
layer = Layer.Entities # xxx Monsters
notMonsters = {"Item", "XPOrb", "Painting", "ItemFrame", "ArmorStand"}
[docs] def makeChunkVertices(self, chunk):
monsterPositions = []
notMonsters = MCEDIT_DEFS.get('notMonsters', self.notMonsters)
for i, ent in enumerate(chunk.Entities):
if i % 10 == 0:
yield
id = ent["id"].value
if id in notMonsters:
continue
pos = pymclevel.Entity.pos(ent)
pos[1] += 0.5
monsterPositions.append(pos)
monsters = self._computeVertices(monsterPositions,
(0xff, 0x22, 0x22, 0x44),
offset=True,
chunkPosition=chunk.chunkPosition)
yield
self.vertexArrays = [monsters]
[docs]class EntityRenderer(BaseEntityRenderer):
@staticmethod
[docs] def makeChunkVertices(chunk):
yield
# entityPositions = []
# for i, ent in enumerate(chunk.Entities):
# if i % 10 == 0:
# yield
# entityPositions.append(pymclevel.Entity.pos(ent))
#
# entities = self._computeVertices(entityPositions, (0x88, 0x00, 0x00, 0x66), offset=True, chunkPosition=chunk.chunkPosition)
# yield
# self.vertexArrays = [entities]
[docs]class ItemRenderer(BaseEntityRenderer):
layer = Layer.Items
[docs] def makeChunkVertices(self, chunk):
entityPositions = []
entityColors = []
colorMap = {
"Item": (0x22, 0xff, 0x22, 0x5f),
"XPOrb": (0x88, 0xff, 0x88, 0x5f),
"Painting": (134, 96, 67, 0x5f),
"ItemFrame": (134, 96, 67, 0x5f),
"ArmorStand": (0x22, 0xff, 0x22, 0x5f),
}
for i, ent in enumerate(chunk.Entities):
if i % 10 == 0:
yield
# Let get the color from the versionned data, and use the 'old' way as fallback
color = MCEDIT_DEFS.get(MCEDIT_IDS.get(ent["id"].value), {}).get("mapcolor")
if color is None:
color = colorMap.get(ent["id"].value)
if color is None:
continue
pos = pymclevel.Entity.pos(ent)
noRenderDelta = MCEDIT_DEFS.get('noRenderDelta', ("Painting", "ItemFrame"))
if ent["id"].value not in noRenderDelta:
pos[1] += 0.5
entityPositions.append(pos)
entityColors.append(color)
entities = self._computeVertices(entityPositions,
numpy.array(entityColors, dtype='uint8')[:, numpy.newaxis, numpy.newaxis],
offset=True, chunkPosition=chunk.chunkPosition)
yield
self.vertexArrays = [entities]
[docs]class TileTicksRenderer(EntityRendererGeneric):
layer = Layer.TileTicks
[docs] def makeChunkVertices(self, chunk):
if hasattr(chunk, "TileTicks"):
self.vertexArrays.append(self._computeVertices([[tick[j].value for j in "xyz"] for i, tick in enumerate(chunk.TileTicks)],
(0xff, 0xff, 0xff, 0x44),
chunkPosition=chunk.chunkPosition))
yield
[docs]class TerrainPopulatedRenderer(EntityRendererGeneric):
layer = Layer.TerrainPopulated
vertexTemplate = numpy.zeros((6, 4, 6), 'float32')
vertexTemplate[_XYZ] = faceVertexTemplates[_XYZ]
vertexTemplate[_XYZ] *= (16, 256, 16)
color = (255, 200, 155)
vertexTemplate.view('uint8')[_RGBA] = color + (72,)
[docs] def drawFaceVertices(self, buf):
if 0 == len(buf):
return
stride = elementByteLength
GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
GL.glDepthMask(False)
GL.glDisable(GL.GL_CULL_FACE)
with gl.glEnable(GL.GL_DEPTH_TEST):
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glEnable(GL.GL_CULL_FACE)
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
GL.glLineWidth(1.0)
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glLineWidth(2.0)
with gl.glEnable(GL.GL_DEPTH_TEST):
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glLineWidth(1.0)
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
GL.glDepthMask(True)
[docs] def makeChunkVertices(self, chunk):
neighbors = self.chunkCalculator.getNeighboringChunks(chunk)
def getpop(ch):
return getattr(ch, "TerrainPopulated", True)
pop = getpop(chunk)
yield
if pop:
return
visibleFaces = [
getpop(neighbors[pymclevel.faces.FaceXIncreasing]),
getpop(neighbors[pymclevel.faces.FaceXDecreasing]),
True,
True,
getpop(neighbors[pymclevel.faces.FaceZIncreasing]),
getpop(neighbors[pymclevel.faces.FaceZDecreasing]),
]
visibleFaces = numpy.array(visibleFaces, dtype='bool')
verts = self.vertexTemplate[visibleFaces]
self.vertexArrays.append(verts)
yield
[docs]class ChunkBorderRenderer(EntityRendererGeneric):
layer = Layer.ChunkBorder
color = (0, 210, 225)
vertexTemplate = numpy.zeros((6, 4, 6), 'float32')
vertexTemplate[_XYZ] = faceVertexTemplates[_XYZ]
vertexTemplate[_XYZ] *= (16, 256, 16)
vertexTemplate.view('uint8')[_RGBA] = color + (150,)
[docs] def makeChunkVertices(self, chunk):
visibleFaces = [
True,
True,
True,
True,
True,
True,
]
yield
visibleFaces = numpy.array(visibleFaces, dtype='bool')
verts = self.vertexTemplate[visibleFaces]
self.vertexArrays.append(verts)
yield
[docs] def drawFaceVertices(self, buf):
if 0 == len(buf):
return
stride = elementByteLength
GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
GL.glLineWidth(1)
with gl.glEnable(GL.GL_DEPTH_TEST):
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glLineWidth(2.0)
with gl.glEnable(GL.GL_DEPTH_TEST):
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glLineWidth(1.0)
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
[docs]class LowDetailBlockRenderer(BlockRenderer):
renderstate = ChunkCalculator.renderstateLowDetail
detailLevels = (1,)
[docs] def drawFaceVertices(self, buf):
if not len(buf):
return
stride = 16
GL.glVertexPointer(3, GL.GL_FLOAT, stride, numpy.ravel(buf.ravel()))
GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype='uint8').ravel()[12:]))
GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
[docs] def setAlpha(self, alpha):
for va in self.vertexArrays:
va.view('uint8')[..., -1] = alpha
[docs] def makeChunkVertices(self, ch):
step = 1
level = ch.world
vertexArrays = []
blocks = ch.Blocks
heightMap = ch.HeightMap
heightMap = heightMap[::step, ::step]
blocks = blocks[::step, ::step]
if 0 in blocks.shape:
return
chunkWidth, chunkLength, chunkHeight = blocks.shape
blockIndices = numpy.zeros((chunkWidth, chunkLength, chunkHeight), bool)
gridaxes = list(numpy.indices((chunkWidth, chunkLength)))
h = numpy.swapaxes(heightMap - 1, 0, 1)[:chunkWidth, :chunkLength]
numpy.clip(h, 0, chunkHeight - 1, out=h)
gridaxes = [gridaxes[0], gridaxes[1], h]
depths = numpy.zeros((chunkWidth, chunkLength), dtype='uint16')
depths[1:-1, 1:-1] = reduce(numpy.minimum, (h[1:-1, :-2], h[1:-1, 2:], h[:-2, 1:-1]), h[2:, 1:-1])
yield
try:
topBlocks = blocks[gridaxes]
nonAirBlocks = (topBlocks != 0)
blockIndices[gridaxes] = nonAirBlocks
h += 1
numpy.clip(h, 0, chunkHeight - 1, out=h)
overblocks = blocks[gridaxes][nonAirBlocks].ravel()
except ValueError, e:
raise ValueError(str(e.args) + "Chunk shape: {0}".format(blockIndices.shape), sys.exc_info()[-1])
if nonAirBlocks.any():
blockTypes = blocks[blockIndices]
flatcolors = level.materials.flatColors[blockTypes, ch.Data[blockIndices] & 0xf][:, numpy.newaxis, :]
# flatcolors[:,:,:3] *= (0.6 + (h * (0.4 / float(chunkHeight-1)))) [topBlocks != 0][:, numpy.newaxis, numpy.newaxis]
x, z, y = blockIndices.nonzero()
yield
vertexArray = numpy.zeros((len(x), 4, 4), dtype='float32')
vertexArray[_XYZ][..., 0] = x[:, numpy.newaxis]
vertexArray[_XYZ][..., 1] = y[:, numpy.newaxis]
vertexArray[_XYZ][..., 2] = z[:, numpy.newaxis]
va0 = numpy.array(vertexArray)
va0[..., :3] += faceVertexTemplates[pymclevel.faces.FaceYIncreasing, ..., :3]
overmask = overblocks > 0
flatcolors[overmask] = level.materials.flatColors[:, 0][overblocks[overmask]][:, numpy.newaxis]
if self.detailLevel == 2:
heightfactor = (y / float(2.0 * ch.world.Height)) + 0.5
flatcolors[..., :3] = flatcolors[..., :3].astype(float) * heightfactor[:, numpy.newaxis, numpy.newaxis]
_RGBA = numpy.s_[..., 12:16]
va0.view('uint8')[_RGBA] = flatcolors
va0[_XYZ][:, :, 0] *= step
va0[_XYZ][:, :, 2] *= step
yield
if self.detailLevel == 2:
self.vertexArrays = [va0]
return
va1 = numpy.array(vertexArray)
va1[..., :3] += faceVertexTemplates[pymclevel.faces.FaceXIncreasing, ..., :3]
va1[_XYZ][:, (0, 1), 1] = depths[nonAirBlocks].ravel()[:, numpy.newaxis] # stretch to floor
va1[_XYZ][:, (1, 2), 0] -= 1.0 # turn diagonally
va1[_XYZ][:, (2, 3), 1] -= 0.5 # drop down to prevent intersection pixels
va1[_XYZ][:, :, 0] *= step
va1[_XYZ][:, :, 2] *= step
flatcolors = flatcolors.astype(float) * 0.8
va1.view('uint8')[_RGBA] = flatcolors
grassmask = topBlocks[nonAirBlocks] == 2
# color grass sides with dirt's color
va1.view('uint8')[_RGBA][grassmask] = level.materials.flatColors[:, 0][[3]][:, numpy.newaxis]
va2 = numpy.array(va1)
va2[_XYZ][:, (1, 2), 0] += step
va2[_XYZ][:, (0, 3), 0] -= step
vertexArrays = [va1, va2, va0]
self.vertexArrays = vertexArrays
[docs]class OverheadBlockRenderer(LowDetailBlockRenderer):
detailLevels = (2,)
[docs]class GenericBlockRenderer(BlockRenderer):
renderstate = ChunkCalculator.renderstateAlphaTest
materialIndex = 1
[docs] def makeGenericVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
vertexArrays = []
materialIndices = self.getMaterialIndices(blockMaterials)
yield
for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
blockIndices = materialIndices & exposedFaceIndices
theseBlocks = blocks[blockIndices]
bdata = blockData[blockIndices]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(theseBlocks, bdata, direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
if self.materials.name in ("Alpha", "Pocket"):
if direction == pymclevel.faces.FaceYIncreasing:
grass = theseBlocks == alphaMaterials.Grass.ID
vertexArray.view('uint8')[_RGB][grass] = vertexArray.view('uint8')[_RGB][grass].astype(float) * self.grassColor
yield
vertexArrays.append(vertexArray)
self.vertexArrays = vertexArrays
grassColor = grassColorDefault = [0.39, 0.71, 0.23] # 62C743
makeVertices = makeGenericVertices
[docs]class LeafBlockRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["LEAVES"]]
@property
def renderstate(self):
if self.chunkCalculator.fastLeaves:
return ChunkCalculator.renderstatePlain
else:
return ChunkCalculator.renderstateAlphaTest
[docs] def makeLeafVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
arrays = []
materialIndices = self.getMaterialIndices(blockMaterials)
yield
if self.materials.name in ("Alpha", "Pocket"):
if not self.chunkCalculator.fastLeaves:
blockIndices = materialIndices
data = blockData[blockIndices]
data &= 0x3 # ignore decay states
leaves = (data == alphaMaterials.Leaves.blockData)
pines = (data == alphaMaterials.PineLeaves.blockData)
birches = (data == alphaMaterials.BirchLeaves.blockData)
jungle = (data == alphaMaterials.JungleLeaves.blockData)
acacia = (data == alphaMaterials.AcaciaLeaves.blockData)
darkoak = (data == alphaMaterials.DarkOakLeaves.blockData)
texes = texMap(blocks[blockIndices], [0], 0)
else:
blockIndices = materialIndices
texes = texMap(blocks[blockIndices], [0], 0)
for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
if self.materials.name in ("Alpha", "Pocket"):
if self.chunkCalculator.fastLeaves:
blockIndices = materialIndices & exposedFaceIndices
data = blockData[blockIndices]
data &= 0x3 # ignore decay states
leaves = (data == alphaMaterials.Leaves.blockData)
pines = (data == alphaMaterials.PineLeaves.blockData)
birches = (data == alphaMaterials.BirchLeaves.blockData)
jungle = (data == alphaMaterials.JungleLeaves.blockData)
acacia = (data == alphaMaterials.AcaciaLeaves.blockData)
darkoak = (data == alphaMaterials.DarkOakLeaves.blockData)
#leaves |= type3
texes = texMap(blocks[blockIndices], data, 0)
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texes[:, numpy.newaxis]
if not self.chunkCalculator.fastLeaves:
vertexArray[_ST] -= (0x10, 0x0)
vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
if self.materials.name in ("Alpha", "Pocket"):
vertexArray.view('uint8')[_RGB][leaves] = vertexArray.view('uint8')[_RGB][leaves].astype(float) * self.leafColor
vertexArray.view('uint8')[_RGB][pines] = vertexArray.view('uint8')[_RGB][pines].astype(float) * self.pineLeafColor
vertexArray.view('uint8')[_RGB][birches] = vertexArray.view('uint8')[_RGB][birches].astype(float) * self.birchLeafColor
vertexArray.view('uint8')[_RGB][jungle] = vertexArray.view('uint8')[_RGB][jungle].astype(float) * self.jungleLeafColor
vertexArray.view('uint8')[_RGB][acacia] = vertexArray.view('uint8')[_RGB][acacia].astype(float) * self.acaciaLeafColor
vertexArray.view('uint8')[_RGB][darkoak] = vertexArray.view('uint8')[_RGB][darkoak].astype(float) * self.darkoakLeafColor
yield
arrays.append(vertexArray)
self.vertexArrays = arrays
leafColor = leafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.] # 48b518
pineLeafColor = pineLeafColorDefault = [0x61 / 255., 0x99 / 255., 0x61 / 255.] # 0x619961
birchLeafColor = birchLeafColorDefault = [0x80 / 255., 0xa7 / 255., 0x55 / 255.] # 0x80a755
jungleLeafColor = jungleLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.] # 48b518
acaciaLeafColor = acaciaLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.] # 48b518
darkoakLeafColor = darkoakLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.] # 48b518
makeVertices = makeLeafVertices
[docs]class PlantBlockRenderer(BlockRenderer):
@classmethod
[docs] def getBlocktypes(cls, mats):
# blocktypes = [6, 37, 38, 39, 40, 59, 83]
# if mats.name != "Classic": blocktypes += [31, 32] # shrubs, tall grass
# if mats.name == "Alpha": blocktypes += [115] # nether wart
blocktypes = [b.ID for b in mats if b.type in ("DECORATION_CROSS", "NETHER_WART", "CROPS", "STEM")]
return blocktypes
renderstate = ChunkCalculator.renderstateAlphaTest
[docs] def makePlantVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
arrays = []
blockIndices = self.getMaterialIndices(blockMaterials)
yield
theseBlocks = blocks[blockIndices]
bdata = blockData[blockIndices]
texes = texMap(blocks[blockIndices], bdata, 0)
blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
lights = blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
colorize = None
if self.materials.name != "Classic": #so hacky, someone more competent fix this
colorize = (theseBlocks == alphaMaterials.TallGrass.ID) & (bdata != 0)
colorize2 = (theseBlocks == alphaMaterials.TallFlowers.ID) & (bdata != 0) & (
bdata != 1) & (bdata != 4) & (bdata != 5)
for direction in (
pymclevel.faces.FaceXIncreasing, pymclevel.faces.FaceXDecreasing, pymclevel.faces.FaceZIncreasing,
pymclevel.faces.FaceZDecreasing):
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
return
if direction == pymclevel.faces.FaceXIncreasing:
vertexArray[_XYZ][..., 1:3, 0] -= 1
if direction == pymclevel.faces.FaceXDecreasing:
vertexArray[_XYZ][..., 1:3, 0] += 1
if direction == pymclevel.faces.FaceZIncreasing:
vertexArray[_XYZ][..., 1:3, 2] -= 1
if direction == pymclevel.faces.FaceZDecreasing:
vertexArray[_XYZ][..., 1:3, 2] += 1
vertexArray[_ST] += texes[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] = 0xf # ignore precomputed directional light
vertexArray.view('uint8')[_RGB] *= lights
if colorize is not None:
vertexArray.view('uint8')[_RGB][colorize] = vertexArray.view('uint8')[_RGB][colorize].astype(float) * LeafBlockRenderer.leafColor
vertexArray.view('uint8')[_RGB][colorize2] = vertexArray.view('uint8')[_RGB][colorize2].astype(float) * LeafBlockRenderer.leafColor
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makePlantVertices
[docs]class TorchBlockRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["TORCH"]]
renderstate = ChunkCalculator.renderstateAlphaTest
torchOffsetsStraight = [
[ # FaceXIncreasing
(-7 / 16., 0, 0),
(-7 / 16., 0, 0),
(-7 / 16., 0, 0),
(-7 / 16., 0, 0),
],
[ # FaceXDecreasing
(7 / 16., 0, 0),
(7 / 16., 0, 0),
(7 / 16., 0, 0),
(7 / 16., 0, 0),
],
[ # FaceYIncreasing
(7 / 16., -6 / 16., 7 / 16.),
(7 / 16., -6 / 16., -7 / 16.),
(-7 / 16., -6 / 16., -7 / 16.),
(-7 / 16., -6 / 16., 7 / 16.),
],
[ # FaceYDecreasing
(7 / 16., 0., 7 / 16.),
(-7 / 16., 0., 7 / 16.),
(-7 / 16., 0., -7 / 16.),
(7 / 16., 0., -7 / 16.),
],
[ # FaceZIncreasing
(0, 0, -7 / 16.),
(0, 0, -7 / 16.),
(0, 0, -7 / 16.),
(0, 0, -7 / 16.)
],
[ # FaceZDecreasing
(0, 0, 7 / 16.),
(0, 0, 7 / 16.),
(0, 0, 7 / 16.),
(0, 0, 7 / 16.)
],
]
torchOffsetsSouth = [
[ # FaceXIncreasing
(-7 / 16., 3 / 16., 0),
(-7 / 16., 3 / 16., 0),
(-7 / 16., 3 / 16., 0),
(-7 / 16., 3 / 16., 0),
],
[ # FaceXDecreasing
(7 / 16., 3 / 16., 0),
(7 / 16., 3 / 16., 0),
(7 / 16., 3 / 16., 0),
(7 / 16., 3 / 16., 0),
],
[ # FaceYIncreasing
(7 / 16., -3 / 16., 7 / 16.),
(7 / 16., -3 / 16., -7 / 16.),
(-7 / 16., -3 / 16., -7 / 16.),
(-7 / 16., -3 / 16., 7 / 16.),
],
[ # FaceYDecreasing
(7 / 16., 3 / 16., 7 / 16.),
(-7 / 16., 3 / 16., 7 / 16.),
(-7 / 16., 3 / 16., -7 / 16.),
(7 / 16., 3 / 16., -7 / 16.),
],
[ # FaceZIncreasing
(0, 3 / 16., -7 / 16.),
(0, 3 / 16., -7 / 16.),
(0, 3 / 16., -7 / 16.),
(0, 3 / 16., -7 / 16.)
],
[ # FaceZDecreasing
(0, 3 / 16., 7 / 16.),
(0, 3 / 16., 7 / 16.),
(0, 3 / 16., 7 / 16.),
(0, 3 / 16., 7 / 16.),
],
]
torchOffsetsNorth = torchOffsetsWest = torchOffsetsEast = torchOffsetsSouth
torchOffsets = [
torchOffsetsStraight,
torchOffsetsSouth,
torchOffsetsNorth,
torchOffsetsWest,
torchOffsetsEast,
torchOffsetsStraight,
] + [torchOffsetsStraight] * 10
torchOffsets = numpy.array(torchOffsets, dtype='float32')
torchOffsets[1][..., 3, :, 0] -= 0.5
torchOffsets[1][..., 0:2, 0:2, 0] -= 0.5
torchOffsets[1][..., 4:6, 0:2, 0] -= 0.5
torchOffsets[1][..., 0:2, 2:4, 0] -= 0.1
torchOffsets[1][..., 4:6, 2:4, 0] -= 0.1
torchOffsets[1][..., 2, :, 0] -= 0.25
torchOffsets[2][..., 3, :, 0] += 0.5
torchOffsets[2][..., 0:2, 0:2, 0] += 0.5
torchOffsets[2][..., 4:6, 0:2, 0] += 0.5
torchOffsets[2][..., 0:2, 2:4, 0] += 0.1
torchOffsets[2][..., 4:6, 2:4, 0] += 0.1
torchOffsets[2][..., 2, :, 0] += 0.25
torchOffsets[3][..., 3, :, 2] -= 0.5
torchOffsets[3][..., 0:2, 0:2, 2] -= 0.5
torchOffsets[3][..., 4:6, 0:2, 2] -= 0.5
torchOffsets[3][..., 0:2, 2:4, 2] -= 0.1
torchOffsets[3][..., 4:6, 2:4, 2] -= 0.1
torchOffsets[3][..., 2, :, 2] -= 0.25
torchOffsets[4][..., 3, :, 2] += 0.5
torchOffsets[4][..., 0:2, 0:2, 2] += 0.5
torchOffsets[4][..., 4:6, 0:2, 2] += 0.5
torchOffsets[4][..., 0:2, 2:4, 2] += 0.1
torchOffsets[4][..., 4:6, 2:4, 2] += 0.1
torchOffsets[4][..., 2, :, 2] += 0.25
upCoords = ((7, 6), (7, 8), (9, 8), (9, 6))
downCoords = ((7, 14), (7, 16), (9, 16), (9, 14))
[docs] def makeTorchVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
blockIndices = self.getMaterialIndices(blockMaterials)
torchOffsets = self.torchOffsets[blockData[blockIndices]]
texes = texMap(blocks[blockIndices], blockData[blockIndices])
yield
arrays = []
for direction in range(6):
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
return
vertexArray.view('uint8')[_RGBA] = 0xff
vertexArray[_XYZ] += torchOffsets[:, direction]
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_ST] = self.upCoords
if direction == pymclevel.faces.FaceYDecreasing:
vertexArray[_ST] = self.downCoords
vertexArray[_ST] += texes[:, numpy.newaxis, direction]
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeTorchVertices
[docs]class LeverBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:lever"].ID]
leverBaseTemplate = makeVertexTemplatesFromJsonModel((5, 0, 4), (11, 3, 12), {
"down": (10, 0, 16, 8),
"up": (10, 0, 16, 8),
"north": (10, 8, 16, 11),
"south": (10, 8, 16, 11),
"west": (2, 0, 10, 3),
"east": (2, 0, 10, 3)
})
leverBaseTemplates = numpy.array([
rotateTemplate(leverBaseTemplate, x=180, y=90),
rotateTemplate(leverBaseTemplate, x=90, y=90),
rotateTemplate(leverBaseTemplate, x=90, y=270),
rotateTemplate(leverBaseTemplate, x=90, y=180),
rotateTemplate(leverBaseTemplate, x=270, y=180),
leverBaseTemplate,
rotateTemplate(leverBaseTemplate, y=90),
rotateTemplate(leverBaseTemplate, x=180),
rotateTemplate(leverBaseTemplate, x=180, y=90),
rotateTemplate(leverBaseTemplate, x=90, y=90),
rotateTemplate(leverBaseTemplate, x=270, y=90),
rotateTemplate(leverBaseTemplate, x=270),
rotateTemplate(leverBaseTemplate, x=270, y=180),
leverBaseTemplate,
rotateTemplate(leverBaseTemplate, y=90),
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
leverTemplate = makeVertexTemplatesFromJsonModel((7, 1, 7), (9, 11, 9), {
"down": (7, 6, 9, 8),
"up": (7, 6, 9, 8),
"north": (7, 6, 9, 16),
"south": (7, 6, 9, 16),
"west": (7, 6, 9, 16),
"east": (7, 6, 9, 16)
})
leverTemplates = numpy.array([
rotateTemplate(leverTemplate, x=180),
rotateTemplate(leverTemplate, x=90, y=90),
rotateTemplate(leverTemplate, x=90, y=270),
rotateTemplate(leverTemplate, x=90, y=180),
rotateTemplate(leverTemplate, x=270, y=180),
leverTemplate,
rotateTemplate(leverTemplate, y=90),
rotateTemplate(leverTemplate, x=180),
rotateTemplate(leverTemplate, x=180),
rotateTemplate(leverTemplate, x=90, y=90),
rotateTemplate(leverTemplate, x=270, y=90),
rotateTemplate(leverTemplate, x=270),
rotateTemplate(leverTemplate, x=270, y=180),
leverTemplate,
leverTemplate,
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
makeVertices = makeVerticesFromModel([leverBaseTemplates, leverTemplates], 15)
[docs]class RailBlockRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["SIMPLE_RAIL"]]
renderstate = ChunkCalculator.renderstateAlphaTest
railTextures = numpy.array([
[(0, 128), (0, 144), (16, 144), (16, 128)], # east-west
[(0, 128), (16, 128), (16, 144), (0, 144)], # north-south
[(0, 128), (16, 128), (16, 144), (0, 144)], # south-ascending
[(0, 128), (16, 128), (16, 144), (0, 144)], # north-ascending
[(0, 128), (0, 144), (16, 144), (16, 128)], # east-ascending
[(0, 128), (0, 144), (16, 144), (16, 128)], # west-ascending
[(0, 112), (0, 128), (16, 128), (16, 112)], # northeast corner
[(0, 128), (16, 128), (16, 112), (0, 112)], # southeast corner
[(16, 128), (16, 112), (0, 112), (0, 128)], # southwest corner
[(16, 112), (0, 112), (0, 128), (16, 128)], # northwest corner
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
], dtype='float32')
railTextures -= alphaMaterials.blockTextures[alphaMaterials.Rail.ID, 0, 0]
railOffsets = numpy.array([
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 1, 1], # south-ascending
[1, 1, 0, 0], # north-ascending
[1, 0, 0, 1], # east-ascending
[0, 1, 1, 0], # west-ascending
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
], dtype='float32')
[docs] def makeRailVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
direction = pymclevel.faces.FaceYIncreasing
blockIndices = self.getMaterialIndices(blockMaterials)
yield
bdata = blockData[blockIndices]
railBlocks = blocks[blockIndices]
tex = texMap(railBlocks, bdata, pymclevel.faces.FaceYIncreasing)[:, numpy.newaxis, :]
# disable 'powered' or 'pressed' bit for powered and detector rails
bdata[railBlocks != alphaMaterials.Rail.ID] = bdata[railBlocks != alphaMaterials.Rail.ID].astype(int) & ~0x8
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
return
vertexArray[_ST] = self.railTextures[bdata]
vertexArray[_ST] += tex
vertexArray[_XYZ][..., 1] -= 0.9
vertexArray[_XYZ][..., 1] += self.railOffsets[bdata]
blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
vertexArray.view('uint8')[_RGB] *= blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
yield
self.vertexArrays = [vertexArray]
makeVertices = makeRailVertices
[docs]class LadderBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:ladder"].ID]
ladderOffsets = numpy.array([
[(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)],
[(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)],
[(0, -1, 0.9), (0, 0, -0.1), (0, 0, -0.1), (0, -1, 0.9)], # facing east
[(0, 0, 0.1), (0, -1, -.9), (0, -1, -.9), (0, 0, 0.1)], # facing west
[(.9, -1, 0), (.9, -1, 0), (-.1, 0, 0), (-.1, 0, 0)], # north
[(0.1, 0, 0), (0.1, 0, 0), (-.9, -1, 0), (-.9, -1, 0)], # south
] + [[(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]] * 10, dtype='float32')
ladderTextures = numpy.array([
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(0, 192), (0, 208), (16, 208), (16, 192)], # unknown
[(64, 96), (64, 80), (48, 80), (48, 96), ], # e
[(48, 80), (48, 96), (64, 96), (64, 80), ], # w
[(48, 96), (64, 96), (64, 80), (48, 80), ], # n
[(64, 80), (48, 80), (48, 96), (64, 96), ], # s
] + [[(0, 192), (0, 208), (16, 208), (16, 192)]] * 10, dtype='float32')
[docs] def ladderVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
blockIndices = self.getMaterialIndices(blockMaterials)
blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
yield
bdata = blockData[blockIndices]
vertexArray = self.makeTemplate(pymclevel.faces.FaceYIncreasing, blockIndices)
if not len(vertexArray):
return
vertexArray[_ST] = self.ladderTextures[bdata]
vertexArray[_XYZ] += self.ladderOffsets[bdata]
vertexArray.view('uint8')[_RGB] *= blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
yield
self.vertexArrays = [vertexArray]
makeVertices = ladderVertices
[docs]class WallSignBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:wall_sign"].ID]
wallSignTemplate = makeVertexTemplatesFromJsonModel((0, 4.5, 0), (16, 13.5, 2), {
"down": (0, 11, 18, 13),
"up": (0, 6, 16, 8),
"north": (0, 4, 16, 13),
"south": (0, 4, 16, 13),
"west": (0, 4, 2, 13),
"east": (10, 4, 12, 13)
})
# I don't know how this sytem works and how it should be structured, but this seem to do the job
wallSignTemplates = numpy.array([
wallSignTemplate,
wallSignTemplate,
rotateTemplate(wallSignTemplate, y=180),
wallSignTemplate,
rotateTemplate(wallSignTemplate, y=90),
rotateTemplate(wallSignTemplate, y=270),
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
makeVertices = makeVerticesFromModel(wallSignTemplates, 7)
[docs]class StandingSignRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:standing_sign"].ID]
signTemplate = makeVertexTemplatesFromJsonModel((0, 7, 7), (16, 16, 9), {
"down": (0, 14, 16, 16),
"up": (0, 12, 16, 14),
"north": (0, 7, 16, 16),
"south": (0, 7, 16, 16),
"west": (0, 7, 2, 16),
"east": (14, 7, 16, 16)
})
signTemplates = numpy.array([
signTemplate,
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
postTemplate = makeVertexTemplatesFromJsonModel((7, 0, 7), (9, 7, 9), {
"down": (7, 0, 9, 6),
"up": (7, 0, 9, 6),
"north": (7, 0, 9, 6),
"south": (7, 0, 9, 6),
"west": (7, 0, 9, 6),
"east": (7, 0, 9, 6),
})
postTemplates = numpy.array([
postTemplate,
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
makeVertices = makeVerticesFromModel([signTemplates, postTemplates])
[docs]class SnowBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:snow_layer"].ID]
[docs] def makeSnowVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
#snowIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
if direction != pymclevel.faces.FaceYIncreasing:
blockIndices = materialIndices & exposedFaceIndices
else:
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], 0)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.875
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.875
vertexArray[_ST][..., 2:4, 1] += 14
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeSnowVertices
[docs]class CarpetBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:carpet"].ID, #Separate before implementing layers
alphaMaterials["minecraft:waterlily"].ID]
[docs] def makeCarpetVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
#snowIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
if direction != pymclevel.faces.FaceYIncreasing:
blockIndices = materialIndices & exposedFaceIndices
else:
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], 0)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.937
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.937
vertexArray[_ST][..., 2:4, 1] += 15
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeCarpetVertices
[docs]class CactusBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:cactus"].ID]
[docs] def makeCactusVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceXIncreasing:
vertexArray[_XYZ][..., 0] -= 0.063
if direction == pymclevel.faces.FaceXDecreasing:
vertexArray[_XYZ][..., 0] += 0.063
if direction == pymclevel.faces.FaceZIncreasing:
vertexArray[_XYZ][..., 2] -= 0.063
if direction == pymclevel.faces.FaceZDecreasing:
vertexArray[_XYZ][..., 2] += 0.063
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeCactusVertices
[docs]class PaneBlockRenderer(BlockRenderer): #Basic no thickness panes, add more faces to widen.
blocktypes = [block.ID for block in alphaMaterials.blocksByType["SOLID_PANE"]]
[docs] def makePaneVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceXIncreasing:
vertexArray[_XYZ][..., 0] -= 0.5
if direction == pymclevel.faces.FaceXDecreasing:
vertexArray[_XYZ][..., 0] += 0.5
if direction == pymclevel.faces.FaceZIncreasing:
vertexArray[_XYZ][..., 2] -= 0.5
if direction == pymclevel.faces.FaceZDecreasing:
vertexArray[_XYZ][..., 2] += 0.5
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makePaneVertices
[docs]class PlateBlockRenderer(BlockRenderer): #suggestions to make this the proper shape is appreciated.
blocktypes = [block.ID for block in alphaMaterials.blocksByType["PRESSURE_PLATE"]]
[docs] def makePlateVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], 0)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.937
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.937
vertexArray[_ST][..., 2:4, 1] += 15
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makePlateVertices
[docs]class EnchantingBlockRenderer(
BlockRenderer): #Note: Enderportal frame side sprite has been lowered 1 pixel to use this renderer, will need separate renderer for eye.
blocktypes = [alphaMaterials["minecraft:enchanting_table"].ID,
alphaMaterials["minecraft:end_portal_frame"].ID]
[docs] def makeEnchantingVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
if direction != pymclevel.faces.FaceYIncreasing:
blockIndices = materialIndices & exposedFaceIndices
else:
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.25
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeEnchantingVertices
[docs]class DaylightBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:daylight_detector"].ID,
alphaMaterials.DaylightSensorOn.ID]
[docs] def makeDaylightVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
if direction != pymclevel.faces.FaceYIncreasing:
blockIndices = materialIndices & exposedFaceIndices
else:
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.625
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.625
vertexArray[_ST][..., 2:4, 1] += 10
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeDaylightVertices
[docs]class BedBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:bed"].ID]
[docs] def makeBedVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
if direction != pymclevel.faces.FaceYIncreasing:
blockIndices = materialIndices & exposedFaceIndices
else:
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.438
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeBedVertices
[docs]class CakeBlockRenderer(BlockRenderer): #Only shows whole cakes
blocktypes = [alphaMaterials["minecraft:cake"].ID]
[docs] def makeCakeVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.5
if direction == pymclevel.faces.FaceXIncreasing:
vertexArray[_XYZ][..., 0] -= 0.063
if direction == pymclevel.faces.FaceXDecreasing:
vertexArray[_XYZ][..., 0] += 0.063
if direction == pymclevel.faces.FaceZIncreasing:
vertexArray[_XYZ][..., 2] -= 0.063
if direction == pymclevel.faces.FaceZDecreasing:
vertexArray[_XYZ][..., 2] += 0.063
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeCakeVertices
[docs]class RepeaterBlockRenderer(BlockRenderer): #Sticks would be nice
blocktypes = [block.ID for block in alphaMaterials.blocksByType["THINSLICE"]]
[docs] def makeRepeaterVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
materialIndices = self.getMaterialIndices(blockMaterials)
arrays = []
yield
for direction, exposedFaceIndices in enumerate(facingBlockIndices):
blockIndices = materialIndices
facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
continue
vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.875
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.875
vertexArray[_ST][..., 2:4, 1] += 14
arrays.append(vertexArray)
yield
self.vertexArrays = arrays
makeVertices = makeRepeaterVertices
[docs]class RedstoneBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:redstone_wire"].ID]
[docs] def redstoneVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
blockIndices = self.getMaterialIndices(blockMaterials)
yield
vertexArray = self.makeTemplate(pymclevel.faces.FaceYIncreasing, blockIndices)
if not len(vertexArray):
return
vertexArray[_ST] += alphaMaterials.blockTextures[55, 0, 0]
vertexArray[_XYZ][..., 1] -= 0.9
bdata = blockData[blockIndices]
bdata <<= 3
# bdata &= 0xe0
bdata[bdata > 0] |= 0x80
vertexArray.view('uint8')[_RGBA][..., 0] = bdata[..., numpy.newaxis]
vertexArray.view('uint8')[_RGBA][..., 0:3] = vertexArray.view('uint8')[_RGBA][..., 0:3] * [1, 0, 0]
yield
self.vertexArrays = [vertexArray]
makeVertices = redstoneVertices
# button, floor plate, door -> 1-cube features
[docs]class DoorRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:wooden_door"].ID,
alphaMaterials["minecraft:iron_door"].ID,
alphaMaterials["minecraft:spruce_door"].ID,
alphaMaterials["minecraft:birch_door"].ID,
alphaMaterials["minecraft:jungle_door"].ID,
alphaMaterials["minecraft:acacia_door"].ID,
alphaMaterials["minecraft:dark_oak_door"].ID]
doorTemplate = makeVertexTemplatesFromJsonModel(
(0, 0, 0), (3, 16, 16),
{
"down": (13, 0, 16, 16),
# TODO handle faces that should not appear
"up": (13, 0, 16, 16),
"north": (3, 0, 0, 16),
"south": (0, 0, 3, 16),
"west": (0, 0, 16, 16),
"east": (16, 0, 0, 16)
}
)
doorRHTemplate = makeVertexTemplatesFromJsonModel(
(0, 0, 0), (3, 16, 16),
{
"down": (13, 0, 16, 16),
# TODO handle faces that should not appear
"up": (13, 0, 16, 16),
"north": (3, 0, 0, 16),
"south": (0, 0, 3, 16),
"west": (16, 0, 0, 16),
"east": (0, 0, 16, 16)
}
)
doorTemplates = numpy.array([
# lower hinge left
doorTemplate,
rotateTemplate(doorTemplate, y=90),
rotateTemplate(doorTemplate, y=180),
rotateTemplate(doorTemplate, y=270),
rotateTemplate(doorRHTemplate, y=90),
rotateTemplate(doorRHTemplate, y=180),
rotateTemplate(doorRHTemplate, y=270),
doorRHTemplate,
# upper hinge left
doorTemplate,
rotateTemplate(doorTemplate, y=90),
rotateTemplate(doorTemplate, y=180),
rotateTemplate(doorTemplate, y=270),
rotateTemplate(doorRHTemplate, y=90),
rotateTemplate(doorRHTemplate, y=180),
rotateTemplate(doorRHTemplate, y=270),
doorRHTemplate,
# lower hinge right
doorRHTemplate,
rotateTemplate(doorRHTemplate, y=90),
rotateTemplate(doorRHTemplate, y=180),
rotateTemplate(doorRHTemplate, y=270),
rotateTemplate(doorTemplate, y=270),
doorTemplate,
rotateTemplate(doorTemplate, y=90),
rotateTemplate(doorTemplate, y=180),
# upper hinge right
doorRHTemplate,
rotateTemplate(doorRHTemplate, y=90),
rotateTemplate(doorRHTemplate, y=180),
rotateTemplate(doorRHTemplate, y=270),
rotateTemplate(doorTemplate, y=270),
doorTemplate,
rotateTemplate(doorTemplate, y=90),
rotateTemplate(doorTemplate, y=180),
])
makeVertices = makeVerticesFromModel(doorTemplates, 31)
[docs]class ButtonRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["BUTTON"]]
buttonTemplate = makeVertexTemplatesFromJsonModel((5, 0, 6), (11, 2, 10), {
"down": (5, 6, 11, 10),
"up": (5, 10, 11, 6),
"north": (5, 14, 11, 16),
"south": (5, 14, 11, 16),
"west": (6, 14, 10, 16),
"east": (6, 14, 10, 16)
})
buttonTemplatePressed = makeVertexTemplatesFromJsonModel((5, 0, 6), (11, 1, 10), {
"down": (5, 6, 11, 10),
"up": (5, 10, 11, 6),
"north": (5, 15, 11, 16),
"south": (5, 15, 11, 16),
"west": (6, 15, 10, 16),
"east": (6, 15, 10, 16)
})
buttonTemplates = numpy.array([
rotateTemplate(buttonTemplate, 180, 0),
rotateTemplate(buttonTemplate, 90, 90),
rotateTemplate(buttonTemplate, 90, 270),
rotateTemplate(buttonTemplate, 90, 180),
rotateTemplate(buttonTemplate, 90, 0),
buttonTemplate,
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6)),
rotateTemplate(buttonTemplatePressed, 180, 0),
rotateTemplate(buttonTemplatePressed, 90, 90),
rotateTemplate(buttonTemplatePressed, 90, 270),
rotateTemplate(buttonTemplatePressed, 90, 180),
rotateTemplate(buttonTemplatePressed, 90, 0),
buttonTemplatePressed,
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6)),
])
makeVertices = makeVerticesFromModel(buttonTemplates, 15)
[docs]class TrapDoorRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["TRAPDOOR"]]
openTemplate = makeVertexTemplatesFromJsonModel((0, 0, 13), (16, 16, 16), {
"down": (0, 13, 16, 16),
"up": (0, 16, 16, 13),
"north": (0, 0, 16, 16),
"south": (0, 0, 16, 16),
"west": (16, 0, 13, 16),
"east": (13, 0, 16, 16)
})
topTemplate = makeVertexTemplatesFromJsonModel((0, 13, 0), (16, 16, 16), {
"down": (0, 0, 16, 16),
"up": (0, 0, 16, 16),
"north": (0, 16, 16, 13),
"south": (0, 16, 16, 13),
"west": (0, 16, 16, 13),
"east": (0, 16, 16, 13)
})
bottomTemplate = makeVertexTemplatesFromJsonModel((0, 0, 0), (16, 3, 16), {
"down": (0, 0, 16, 16),
"up": (0, 0, 16, 16),
"north": (0, 16, 16, 13),
"south": (0, 16, 16, 13),
"west": (0, 16, 16, 13),
"east": (0, 16, 16, 13)
})
trapDoorTemplates = numpy.array([
bottomTemplate,
bottomTemplate,
bottomTemplate,
bottomTemplate,
openTemplate,
rotateTemplate(openTemplate, y=180),
rotateTemplate(openTemplate, y=270),
rotateTemplate(openTemplate, y=90),
topTemplate,
topTemplate,
topTemplate,
topTemplate,
openTemplate,
rotateTemplate(openTemplate, y=180),
rotateTemplate(openTemplate, y=270),
rotateTemplate(openTemplate, y=90),
])
makeVertices = makeVerticesFromModel(trapDoorTemplates, 15)
[docs]class FenceBlockRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["FENCE"]]
fenceTemplates = makeVertexTemplates(3 / 8., 0, 3 / 8., 5 / 8., 1, 5 / 8.)
makeVertices = makeVerticesFromModel(fenceTemplates)
[docs]class FenceGateBlockRenderer(BlockRenderer):
blocktypes = [block.ID for block in alphaMaterials.blocksByType["FENCE_GATE"]]
closedFenceTemplates = numpy.array([
makeVertexTemplates(0, 0, 3 / 8., 1, .8, 5 / 8.),
makeVertexTemplates(3 / 8., 0, 0, 5 / 8., .8, 1)])
openFenceTemplates = numpy.array([
[makeVertexTemplates(0, 0, 3 / 8., 1 / 8., .8, 1),
makeVertexTemplates(7 / 8., 0, 3 / 8., 1, .8, 1)],
[makeVertexTemplates(0, 0, 0, 5 / 8., .8, 1 / 8.),
makeVertexTemplates(0, 0, 7 / 8., 5 / 8., .8, 1)],
[makeVertexTemplates(0, 0, 0, 1 / 8., .8, 5 / 8.),
makeVertexTemplates(7 / 8., 0, 0, 1, .8, 5 / 8.)],
[makeVertexTemplates(3 / 8., 0, 0, 1, .8, 1 / 8.),
makeVertexTemplates(3 / 8., 0, 7 / 8., 1, .8, 1)]])
[docs] def fenceGateVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
fenceMask = self.getMaterialIndices(blockMaterials)
closedGateMask = fenceMask.copy()
closedGateMask[blockData & 4 == 4] = 0
openGateMask = fenceMask.copy()
openGateMask[blockData & 4 == 0] = 0
closedGateIndices = closedGateMask.nonzero()
openGateIndices = openGateMask.nonzero()
closedGateData = blockData[closedGateMask]
closedGateData &= 1
openGateData = blockData[openGateMask]
openGateData &= 3
yield
# closed gate
vertexArray = numpy.zeros((len(closedGateIndices[0]), 6, 4, 6), dtype='float32')
for indicies in range(3):
dimension = (0, 2, 1)[indicies]
vertexArray[..., indicies] = closedGateIndices[dimension][:, numpy.newaxis,
numpy.newaxis] # xxx swap z with y using ^
vertexArray[..., 0:5] += self.closedFenceTemplates[closedGateData][..., 0:5]
vertexArray[_ST] += texMap(blocks[closedGateIndices], 0)[..., numpy.newaxis, :]
vertexArray.view('uint8')[_RGB] = self.closedFenceTemplates[closedGateData][..., 5][..., numpy.newaxis]
vertexArray.view('uint8')[_A] = 0xFF
vertexArray.view('uint8')[_RGB] *= areaBlockLights[1:-1, 1:-1, 1:-1][closedGateIndices][
..., numpy.newaxis, numpy.newaxis, numpy.newaxis]
vertexArray.shape = (vertexArray.shape[0] * 6, 4, 6)
yield
self.vertexArrays = [vertexArray]
# open gate
for i in range(2):
vertexArray = numpy.zeros((len(openGateIndices[0]), 6, 4, 6), dtype='float32')
for indicies in range(3):
dimension = (0, 2, 1)[indicies]
vertexArray[..., indicies] = openGateIndices[dimension][:, numpy.newaxis,
numpy.newaxis] # xxx swap z with y using ^
vertexArray[..., 0:5] += self.openFenceTemplates[openGateData, i][..., 0:5]
vertexArray[_ST] += texMap(blocks[openGateIndices], 0)[..., numpy.newaxis, :]
vertexArray.view('uint8')[_RGB] = self.openFenceTemplates[openGateData, i] \
[..., 5][..., numpy.newaxis]
vertexArray.view('uint8')[_A] = 0xFF
vertexArray.view('uint8')[_RGB] *= areaBlockLights[1:-1, 1:-1, 1:-1][openGateIndices][
..., numpy.newaxis, numpy.newaxis, numpy.newaxis]
vertexArray.shape = (vertexArray.shape[0] * 6, 4, 6)
yield
self.vertexArrays.append(vertexArray)
makeVertices = fenceGateVertices
[docs]class StairBlockRenderer(BlockRenderer):
@classmethod
[docs] def getBlocktypes(cls, mats):
return [a.ID for a in mats.AllStairs]
# South - FaceXIncreasing
# North - FaceXDecreasing
# West - FaceZIncreasing
# East - FaceZDecreasing
stairTemplates = numpy.array([makeVertexTemplates(**kw) for kw in [
# South - FaceXIncreasing
{"xmin": 0.5},
# North - FaceXDecreasing
{"xmax": 0.5},
# West - FaceZIncreasing
{"zmin": 0.5},
# East - FaceZDecreasing
{"zmax": 0.5},
# Slabtype
{"ymax": 0.5},
]
])
[docs] def stairVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
arrays = []
materialIndices = self.getMaterialIndices(blockMaterials)
yield
stairBlocks = blocks[materialIndices]
stairData = blockData[materialIndices]
stairTop = (stairData >> 2).astype(bool)
stairData &= 3
x, z, y = materialIndices.nonzero()
for _ in ("slab", "step"):
vertexArray = numpy.zeros((len(x), 6, 4, 6), dtype='float32')
for i in range(3):
vertexArray[_XYZ][..., i] = (x, y, z)[i][:, numpy.newaxis, numpy.newaxis]
if _ == "step":
vertexArray[_XYZST] += self.stairTemplates[4][..., :5]
vertexArray[_XYZ][..., 1][stairTop] += 0.5
else:
vertexArray[_XYZST] += self.stairTemplates[stairData][..., :5]
vertexArray[_ST] += texMap(stairBlocks, 0)[..., numpy.newaxis, :]
vertexArray.view('uint8')[_RGB] = self.stairTemplates[4][numpy.newaxis, ..., 5, numpy.newaxis]
vertexArray.view('uint8')[_RGB] *= 0xf
vertexArray.view('uint8')[_A] = 0xff
vertexArray.shape = (len(x) * 6, 4, 6)
yield
arrays.append(vertexArray)
self.vertexArrays = arrays
makeVertices = stairVertices
[docs]class VineBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:vine"].ID]
SouthBit = 1 #FaceZIncreasing
WestBit = 2 #FaceXDecreasing
NorthBit = 4 #FaceZDecreasing
EastBit = 8 #FaceXIncreasing
renderstate = ChunkCalculator.renderstateVines
[docs] def vineFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight,
facingBlockLight, texMap):
bdata = blockData[blockIndices]
blockIndices = numpy.array(blockIndices)
if direction == pymclevel.faces.FaceZIncreasing:
blockIndices[blockIndices] = (bdata & 1).astype(bool)
elif direction == pymclevel.faces.FaceXDecreasing:
blockIndices[blockIndices] = (bdata & 2).astype(bool)
elif direction == pymclevel.faces.FaceZDecreasing:
blockIndices[blockIndices] = (bdata & 4).astype(bool)
elif direction == pymclevel.faces.FaceXIncreasing:
blockIndices[blockIndices] = (bdata & 8).astype(bool)
else:
return []
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
return vertexArray
vertexArray[_ST] += texMap(self.blocktypes[0], [0], direction)[:, numpy.newaxis, 0:2]
lights = blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
vertexArray.view('uint8')[_RGB] *= lights
vertexArray.view('uint8')[_RGB] = vertexArray.view('uint8')[_RGB].astype(float) * LeafBlockRenderer.leafColor
if direction == pymclevel.faces.FaceZIncreasing:
vertexArray[_XYZ][..., 2] -= 0.0625
if direction == pymclevel.faces.FaceXDecreasing:
vertexArray[_XYZ][..., 0] += 0.0625
if direction == pymclevel.faces.FaceZDecreasing:
vertexArray[_XYZ][..., 2] += 0.0625
if direction == pymclevel.faces.FaceXIncreasing:
vertexArray[_XYZ][..., 0] -= 0.0625
return vertexArray
makeFaceVertices = vineFaceVertices
[docs]class SlabBlockRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:wooden_slab"].ID,
alphaMaterials["minecraft:stone_slab"].ID,
alphaMaterials["minecraft:stone_slab2"].ID,
alphaMaterials["minecraft:purpur_slab"].ID]
[docs] def slabFaceVertices(self, direction, blockIndices, facingBlockLight, blocks, blockData, blockLight,
areaBlockLights, texMap):
lights = areaBlockLights[blockIndices][..., numpy.newaxis, numpy.newaxis]
bdata = blockData[blockIndices]
top = (bdata >> 3).astype(bool)
bdata &= 7
vertexArray = self.makeTemplate(direction, blockIndices)
if not len(vertexArray):
return vertexArray
vertexArray[_ST] += texMap(blocks[blockIndices], bdata, direction)[:, numpy.newaxis, 0:2]
vertexArray.view('uint8')[_RGB] *= lights
if direction == pymclevel.faces.FaceYIncreasing:
vertexArray[_XYZ][..., 1] -= 0.5
if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
vertexArray[_XYZ][..., 2:4, 1] -= 0.5
vertexArray[_ST][..., 2:4, 1] += 8
vertexArray[_XYZ][..., 1][top] += 0.5
return vertexArray
makeFaceVertices = slabFaceVertices
# 1.9 renderer's
[docs]class EndRodRenderer(BlockRenderer):
blocktypes = [alphaMaterials["minecraft:end_rod"].ID]
rodTemplate = makeVertexTemplatesFromJsonModel((7, 1, 7), (9, 16, 9), {
"down": (4, 2, 2, 0),
"up": (2, 0, 4, 2),
"north": (0, 0, 2, 15),
"south": (0, 0, 2, 15),
"west": (0, 0, 2, 15),
"east": (0, 0, 2, 15)
})
rodTemplates = numpy.array([
rotateTemplate(rodTemplate, x=180),
rodTemplate,
rotateTemplate(rodTemplate, x=90),
rotateTemplate(rodTemplate, y=180, x=90),
rotateTemplate(rodTemplate, y=270, x=90),
rotateTemplate(rodTemplate, y=90, x=90),
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
handleTemplate = makeVertexTemplatesFromJsonModel((6, 0, 6), (10, 1, 10), {
"down": (6, 6, 2, 2),
"up": (2, 2, 6, 6),
"north": (2, 6, 6, 7),
"south": (2, 6, 6, 7),
"west": (2, 6, 6, 7),
"east": (2, 6, 6, 7)
})
handleTemplates = numpy.array([
rotateTemplate(handleTemplate, x=180),
handleTemplate,
rotateTemplate(handleTemplate, x=90),
rotateTemplate(handleTemplate, y=180, x=90),
rotateTemplate(handleTemplate, y=270, x=90),
rotateTemplate(handleTemplate, y=90, x=90),
numpy.zeros((6, 4, 6)), numpy.zeros((6, 4, 6))
])
makeVertices = makeVerticesFromModel([rodTemplates, handleTemplates], 7)
[docs]class WaterBlockRenderer(BlockRenderer):
waterID = alphaMaterials["minecraft:water"].ID
blocktypes = [alphaMaterials["minecraft:flowing_water"].ID, waterID]
renderstate = ChunkCalculator.renderstateWater
[docs] def waterFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight,
facingBlockLight, texMap):
blockIndices = blockIndices & exposedFaceIndices
vertexArray = self.makeTemplate(direction, blockIndices)
vertexArray[_ST] += texMap(self.waterID, 0, 0)[numpy.newaxis, numpy.newaxis]
vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
return vertexArray
makeFaceVertices = waterFaceVertices
[docs]class IceBlockRenderer(BlockRenderer):
iceID = alphaMaterials["minecraft:ice"].ID
blocktypes = [iceID]
renderstate = ChunkCalculator.renderstateIce
[docs] def iceFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight,
facingBlockLight, texMap):
blockIndices = blockIndices & exposedFaceIndices
vertexArray = self.makeTemplate(direction, blockIndices)
vertexArray[_ST] += texMap(self.iceID, 0, 0)[numpy.newaxis, numpy.newaxis]
vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
return vertexArray
makeFaceVertices = iceFaceVertices
from glutils import DisplayList
[docs]class MCRenderer(object):
isPreviewer = False
def __init__(self, level=None, alpha=1.0):
self.render = True
self.origin = (0, 0, 0)
self.rotation = 0
self.bufferUsage = 0
self.invalidChunkQueue = deque()
self._chunkWorker = None
self.chunkRenderers = {}
self.loadableChunkMarkers = DisplayList()
self.visibleLayers = set(Layer.AllLayers)
self.masterLists = None
alpha *= 255
self.alpha = (int(alpha) & 0xff)
self.chunkStartTime = datetime.now()
self.oldChunkStartTime = self.chunkStartTime
self.oldPosition = None
self.chunkSamples = [timedelta(0, 0, 0)] * 15
self.chunkIterator = None
config.settings.fastLeaves.addObserver(self)
config.settings.roughGraphics.addObserver(self)
config.settings.showHiddenOres.addObserver(self)
config.settings.vertexBufferLimit.addObserver(self)
config.settings.drawEntities.addObserver(self)
config.settings.drawTileEntities.addObserver(self)
config.settings.drawTileTicks.addObserver(self)
config.settings.drawUnpopulatedChunks.addObserver(self, "drawTerrainPopulated")
config.settings.drawChunkBorders.addObserver(self, "drawChunkBorder")
config.settings.drawMonsters.addObserver(self)
config.settings.drawItems.addObserver(self)
config.settings.showChunkRedraw.addObserver(self, "showRedraw")
config.settings.spaceHeight.addObserver(self)
config.settings.targetFPS.addObserver(self, "targetFPS")
config.settings.maxViewDistance.addObserver(self, "maxViewDistance")
for ore in config.settings.hiddableOres.get():
config.settings["showOre{}".format(ore)].addObserver(self, callback=lambda x, id=ore: self.showOre(id, x))
self.level = level
if self.level.__class__.__name__ in ("FakeLevel","MCSchematic"):
self.toggleLayer(False, 'ChunkBorder')
chunkClass = ChunkRenderer
calculatorClass = ChunkCalculator
minViewDistance = 2
maxViewDistance = 32
_viewDistance = 8
needsRedraw = True
[docs] def toggleLayer(self, val, layer):
if val:
self.visibleLayers.add(layer)
else:
self.visibleLayers.discard(layer)
for cr in self.chunkRenderers.itervalues():
cr.invalidLayers.add(layer)
self.loadNearbyChunks()
[docs] def layerProperty(layer, default=True): # @NoSelf
attr = intern("_draw" + layer)
def _get(self):
return getattr(self, attr, default)
def _set(self, val):
if val != _get(self):
setattr(self, attr, val)
self.toggleLayer(val, layer)
return property(_get, _set)
drawEntities = layerProperty(Layer.Entities)
drawTileEntities = layerProperty(Layer.TileEntities)
drawTileTicks = layerProperty(Layer.TileTicks)
drawMonsters = layerProperty(Layer.Monsters)
drawItems = layerProperty(Layer.Items)
drawTerrainPopulated = layerProperty(Layer.TerrainPopulated)
drawChunkBorder = layerProperty(Layer.ChunkBorder)
[docs] def inSpace(self):
if self.level is None:
return True
h = self.position[1]
if self.level.dimNo == 1:
_2478aq_heot(h)
return ((h > self.level.Height + self.spaceHeight) or
(h <= -self.spaceHeight))
[docs] def chunkDistance(self, cpos):
camx, camy, camz = self.position
# if the renderer is offset into the world somewhere, adjust for that
ox, oy, oz = self.origin
camx -= ox
camz -= oz
camcx = int(numpy.floor(camx)) >> 4
camcz = int(numpy.floor(camz)) >> 4
cx, cz = cpos
return max(abs(cx - camcx), abs(cz - camcz))
overheadMode = False
[docs] def detailLevelForChunk(self, cpos):
if self.overheadMode:
return 2
if self.isPreviewer:
w, l, h = self.level.bounds.size
if w + l < 256:
return 0
distance = self.chunkDistance(cpos) - self.viewDistance
if distance > 0 or self.inSpace():
return 1
return 0
[docs] def getViewDistance(self):
return self._viewDistance
[docs] def setViewDistance(self, vd):
vd = int(vd) & 0xfffe
vd = min(max(vd, self.minViewDistance), self.maxViewDistance)
if vd != self._viewDistance:
self._viewDistance = vd
self.viewDistanceChanged()
# self.invalidateChunkMarkers()
viewDistance = property(getViewDistance, setViewDistance, None, "View Distance")
@property
def effectiveViewDistance(self):
if self.inSpace():
return self.viewDistance * 4
else:
return self.viewDistance * 2
[docs] def viewDistanceChanged(self):
self.oldPosition = None # xxx
self.discardMasterList()
self.loadNearbyChunks()
self.discardChunksOutsideViewDistance()
maxWorkFactor = 64
minWorkFactor = 1
workFactor = 2
chunkCalculator = None
_level = None
@property
def level(self):
return self._level
@level.setter
def level(self, level):
""" this probably warrants creating a new renderer """
self.stopWork()
self._level = level
self.oldPosition = None
self.position = (0, 0, 0)
self.chunkCalculator = None
self.invalidChunkQueue = deque()
self.discardAllChunks()
self.loadableChunkMarkers.invalidate()
if level:
self.chunkCalculator = self.calculatorClass(self.level)
self.oldPosition = None
self.loadNearbyChunks()
position = (0, 0, 0)
[docs] def loadChunksStartingFrom(self, wx, wz, distance=None): # world position
if None is self.level:
return
if self.level.saving:
return
if distance is None:
d = self.effectiveViewDistance
else:
d = distance
self.chunkIterator = self.iterateChunks(wx, wz, d * 2)
[docs] def iterateChunks(self, x, z, d):
cx = x >> 4
cz = z >> 4
yield (cx, cz)
step = dir = 1
while True:
for i in range(step):
cx += dir
yield (cx, cz)
for i in range(step):
cz += dir
yield (cx, cz)
step += 1
if step > d and not self.overheadMode:
raise StopIteration
dir = -dir
chunkIterator = None
@property
def chunkWorker(self):
if self._chunkWorker is None:
self._chunkWorker = self.makeWorkIterator()
return self._chunkWorker
[docs] def stopWork(self):
self._chunkWorker = None
[docs] def discardAllChunks(self):
self.bufferUsage = 0
self.forgetAllDisplayLists()
self.chunkRenderers = {}
self.oldPosition = None # xxx force reload
[docs] def discardChunksInBox(self, box):
self.discardChunks(box.chunkPositions)
[docs] def discardChunksOutsideViewDistance(self):
if self.overheadMode:
return
# print "discardChunksOutsideViewDistance"
d = self.effectiveViewDistance
cx = (self.position[0] - self.origin[0]) / 16
cz = (self.position[2] - self.origin[2]) / 16
origin = (cx - d, cz - d)
size = d * 2
if not len(self.chunkRenderers):
return
(ox, oz) = origin
# chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='int32', count=len(self.chunkRenderers))
chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='i,i', count=len(self.chunkRenderers))
chunks.dtype = 'int32'
chunks.shape = len(self.chunkRenderers), 2
if size:
outsideChunks = chunks[:, 0] < ox - 1
outsideChunks |= chunks[:, 0] > ox + size
outsideChunks |= chunks[:, 1] < oz - 1
outsideChunks |= chunks[:, 1] > oz + size
chunks = chunks[outsideChunks]
self.discardChunks(chunks)
[docs] def discardChunks(self, chunks):
for cx, cz in chunks:
self.discardChunk(cx, cz)
self.oldPosition = None # xxx force reload
[docs] def discardChunk(self, cx, cz):
" discards the chunk renderer for this chunk and compresses the chunk "
if (cx, cz) in self.chunkRenderers:
self.bufferUsage -= self.chunkRenderers[cx, cz].bufferSize
self.chunkRenderers[cx, cz].forgetDisplayLists()
del self.chunkRenderers[cx, cz]
_fastLeaves = False
@property
def fastLeaves(self):
return self._fastLeaves
@fastLeaves.setter
def fastLeaves(self, val):
if self._fastLeaves != bool(val):
self.discardAllChunks()
self._fastLeaves = bool(val)
_roughGraphics = False
@property
def roughGraphics(self):
return self._roughGraphics
@roughGraphics.setter
def roughGraphics(self, val):
if self._roughGraphics != bool(val):
self.discardAllChunks()
self._roughGraphics = bool(val)
_showHiddenOres = False
@property
def showHiddenOres(self):
return self._showHiddenOres
@showHiddenOres.setter
def showHiddenOres(self, val):
if self._showHiddenOres != bool(val):
self.discardAllChunks()
self._showHiddenOres = bool(val)
[docs] def showOre(self, ore, show):
ChunkCalculator.hiddenOreMaterials[ore] = ore if show else 1
if self.showHiddenOres:
self.discardAllChunks()
[docs] def invalidateChunk(self, cx, cz, layers=None):
" marks the chunk for regenerating vertex data and display lists "
if (cx, cz) in self.chunkRenderers:
# self.chunkRenderers[(cx,cz)].invalidate()
# self.bufferUsage -= self.chunkRenderers[(cx, cz)].bufferSize
self.chunkRenderers[(cx, cz)].invalidate(layers)
# self.bufferUsage += self.chunkRenderers[(cx, cz)].bufferSize
self.invalidChunkQueue.append((cx, cz)) # xxx encapsulate
[docs] def invalidateChunksInBox(self, box, layers=None):
# If the box is at the edge of any chunks, expanding by 1 makes sure the neighboring chunk gets redrawn.
box = box.expand(1)
self.invalidateChunks(box.chunkPositions, layers)
[docs] def invalidateEntitiesInBox(self, box):
self.invalidateChunks(box.chunkPositions, [Layer.Entities])
[docs] def invalidateTileTicksInBox(self, box):
self.invalidateChunks(box.chunkPositions, [Layer.TileTicks])
[docs] def invalidateChunks(self, chunks, layers=None):
for (cx, cz) in chunks:
self.invalidateChunk(cx, cz, layers)
self.stopWork()
self.discardMasterList()
self.loadNearbyChunks()
[docs] def invalidateAllChunks(self, layers=None):
self.invalidateChunks(self.chunkRenderers.iterkeys(), layers)
[docs] def forgetAllDisplayLists(self):
for cr in self.chunkRenderers.itervalues():
cr.forgetDisplayLists()
[docs] def invalidateMasterList(self):
self.discardMasterList()
shouldRecreateMasterList = True
[docs] def discardMasterList(self):
self.shouldRecreateMasterList = True
@property
def shouldDrawAll(self):
box = self.level.bounds
return self.isPreviewer and box.width + box.length < 256
distanceToChunkReload = 32.0
[docs] def cameraMovedFarEnough(self):
if self.shouldDrawAll:
return False
if self.oldPosition is None:
return True
cPos = self.position
oldPos = self.oldPosition
cameraDelta = self.distanceToChunkReload
return any([abs(x - y) > cameraDelta for x, y in zip(cPos, oldPos)])
[docs] def loadVisibleChunks(self):
""" loads nearby chunks if the camera has moved beyond a certain distance """
# print "loadVisibleChunks"
if self.cameraMovedFarEnough():
if datetime.now() - self.lastVisibleLoad > timedelta(0, 0.5):
self.discardChunksOutsideViewDistance()
self.loadNearbyChunks()
self.oldPosition = self.position
self.lastVisibleLoad = datetime.now()
lastVisibleLoad = datetime.now()
[docs] def loadNearbyChunks(self):
if None is self.level:
return
# print "loadNearbyChunks"
cameraPos = self.position
if self.shouldDrawAll:
self.loadAllChunks()
else:
# subtract self.origin to load nearby chunks correctly for preview renderers
self.loadChunksStartingFrom(int(cameraPos[0]) - self.origin[0], int(cameraPos[2]) - self.origin[2])
[docs] def loadAllChunks(self):
box = self.level.bounds
self.loadChunksStartingFrom(box.origin[0] + box.width / 2, box.origin[2] + box.length / 2,
max(box.width, box.length))
_floorTexture = None
@property
def floorTexture(self):
if self._floorTexture is None:
self._floorTexture = Texture(self.makeFloorTex)
return self._floorTexture
@staticmethod
[docs] def makeFloorTex():
color0 = (0xff, 0xff, 0xff, 0x22)
color1 = (0xff, 0xff, 0xff, 0x44)
img = numpy.array([color0, color1, color1, color0], dtype='uint8')
GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST)
GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST)
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, 2, 2, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, img)
[docs] def invalidateChunkMarkers(self):
self.loadableChunkMarkers.invalidate()
def _drawLoadableChunkMarkers(self):
if self.level.chunkCount:
chunkSet = set(self.level.allChunks)
sizedChunks = chunkMarkers(chunkSet)
GL.glPushAttrib(GL.GL_FOG_BIT)
GL.glDisable(GL.GL_FOG)
GL.glEnable(GL.GL_BLEND)
GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
GL.glPolygonOffset(DepthOffset.ChunkMarkers, DepthOffset.ChunkMarkers)
GL.glEnable(GL.GL_DEPTH_TEST)
GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
GL.glEnable(GL.GL_TEXTURE_2D)
GL.glColor(1.0, 1.0, 1.0, 1.0)
self.floorTexture.bind()
# chunkColor = numpy.zeros(shape=(chunks.shape[0], 4, 4), dtype='float32')
# chunkColor[:]= (1, 1, 1, 0.15)
#
# cc = numpy.array(chunks[:,0] + chunks[:,1], dtype='int32')
# cc &= 1
# coloredChunks = cc > 0
# chunkColor[coloredChunks] = (1, 1, 1, 0.28)
# chunkColor *= 255
# chunkColor = numpy.array(chunkColor, dtype='uint8')
#
# GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, 0, chunkColor)
for size, chunks in sizedChunks.iteritems():
if not len(chunks):
continue
chunks = numpy.array(chunks, dtype='float32')
chunkPosition = numpy.zeros(shape=(chunks.shape[0], 4, 3), dtype='float32')
chunkPosition[:, :, (0, 2)] = numpy.array(((0, 0), (0, 1), (1, 1), (1, 0)), dtype='float32')
chunkPosition[:, :, (0, 2)] *= size
chunkPosition[:, :, (0, 2)] += chunks[:, numpy.newaxis, :]
chunkPosition *= 16
GL.glVertexPointer(3, GL.GL_FLOAT, 0, chunkPosition.ravel())
GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, (chunkPosition[..., (0, 2)] * 16).ravel())
GL.glDrawArrays(GL.GL_QUADS, 0, len(chunkPosition) * 4)
GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
GL.glDisable(GL.GL_TEXTURE_2D)
GL.glDisable(GL.GL_BLEND)
GL.glDisable(GL.GL_DEPTH_TEST)
GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
GL.glPopAttrib()
[docs] def drawLoadableChunkMarkers(self):
if not self.isPreviewer or isinstance(self.level, pymclevel.MCInfdevOldLevel):
self.loadableChunkMarkers.call(self._drawLoadableChunkMarkers)
# self.drawCompressedChunkMarkers()
needsImmediateRedraw = False
viewingFrustum = None
if "-debuglists" in sys.argv:
def createMasterLists(self):
pass
def callMasterLists(self):
for cr in self.chunkRenderers.itervalues():
cr.debugDraw()
else:
[docs] def createMasterLists(self):
if self.shouldRecreateMasterList:
lists = {}
chunkLists = defaultdict(list)
chunksPerFrame = 80
shouldRecreateAgain = False
for ch in self.chunkRenderers.itervalues():
if chunksPerFrame:
if ch.needsRedisplay:
chunksPerFrame -= 1
ch.makeDisplayLists()
else:
shouldRecreateAgain = True
if ch.renderstateLists:
for rs in ch.renderstateLists:
chunkLists[rs] += ch.renderstateLists[rs]
for rs in chunkLists:
if len(chunkLists[rs]):
lists[rs] = numpy.array(chunkLists[rs], dtype='uint32').ravel()
# lists = lists[lists.nonzero()]
self.masterLists = lists
self.shouldRecreateMasterList = shouldRecreateAgain
self.needsImmediateRedraw = shouldRecreateAgain
[docs] def callMasterLists(self):
for renderstate in self.chunkCalculator.renderstates:
if renderstate not in self.masterLists:
continue
if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
GL.glEnable(GL.GL_BLEND)
renderstate.bind()
GL.glCallLists(self.masterLists[renderstate])
renderstate.release()
if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
GL.glDisable(GL.GL_BLEND)
errorLimit = 10
[docs] def draw(self):
self.needsRedraw = False
if not self.level:
return
if not self.chunkCalculator:
return
if not self.render:
return
if self.level.materials.name in ("Pocket", "Alpha"):
GL.glMatrixMode(GL.GL_TEXTURE)
GL.glScalef(1 / 2., 1 / 2., 1 / 2.)
with gl.glPushMatrix(GL.GL_MODELVIEW):
dx, dy, dz = self.origin
GL.glTranslate(dx, dy, dz)
GL.glEnable(GL.GL_CULL_FACE)
GL.glEnable(GL.GL_DEPTH_TEST)
self.level.materials.terrainTexture.bind()
GL.glEnable(GL.GL_TEXTURE_2D)
GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
offset = DepthOffset.PreviewRenderer if self.isPreviewer else DepthOffset.Renderer
GL.glPolygonOffset(offset, offset)
GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
self.createMasterLists()
try:
self.callMasterLists()
except GL.GLError, e:
if self.errorLimit:
self.errorLimit -= 1
traceback.print_exc()
print e
GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
GL.glDisable(GL.GL_CULL_FACE)
GL.glDisable(GL.GL_DEPTH_TEST)
GL.glDisable(GL.GL_TEXTURE_2D)
GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
# if self.drawLighting:
self.drawLoadableChunkMarkers()
if self.level.materials.name in ("Pocket", "Alpha"):
GL.glMatrixMode(GL.GL_TEXTURE)
GL.glScalef(2., 2., 2.)
renderErrorHandled = False
[docs] def addDebugInfo(self, addDebugString):
addDebugString("BU: {0} MB, ".format(
self.bufferUsage / 1000000,
))
addDebugString("WQ: {0}, ".format(len(self.invalidChunkQueue)))
if self.chunkIterator:
addDebugString("[LR], ")
addDebugString("CR: {0}, ".format(len(self.chunkRenderers), ))
[docs] def next(self):
self.chunkWorker.next()
[docs] def makeWorkIterator(self):
''' does chunk face and vertex calculation work. returns a generator that can be
iterated over for smaller work units.'''
try:
while True:
if self.level is None:
raise StopIteration
if len(self.invalidChunkQueue) > 1024:
self.invalidChunkQueue.clear()
if len(self.invalidChunkQueue):
c = self.invalidChunkQueue[0]
for _ in self.workOnChunk(c):
yield
self.invalidChunkQueue.popleft()
elif self.chunkIterator is None:
raise StopIteration
else:
c = self.chunkIterator.next()
if self.vertexBufferLimit:
while self.bufferUsage > (0.9 * (self.vertexBufferLimit << 20)):
deadChunk = None
deadDistance = self.chunkDistance(c)
for cr in self.chunkRenderers.itervalues():
dist = self.chunkDistance(cr.chunkPosition)
if dist > deadDistance:
deadChunk = cr
deadDistance = dist
if deadChunk is not None:
self.discardChunk(*deadChunk.chunkPosition)
else:
break
else:
for _ in self.workOnChunk(c):
yield
else:
for _ in self.workOnChunk(c):
yield
yield
finally:
self._chunkWorker = None
if self.chunkIterator:
self.chunkIterator = None
vertexBufferLimit = 384
[docs] def getChunkRenderer(self, c):
if not (c in self.chunkRenderers):
cr = self.chunkClass(self, c)
else:
cr = self.chunkRenderers[c]
return cr
[docs] def calcFacesForChunkRenderer(self, cr):
self.bufferUsage -= cr.bufferSize
calc = cr.calcFaces()
work = 0
for _ in calc:
yield
work += 1
self.chunkDone(cr, work)
[docs] def workOnChunk(self, c):
work = 0
if self.level.containsChunk(*c):
cr = self.getChunkRenderer(c)
if self.viewingFrustum:
# if not self.viewingFrustum.visible(numpy.array([[c[0] * 16 + 8, 64, c[1] * 16 + 8, 1.0]]), 64).any():
if not self.viewingFrustum.visible1([c[0] * 16 + 8, self.level.Height / 2, c[1] * 16 + 8, 1.0],
self.level.Height / 2):
raise StopIteration
faceInfoCalculator = self.calcFacesForChunkRenderer(cr)
try:
for _ in faceInfoCalculator:
work += 1
if (work % MCRenderer.workFactor) == 0:
yield
self.invalidateMasterList()
except Exception, e:
traceback.print_exc()
fn = c
logging.info(u"Skipped chunk {f}: {e}".format(e=e, f=fn))
redrawChunks = 0
[docs] def chunkDone(self, chunkRenderer, work):
self.chunkRenderers[chunkRenderer.chunkPosition] = chunkRenderer
self.bufferUsage += chunkRenderer.bufferSize
# print "Chunk {0} used {1} work units".format(chunkRenderer.chunkPosition, work)
if not self.needsRedraw:
if self.redrawChunks:
self.redrawChunks -= 1
if not self.redrawChunks:
self.needsRedraw = True
else:
self.redrawChunks = 2
if work > 0:
self.oldChunkStartTime = self.chunkStartTime
self.chunkStartTime = datetime.now()
self.chunkSamples.pop(0)
self.chunkSamples.append(self.chunkStartTime - self.oldChunkStartTime)
[docs]class PreviewRenderer(MCRenderer):
isPreviewer = True
[docs]def rendermain():
renderer = MCRenderer()
renderer.level = pymclevel.mclevel.loadWorld("World1")
renderer.viewDistance = 6
renderer.detailLevelForChunk = lambda *x: 0
start = datetime.now()
renderer.loadVisibleChunks()
try:
while True:
# for i in range(100):
renderer.next()
except StopIteration:
pass
except Exception, e:
traceback.print_exc()
print repr(e)
duration = datetime.now() - start
perchunk = duration / len(renderer.chunkRenderers)
print "Duration: {0} ({1} chunks per second, {2} per chunk, {3} chunks)".format(duration,
1000000.0 / perchunk.microseconds,
perchunk,
len(renderer.chunkRenderers))
# display.init( (640, 480), OPENGL | DOUBLEBUF )
from utilities.gl_display_context import GLDisplayContext
from OpenGL import GLU
import pygame
# distance = 4000
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
GLU.gluPerspective(35, 640.0 / 480.0, 0.5, 4000.0)
h = 366
pos = (0, h, 0)
look = (0.0001, h - 1, 0.0001)
up = (0, 1, 0)
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
GLU.gluLookAt(pos[0], pos[1], pos[2],
look[0], look[1], look[2],
up[0], up[1], up[2])
GL.glClearColor(0.0, 0.0, 0.0, 1.0)
framestart = datetime.now()
frames = 200
for i in range(frames):
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
renderer.draw()
pygame.display.flip()
delta = datetime.now() - framestart
seconds = delta.seconds + delta.microseconds / 1000000.0
print "{0} frames in {1} ({2} per frame, {3} FPS)".format(frames, delta, delta / frames, frames / seconds)
while True:
evt = pygame.event.poll()
if evt.type == pygame.MOUSEBUTTONDOWN:
break
# time.sleep(3.0)
import traceback
import cProfile
if __name__ == "__main__":
cProfile.run("rendermain()", "mcedit.profile")