modelviewer: begin work on tile shapes

This commit is contained in:
Adam
2018-05-20 20:15:37 -04:00
parent 3b89933e37
commit aa30d2c782
8 changed files with 1185 additions and 275 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* Copyright (c) 2016-2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,8 @@ package net.runelite.modelviewer;
import com.google.gson.Gson;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -37,28 +35,36 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import net.runelite.cache.IndexType;
import net.runelite.cache.ObjectManager;
import net.runelite.cache.OverlayManager;
import net.runelite.cache.SpriteManager;
import net.runelite.cache.TextureManager;
import net.runelite.cache.UnderlayManager;
import net.runelite.cache.definitions.KitDefinition;
import net.runelite.cache.definitions.LocationsDefinition;
import net.runelite.cache.definitions.MapDefinition;
import net.runelite.cache.definitions.ModelDefinition;
import net.runelite.cache.definitions.NpcDefinition;
import net.runelite.cache.definitions.ObjectDefinition;
import net.runelite.cache.definitions.OverlayDefinition;
import net.runelite.cache.definitions.SpriteDefinition;
import net.runelite.cache.definitions.TextureDefinition;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.loaders.LocationsLoader;
import net.runelite.cache.definitions.loaders.MapLoader;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Storage;
import net.runelite.cache.fs.Store;
import net.runelite.cache.models.Vector3f;
import net.runelite.cache.models.VertexNormal;
import net.runelite.cache.region.Location;
import net.runelite.cache.region.Position;
import net.runelite.cache.region.Region;
import net.runelite.cache.util.XteaKeyManager;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.compress.utils.IOUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
@@ -77,27 +83,24 @@ public class ModelViewer
{
private static final Logger logger = LoggerFactory.getLogger(ModelViewer.class);
private static final int NUM_UNDERLAYS = 150;
private static final int NUM_OVERLAYS = 174;
private static final int NUM_TEXTURES = 61;
private static final int NUM_OBJECTS = 28598;
/**
* size of a tile in local coordinates
*/
private static final int TILE_SCALE = 128;
private static final int HEIGHT_MOD = 4;
private static UnderlayDefinition[] underlays = new UnderlayDefinition[NUM_UNDERLAYS];
private static OverlayDefinition[] overlays = new OverlayDefinition[NUM_OVERLAYS];
private static ObjectManager objectManager;
private static TextureManager textureManager;
private static SpriteManager spriteManager;
private static Map<Integer, Texture> textures = new HashMap<>();
private static ObjectDefinition[] objects = new ObjectDefinition[NUM_OBJECTS];
public static void main(String[] args) throws Exception
{
Options options = new Options();
options.addOption(null, "store", true, "store directory");
options.addOption(null, "npcdir", true, "npc directory");
options.addOption(null, "mapdir", true, "maps directory");
options.addOption(null, "objectdir", true, "objects directory");
@@ -117,10 +120,33 @@ public class ModelViewer
NpcDefinition npcdef = null;
ObjectDefinition objdef = null;
Store store = null;
UnderlayManager underlayManager = null;
OverlayManager overlayManager = null;
List<ModelDefinition> models = new ArrayList<>();
Region region = null;
if (cmd.hasOption("store"))
{
store = new Store(new File(cmd.getOptionValue("store")));
store.load();
underlayManager = new UnderlayManager(store);
underlayManager.load();
overlayManager = new OverlayManager(store);
overlayManager.load();
objectManager = new ObjectManager(store);
objectManager.load();
textureManager = new TextureManager(store);
textureManager.load();
spriteManager = new SpriteManager(store);
spriteManager.load();
}
if (cmd.hasOption("model"))
{
// render model
@@ -166,30 +192,31 @@ public class ModelViewer
int x = Integer.parseInt(s[0]), y = Integer.parseInt(s[1]);
XteaKeyManager keyManager = new XteaKeyManager();
keyManager.loadKeys();
int[] keys = keyManager.getKeys(x << 8 | y);
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.MAPS);
Archive mapArchive = index.findArchiveByName("m" + x + "_" + y);
Archive locationsArchive = index.findArchiveByName("l" + x + "_" + y);
region = new Region(x, y);
byte[] mapData = mapArchive.decompress(storage.loadArchive(mapArchive));
MapLoader mapLoader = new MapLoader();
LocationsLoader locationsLoader = new LocationsLoader();
MapDefinition mapDef = mapLoader.load(x, y, mapData);
region.loadTerrain(mapDef);
try (FileInputStream fin = new FileInputStream(mapdir + "/m" + x + "_" + y + ".dat"))
if (keys != null)
{
byte[] b = IOUtils.toByteArray(fin);
MapDefinition mapDef = mapLoader.load(x, y, b);
region.loadTerrain(mapDef);
}
try (FileInputStream fin = new FileInputStream(mapdir + "/l" + x + "_" + y + ".dat"))
{
byte[] b = IOUtils.toByteArray(fin);
LocationsDefinition locDef = locationsLoader.load(x, y, b);
byte[] locationData = locationsArchive.decompress(storage.loadArchive(locationsArchive), keys);
LocationsLoader locationsLoader = new LocationsLoader();
LocationsDefinition locDef = locationsLoader.load(x, y, locationData);
region.loadLocations(locDef);
}
catch (FileNotFoundException ex)
{
logger.info("No landscape file for {},{}", x, y);
}
loadUnderlays();
loadOverlays();
}
if (cmd.hasOption("kits"))
{
@@ -232,6 +259,8 @@ public class ModelViewer
long last = 0;
Camera camera = new Camera();
Scene scene = new Scene(underlayManager, overlayManager);
scene.loadRegion(region);
while (!Display.isCloseRequested())
{
@@ -255,10 +284,11 @@ public class ModelViewer
drawModel(def, recolourToFind, recolourToReplace);
}
drawRegion(region);
drawScene(region, scene);
//drawRegion(region);
Display.update();
Display.sync(50); // fps
Display.sync(20); // fps
long delta = System.currentTimeMillis() - last;
last = System.currentTimeMillis();
@@ -410,139 +440,159 @@ public class ModelViewer
}
}
private static void drawRegion(Region region)
private static void drawScene(Region region, Scene scene)
{
if (region == null)
for (int z = 0; z < Region.Z; ++z)
{
SceneTileModel[][] sceneTileModels = scene.getSceneTiles()[z];
SceneTilePaint[][] sceneTilePaint = scene.getSceneTilePaint()[z];
for (int x = 0; x < Region.X; ++x)
{
for (int y = 0; y < Region.Y; ++y)
{
drawSceneTileModel(region, x, y, z, sceneTileModels[x][y]);
drawSceneTilePaint(region, x, y, z, sceneTilePaint[x][y]);
}
}
}
drawLocations(region);
}
private static void drawSceneTilePaint(Region region, int regionX, int regionY, int z, SceneTilePaint sceneTilePaint)
{
if (sceneTilePaint == null)
{
return;
}
for (int regionX = 0; regionX < Region.X; ++regionX)
int glTexture = -1;
Color color;
if (sceneTilePaint.texture > -1)
{
for (int regionY = 0; regionY < Region.Y; ++regionY)
{
int x = regionX;
int y = regionY;
color = Color.WHITE;
x *= TILE_SCALE;
y *= TILE_SCALE;
Texture texture = getTexture(sceneTilePaint.texture);
glTexture = texture.getOpenglId();
assert glTexture > -1;
/*
Split into two triangles with vertices
x,y,z1 x+1,y,z2 x,y+1,z3
x,y+1,z3 x+1,y,z2 x+1,y+1,z4
z1 = height
z2 = height of tile x+1
z3 = height of tile y-1
in rs 0,0 (x,y) is the bottom left with
y increasing going further from you
in opengl, 0,0 (x,z) is the bottom left
with z decreasing going further from you
in rs, height is also negative
so we do rs(x,y,z) -> opengl(x,-z,-y)
*/
int z1 = -region.getTileHeight(0, regionX, regionY);
int z2 = regionX + 1 < Region.X ? -region.getTileHeight(0, regionX + 1, regionY) : z1;
int z3 = regionY + 1 < Region.Y ? -region.getTileHeight(0, regionX, regionY + 1) : z1;
int z4 = regionX + 1 < Region.X && regionY + 1 < Region.Y ? -region.getTileHeight(0, regionX + 1, regionY + 1) : z1;
// scale down height
z1 /= HEIGHT_MOD;
z2 /= HEIGHT_MOD;
z3 /= HEIGHT_MOD;
z4 /= HEIGHT_MOD;
int underlayId = region.getUnderlayId(0, regionX, regionY);
int overlayId = region.getOverlayId(0, regionX, regionY);
Color color = null;
int glTexture = -1;
if (underlayId > 0)
{
UnderlayDefinition ud = underlays[underlayId - 1];
color = new Color(ud.getColor());
}
if (overlayId > 0)
{
OverlayDefinition od = overlays[overlayId - 1];
color = new Color(od.getRgbColor());
if (od.getSecondaryRgbColor() > -1)
{
color = new Color(od.getSecondaryRgbColor());
}
if (od.getTexture() > -1)
{
color = Color.WHITE;
Texture texture = getTexture(od.getTexture());
glTexture = texture.getOpenglId();
assert glTexture > -1;
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
}
}
GL11.glBegin(GL11.GL_TRIANGLES);
if (color != null)
{
GL11.glColor3f((float) color.getRed() / 255f, (float) color.getGreen() / 255f, (float) color.getBlue() / 255f);
}
// triangle 1
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 0);
}
GL11.glVertex3i(x, z1, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 0);
}
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 1);
}
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
// triangle 2
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 1);
}
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 0);
}
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 1);
}
GL11.glVertex3i(x + TILE_SCALE, z4, -(y + TILE_SCALE));
GL11.glEnd();
if (glTexture > -1)
{
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
}
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
}
else
{
color = new Color(RS2HSB_to_RGB(sceneTilePaint.color));
}
drawLocations(region);
GL11.glBegin(GL11.GL_TRIANGLES);
int x = regionX * TILE_SCALE;
int y = regionY * TILE_SCALE;
GL11.glColor3f((float) color.getRed() / 255f, (float) color.getGreen() / 255f, (float) color.getBlue() / 255f);
int z1 = -region.getTileHeight(z, regionX, regionY);
int z2 = regionX + 1 < Region.X ? -region.getTileHeight(z, regionX + 1, regionY) : z1;
int z3 = regionY + 1 < Region.Y ? -region.getTileHeight(z, regionX, regionY + 1) : z1;
int z4 = regionX + 1 < Region.X && regionY + 1 < Region.Y ? -region.getTileHeight(z, regionX + 1, regionY + 1) : z1;
// triangle 1
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 0);
}
GL11.glVertex3i(x, z1, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 0);
}
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 1);
}
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
// triangle 2
if (glTexture > -1)
{
GL11.glTexCoord2f(0, 1);
}
GL11.glVertex3i(x, z3, -(y + TILE_SCALE));
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 0);
}
GL11.glVertex3i(x + TILE_SCALE, z2, -y);
if (glTexture > -1)
{
GL11.glTexCoord2f(1, 1);
}
GL11.glVertex3i(x + TILE_SCALE, z4, -(y + TILE_SCALE));
GL11.glEnd();
if (glTexture > -1)
{
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
}
private static void drawSceneTileModel(Region region, int regionX, int regionY, int z, SceneTileModel sceneTileModel)
{
if (sceneTileModel == null)
{
return;
}
for (int i = 0; i < sceneTileModel.faceX.length; ++i)
{
int vertexA = sceneTileModel.faceX[i];
int vertexB = sceneTileModel.faceY[i];
int vertexC = sceneTileModel.faceZ[i];
int vertexAx = sceneTileModel.vertexX[vertexA];
int vertexAy = sceneTileModel.vertexY[vertexA];
int vertexAz = sceneTileModel.vertexZ[vertexA];
int vertexBx = sceneTileModel.vertexX[vertexB];
int vertexBy = sceneTileModel.vertexY[vertexB];
int vertexBz = sceneTileModel.vertexZ[vertexB];
int vertexCx = sceneTileModel.vertexX[vertexC];
int vertexCy = sceneTileModel.vertexY[vertexC];
int vertexCz = sceneTileModel.vertexZ[vertexC];
int colorA = sceneTileModel.triangleColorA[i];
int colorB = sceneTileModel.triangleColorB[i];
int colorC = sceneTileModel.triangleColorC[i];
Color color = null;
int glTexture = -1;
color = new Color(RS2HSB_to_RGB(colorA));
GL11.glBegin(GL11.GL_TRIANGLES);
if (color != null)
{
GL11.glColor3f((float) color.getRed() / 255f, (float) color.getGreen() / 255f, (float) color.getBlue() / 255f);
}
GL11.glVertex3i(vertexAx, -vertexAy, -vertexAz);
GL11.glVertex3i(vertexBx, -vertexBy, -vertexBz);
GL11.glVertex3i(vertexCx, -vertexCy, -vertexCz);
GL11.glEnd();
if (glTexture > -1)
{
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
}
}
private static void drawLocations(Region region)
@@ -559,18 +609,13 @@ public class ModelViewer
Position objectPos = location.getPosition();
if (location.getPosition().getZ() != 0)
{
continue;
}
int regionX = objectPos.getX() - region.getBaseX();
int regionY = objectPos.getY() - region.getBaseY();
int height = -region.getTileHeight(objectPos.getZ(), regionX, regionY) / HEIGHT_MOD;
int height = -region.getTileHeight(objectPos.getZ(), regionX, regionY); // / HEIGHT_MOD;
GL11.glMatrixMode(GL11.GL_MODELVIEW);
// TILE_SCALE/2 to draw the object from the center of the tile it is on
GL11.glTranslatef(regionX * TILE_SCALE + (TILE_SCALE / 2), height * (location.getPosition().getZ() + 1), -regionY * TILE_SCALE - (TILE_SCALE / 2));
GL11.glTranslatef(regionX * TILE_SCALE + (TILE_SCALE / 2), height + (location.getPosition().getZ() * 0), -regionY * TILE_SCALE - (TILE_SCALE / 2));
for (int i = 0; i < object.getObjectModels().length; ++i)
{
@@ -589,60 +634,14 @@ public class ModelViewer
drawModel(md, object.getRecolorToFind(), object.getRecolorToReplace());
}
GL11.glTranslatef(-regionX * TILE_SCALE - (TILE_SCALE / 2), -(height * (location.getPosition().getZ() + 1)), regionY * TILE_SCALE + (TILE_SCALE / 2));
GL11.glTranslatef(-regionX * TILE_SCALE - (TILE_SCALE / 2), -(height + (location.getPosition().getZ() * 0)), regionY * TILE_SCALE + (TILE_SCALE / 2));
GL11.glPopMatrix();
}
}
private static void loadUnderlays() throws IOException
{
for (int i = 0; i < NUM_UNDERLAYS; ++i)
{
try (FileInputStream fin = new FileInputStream("underlays/" + i + ".json"))
{
UnderlayDefinition underlay = new Gson().fromJson(new InputStreamReader(fin), UnderlayDefinition.class);
underlays[i] = underlay;
}
catch (FileNotFoundException ex)
{
}
}
}
private static void loadOverlays() throws IOException
{
for (int i = 0; i < NUM_OVERLAYS; ++i)
{
try (FileInputStream fin = new FileInputStream("overlays/" + i + ".json"))
{
OverlayDefinition overlay = new Gson().fromJson(new InputStreamReader(fin), OverlayDefinition.class);
overlays[i] = overlay;
}
catch (FileNotFoundException ex)
{
}
}
}
private static ObjectDefinition getObject(int id)
{
ObjectDefinition object = objects[id];
if (object != null)
{
return object;
}
try (FileInputStream fin = new FileInputStream("objects/" + id + ".json"))
{
object = new Gson().fromJson(new InputStreamReader(fin), ObjectDefinition.class);
objects[id] = object;
return object;
}
catch (IOException ex)
{
logger.warn(null, ex);
return null;
}
return objectManager.getObject(id);
}
private static Texture getTexture(int id)
@@ -653,73 +652,54 @@ public class ModelViewer
return texture;
}
TextureDefinition td;
try (FileInputStream fin = new FileInputStream("textures/" + id + ".json"))
TextureDefinition textureDefinition = textureManager.findTexture(id);
SpriteDefinition spriteDefinition = spriteManager.findSprite(textureDefinition.getFileIds()[0], 0);
int width = spriteDefinition.getWidth();
int height = spriteDefinition.getHeight();
int[] rgb = spriteDefinition.getPixels();
ByteBuffer buffer = ByteBuffer.allocateDirect(rgb.length * 4);
for (int i = 0; i < rgb.length; ++i)
{
td = new Gson().fromJson(new InputStreamReader(fin), TextureDefinition.class);
}
catch (IOException ex)
{
logger.warn(null, ex);
return null;
int pixel = rgb[i];
// argb -> rgba
int a = pixel >>> 24;
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
buffer.put((byte) r);
buffer.put((byte) g);
buffer.put((byte) b);
buffer.put((byte) a);
}
buffer.position(0);
try (FileInputStream fin = new FileInputStream("sprite/" + td.getFileIds()[0] + "-0.png"))
{
BufferedImage image = ImageIO.read(fin);
int glTexture = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
int width = image.getWidth();
int height = image.getHeight();
int[] rgb = new int[width * height];
//Setup filtering, i.e. how OpenGL will interpolate the pixels when scaling up or down
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
int[] out = image.getRGB(0, 0, width, height, rgb, 0, width);
assert rgb == out;
//Setup wrap mode, i.e. how OpenGL will handle pixels outside of the expected range
//Note: GL_CLAMP_TO_EDGE is part of GL12
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
ByteBuffer buffer = ByteBuffer.allocateDirect(rgb.length * 4);
for (int i = 0; i < rgb.length; ++i)
{
int pixel = rgb[i];
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
// argb -> rgba
int a = pixel >>> 24;
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); // Linear Filtering
GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); // Linear Filtering
buffer.put((byte) r);
buffer.put((byte) g);
buffer.put((byte) b);
buffer.put((byte) a);
}
buffer.position(0);
texture = new Texture(rgb, width, height, glTexture);
textures.put(id, texture);
int glTexture = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
//Setup filtering, i.e. how OpenGL will interpolate the pixels when scaling up or down
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Setup wrap mode, i.e. how OpenGL will handle pixels outside of the expected range
//Note: GL_CLAMP_TO_EDGE is part of GL12
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); // Linear Filtering
GL11.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); // Linear Filtering
texture = new Texture(rgb, width, height, glTexture);
textures.put(id, texture);
return texture;
}
catch (IOException ex)
{
logger.warn(null, ex);
return null;
}
return texture;
}
// found these two functions here https://www.rune-server.org/runescape-development/rs2-client/tools/589900-rs2-hsb-color-picker.html
@@ -729,9 +709,9 @@ public class ModelViewer
float hue = (HSB[0]);
float saturation = (HSB[1]);
float brightness = (HSB[2]);
int encode_hue = (int) (hue * 63); //to 6-bits
int encode_saturation = (int) (saturation * 7); //to 3-bits
int encode_brightness = (int) (brightness * 127); //to 7-bits
int encode_hue = (int) (hue * 63); //to 6-bits
int encode_saturation = (int) (saturation * 7); //to 3-bits
int encode_brightness = (int) (brightness * 127); //to 7-bits
return (encode_hue << 10) + (encode_saturation << 7) + (encode_brightness);
}

View File

@@ -0,0 +1,367 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.modelviewer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.cache.definitions.OverlayDefinition;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.providers.OverlayProvider;
import net.runelite.cache.definitions.providers.UnderlayProvider;
import net.runelite.cache.item.ColorPalette;
import net.runelite.cache.region.Region;
@AllArgsConstructor
public class Scene
{
private static final int REGION_SIZE = 64;
private static final int BLEND = 5;
private static final ColorPalette colorPalette = new ColorPalette(0.9d, 0, 512);
static
{
assert Region.X == Region.Y;
}
private final int[] hues = new int[REGION_SIZE];
private final int[] sats = new int[REGION_SIZE];
private final int[] light = new int[REGION_SIZE];
private final int[] mul = new int[REGION_SIZE];
private final int[] num = new int[REGION_SIZE];
private final UnderlayProvider underlayProvider;
private final OverlayProvider overlayProvider;
@Getter
private final SceneTileModel[][][] sceneTiles = new SceneTileModel[Region.Z][Region.X][Region.Y];
@Getter
private final SceneTilePaint[][][] sceneTilePaint = new SceneTilePaint[Region.Z][Region.X][Region.Y];
public void loadRegion(Region region)
{
int[][] field3834 = new int[Region.X][Region.Y];
int var11 = (int) Math.sqrt(5100.0D);
int var63 = var11 * 768 >> 8;
for (int z = 0; z < Region.Z; ++z)
{
for (int x = 1; x < Region.X - 1; ++x)
{
for (int y = 1; y < Region.Y - 1; ++y)
{
int dx = region.getTileHeight(z, x + 1, y) - region.getTileHeight(z, x - 1, y);
int dy = region.getTileHeight(z, x, y + 1) - region.getTileHeight(z, x, y - 1);
int var17 = (int) Math.sqrt((double) (dx * dx + dy * dy + 65536));
int var18 = (dx << 8) / var17;
int var50 = 65536 / var17;
int var20 = (dy << 8) / var17;
int var21 = (var20 * -50 + var18 * -50 + var50 * -10) / var63 + 96;
// int var22 = (var68[y - 1][x] >> 2) + (var68[y][x - 1] >> 2) + (var68[y + 1][x] >> 3) + (var68[y][x + 1] >> 3) + (var68[y][x] >> 1);
int var22 = 0;
field3834[y][x] = var21 - var22;
}
}
for (int i = 0; i < REGION_SIZE; ++i)
{
hues[i] = 0;
sats[i] = 0;
light[i] = 0;
mul[i] = 0;
num[i] = 0;
}
for (int x = -BLEND; x < Region.X + BLEND; ++x)
{
for (int y = 0; y < Region.Y; ++y)
{
int xr = x + BLEND;
if (xr >= 0 && xr < Region.X)
{
int underlayId = region.getUnderlayId(z, xr, y);
if (underlayId > 0)
{
UnderlayDefinition underlay = underlayProvider.provide(underlayId - 1);
hues[y] += underlay.getHue();
sats[y] += underlay.getSaturation();
light[y] += underlay.getLightness();
mul[y] += underlay.getHueMultiplier();
num[y]++;
}
}
int xl = x - BLEND;
if (xl >= 0 && xl < Region.X)
{
int underlayId = region.getUnderlayId(z, xl, y);
if (underlayId > 0)
{
UnderlayDefinition underlay = underlayProvider.provide(underlayId - 1);
hues[y] -= underlay.getHue();
sats[y] -= underlay.getSaturation();
light[y] -= underlay.getLightness();
mul[y] -= underlay.getHueMultiplier();
num[y]--;
}
}
}
if (x >= 1 && x < Region.X - 1)
{
int runningHues = 0;
int runningSat = 0;
int runningLight = 0;
int runningMultiplier = 0;
int runningNumber = 0;
for (int y = -BLEND; y < Region.Y + BLEND; ++y)
{
int yu = y + BLEND;
if (yu >= 0 && yu < Region.Y)
{
runningHues += hues[yu];
runningSat += sats[yu];
runningLight += light[yu];
runningMultiplier += mul[yu];
runningNumber += num[yu];
}
int yd = y - BLEND;
if (yd >= 0 && yd < Region.Y)
{
runningHues -= hues[yd];
runningSat -= sats[yd];
runningLight -= light[yd];
runningMultiplier -= mul[yd];
runningNumber -= num[yd];
}
if (y >= 1 && y < Region.Y - 1)
{
int underlayId = region.getUnderlayId(z, x, y);
int overlayId = region.getOverlayId(z, x, y);
if (underlayId > 0 || overlayId > 0)
{
int tileHeight = region.getTileHeight(z, x, y);
int tileHeightX1 = region.getTileHeight(z, x + 1, y);
int tileHeightX1Y1 = region.getTileHeight(z, x + 1, y + 1);
int tileHeightY1 = region.getTileHeight(z, x, y + 1);
int m1 = field3834[x][y];
int m2 = field3834[x + 1][y];
int m3 = field3834[x + 1][y + 1];
int m4 = field3834[x][y + 1];
int underlayHsl = -1;
if (underlayId > 0)
{
int avgHue = runningHues * 256 / runningMultiplier;
int avgSat = runningSat / runningNumber;
int avgLight = runningLight / runningNumber;
underlayHsl = packHsl(avgHue, avgSat, avgLight);
}
int underlayColor = 0;
if (underlayHsl != -1)
{
underlayColor = colorPalette.getColorPalette()[method2115(underlayHsl, 96)];
}
if (overlayId == 0)
{
addTile(z, x, y, 0, 0, -1,
tileHeight, tileHeightX1, tileHeightX1Y1, tileHeightY1,
method2115(underlayHsl, m1), method2115(underlayHsl, m2), method2115(underlayHsl, m3), method2115(underlayHsl, m4),
0, 0, 0, 0,
underlayColor, 0);
}
else
{
int shape = region.getOverlayPath(z, x, y) + 1;
int rotation = region.getOverlayRotation(z, x, y);
OverlayDefinition overlay = overlayProvider.provide(overlayId - 1);
int textureId = overlay.getTexture();
int overlayHsl;
if (textureId >= 0)
{
overlayHsl = -2;
}
else if (overlay.getRgbColor() == 0xFF_00FF)
{
overlayHsl = -2;
textureId = -1;
}
else
{
int hue = overlay.getHue();
int sat = overlay.getSaturation();
int light = overlay.getLightness();
overlayHsl = packHsl(hue, sat, light);
}
int overlayColor = 0;
if (overlayHsl != -2)
{
int var0 = adjustHSLListness0(overlayHsl, 96);
overlayColor = colorPalette.getColorPalette()[var0];
}
if (overlay.getSecondaryRgbColor() != -1)
{
int hue = overlay.getOtherHue();
int sat = overlay.getOtherSaturation();
int olight = overlay.getOtherLightness();
int hsl = packHsl(hue, sat, olight);
int var0 = adjustHSLListness0(hsl, 96);
overlayColor = colorPalette.getColorPalette()[var0];
}
addTile(z, x, y, shape, rotation, textureId,
tileHeight, tileHeightX1, tileHeightX1Y1, tileHeightY1,
method2115(underlayHsl, m1), method2115(underlayHsl, m2), method2115(underlayHsl, m3), method2115(underlayHsl, m4),
adjustHSLListness0(overlayHsl, m1), adjustHSLListness0(overlayHsl, m2), adjustHSLListness0(overlayHsl, m3), adjustHSLListness0(overlayHsl, m4),
underlayColor, overlayColor);
}
}
}
}
}
}
}
}
private void addTile(int z, int x, int y, int shape, int overlayRot, int texture,
int tileHeight, int tileHeightX1, int tileHeightX1Y1, int tileHeightY1,
int underlayColor, int underlayColorX1, int underlayColorX1Y1, int underlayColorY1,
int overlayColor, int overlayColorX1, int overlayColorX1Y1, int overlayColorY1,
int underlayColorRgb, int overlayColorRgb) // for the map
{
if (shape == 0)
{
SceneTilePaint sceneTilePaint = new SceneTilePaint(underlayColor, texture);
this.sceneTilePaint[z][x][y] = sceneTilePaint;
}
else if (shape == 1)
{
SceneTilePaint sceneTilePaint = new SceneTilePaint(overlayColor, texture);
this.sceneTilePaint[z][x][y] = sceneTilePaint;
}
else
{
SceneTileModel sceneTileModel = new SceneTileModel(shape, overlayRot, texture, x, y,
tileHeight, tileHeightX1, tileHeightX1Y1, tileHeightY1,
underlayColor, underlayColorX1, underlayColorX1Y1, underlayColorY1,
overlayColor, overlayColorX1, overlayColorX1Y1, overlayColorY1,
underlayColorRgb, overlayColorRgb);
sceneTiles[z][x][y] = sceneTileModel;
}
}
private static int packHsl(int h, int s, int l)
{
if (l > 179)
{
s /= 2;
}
if (l > 192)
{
s /= 2;
}
if (l > 217)
{
s /= 2;
}
if (l > 243)
{
s /= 2;
}
int hsl = (s / 32 << 7) + (h / 4 << 10) + l / 2;
return hsl;
}
private static int method2115(int hsl, int var1)
{
if (hsl == -1)
{
return 12345678;
}
else
{
var1 = (hsl & 127) * var1 / 128;
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return (hsl & 65408) + var1;
}
}
static final int adjustHSLListness0(int var0, int var1)
{
if (var0 == -2)
{
return 12345678;
}
else if (var0 == -1)
{
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return var1;
}
else
{
var1 = (var0 & 127) * var1 / 128;
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return (var0 & 65408) + var1;
}
}
}

View File

@@ -0,0 +1,305 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.modelviewer;
import lombok.Getter;
@Getter
public class SceneTileModel
{
private static final int[][] TILE_VERTICES;
private static final int[][] TILE_FACES;
private static final int TILE_SIZE = 128;
private static final int HALF_TILE_SIZE = TILE_SIZE / 2;
private static final int QUARTER_TILE_SIZE = TILE_SIZE / 4;
private static final int THREE_QUARTER_TILE_SIZE = TILE_SIZE * 3 / 4;
final int[] vertexX;
final int[] vertexY;
final int[] vertexZ;
private final int[] var26;
private final int[] var27;
final int[] faceX;
final int[] faceY;
final int[] faceZ;
final int[] triangleColorA;
final int[] triangleColorB;
final int[] triangleColorC;
int[] triangleTextureId;
static
{
TILE_VERTICES = new int[][]{{1, 3, 5, 7}, {1, 3, 5, 7}, {1, 3, 5, 7}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 2, 6}, {1, 3, 5, 7, 2, 8}, {1, 3, 5, 7, 2, 8}, {1, 3, 5, 7, 11, 12}, {1, 3, 5, 7, 11, 12}, {1, 3, 5, 7, 13, 14}};
TILE_FACES = new int[][]{{0, 1, 2, 3, 0, 0, 1, 3}, {1, 1, 2, 3, 1, 0, 1, 3}, {0, 1, 2, 3, 1, 0, 1, 3}, {0, 0, 1, 2, 0, 0, 2, 4, 1, 0, 4, 3}, {0, 0, 1, 4, 0, 0, 4, 3, 1, 1, 2, 4}, {0, 0, 4, 3, 1, 0, 1, 2, 1, 0, 2, 4}, {0, 1, 2, 4, 1, 0, 1, 4, 1, 0, 4, 3}, {0, 4, 1, 2, 0, 4, 2, 5, 1, 0, 4, 5, 1, 0, 5, 3}, {0, 4, 1, 2, 0, 4, 2, 3, 0, 4, 3, 5, 1, 0, 4, 5}, {0, 0, 4, 5, 1, 4, 1, 2, 1, 4, 2, 3, 1, 4, 3, 5}, {0, 0, 1, 5, 0, 1, 4, 5, 0, 1, 2, 4, 1, 0, 5, 3, 1, 5, 4, 3, 1, 4, 2, 3}, {1, 0, 1, 5, 1, 1, 4, 5, 1, 1, 2, 4, 0, 0, 5, 3, 0, 5, 4, 3, 0, 4, 2, 3}, {1, 0, 5, 4, 1, 0, 1, 5, 0, 0, 4, 3, 0, 4, 5, 3, 0, 5, 2, 3, 0, 1, 2, 5}};
}
public SceneTileModel(int shape, int overlayRott, int texture, int x, int y,
int tileHeight, int tileHeightX1, int tileHeightX1Y1, int tileHeightY1,
int var10, int var11, int var12, int var13,
int var14, int var15, int var16, int var17,
int var18, int var19)
{
final int[] vertexInfo = TILE_VERTICES[shape];
final int vertexInfoLength = vertexInfo.length;
vertexX = new int[vertexInfoLength];
vertexY = new int[vertexInfoLength];
vertexZ = new int[vertexInfoLength];
var26 = new int[vertexInfoLength];
var27 = new int[vertexInfoLength];
final int tileBaseX = x * TILE_SIZE;
final int tileBaseY = y * TILE_SIZE;
for (int idx = 0; idx < vertexInfoLength; ++idx)
{
int curInfo = vertexInfo[idx];
if ((curInfo & 1) == 0 && curInfo <= 8)
{
curInfo = (curInfo - overlayRott - overlayRott - 1 & 7) + 1;
}
if (curInfo > 8 && curInfo <= 12)
{
curInfo = (curInfo - 9 - overlayRott & 3) + 9;
}
if (curInfo > 12 && curInfo <= 16)
{
curInfo = (curInfo - 13 - overlayRott & 3) + 13;
}
int vertexX, vertexY, vertexZ;
int var35, var36;
if (curInfo == 1)
{
vertexX = tileBaseX;
vertexZ = tileBaseY;
vertexY = tileHeight;
var35 = var10;
var36 = var14;
}
else if (curInfo == 2)
{
vertexX = tileBaseX + HALF_TILE_SIZE;
vertexZ = tileBaseY;
vertexY = tileHeightX1 + tileHeight >> 1;
var35 = var11 + var10 >> 1;
var36 = var15 + var14 >> 1;
}
else if (curInfo == 3)
{
vertexX = tileBaseX + TILE_SIZE;
vertexZ = tileBaseY;
vertexY = tileHeightX1;
var35 = var11;
var36 = var15;
}
else if (curInfo == 4)
{
vertexX = tileBaseX + TILE_SIZE;
vertexZ = tileBaseY + HALF_TILE_SIZE;
vertexY = tileHeightX1Y1 + tileHeightX1 >> 1;
var35 = var11 + var12 >> 1;
var36 = var15 + var16 >> 1;
}
else if (curInfo == 5)
{
vertexX = tileBaseX + TILE_SIZE;
vertexZ = tileBaseY + TILE_SIZE;
vertexY = tileHeightX1Y1;
var35 = var12;
var36 = var16;
}
else if (curInfo == 6)
{
vertexX = tileBaseX + HALF_TILE_SIZE;
vertexZ = tileBaseY + TILE_SIZE;
vertexY = tileHeightX1Y1 + tileHeightY1 >> 1;
var35 = var13 + var12 >> 1;
var36 = var17 + var16 >> 1;
}
else if (curInfo == 7)
{
vertexX = tileBaseX;
vertexZ = tileBaseY + TILE_SIZE;
vertexY = tileHeightY1;
var35 = var13;
var36 = var17;
}
else if (curInfo == 8)
{
vertexX = tileBaseX;
vertexZ = tileBaseY + HALF_TILE_SIZE;
vertexY = tileHeightY1 + tileHeight >> 1;
var35 = var13 + var10 >> 1;
var36 = var17 + var14 >> 1;
}
else if (curInfo == 9)
{
vertexX = tileBaseX + HALF_TILE_SIZE;
vertexZ = tileBaseY + QUARTER_TILE_SIZE;
vertexY = tileHeightX1 + tileHeight >> 1;
var35 = var11 + var10 >> 1;
var36 = var15 + var14 >> 1;
}
else if (curInfo == 10)
{
vertexX = tileBaseX + THREE_QUARTER_TILE_SIZE;
vertexZ = tileBaseY + HALF_TILE_SIZE;
vertexY = tileHeightX1Y1 + tileHeightX1 >> 1;
var35 = var11 + var12 >> 1;
var36 = var15 + var16 >> 1;
}
else if (curInfo == 11)
{
vertexX = tileBaseX + HALF_TILE_SIZE;
vertexZ = tileBaseY + THREE_QUARTER_TILE_SIZE;
vertexY = tileHeightX1Y1 + tileHeightY1 >> 1;
var35 = var13 + var12 >> 1;
var36 = var17 + var16 >> 1;
}
else if (curInfo == 12)
{
vertexX = tileBaseX + QUARTER_TILE_SIZE;
vertexZ = tileBaseY + HALF_TILE_SIZE;
vertexY = tileHeightY1 + tileHeight >> 1;
var35 = var13 + var10 >> 1;
var36 = var17 + var14 >> 1;
}
else if (curInfo == 13)
{
vertexX = tileBaseX + QUARTER_TILE_SIZE;
vertexZ = tileBaseY + QUARTER_TILE_SIZE;
vertexY = tileHeight;
var35 = var10;
var36 = var14;
}
else if (curInfo == 14)
{
vertexX = tileBaseX + THREE_QUARTER_TILE_SIZE;
vertexZ = tileBaseY + QUARTER_TILE_SIZE;
vertexY = tileHeightX1;
var35 = var11;
var36 = var15;
}
else if (curInfo == 15)
{
vertexX = tileBaseX + THREE_QUARTER_TILE_SIZE;
vertexZ = tileBaseY + THREE_QUARTER_TILE_SIZE;
vertexY = tileHeightX1Y1;
var35 = var12;
var36 = var16;
}
else
{
vertexX = tileBaseX + QUARTER_TILE_SIZE;
vertexZ = tileBaseY + THREE_QUARTER_TILE_SIZE;
vertexY = tileHeightY1;
var35 = var13;
var36 = var17;
}
this.vertexX[idx] = vertexX;
this.vertexY[idx] = vertexY;
this.vertexZ[idx] = vertexZ;
var26[idx] = var35;
var27[idx] = var36;
}
final int[] faceInfo = TILE_FACES[shape];
final int faceInfoLength = faceInfo.length / 4;
faceX = new int[faceInfoLength];
faceY = new int[faceInfoLength];
faceZ = new int[faceInfoLength];
triangleColorA = new int[faceInfoLength];
triangleColorB = new int[faceInfoLength];
triangleColorC = new int[faceInfoLength];
if (texture != -1)
{
triangleTextureId = new int[faceInfoLength];
}
int offset = 0;
for (int idx = 0; idx < faceInfoLength; ++idx)
{
int var = faceInfo[offset];
int faceX = faceInfo[offset + 1];
int faceY = faceInfo[offset + 2];
int faceZ = faceInfo[offset + 3];
offset += 4;
if (faceX < 4)
{
faceX = faceX - overlayRott & 3;
}
if (faceY < 4)
{
faceY = faceY - overlayRott & 3;
}
if (faceZ < 4)
{
faceZ = faceZ - overlayRott & 3;
}
this.faceX[idx] = faceX;
this.faceY[idx] = faceY;
this.faceZ[idx] = faceZ;
if (var == 0)
{
this.triangleColorA[idx] = var26[faceX];
this.triangleColorB[idx] = var26[faceY];
this.triangleColorC[idx] = var26[faceZ];
if (this.triangleTextureId != null)
{
this.triangleTextureId[idx] = -1;
}
}
else
{
this.triangleColorA[idx] = var27[faceX];
this.triangleColorB[idx] = var27[faceY];
this.triangleColorC[idx] = var27[faceZ];
if (this.triangleTextureId != null)
{
this.triangleTextureId[idx] = texture;
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.modelviewer;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class SceneTilePaint
{
final int color;
final int texture;
}