From f5ddfa711758bce04fe851a1ace428c7bfc520b7 Mon Sep 17 00:00:00 2001 From: David Vierra Date: Tue, 14 Jul 2015 13:38:04 -1000 Subject: [PATCH] Add color coding to command blocks, split locations from colors --- .../rendering/chunkmeshes/entitymesh.py | 275 +++++++++++++++++- src/mcedit2/rendering/chunkupdate.py | 10 +- src/mcedit2/rendering/layers.py | 11 +- 3 files changed, 278 insertions(+), 18 deletions(-) diff --git a/src/mcedit2/rendering/chunkmeshes/entitymesh.py b/src/mcedit2/rendering/chunkmeshes/entitymesh.py index 21187c2..49ec763 100644 --- a/src/mcedit2/rendering/chunkmeshes/entitymesh.py +++ b/src/mcedit2/rendering/chunkmeshes/entitymesh.py @@ -15,8 +15,9 @@ from mcedit2.rendering.chunkmeshes.entity import models from mcedit2.rendering.layers import Layer from mcedit2.rendering.scenegraph.bind_texture import BindTextureNode from mcedit2.rendering.scenegraph.matrix import TranslateNode, RotateNode -from mcedit2.rendering.scenegraph.misc import PolygonModeNode +from mcedit2.rendering.scenegraph.misc import PolygonModeNode, LineWidthNode from mcedit2.rendering.scenegraph.depth_test import DepthFuncNode +from mcedit2.rendering.scenegraph.scenenode import Node from mcedit2.rendering.scenegraph.vertex_array import VertexNode from mcedit2.rendering.slices import _XYZ from mcedit2.rendering.vertexarraybuffer import QuadVertexArrayBuffer @@ -25,11 +26,20 @@ from mceditlib.anvil.entities import PCPaintingEntityRefBase log = logging.getLogger(__name__) +# TODO: allow these damned things to return multiple scenenodes on different layers, +# TODO: so we don't have to iterate TileEntities over and over + class EntityMeshBase(ChunkMeshBase): renderstate = renderstates.RenderstateEntityNode detailLevels = (0, 1, 2) def _computeVertices(self, positions, colors, offset=False, chunkPosition=(0, 0)): + colors = numpy.asarray(colors, dtype=numpy.uint8) + if len(colors.shape) > 1: + colors = colors[:, None, None, :] + else: + colors = colors[None, None, None, :] + cx, cz = chunkPosition x = cx << 4 z = cz << 4 @@ -54,23 +64,258 @@ class EntityMeshBase(ChunkMeshBase): return vertexBuffer -class TileEntityMesh(EntityMeshBase): - layer = Layer.TileEntities +def colorHash(text): + """ + Stolen from https://github.com/zenozeng/color-hash + """ + SaturationArray = [0.66, 0.83, .95] + LightnessArray = [0.5, 0.66, 0.75] + + h = hash(text) + Hue = h % 359 # (Note that 359 is a prime) + Saturation = SaturationArray[h // 360 % len(SaturationArray)] + Lightness = LightnessArray[h // 360 // len(SaturationArray) % len(LightnessArray)] + return Hue, Saturation, Lightness + +allCommands = [ + "achievement", + "blockdata", + "clear", + "clone", + "defaultgamemod", + "difficulty", + "effect", + "enchant", + "entitydata", + "execute", + "fill", + "gamemode", + "gamerule", + "give", + "help", + "kill", + "list", + "me", + "particle", + "playsound", + "replaceitem", + "say", + "scoreboard", + "seed", + "setblock", + "setworldspawn", + "spawnpoint", + "spreadplayers", + "stats", + "summon", + "tell", + "tellraw", + "testfor", + "testforblock", + "testforblocks", + "time", + "title", + "toggledownfall", + "tp", + "trigger", + "weather", + "worldborder", + "xp", +] + +def HSL2RGB(H, S, L): + H /= 360 + + q = L * (1 + S) if L < 0.5 else L + S - L * S + p = 2 * L - q + + def something(color): + if color < 0: + color += 1 + + if color > 1: + color -= 1 + + if color < 1/6: + color = p + (q - p) * 6 * color + elif color < 0.5: + color = q + elif color < 2/3: + color = p + (q - p) * 6 * (2/3 - color) + else: + color = p + + return int(color * 255) + + return [something(a) for a in (H + 1/3, H, H - 1/3)] + + +def computeCommandColor(cmdName): + return tuple(HSL2RGB(*colorHash(cmdName))) + +if __name__ == '__main__': + colors = {c:computeCommandColor(c) for c in allCommands} + from pprint import pprint + pprint(colors) + +_commandColors = { + 'achievement': (38, 248, 6), + 'blockdata': (6, 18, 248), + 'clear': (240, 96, 197), + 'clone': (138, 244, 203), + 'defaultgamemod': (6, 248, 30), + 'difficulty': (219, 233, 21), + 'effect': (115, 248, 6), + 'enchant': (95, 21, 233), + 'entitydata': (250, 85, 96), + 'execute': (225, 111, 189), + 'fill': (233, 191, 149), + 'gamemode': (130, 148, 251), + 'gamerule': (244, 189, 138), + 'give': (244, 138, 180), + 'help': (250, 168, 85), + 'kill': (111, 225, 185), + 'list': (6, 248, 127), + 'me': (176, 233, 21), + 'particle': (43, 211, 60), + 'playsound': (251, 156, 130), + 'replaceitem': (21, 233, 134), + 'say': (240, 96, 223), + 'scoreboard': (240, 96, 96), + 'seed': (43, 211, 127), + 'setblock': (233, 116, 21), + 'setworldspawn': (85, 160, 250), + 'spawnpoint': (149, 233, 206), + 'spreadplayers': (138, 141, 244), + 'stats': (233, 64, 21), + 'summon': (233, 222, 149), + 'tell': (224, 248, 6), + 'tellraw': (149, 233, 201), + 'testfor': (211, 43, 65), + 'testforblock': (206, 233, 149), + 'testforblocks': (96, 240, 199), + 'time': (247, 130, 251), + 'title': (91, 248, 6), + 'toggledownfall': (250, 85, 245), + 'tp': (251, 201, 130), + 'trigger': (143, 85, 250), + 'weather': (21, 233, 81), + 'worldborder': (21, 233, 102), + 'xp': (98, 96, 240) +} + +def commandColor(cmd): + cmd = cmd.lower() + color = _commandColors.get(cmd) + if color is None: + color = computeCommandColor(cmd) + return color + +class TileEntityLocationMesh(EntityMeshBase): + layer = Layer.TileEntityLocations def makeChunkVertices(self, chunk, limitBox): tilePositions = [] + defaultColor = (0xff, 0xff, 0x33, 0x44) for i, ref in enumerate(chunk.TileEntities): if i % 10 == 0: yield if limitBox and ref.Position not in limitBox: continue + if ref.id == "Control": + continue tilePositions.append(ref.Position) - tiles = self._computeVertices(tilePositions, (0xff, 0xff, 0x33, 0x44), chunkPosition=chunk.chunkPosition) - yield - self.sceneNode = VertexNode(tiles) + tiles = self._computeVertices(tilePositions, defaultColor, chunkPosition=chunk.chunkPosition) + vertexNode = VertexNode([tiles]) + polyNode = PolygonModeNode(GL.GL_FRONT_AND_BACK, GL.GL_LINE) + polyNode.addChild(vertexNode) + lineNode = LineWidthNode(2.0) + lineNode.addChild(polyNode) + depthNode = DepthFuncNode(GL.GL_ALWAYS) + depthNode.addChild(lineNode) + + self.sceneNode = Node() + self.sceneNode.addChild(depthNode) + + vertexNode = VertexNode([tiles]) + self.sceneNode.addChild(vertexNode) + + +class CommandBlockLocationMesh(EntityMeshBase): + layer = Layer.CommandBlockLocations + + def makeChunkVertices(self, chunk, limitBox): + tilePositions = [] + tileColors = [] + defaultColor = (0xff, 0x33, 0x33, 0x44) + for i, ref in enumerate(chunk.TileEntities): + if i % 10 == 0: + yield + + if limitBox and ref.Position not in limitBox: + continue + if ref.id == "Control": + tilePositions.append(ref.Position) + cmdText = ref.Command + if len(cmdText): + if cmdText[0] == "/": + cmdText = cmdText[1:] + command, _ = cmdText.split(None, 1) + color = commandColor(command) + tileColors.append(color + (0x44,)) + else: + tileColors.append(defaultColor) + else: + continue + + tiles = self._computeVertices(tilePositions, tileColors, chunkPosition=chunk.chunkPosition) + + vertexNode = VertexNode([tiles]) + polyNode = PolygonModeNode(GL.GL_FRONT_AND_BACK, GL.GL_LINE) + polyNode.addChild(vertexNode) + lineNode = LineWidthNode(2.0) + lineNode.addChild(polyNode) + depthNode = DepthFuncNode(GL.GL_ALWAYS) + depthNode.addChild(lineNode) + + self.sceneNode = Node() + self.sceneNode.addChild(depthNode) + + +class CommandBlockColorsMesh(EntityMeshBase): + layer = Layer.CommandBlockColors + + def makeChunkVertices(self, chunk, limitBox): + tilePositions = [] + tileColors = [] + defaultColor = (0xff, 0xff, 0x33, 0x44) + for i, ref in enumerate(chunk.TileEntities): + if i % 10 == 0: + yield + + if limitBox and ref.Position not in limitBox: + continue + if ref.id == "Control": + cmdText = ref.Command + if len(cmdText): + if cmdText[0] == "/": + cmdText = cmdText[1:] + command, _ = cmdText.split(None, 1) + color = commandColor(command) + tileColors.append(color + (0x44,)) + else: + tileColors.append(defaultColor) + else: + continue + tilePositions.append(ref.Position) + + tiles = self._computeVertices(tilePositions, tileColors, chunkPosition=chunk.chunkPosition) + + vertexNode = VertexNode([tiles]) + self.sceneNode = vertexNode class ItemFrameMesh(EntityMeshBase): @@ -190,8 +435,8 @@ class MonsterModelRenderer(ChunkMeshBase): self.sceneNode = sceneNode -class MonsterRenderer(EntityMeshBase): - layer = Layer.Entities # xxx Monsters +class MonsterLocationRenderer(EntityMeshBase): + layer = Layer.MonsterLocations notMonsters = {"Item", "XPOrb", "Painting", "ItemFrame"} def makeChunkVertices(self, chunk, limitBox): @@ -217,10 +462,16 @@ class MonsterRenderer(EntityMeshBase): vertexNode = VertexNode(monsters) polyNode = PolygonModeNode(GL.GL_FRONT_AND_BACK, GL.GL_LINE) polyNode.addChild(vertexNode) + lineNode = LineWidthNode(2.0) + lineNode.addChild(polyNode) depthNode = DepthFuncNode(GL.GL_ALWAYS) - depthNode.addChild(polyNode) + depthNode.addChild(lineNode) - self.sceneNode = depthNode + self.sceneNode = Node() + self.sceneNode.addChild(depthNode) + + vertexNode = VertexNode(monsters) + self.sceneNode.addChild(vertexNode) @@ -249,8 +500,8 @@ class ItemRenderer(EntityMeshBase): entityColors.append(color) items = self._computeVertices(entityPositions, - numpy.array(entityColors, dtype='uint8')[:, numpy.newaxis, numpy.newaxis], - offset=True, chunkPosition=chunk.chunkPosition) + entityColors, + offset=True, chunkPosition=chunk.chunkPosition) yield self.sceneNode = VertexNode(items) diff --git a/src/mcedit2/rendering/chunkupdate.py b/src/mcedit2/rendering/chunkupdate.py index fffddad..86abeba 100644 --- a/src/mcedit2/rendering/chunkupdate.py +++ b/src/mcedit2/rendering/chunkupdate.py @@ -9,8 +9,8 @@ from mcedit2.rendering.modelmesh import BlockModelMesh from mcedit2.rendering import layers from mcedit2.rendering.chunkmeshes.chunksections import ChunkSectionsRenderer -from mcedit2.rendering.chunkmeshes.entitymesh import TileEntityMesh, MonsterRenderer, ItemRenderer, \ - ItemFrameMesh, MonsterModelRenderer +from mcedit2.rendering.chunkmeshes.entitymesh import TileEntityLocationMesh, MonsterLocationRenderer, ItemRenderer, \ + ItemFrameMesh, MonsterModelRenderer, CommandBlockColorsMesh, CommandBlockLocationMesh from mcedit2.rendering.chunkmeshes.lowdetail import LowDetailBlockMesh, OverheadBlockMesh from mcedit2.rendering.chunkmeshes.terrainpop import TerrainPopulatedRenderer from mcedit2.rendering.chunkmeshes.tileticks import TileTicksRenderer @@ -122,9 +122,11 @@ class ChunkUpdate(object): return self.chunkInfo.worldScene.roughGraphics wholeChunkMeshClasses = [ - TileEntityMesh, + CommandBlockLocationMesh, + CommandBlockColorsMesh, + TileEntityLocationMesh, ItemFrameMesh, - MonsterRenderer, + MonsterLocationRenderer, MonsterModelRenderer, ItemRenderer, TileTicksRenderer, diff --git a/src/mcedit2/rendering/layers.py b/src/mcedit2/rendering/layers.py index 0331730..81722f7 100644 --- a/src/mcedit2/rendering/layers.py +++ b/src/mcedit2/rendering/layers.py @@ -9,11 +9,18 @@ class Layer: Blocks = "Blocks" Entities = "Entities" Monsters = "Monsters" + MonsterLocations = "MonsterLocations" Items = "Items" TileEntities = "TileEntities" + TileEntityLocations = "TileEntityLocations" + CommandBlockColors = "CommandBlockColors" + CommandBlockLocations = "CommandBlockLocations" ItemFrames = "ItemFrames" TileTicks = "TileTicks" TerrainPopulated = "TerrainPopulated" ChunkSections = "ChunkSections" - AllLayers = (Blocks, Entities, Items, TileEntities, ItemFrames, TileTicks, ChunkSections) - DefaultVisibleLayers = (Blocks, Entities, Items, TileEntities, ItemFrames) + AllLayers = (Blocks, Entities, Monsters, MonsterLocations, Items, + TileEntities, TileEntityLocations, + CommandBlockColors, CommandBlockLocations, + ItemFrames, TileTicks, ChunkSections) + DefaultVisibleLayers = (Blocks, Entities, Items, TileEntities, CommandBlockColors, ItemFrames)