Entities, TileEntities etc can now be toggled on and off

Restored another MCEdit1 feature!
This commit is contained in:
David Vierra 2015-03-29 23:57:27 -10:00
parent 88d7f61666
commit ba53617acf
9 changed files with 129 additions and 56 deletions

View File

@ -26,6 +26,8 @@ class ChunkRenderInfo(object):
self.worldScene = worldScene
self.detailLevel = worldScene.minlod
self.invalidLayers = set(layers.Layer.AllLayers)
self.renderedLayers = set()
self.chunkPosition = chunkPosition
self.bufferSize = 0
self.vertexNodes = []
@ -40,8 +42,8 @@ class ChunkRenderInfo(object):
return self.worldScene.visibleLayers #xxxx
@property
def done(self):
return len(self.invalidLayers) == 0
def layersToRender(self):
return len(self.invalidLayers) + len(self.visibleLayers - self.renderedLayers)
class ChunkNode(scenegraph.Node):
RenderNodeClass = rendergraph.TranslateRenderNode
@ -58,6 +60,7 @@ class ChunkNode(scenegraph.Node):
self.chunkPosition = chunkPosition
cx, cz = chunkPosition
self.translateOffset = (cx << 4, 0, cz << 4)
self.layers = {}
class ChunkGroupNode(NamedChildrenNode):
@ -117,3 +120,10 @@ class ChunkGroupNode(NamedChildrenNode):
def clear(self):
super(ChunkGroupNode, self).clear()
def setLayerVisible(self, layerName, visible):
for area in self._children.itervalues():
for chunkNode in area._children.itervalues():
node = chunkNode.layers.get(layerName)
if node is not None:
node.visible = visible

View File

@ -95,7 +95,7 @@ class ChunkUpdate(object):
def __iter__(self):
chunkInfo = self.chunkInfo
if 0 == len(chunkInfo.invalidLayers):
if 0 == chunkInfo.layersToRender:
yield
return
@ -109,6 +109,7 @@ class ChunkUpdate(object):
yield
self.blockMeshes.extend(highDetailBlocks)
chunkInfo.invalidLayers.clear()
raise StopIteration
@ -116,12 +117,12 @@ class ChunkUpdate(object):
def buildChunkMeshes(self):
"""
Rebuild the meshes which render the entire chunk from top to bottom
:return:
:rtype:
"""
chunkInfo = self.chunkInfo
blockMeshes = self.blockMeshes
existingBlockVertexNodes = sum([list(node.children) for node in chunkInfo.getChunkVertexNodes()], [])
for cls in self.wholeChunkMeshClasses:
if chunkInfo.detailLevel not in cls.detailLevels:
@ -129,11 +130,7 @@ class ChunkUpdate(object):
continue
if cls.layer not in chunkInfo.visibleLayers:
continue
if cls.layer not in chunkInfo.invalidLayers:
for vertexNode in existingBlockVertexNodes:
if vertexNode.meshClass is cls: # xxxxx
blockMeshes.append(existingBlockVertexNodes[cls])
if cls.layer not in chunkInfo.invalidLayers and cls.layer in chunkInfo.renderedLayers:
continue
chunkMesh = cls(self)

View File

@ -13,4 +13,4 @@ class Layer:
TileEntities = "TileEntities"
TileTicks = "TileTicks"
TerrainPopulated = "TerrainPopulated"
AllLayers = (Blocks, Entities, Monsters, Items, TileEntities, TileTicks)
AllLayers = (Blocks, Entities, Items, TileEntities, TileTicks)

View File

@ -9,6 +9,7 @@ import numpy
cimport numpy
from mcedit2.rendering import renderstates
from mcedit2.rendering.layers import Layer
from mcedit2.rendering.vertexarraybuffer import VertexArrayBuffer
cimport mcedit2.rendering.blockmodels as blockmodels
@ -30,6 +31,7 @@ class BlockModelMesh(object):
"""
self.sectionUpdate = sectionUpdate
self.vertexArrays = []
self.layer = Layer.Blocks
def createVertexArrays(self):
DEF quadBytes = 32

View File

@ -66,6 +66,9 @@ class Node(object):
self.touchChildren()
self._children[:] = []
def childCount(self):
return len(self._children)
@property
def children(self):
return iter(self._children)

View File

@ -6,6 +6,7 @@ import logging
import sys
import collections
import numpy
import itertools
from mcedit2.rendering.layers import Layer
from mcedit2.rendering import chunkupdate, scenegraph
@ -74,8 +75,7 @@ class SceneUpdateTask(object):
if chunkInfo is None:
return True
#log.info("Wants chunk %s? %s", cPos, len(chunkNode.invalidLayers))
return len(chunkInfo.invalidLayers)
return chunkInfo.layersToRender
def workOnChunk(self, chunk, visibleSections=None):
work = 0
@ -91,31 +91,47 @@ class SceneUpdateTask(object):
if (work % SceneUpdateTask.workFactor) == 0:
yield
chunkInfo.invalidLayers = set()
meshesByRS = collections.defaultdict(list)
for mesh in chunkUpdate.blockMeshes:
meshesByRS[mesh.renderstate].append(mesh)
# Create one ChunkNode for each renderstate group, if needed
for renderstate in renderstates.allRenderstates:
groupNode = self.worldScene.getRenderstateGroup(renderstate)
if groupNode.containsChunkNode(cPos):
chunkNode = groupNode.getChunkNode(cPos)
else:
chunkNode = ChunkNode(cPos)
groupNode.addChunkNode(chunkNode)
meshes = meshesByRS[renderstate]
if len(meshes):
arrays = sum([mesh.vertexArrays for mesh in meshes], [])
if len(arrays):
chunkNode = ChunkNode(cPos)
groupNode.addChunkNode(chunkNode)
node = scenegraph.VertexNode(arrays)
chunkNode.addChild(node)
else:
groupNode.discardChunkNode(*cPos)
meshes = sorted(meshes, key=lambda m: m.layer)
# Need a way to signal WorldScene that this chunk didn't need updating in this renderstate,
# but it should keep the old vertex arrays for the state.
# Alternately, signal WorldScene that the chunk did update for the renderstate and no
# vertex arrays resulted. Return a mesh with zero length vertexArrays
#else:
# groupNode.discardChunkNode(*cPos)
for layer, layerMeshes in itertools.groupby(meshes, lambda m: m.layer):
if layer not in self.worldScene.visibleLayers:
continue
layerMeshes = list(layerMeshes)
# Check if the mesh was re-rendered and remove the old mesh
meshTypes = set(type(m) for m in layerMeshes)
for arrayNode in chunkNode.children:
if arrayNode.meshType in meshTypes:
chunkNode.removeChild(arrayNode)
# Create one VertexNode for each visible layer in this chunk
for mesh in layerMeshes:
if len(mesh.vertexArrays):
node = scenegraph.VertexNode(mesh.vertexArrays)
node.layerName = layer
node.meshType = type(mesh)
chunkNode.layers[layer] = node
chunkNode.addChild(node)
chunkInfo.renderedLayers.add(layer)
if chunkNode.childCount() == 0:
groupNode.discardChunkNode(*cPos)
except Exception as e:
logging.exception(u"Rendering chunk %s failed: %r", cPos, e)
@ -165,16 +181,6 @@ class WorldScene(scenegraph.Node):
self.renderstateNodes[rsClass].addChild(groupNode)
return groupNode
#
# def toggleLayer(self, val, layer):
# self.chunkGroupNode.toggleLayer(val, layer)
#
# drawEntities = layerProperty(Layer.Entities)
# drawTileEntities = layerProperty(Layer.TileEntities)
# drawTileTicks = layerProperty(Layer.TileTicks)
# drawMonsters = layerProperty(Layer.Monsters)
# drawItems = layerProperty(Layer.Items)
# drawTerrainPopulated = layerProperty(Layer.TerrainPopulated)
def discardChunk(self, cx, cz):
"""
@ -254,3 +260,15 @@ class WorldScene(scenegraph.Node):
self.chunkRenderInfo[cPos] = chunkInfo
return chunkInfo
def setLayerVisible(self, layerName, visible):
if visible:
self.visibleLayers.add(layerName)
else:
self.visibleLayers.discard(layerName)
for groupNode in self.groupNodes.itervalues():
groupNode.setLayerVisible(layerName, visible)
def setVisibleLayers(self, layerNames):
self.visibleLayers = set(layerNames)

View File

@ -8,18 +8,18 @@ import math
from math import degrees, atan, tan, radians, cos, sin
import numpy
from PySide.QtCore import Qt
from PySide import QtGui, QtCore
from mcedit2.rendering.layers import Layer
from mcedit2.util import profiler
from mcedit2.widgets.layout import Column, Row
from mcedit2.util.lazyprop import lazyprop
from mcedit2.worldview.viewcontrols import ViewControls
from mcedit2.worldview.worldview import WorldView, iterateChunks
from mcedit2.worldview.worldview import WorldView, iterateChunks, LayerToggleGroup
from mcedit2.worldview.viewaction import ViewAction
log = logging.getLogger(__name__)
@ -40,13 +40,7 @@ class CameraWorldViewFrame(QtGui.QWidget):
perspectiveInput.toggled.connect(view.setPerspective)
showButton = QtGui.QPushButton("Show...")
showMenu = QtGui.QMenu()
for layer in Layer.AllLayers:
showMenu.addAction(layer)
showButton.setMenu(showMenu)
view._showMenu = showMenu
showButton.setMenu(view.layerToggleGroup.menu)
self.setLayout(Column(Row(None,
showButton,
@ -214,6 +208,7 @@ class CameraWorldView(WorldView):
self.viewDistance = val
self._chunkIter = None
self.discardChunksOutsideViewDistance()
self.update()
def centerOnPoint(self, pos, distance=20):
awayVector = self.cameraVector * -distance
@ -257,7 +252,7 @@ class CameraWorldView(WorldView):
fovy = degrees(atan(w / h * tan(radians(self.fov) * 0.5)))
projection = QtGui.QMatrix4x4()
projection.perspective(fovy, w / h, 0.05, self.viewDistance * 20)
projection.perspective(fovy, w / h, 0.05, max(256, self.viewDistance * 20))
self.matrixNode.projection = projection
@lazyprop
@ -296,11 +291,8 @@ class CameraWorldView(WorldView):
self.viewportMoved.emit(self)
@profiler.function("discardChunks")
def discardChunksOutsideViewDistance(self, worldScene=None):
if worldScene is None:
worldScene = self.worldScene
positions = list(worldScene.chunkPositions()) # xxxx
def discardChunksOutsideViewDistance(self):
positions = list(self.worldScene.chunkPositions()) # xxxx
if not len(positions):
return
@ -332,7 +324,7 @@ class CameraWorldView(WorldView):
chunks = chunks[outsideCenter & outsideFocus]
log.debug("Discarding %d chunks...", len(chunks))
worldScene.discardChunks(chunks)
self.worldScene.discardChunks(chunks)
def recieveChunk(self, chunk):
cx, cz = chunk.chunkPosition
@ -343,7 +335,6 @@ class CameraWorldView(WorldView):
return iter([])
return super(CameraWorldView, self).recieveChunk(chunk)
class CameraPanMouseAction(ViewAction):
button = Qt.RightButton
mouseDragStart = None

View File

@ -159,6 +159,10 @@ class SlicedWorldScene(scenegraph.Node):
for mesh in self.sliceScenes.values():
mesh.invalidateChunk(*c)
def setVisibleLayers(self, layerNames):
for scene in self.sliceScenes.itervalues():
scene.setVisibleLayers(layerNames)
class CutawayWorldView(WorldView):
def __init__(self, *a, **kw):

View File

@ -18,10 +18,12 @@ from mcedit2.rendering import worldscene, loadablechunks, sky, compass
from mcedit2.rendering.chunknode import ChunkNode
from mcedit2.rendering.frustum import Frustum
from mcedit2.rendering.geometrycache import GeometryCache
from mcedit2.rendering.layers import Layer
from mcedit2.rendering.textureatlas import TextureAtlas
from mcedit2.rendering.vertexarraybuffer import VertexArrayBuffer
from mcedit2.rendering import scenegraph, rendergraph
from mcedit2.util import profiler, raycast
from mcedit2.util.settings import Settings
from mcedit2.widgets.infopanel import InfoPanel
from mceditlib import faces, exceptions
from mceditlib.geometry import Vector, Ray
@ -88,6 +90,10 @@ class WorldView(QGLWidget):
QGLWidget.__init__(self, shareWidget=sharedGLWidget)
self.setSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Expanding)
self.setFocusPolicy(Qt.ClickFocus)
self.layerToggleGroup = LayerToggleGroup()
self.layerToggleGroup.layerToggled.connect(self.setLayerVisible)
self.dimension = dimension
self.worldScene = None
self.loadableChunksNode = None
@ -144,6 +150,7 @@ class WorldView(QGLWidget):
def createSceneGraph(self):
sceneGraph = scenegraph.Node()
self.worldScene = self.createWorldScene()
self.worldScene.setVisibleLayers(self.layerToggleGroup.getVisibleLayers())
clearNode = scenegraph.ClearNode()
skyNode = sky.SkyNode()
@ -562,7 +569,9 @@ class WorldView(QGLWidget):
self.worldScene.invalidateChunk(cx, cz)
self.resetLoadOrder()
def setLayerVisible(self, layerName, visible):
self.worldScene.setLayerVisible(layerName, visible)
self.resetLoadOrder()
def iterateChunks(x, z, radius):
"""
@ -708,3 +717,42 @@ class WorldCursorInfo(InfoPanel):
except Exception as e:
log.exception("Error describing block: %r", e)
return "Error describing block: %r" % e
LayerToggleOptions = Settings().getNamespace("layertoggleoptions")
class LayerToggleGroup(QtCore.QObject):
def __init__(self, *args, **kwargs):
super(LayerToggleGroup, self).__init__(*args, **kwargs)
self.actions = {}
self.actionGroup = QtGui.QActionGroup(self)
self.actionGroup.setExclusive(False)
self.options = {}
for layer in Layer.AllLayers:
option = LayerToggleOptions.getOption("%s_visible" % layer, bool, True)
self.options[layer] = option
action = QtGui.QAction(layer, self)
action.setCheckable(True)
action.setChecked(option.value())
log.info("LAYER %s VISIBLE %s", layer, option.value())
action.layerName = layer
self.actions[layer] = action
self.actionGroup.addAction(action)
self.actionGroup.triggered.connect(self.actionTriggered)
self.menu = QtGui.QMenu()
for layer in Layer.AllLayers:
self.menu.addAction(self.actions[layer])
def actionTriggered(self, action):
checked = action.isChecked()
log.info("Set layer %s to %s", action.layerName, checked)
self.options[action.layerName].setValue(checked)
self.layerToggled.emit(action.layerName, checked)
layerToggled = QtCore.Signal(str, bool)
def getVisibleLayers(self):
return [layer for layer in self.actions if self.actions[layer].isChecked()]