Decent, yet slow attempt at updating SkyLight and HeightMap.
This commit is contained in:
parent
301f178c26
commit
304329ad6a
@ -35,31 +35,57 @@ cdef struct RelightSection:
|
||||
void * chunk
|
||||
char dirty
|
||||
|
||||
ctypedef long long section_key_t
|
||||
cdef struct RelightChunk:
|
||||
# This guy is a big endian array, but Cython only wants to operate on little-endians.
|
||||
# We byteswap the entire thing on read, and if we're dirty, byteswap it all out again
|
||||
# on write.
|
||||
unsigned int[:,:] HeightMap
|
||||
# Ditto.
|
||||
void * chunk
|
||||
char dirty
|
||||
|
||||
|
||||
ctypedef unsigned long long section_key_t
|
||||
ctypedef unsigned long long chunk_key_t
|
||||
|
||||
cdef section_key_t section_key(int cx, int cy, int cz):
|
||||
# assume 0 < cy < 256
|
||||
return (<section_key_t>cx) << 36 | cz << 8 | cy
|
||||
return (<section_key_t>cx) << 36 | (<section_key_t>cz & 0xFFFFFFF) << 8 | (<section_key_t>cy) & 0xFF
|
||||
|
||||
cdef chunk_key_t chunk_key(int cx, int cz):
|
||||
return (<chunk_key_t>cx) << 32 | (<section_key_t>cz) & 0xFFFFFFFFLL
|
||||
|
||||
DEF CACHE_LIMIT = 100
|
||||
|
||||
|
||||
@cython.final
|
||||
cdef class RelightCtx(object):
|
||||
cdef:
|
||||
map[section_key_t, RelightSection] section_cache
|
||||
map[chunk_key_t, RelightChunk] chunk_cache
|
||||
|
||||
object dimension
|
||||
unsigned char [:] brightness
|
||||
unsigned char [:] opacity
|
||||
IF OUTPUT_STATS:
|
||||
unsigned int spreadCount, drawCount, fadeCount
|
||||
|
||||
int _useBlockLight
|
||||
|
||||
def __init__(self, dim):
|
||||
self.dimension = dim
|
||||
self.brightness = self.dimension.blocktypes.brightness
|
||||
self.opacity = self.dimension.blocktypes.opacity
|
||||
self._useBlockLight = 1
|
||||
IF OUTPUT_STATS:
|
||||
self.spreadCount = self.drawCount = self.fadeCount = 0
|
||||
|
||||
cdef void useBlockLight(self):
|
||||
self._useBlockLight = 1
|
||||
|
||||
cdef void useSkyLight(self):
|
||||
self._useBlockLight = 0
|
||||
|
||||
cdef RelightSection * getSection(self, int cx, int cy, int cz):
|
||||
cdef section_key_t key = section_key(cx, cy, cz)
|
||||
cdef map[section_key_t, RelightSection].iterator i = self.section_cache.find(key)
|
||||
@ -78,7 +104,7 @@ cdef class RelightCtx(object):
|
||||
if not self.dimension.containsChunk(cx, cz):
|
||||
return NULL
|
||||
chunk = self.dimension.getChunk(cx, cz)
|
||||
section = chunk.getSection(cy)
|
||||
section = chunk.getSection(cy, create=True)
|
||||
if section is None:
|
||||
return NULL
|
||||
if self.section_cache.size() > CACHE_LIMIT:
|
||||
@ -100,6 +126,45 @@ cdef class RelightCtx(object):
|
||||
ret[0] = cachedSection
|
||||
return ret
|
||||
|
||||
# ---
|
||||
|
||||
|
||||
cdef RelightChunk * getChunk(self, int cx, int cz):
|
||||
cdef chunk_key_t key = chunk_key(cx, cz)
|
||||
cdef map[chunk_key_t, RelightChunk].iterator i = self.chunk_cache.find(key)
|
||||
cdef RelightChunk * ret
|
||||
if i == self.chunk_cache.end():
|
||||
ret = self.cacheChunk(cx, cz)
|
||||
else:
|
||||
ret = &(deref(i).second)
|
||||
return ret
|
||||
|
||||
cdef RelightChunk * cacheChunk(self, int cx, int cz):
|
||||
# Initializer is *required* - if memoryview fields are uninitialized, they cannot be assigned
|
||||
# later as Cython attempts to decref the uninitialized memoryview and segfaults.
|
||||
cdef RelightChunk cachedChunk = [None, NULL, 0]
|
||||
cdef long long key = chunk_key(cx, cz)
|
||||
if not self.dimension.containsChunk(cx, cz):
|
||||
return NULL
|
||||
chunk = self.dimension.getChunk(cx, cz)
|
||||
if self.chunk_cache.size() > CACHE_LIMIT:
|
||||
# xxx decache something!
|
||||
pass
|
||||
|
||||
cachedChunk.HeightMap = np.array(chunk.HeightMap, dtype='u4')
|
||||
cachedChunk.chunk = <void *>chunk
|
||||
Py_INCREF(chunk)
|
||||
|
||||
# weird hack because in Python an assignment is a statement so we cannot write
|
||||
# `return self.chunk_cache[key] = cachedChunk` to return a reference to the
|
||||
# RelightChunk in the cache...
|
||||
cdef RelightChunk * ret
|
||||
ret = &self.chunk_cache[key]
|
||||
ret[0] = cachedChunk
|
||||
return ret
|
||||
|
||||
# ---
|
||||
|
||||
def __dealloc__(self):
|
||||
cdef RelightSection cachedSection
|
||||
cdef section_key_t key
|
||||
@ -114,23 +179,63 @@ cdef class RelightCtx(object):
|
||||
Py_DECREF(<object>cachedSection.chunk)
|
||||
cachedSection.chunk = NULL
|
||||
|
||||
for ckeyval in self.chunk_cache:
|
||||
key = ckeyval.first
|
||||
cachedChunk = ckeyval.second
|
||||
if cachedChunk.dirty:
|
||||
assert ((<object>cachedChunk.chunk).HeightMap != cachedChunk.HeightMap).any()
|
||||
(<object>cachedChunk.chunk).HeightMap[:] = cachedChunk.HeightMap
|
||||
(<object>cachedChunk.chunk).dirty = True
|
||||
cachedChunk.HeightMap = None
|
||||
Py_DECREF(<object>cachedChunk.chunk)
|
||||
cachedChunk.chunk = NULL
|
||||
|
||||
# ---
|
||||
|
||||
cdef int getHeightMap(self, int x, int z):
|
||||
cdef RelightChunk * chunk = self.getChunk(x >> 4, z >> 4)
|
||||
if chunk is NULL:
|
||||
return 0
|
||||
return chunk.HeightMap[<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)]
|
||||
|
||||
cdef void setHeightMap(self, int x, int z, int value):
|
||||
cdef RelightChunk * chunk = self.getChunk(x >> 4, z >> 4)
|
||||
if chunk is NULL:
|
||||
return
|
||||
chunk.dirty = True
|
||||
chunk.HeightMap[<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)] = value
|
||||
|
||||
# ---
|
||||
|
||||
cdef char getBlockLight(self, int x, int y, int z):
|
||||
cdef RelightSection * section = self.getSection(x >> 4, y >> 4, z >> 4)
|
||||
if section is NULL:
|
||||
return 0
|
||||
if self._useBlockLight:
|
||||
return section.BlockLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)]
|
||||
else:
|
||||
return section.SkyLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)]
|
||||
|
||||
return section.BlockLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)]
|
||||
|
||||
cdef void setBlockLight(self, int x, int y, int z, char value):
|
||||
cdef RelightSection * section = self.getSection(x >> 4, y >> 4, z >> 4)
|
||||
if section is NULL:
|
||||
return
|
||||
section.dirty = 1
|
||||
section.BlockLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)] = value
|
||||
if self._useBlockLight:
|
||||
section.BlockLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)] = value
|
||||
else:
|
||||
section.SkyLight[<unsigned int>(y & 0xf),
|
||||
<unsigned int>(z & 0xf),
|
||||
<unsigned int>(x & 0xf)] = value
|
||||
|
||||
|
||||
cdef unsigned char getBlockBrightness(self, int x, int y, int z):
|
||||
@ -155,6 +260,99 @@ cdef class RelightCtx(object):
|
||||
return max(<unsigned char>1, # truncation warning
|
||||
self.opacity[blockID])
|
||||
|
||||
cdef updateSkyLight(RelightCtx ctx,
|
||||
cnp.ndarray[ndim=1, dtype=int] ax,
|
||||
cnp.ndarray[ndim=1, dtype=int] ay,
|
||||
cnp.ndarray[ndim=1, dtype=int] az):
|
||||
|
||||
cdef ssize_t i, n
|
||||
cdef chunk_key_t k
|
||||
cdef int x, y, z, y2, h, oldH, newH, oldLight
|
||||
cdef map[chunk_key_t, int] oldHeights
|
||||
cdef map[chunk_key_t, int] newHeights
|
||||
cdef coord c
|
||||
|
||||
cdef deque[coord] litCoords
|
||||
cdef deque[coord] dimCoords
|
||||
|
||||
cdef pair[chunk_key_t, int] p
|
||||
|
||||
ctx.useSkyLight()
|
||||
|
||||
n = ax.shape[0]
|
||||
for i in range(n):
|
||||
x = ax[i]
|
||||
z = az[i]
|
||||
oldHeights[chunk_key(x, z)] = ctx.getHeightMap(x, z)
|
||||
|
||||
# HeightMap stores the block height above the highest opacity>0 block
|
||||
|
||||
for i in range(n):
|
||||
x = ax[i]
|
||||
y = ay[i]
|
||||
z = az[i]
|
||||
k = chunk_key(x, z)
|
||||
h = oldHeights[k]
|
||||
if y >= h:
|
||||
# Block was set above current height - if opaque, increase height
|
||||
if ctx.getBlockOpacity(x, y, z):
|
||||
newHeights[k] = y + 1
|
||||
elif y == h-1:
|
||||
# Block was set below current height
|
||||
# if it was height-1, and it was set to opacity==0, then drop height
|
||||
# until we find an opacity>0 block
|
||||
if 0 == ctx.getBlockOpacity(x, y, z):
|
||||
for y2 in range(h, y):
|
||||
if ctx.getBlockOpacity(x, y2, z):
|
||||
newHeights[k] = y2 + 1
|
||||
break
|
||||
else:
|
||||
newHeights[k] = y+1
|
||||
else:
|
||||
newHeights[k] = h
|
||||
else:
|
||||
newHeights[k] = h
|
||||
|
||||
for p in oldHeights:
|
||||
k = p.first
|
||||
oldH = p.second
|
||||
x = k >> 32
|
||||
z = k & 0xffffffffLL
|
||||
h = newHeights[k]
|
||||
c.x = x
|
||||
c.z = z
|
||||
if h > oldH:
|
||||
# Column shifted up - blocks in changed segment reduced light level
|
||||
for y2 in range(oldH, h):
|
||||
c.y = y2
|
||||
dimCoords.push_back(c)
|
||||
# Blocks above column may be in a newly created section.
|
||||
# This is a shitty, shitty answer. FIXME FIXME FIXME.
|
||||
c.y += 1
|
||||
ctx.setBlockLight(c.x, c.y, c.z, 15)
|
||||
litCoords.push_back(c)
|
||||
if h < oldH:
|
||||
# Column shifted down - blocks in changed segment increased light level
|
||||
for y2 in range(h, oldH):
|
||||
c.y = y2
|
||||
ctx.setBlockLight(c.x, c.y, c.z, 15)
|
||||
litCoords.push_back(c)
|
||||
|
||||
if h != oldH:
|
||||
ctx.setHeightMap(x, z, h)
|
||||
|
||||
print("Dimming %d, brightening %d" % (dimCoords.size(), litCoords.size()))
|
||||
for c in dimCoords:
|
||||
oldLight = ctx.getBlockLight(c.x, c.y, c.z)
|
||||
ctx.setBlockLight(c.x, c.y, c.z, 0)
|
||||
drawLight(ctx, c.x, c.y, c.z)
|
||||
fadeLight(ctx, c.x, c.y, c.z, oldLight)
|
||||
|
||||
# lots of repeated work here
|
||||
for c in litCoords:
|
||||
spreadLight(ctx, c.x, c.y, c.z)
|
||||
|
||||
ctx.useBlockLight()
|
||||
|
||||
def updateLightsByCoord(dim, x, y, z):
|
||||
x = np.asarray(x, 'i32').ravel()
|
||||
@ -165,11 +363,14 @@ def updateLightsByCoord(dim, x, y, z):
|
||||
cdef cnp.ndarray[ndim=1, dtype=int] az = z
|
||||
cdef size_t i
|
||||
|
||||
|
||||
if not (x.shape == y.shape == z.shape):
|
||||
raise ValueError("All coord arrays must be the same size. (No broadcasting.)")
|
||||
|
||||
ctx = RelightCtx(dim)
|
||||
|
||||
updateSkyLight(ctx, ax, ay, az)
|
||||
|
||||
for i in range(<size_t>len(ax)):
|
||||
updateLights(ctx, ax[i], ay[i], az[i])
|
||||
|
||||
|
Reference in New Issue
Block a user