Entities, TileEntities etc can now be toggled on and off
Restored another MCEdit1 feature!
This commit is contained in:
parent
88d7f61666
commit
ba53617acf
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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()]
|
||||
|
Reference in New Issue
Block a user