/* Copyright (c) 2015 Jesper Öqvist <jesper@llbit.se> * * This file is part of Chunky. * * Chunky is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Chunky is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Chunky. If not, see <http://www.gnu.org/licenses/>. */ package se.llbit.chunky.world.entity; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import se.llbit.chunky.model.Model; import se.llbit.chunky.resources.SignTexture; import se.llbit.chunky.world.Material; import se.llbit.chunky.world.material.SignMaterial; import se.llbit.chunky.world.material.TextureMaterial; import se.llbit.json.JsonArray; import se.llbit.json.JsonObject; import se.llbit.json.JsonParser; import se.llbit.json.JsonParser.SyntaxError; import se.llbit.json.JsonValue; import se.llbit.math.Quad; import se.llbit.math.Transform; import se.llbit.math.Vector3; import se.llbit.math.Vector4; import se.llbit.math.primitive.Primitive; import se.llbit.nbt.Tag; import se.llbit.nbt.CompoundTag; public class SignEntity extends Entity { public enum Color { BLACK(0, 0xFF000000), DARK_BLUE(1, 0xFF0000AA), DARK_GREEN(2, 0xFF00AA00), DARK_AQUA(3, 0xFF00AAAA), DARK_RED(4, 0xFFAA0000), DARK_PURPLE(5, 0xFFAA00AA), GOLD(6, 0xFFFFAA00), GRAY(7, 0xFFAAAAAA), DARK_GRAY(8, 0xFF555555), BLUE(9, 0xFF5555FF), GREEN(10, 0xFF55FF55), AQUA(11, 0xFF55FFFF), RED(12, 0xFFFF5555), LIGHT_PURPLE(13, 0xFFFF55FF), YELLOW(14, 0xFFFFFF55), WHITE(15, 0xFFFFFFFF); public final int id; public final int rgbColor; private static final Map<String, Color> map = new HashMap<>(); static { map.put("dark_blue", DARK_BLUE); map.put("dark_green", DARK_GREEN); map.put("dark_aqua", DARK_AQUA); map.put("dark_red", DARK_RED); map.put("dark_purple", DARK_PURPLE); map.put("gold", GOLD); map.put("gray", GRAY); map.put("dark_gray", DARK_GRAY); map.put("blue", BLUE); map.put("green", GREEN); map.put("aqua", AQUA); map.put("red", RED); map.put("light_purple", LIGHT_PURPLE); map.put("yellow", YELLOW); map.put("white", WHITE); } Color(int id, int color) { this.id = id; this.rgbColor = color; } public static Color get(String color) { if (map.containsKey(color)) { return map.get(color); } else { return Color.BLACK; } } public static Color get(int id) { return values()[id & 0xF]; } } // Facing south. protected static Quad[] sides = { // Front face. new Quad(new Vector3(0, 9 / 16., 9 / 16.), new Vector3(1, 9 / 16., 9 / 16.), new Vector3(0, 17 / 16., 9 / 16.), new Vector4(0, 1, 0, 1)), // Back face. new Quad(new Vector3(1, 9 / 16., 7 / 16.), new Vector3(0, 9 / 16., 7 / 16.), new Vector3(1, 17 / 16., 7 / 16.), new Vector4(28 / 64., 52 / 64., 18 / 32., 30 / 32.)), // Left face. new Quad(new Vector3(0, 9 / 16., 7 / 16.), new Vector3(0, 9 / 16., 9 / 16.), new Vector3(0, 17 / 16., 7 / 16.), new Vector4(0, 2 / 64., 18 / 32., 30 / 32.)), // Right face. new Quad(new Vector3(1, 9 / 16., 9 / 16.), new Vector3(1, 9 / 16., 7 / 16.), new Vector3(1, 17 / 16., 9 / 16.), new Vector4(26 / 64., 28 / 64., 18 / 32., 30 / 32.)), // Top face. new Quad(new Vector3(1, 17 / 16., 7 / 16.), new Vector3(0, 17 / 16., 7 / 16.), new Vector3(1, 17 / 16., 9 / 16.), new Vector4(2 / 64., 26 / 64., 1, 30 / 32.)), // Bottom face. new Quad(new Vector3(0, 9 / 16., 7 / 16.), new Vector3(1, 9 / 16., 7 / 16.), new Vector3(0, 9 / 16., 9 / 16.), new Vector4(26 / 64., 50 / 64., 1, 30 / 32.)), // Post front. new Quad(new Vector3(7 / 16., 0, 9 / 16.), new Vector3(9 / 16., 0, 9 / 16.), new Vector3(7 / 16., 9 / 16., 9 / 16.), new Vector4(2 / 64., 4 / 64., 2 / 32., 16 / 32.)), // Post back. new Quad(new Vector3(9 / 16., 0, 7 / 16.), new Vector3(7 / 16., 0, 7 / 16.), new Vector3(9 / 16., 9 / 16., 7 / 16.), new Vector4(4 / 64., 6 / 64., 2 / 32., 16 / 32.)), // Post left. new Quad(new Vector3(7 / 16., 0, 7 / 16.), new Vector3(7 / 16., 0, 9 / 16.), new Vector3(7 / 16., 9 / 16., 7 / 16.), new Vector4(0, 2 / 64., 2 / 32., 16 / 32.)), // Post right. new Quad(new Vector3(9 / 16., 0, 9 / 16.), new Vector3(9 / 16., 0, 7 / 16.), new Vector3(9 / 16., 9 / 16., 9 / 16.), new Vector4(6 / 64., 8 / 64., 2 / 32., 16 / 32.)), // Post bottom. new Quad(new Vector3(7 / 16., 0, 7 / 16.), new Vector3(9 / 16., 0, 7 / 16.), new Vector3(7 / 16., 0, 9 / 16.), new Vector4(4 / 64., 6 / 64., 16 / 32., 18 / 32.)), }; private static final Quad[][] rot = new Quad[16][]; static { // Rotate the sign post to face the correct direction. rot[0] = sides; for (int i = 1; i < 16; ++i) { rot[i] = Model.rotateY(sides, -i * Math.PI / 8); } } private final JsonArray[] text; private final int angle; private final SignTexture texture; public SignEntity(Vector3 position, CompoundTag entityTag, int blockData) { this(position, getTextLines(entityTag), blockData & 0xF); } public SignEntity(Vector3 position, JsonArray[] text, int direction) { super(position); this.text = text; this.angle = direction; this.texture = new SignTexture(text); } /** * Extracts the text lines from a sign entity tag. * * @return array of text lines. */ protected static JsonArray[] getTextLines(CompoundTag entityTag) { return new JsonArray[] {extractText(entityTag.get("Text1")), extractText(entityTag.get("Text2")), extractText(entityTag.get("Text3")), extractText(entityTag.get("Text4")),}; } /** * Extract text from entity tag. */ private static JsonArray extractText(Tag tag) { JsonArray array = new JsonArray(); String data = tag.stringValue(""); if (data.startsWith("\"")) { addText(array, data.substring(1, data.length() - 1)); } else { JsonParser parser = new JsonParser(new ByteArrayInputStream(data.getBytes())); try { JsonValue value = parser.parse(); if (value.isObject()) { JsonObject obj = value.object(); addText(array, obj.get("text").stringValue("")); JsonArray extraArray = obj.get("extra").array(); for (JsonValue extra : extraArray) { if (extra.isObject()) { JsonObject extraObject = extra.object(); addText(array, extraObject.get("text").stringValue(""), extraObject.get("color").stringValue("")); } else { addText(array, extra.stringValue("")); } } } else { for (JsonValue item : value.array()) { addText(array, item.stringValue("")); } } } catch (IOException | SyntaxError e) { } } return array; } /** * Add a text entry to a JSON text array. */ private static void addText(JsonArray array, String text) { if (!text.isEmpty()) { JsonObject object = new JsonObject(); object.add("text", text); array.add(object); } } /** * Add a text entry with color to a JSON text array. */ private static void addText(JsonArray array, String text, String color) { if (!color.isEmpty() && !text.isEmpty()) { JsonObject object = new JsonObject(); object.add("text", text); object.add("color", Color.get(color).id); array.add(object); } else { addText(array, text); } } @Override public Collection<Primitive> primitives(Vector3 offset) { Collection<Primitive> primitives = new LinkedList<>(); Transform transform = Transform.NONE .translate(position.x + offset.x, position.y + offset.y, position.z + offset.z); for (int i = 0; i < sides.length; ++i) { Quad quad = rot[angle][i]; Material material; if (i != 0) { material = SignMaterial.INSTANCE; } else { material = new TextureMaterial(texture); } quad.addTriangles(primitives, material, transform); } return primitives; } @Override public JsonValue toJson() { JsonObject json = new JsonObject(); json.add("kind", "sign"); json.add("position", position.toJson()); json.add("text", textToJson(text)); json.add("direction", angle); return json; } /** * Unmarshalls a sign entity from JSON data. */ public static Entity fromJson(JsonObject json) { Vector3 position = new Vector3(); position.fromJson(json.get("position").object()); JsonArray[] text = textFromJson(json.get("text")); int direction = json.get("direction").intValue(0); return new SignEntity(position, text, direction); } /** * Marshalls sign text to JSON representation. */ protected static JsonArray textToJson(JsonArray[] text) { JsonArray array = new JsonArray(); array.add(text[0].copy()); array.add(text[1].copy()); array.add(text[2].copy()); array.add(text[3].copy()); return array; } /** * Unmarshalls sign text from JSON representation. */ protected static JsonArray[] textFromJson(JsonValue json) { JsonArray array = json.array(); JsonArray[] text = new JsonArray[4]; text[0] = array.get(0).array(); text[1] = array.get(1).array(); text[2] = array.get(2).array(); text[3] = array.get(3).array(); return text; } }