Merge pull request #7200 from Abextm/skybox-y

Add Skybox plugin
This commit is contained in:
Adam
2019-01-07 13:48:49 -05:00
committed by GitHub
11 changed files with 1736 additions and 1 deletions

View File

@@ -1513,6 +1513,16 @@ public interface Client extends GameEngine
*/
void setOculusOrbNormalSpeed(int speed);
/**
* Gets local X coord where the camera is pointing when the Oculus orb is active
*/
int getOculusOrbFocalPointX();
/**
* Gets local Y coord where the camera is pointing when the Oculus orb is active
*/
int getOculusOrbFocalPointY();
/**
* Opens in-game world hopper interface
*/
@@ -1524,6 +1534,16 @@ public interface Client extends GameEngine
*/
void hopToWorld(World world);
/**
* Sets the RGB color of the skybox
*/
void setSkyboxColor(int skyboxColor);
/**
* Gets the RGB color of the skybox
*/
int getSkyboxColor();
boolean isGpu();
void setGpu(boolean gpu);

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 Abex
* 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.api.events;
/**
* Posted at the start of every frame
*/
public class BeforeRender
{
}

View File

@@ -46,6 +46,7 @@ import net.runelite.api.MainBufferProvider;
import net.runelite.api.RenderOverview;
import net.runelite.api.Renderable;
import net.runelite.api.WorldMapManager;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.GameTick;
import net.runelite.api.hooks.Callbacks;
import net.runelite.api.hooks.DrawCallbacks;
@@ -81,6 +82,7 @@ public class Hooks implements Callbacks
private static final OverlayRenderer renderer = injector.getInstance(OverlayRenderer.class);
private static final GameTick GAME_TICK = new GameTick();
private static final BeforeRender BEFORE_RENDER = new BeforeRender();
@Inject
private EventBus eventBus;
@@ -149,6 +151,8 @@ public class Hooks implements Callbacks
client.setTickCount(tick + 1);
}
eventBus.post(BEFORE_RENDER);
clientThread.invoke();
long now = System.currentTimeMillis();

View File

@@ -849,6 +849,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
lastAntiAliasingMode = antiAliasingMode;
// Clear scene
int sky = client.getSkyboxColor();
gl.glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
gl.glClear(gl.GL_COLOR_BUFFER_BIT);
// Upload buffers

View File

@@ -0,0 +1,510 @@
/*
* Copyright (c) 2019 Abex
* 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.client.plugins.skybox;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.runelite.api.Client;
class Skybox
{
@FunctionalInterface
public interface ChunkMapper
{
/**
* Gets the instance template chunk data for the specified point
*
* @see Client#getInstanceTemplateChunks
*/
int getTemplateChunk(int cx, int cy, int plane);
}
private static final double SQRT2 = Math.sqrt(2);
// How many stddev per direction we need to stay visibly continuous
// 511/512 accuracy
private static final double BLEND_DISTRIBUTION = 3.075;
// This has a worst case complexity of O((BLEND_RADUS*2)^2)
// BLEND_RADIUS is in chunks (8 tiles)
private static final int BLEND_RADIUS = 5;
// The maximum number of tiles that can be blended before becoming visibly discontinuous
private static final int MAX_BLEND = (int) ((BLEND_RADIUS * 8) / BLEND_DISTRIBUTION);
private static final int PLANE_ALL = 0b1111;
private static final Pattern PATTERN = Pattern.compile("^[ \\t]*(?<expr>" +
"//.*$|" + // //comment
"m[ \\t]*(?<mrx>[0-9]+)[ \\t]+(?<mry>[0-9]+)|" + // m <rx> <ry>
"r[ \\t]*(?<rx>[0-9]+)[ \\t]+(?<ry>[0-9]+)|" + // r <rx> <ry>
"R[ \\t]*(?<rx1>[0-9]+)[ \\t]+(?<ry1>[0-9]+)[ \\t]+(?<rx2>[0-9]+)[ \\t]+(?<ry2>[0-9]+)|" + // R <rx1> <ry1> <rx2> <ry2>
"c[ \\t]*(?<cx>[0-9-]+)[ \\t]+(?<cy>[0-9-]+)|" + // c <cx> <cy>
"C[ \\t]*(?<cx1>[0-9-]+)[ \\t]+(?<cy1>[0-9-]+)[ \\t]+(?<cx2>[0-9-]+)[ \\t]+(?<cy2>[0-9-]+)|" + // C <cx1> <cy1> <cx2> <cy2>
"#[ \\t]*(?<color>[0-9a-fA-F]{6}|[0-9a-fA-F]{3})|" + // #<RRGGBB> or #<RGB>
"p[ \\t]*(?<plane>all|0?[ \\t]*1?[ \\t]*2?[ \\t]*3?)|" + // p all or p<1><2><3><4>
"b[ \\t]*(?<blend>[0-9]+)|" + // b <blend>
"bounds[ \\t]+(?<bx1>[0-9]+)[ \\t]+(?<by1>[0-9]+)[ \\t]+(?<bx2>[0-9]+)[ \\t]+(?<by2>[0-9]+)" + // bounds <x0> <y0> <x1> <y1>
")[ \\t]*");
private final int[] chunks;
private final int[] planeOverrides;
private final int x1;
private final int y1;
private final int x2;
private final int y2;
private final int stride;
public Skybox(InputStream is, String filename) throws IOException
{
this(new InputStreamReader(is), filename);
}
public Skybox(Reader reader, String filename) throws IOException
{
int[] chunks = null;
int[] planeOverrides = new int[64];
int planeOverrideEnd = 0;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0, stride = 0;
BufferedReader br = new BufferedReader(reader);
int lineNo = 1;
int color = 0;
int plane = PLANE_ALL;
int rx1 = 0, ry1 = 0, rx2 = 0, ry2 = 0;
try
{
Matcher m = PATTERN.matcher("");
for (String line; (line = br.readLine()) != null; lineNo++)
{
m.reset(line);
int end = 0;
for (; end < line.length(); )
{
m.region(end, line.length());
if (!m.find())
{
throw new IllegalArgumentException("Unexpected: \"" + line.substring(end) + "\" (" + filename + ":" + lineNo + ")");
}
end = m.end();
String expr = m.group("expr");
if (expr == null || expr.length() <= 0 || expr.startsWith("//"))
{
continue;
}
if (chunks == null)
{
if (!expr.startsWith("bounds"))
{
throw new IllegalArgumentException("Expceted bounds (" + filename + ":" + lineNo + ")");
}
x1 = Integer.parseInt(m.group("bx1")) * 8;
y1 = Integer.parseInt(m.group("by1")) * 8;
x2 = (Integer.parseInt(m.group("bx2")) + 1) * 8;
y2 = (Integer.parseInt(m.group("by2")) + 1) * 8;
stride = (x2 - x1);
chunks = new int[stride * (y2 - y1)];
Arrays.fill(chunks, -1);
continue;
}
char cha = expr.charAt(0);
switch (cha)
{
case '#':
String sColor = m.group("color");
int scolor = Integer.parseInt(sColor, 16);
int cr, cg, cb;
if (sColor.length() == 3)
{
// Expand #RGB to #RRGGBB
cr = scolor >> 8 & 0xF;
cr |= cr << 4;
cg = scolor >> 4 & 0xF;
cg |= cg << 4;
cb = scolor & 0xF;
cb |= cb << 4;
}
else
{
cr = scolor >> 16 & 0xFF;
cg = scolor >> 8 & 0xFF;
cb = scolor & 0xFF;
}
// Convert to YCoCg24 because it produces less blending artifacts due
// to mismatched skew rates
// See: https://stackoverflow.com/questions/10566668/lossless-rgb-to-ycbcr-transformation
byte cco = (byte) (cb - cr);
byte tmp = (byte) (cr + (cco >> 1));
byte ccg = (byte) (tmp - cg);
byte cy = (byte) (cg + (ccg >> 1));
color = color & 0xFF000000 | (cy & 0xFF) << 16 | (cco & 0xFF) << 8 | (ccg & 0xFF);
break;
case 'b':
int iblend = Integer.parseInt(m.group("blend"));
if (iblend < 0)
{
throw new IllegalArgumentException("Blend must be >=0 (" + filename + ":" + lineNo + ")");
}
if (iblend > MAX_BLEND)
{
throw new IllegalArgumentException("Blend must be <= " + MAX_BLEND + " (" + filename + ":" + lineNo + ")");
}
color = color & 0x00FFFFFF | iblend << 24;
break;
case 'm':
rx2 = rx1 = Integer.parseInt(m.group("mrx"));
ry2 = ry1 = Integer.parseInt(m.group("mry"));
break;
case 'p':
String planes = m.group("plane");
if ("all".equals(planes))
{
plane = PLANE_ALL;
}
else
{
plane = 0;
for (int i = 0; i < planes.length(); i++)
{
plane |= 1 << (planes.charAt(i) - '0');
}
}
break;
case 'r':
case 'R':
if (cha == 'r')
{
rx2 = rx1 = Integer.parseInt(m.group("rx"));
ry2 = ry1 = Integer.parseInt(m.group("ry"));
}
else
{
rx1 = Integer.parseInt(m.group("rx1"));
ry1 = Integer.parseInt(m.group("ry1"));
rx2 = Integer.parseInt(m.group("rx2"));
ry2 = Integer.parseInt(m.group("ry2"));
}
// fallthrough
case 'c':
case 'C':
int cx1 = rx1 * 8;
int cy1 = ry1 * 8;
int cx2 = rx2 * 8 + 7;
int cy2 = ry2 * 8 + 7;
if (cha == 'c')
{
cx2 = cx1 = cx1 + Integer.parseInt(m.group("cx"));
cy2 = cy1 = cy1 + Integer.parseInt(m.group("cy"));
}
else if (cha == 'C')
{
cx2 = cx1 + Integer.parseInt(m.group("cx2"));
cy2 = cy1 + Integer.parseInt(m.group("cy2"));
cx1 = cx1 + Integer.parseInt(m.group("cx1"));
cy1 = cy1 + Integer.parseInt(m.group("cy1"));
}
if (cx1 < x1 || cy1 < y1 || cx2 >= x2 || cy2 >= y2)
{
throw new IllegalArgumentException("Coordinate out of bounds (" + filename + ":" + lineNo + ")");
}
if (cx1 > cx2 || cy1 > cy2)
{
throw new IllegalArgumentException("First coord must be before second (" + filename + ":" + lineNo + ")");
}
for (int y = cy1; y <= cy2; y++)
{
int yoffset = stride * (y - y1);
for (int x = cx1; x <= cx2; x++)
{
int offset = (x - x1) + yoffset;
if (plane == PLANE_ALL)
{
chunks[offset] = color;
}
else
{
// We are not setting all planes in this chunk, so allocate a plane override section
// and add a pointer to it in the normal chunk's space. We do this because most chunks
// do not have plane-specific data
int ocv = chunks[offset];
int poptr;
if ((ocv & 0x8000_0000) != 0 && ocv != -1)
{
// Existing plane override
poptr = ocv & 0x7FFF_FFFF;
}
else
{
poptr = planeOverrideEnd;
planeOverrideEnd += 4;
if (planeOverrideEnd > planeOverrides.length)
{
planeOverrides = Arrays.copyOf(planeOverrides, planeOverrideEnd + 64);
}
chunks[offset] = poptr | 0x8000_0000;
for (int i = 0; i < 4; i++)
{
planeOverrides[poptr + i] = ocv;
}
}
for (int i = 0; i < 4; i++)
{
if ((plane & (1 << i)) != 0)
{
planeOverrides[poptr + i] = color;
}
}
}
}
}
break;
}
}
}
}
catch (NumberFormatException ex)
{
throw new IllegalArgumentException("Expected number (" + filename + ":" + lineNo + ")", ex);
}
if (chunks == null)
{
throw new IllegalArgumentException(filename + ": no data");
}
this.chunks = chunks;
this.planeOverrides = planeOverrides;
this.stride = stride;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
private int chunkData(int cx, int cy, int plane, ChunkMapper chunkMapper)
{
if (chunkMapper != null)
{
int itp = chunkMapper.getTemplateChunk(cx, cy, plane);
if (itp == -1)
{
return -1;
}
cy = itp >> 3 & 0x7FF;
cx = itp >> 14 & 0x3FF;
plane = itp >> 24 & 0x3;
}
if (cx < x1)
{
cx = x1;
}
if (cx >= x2)
{
cx = x2 - 1;
}
if (cy < y1)
{
cy = y1;
}
if (cy >= y2)
{
cy = y2 - 1;
}
int cv = chunks[(stride * (cy - y1)) + (cx - x1)];
if (cv == -1)
{
return -1;
}
if ((cv & 0x8000_0000) != 0)
{
cv = planeOverrides[(cv & 0x7FFF_FFFF) | plane];
}
return cv;
}
/**
* Calculates the RGB color for a specific world coordinate. Arguments are floats for sub-tile accuracy.
*
* @param x X coordinate in tiles
* @param y Y coordinate in tiles
* @param chunkMapper maps chunks to their instance templates, or null if not in an instance
*/
public int getColorForPoint(double x, double y, int plane, double brightness, ChunkMapper chunkMapper)
{
x /= 8.d;
y /= 8.d;
int cx = (int) x;
int cy = (int) y;
int centerChunkData = chunkData(cx, cy, plane, chunkMapper);
if (centerChunkData == -1)
{
// No data in the center chunk?
return 0;
}
double t = 0;
double ty = 0;
double tco = 0;
double tcg = 0;
int xmin = (int) (x - BLEND_RADIUS);
int xmax = (int) Math.ceil(x + BLEND_RADIUS);
int ymin = (int) (y - BLEND_RADIUS);
int ymax = (int) Math.ceil(y + BLEND_RADIUS);
for (int ucx = xmin; ucx < xmax; ucx++)
{
for (int ucy = ymin; ucy <= ymax; ucy++)
{
int val = chunkData(ucx, ucy, plane, chunkMapper);
if (val == -1)
{
continue;
}
// Get the blend value, add 1/8 tile to make sure we don't div/0, convert to chunks
double sigma = ((val >>> 24) + .125) / 8.d;
// Calculate how far we have to be away before we can discard this value without
// becoming visibly discontinuous
double minDist = 1 + (sigma * BLEND_DISTRIBUTION);
// Try to fast-fail
double dxl = ucx - x;
double dxh = dxl + 1.d;
if (dxl < -minDist || dxl > minDist)
{
continue;
}
double dyl = ucy - y;
double dyh = dyl + 1.d;
if (dyl < -minDist || dyh > minDist)
{
continue;
}
// Calculate integrate a gaussian distribution in each dimension for
// this chunk relative to the requested point
double erfdivc = sigma * SQRT2;
double m = (erf(dxl / erfdivc) - erf(dxh / erfdivc)) * (erf(dyl / erfdivc) - erf(dyh / erfdivc));
// Load our YCoCg24 values into floats
double vy = (val >>> 16 & 0xFF) / 255.d;
double vco = (byte) (val >>> 8) / 128.d;
double vcg = (byte) val / 128.d;
// And multiply by the weight
ty += vy * m;
tco += vco * m;
tcg += vcg * m;
t += m;
}
}
// Convert back to int range values, and bounds check while we are at it
byte ay = (byte) Math.min(Math.max(Math.round(Math.pow(ty / t, brightness) * 255.d), 0), 255);
byte aco = (byte) Math.min(Math.max(Math.round(tco * 128.d / t), -128), 127);
byte acg = (byte) Math.min(Math.max(Math.round(tcg * 128.d / t), -128), 127);
// convert back to rgb from YCoCg24
int g = (ay - (acg >> 1)) & 0xFF;
int tmp = (g + acg) & 0xFF;
int r = (tmp - (aco >> 1)) & 0xFF;
int b = (r + aco) & 0xFF;
return r << 16 | g << 8 | b;
}
/**
* Approximation of erf 'Gauss error function' which is used to calculate
* the cumulative distribution of a gaussian distribution.
* This is used to simulate a large kernel gaussian blur without having
* to sample the same chunk multiple times.
*/
private double erf(double x)
{
double ax = Math.abs(x);
double t = 1.d / (1.d + (ax * .3275911d));
double y = 1.d - ((((((1.061405429d * t) - 1.453152027d) * t) + 1.421413741d) * t - 0.284496736d) * t + 0.254829592d) * t * Math.exp(-ax * ax);
return Math.copySign(y, x);
}
/**
* Draws the skybox map to an image
*
* @param resolution The number of pixels per tile
* @param line How many tiles to put a line
* @param plane the plane (0-4) to render
*/
BufferedImage render(double resolution, int line, int plane, ChunkMapper chunkMapper)
{
int w = (int) (((x2 - x1) * 8) * resolution);
int h = (int) (((y2 - y1) * 8) * resolution);
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
int lineEvery = line <= 0 ? Integer.MAX_VALUE : (int) (line * resolution);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int color;
if (x % lineEvery == 0 || y % lineEvery == 0)
{
color = 0x00FFFFFF;
}
else
{
double fx = (x1 * 8) + (x / resolution);
double fy = (y1 * 8) + (y / resolution);
color = getColorForPoint(fx, fy, plane, .8, chunkMapper);
}
img.setRGB(x, h - 1 - y, color | 0xFF000000);
}
}
return img;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2019 Abex
* 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.client.plugins.skybox;
import com.google.inject.Inject;
import java.io.IOException;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@PluginDescriptor(
name = "Skybox",
description = "Draws a oldschool styled skybox",
enabledByDefault = false,
tags = {"sky"}
)
public class SkyboxPlugin extends Plugin
{
@Inject
private Client client;
private Skybox skybox;
@Override
public void startUp() throws IOException
{
skybox = new Skybox(SkyboxPlugin.class.getResourceAsStream("skybox.txt"), "skybox.txt");
}
@Override
public void shutDown()
{
client.setSkyboxColor(0);
skybox = null;
}
private int mapChunk(int cx, int cy, int plane)
{
cx -= client.getBaseX() / 8;
cy -= client.getBaseY() / 8;
int[][] instanceTemplateChunks = client.getInstanceTemplateChunks()[plane];
// Blending can access this out of bounds, so do a range check
if (cx < 0 || cx >= instanceTemplateChunks.length || cy < 0 || cy >= instanceTemplateChunks[cx].length)
{
return -1;
}
return instanceTemplateChunks[cx][cy];
}
@Subscribe
public void onBeforeRender(BeforeRender r)
{
if (skybox == null || client.getGameState() != GameState.LOGGED_IN)
{
return;
}
Player player = client.getLocalPlayer();
if (player == null)
{
return;
}
int px, py;
if (client.getOculusOrbState() == 1)
{
px = client.getOculusOrbFocalPointX();
py = client.getOculusOrbFocalPointY();
}
else
{
LocalPoint p = client.getLocalPlayer().getLocalLocation();
px = p.getX();
py = p.getY();
}
// Inverse of camera location / 2
int spx = px - ((client.getCameraX() - px) >> 1);
int spy = py - ((client.getCameraY() - py) >> 1);
client.setSkyboxColor(skybox.getColorForPoint(
client.getBaseX() + (spx / 128.f),
client.getBaseY() + (spy / 128.f),
client.getPlane(),
client.getTextureProvider().getBrightness(),
client.isInInstancedRegion() ? this::mapChunk : null
));
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
{
client.setSkyboxColor(0);
}
}
}

View File

@@ -0,0 +1,925 @@
// Copyright (c) 2019, Abex
// Copyright (c) 2019, Psikoi <https://github.com/psikoi>
// Copyright (c) 2019, Ron Young <https://github.com/raiyni>
//
// 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.
// Skybox format
//
// Comments are c-style; Starts with // and ends with the next newline
//
// The first expression must be
// bounds rx1 ry1 rx2 ry2
// which sets the bounds of the image
//
// Color is set with #RRGGBB or #RGB and persists until set again
// Blending is set with b<blend radius>. It persists until set again. The number is in tiles, up to 13
//
// p <0> <1> <2> <3>
// p all
// Sets a mask for the planes to be altered by the next operation
//
// m <region X> <region Y>
// Moves the cursor to the specified region. This does not change any chunks
//
// The following operations commit chunks to the map:
//
// r <region X> <region Y>
// Sets all of the chunks in a region to the current color and blending
//
// R <region X 1> <region Y 1> <region X 2> <region Y 2>
// Sets all of the chunks in [x1,x2] [y1,y2] are set to the current color and blending
//
// c <chunk X> <chunk Y>
// Sets the chunk relative to the last region/region1 to the current color and blending
//
// C <chunk X 1> <chunk Y 1> <chunk X 2> <chunk Y 2>
// Sets the chunks relative to the last region/region1 to the current color and blending
bounds 18 39 60 162
b 8
#8AD2DF
R 18 39 60 64
#0f0f0f
r 34 75
#161616
R 47 61 53 62
#181818
R 37 79 39 80
#1a1a1a
R 47 60 53 60
#202020
R 46 59 53 59
#242424
r 29 81
#242e2c
r 56 160
#262626
R 46 58 53 58
r 24 79
#271010
R 46 74 48 75
r 47 76
#2a2f42
r 38 75
#2b2b2b
R 46 57 53 57
R 55 62 56 62
r 45 151
#2e2e2e
R 36 147 37 147
#323232
R 46 56 52 56
R 51 74 52 75
R 54 75 55 76
R 58 58 59 58
r 59 59
#332a15
R 26 73 27 73
R 27 80 27 81
R 27 158 28 158
R 29 84 29 85
R 30 72 31 72
R 36 153 36 154
R 37 150 41 154
R 39 147 42 149
R 40 69 40 70
R 40 81 40 82
R 41 70 41 71
R 41 79 42 79
R 42 142 43 142
R 42 150 42 152
R 44 148 44 150
R 44 152 49 153
R 45 150 46 150
R 46 161 47 161
R 47 151 48 151
R 48 150 49 150
R 48 154 50 155
R 49 77 49 79
R 52 152 52 153
r 20 155
r 22 157
r 24 154
r 28 66
r 30 80
r 31 81
r 34 77
r 34 85
r 35 73
r 35 75
r 35 156
r 36 79
r 36 146
r 36 150
r 37 73
r 37 148
r 38 147
r 39 145
r 41 75
r 41 146
r 42 154
r 43 146
r 43 149
r 45 79
r 45 148
r 45 154
r 46 149
r 47 85
r 47 87
r 47 160
r 48 148
r 50 151
r 51 147
r 54 151
r 54 153
r 58 88
#342b29
R 50 144 51 144
#353535
R 42 81 42 83
#383838
R 46 55 52 55
#393326
R 51 77 51 79
#3c3c3c
r 30 73
r 39 73
r 49 81
#3e3e3e
r 48 52
#3f384a
R 25 156 26 157
#40645d
r 49 49
#406e8c
R 37 68 38 69
r 26 154
r 36 69
#413d2d
r 50 145
#424242
r 50 150
#424940
R 29 68 30 68
R 38 158 39 158
R 42 69 43 71
R 45 68 45 69
R 49 149 50 149
R 55 151 56 151
R 58 151 58 152
r 39 72
r 39 156
r 51 150
r 53 150
r 53 154
r 55 154
r 56 150
#474747
R 50 146 51 146
r 25 83
r 27 83
#49432a
R 25 152 26 153
R 51 148 52 149
r 42 76
r 46 148
r 53 149
r 54 148
#496e53
r 33 82
#4a706a
R 18 54 19 54
R 20 54 21 56
R 31 78 38 78
R 34 47 34 48
R 42 73 43 74
R 54 50 56 50
R 54 51 57 55
R 56 58 56 59
R 56 141 57 142
R 57 57 57 58
R 58 51 58 53
r 43 72
r 44 71
r 53 55
r 53 77
#4a718f
R 25 58 25 59
r 26 58
#4c463a
R 58 146 58 147
r 59 147
#4f4f4f
r 48 81
#51649c
r 37 59
r 39 58
#543232
r 32 76
r 36 75
r 36 81
r 46 81
r 47 83
#553d75
R 26 60 28 60
#57493e
r 35 83
#575757
R 49 157 50 159
R 52 150 52 151
#576f6f
r 56 159
#606060
r 43 75
#64554b
R 22 59 22 60
R 23 58 23 59
r 24 58
#665742
r 40 75
#697387
R 40 57 41 57
r 36 57
r 41 58
#6a5151
r 39 75
#6d758e
R 44 55 45 57
r 43 56
#707b98
R 32 60 32 61
r 33 61
r 34 59
#7fc7d9
R 27 53 28 53
R 27 58 28 58
R 29 54 29 55
R 38 44 40 44
R 42 42 43 43
R 42 49 43 50
R 43 45 46 48
R 44 49 45 49
R 49 45 49 47
R 50 43 51 44
R 50 45 53 48
R 51 49 52 51
R 57 46 59 47
r 28 57
r 45 42
r 52 44
r 53 49
r 54 48
r 57 60
#817843
R 58 160 59 160
#85857a
R 45 70 46 71
r 25 77
r 27 77
r 47 68
#89a4ab
r 44 51
r 59 44
#a4d3df
R 25 75 26 75
R 28 75 31 75
r 28 74
r 37 75
r 38 71
r 42 75
r 47 73
r 49 75
#a5996a
r 29 72
#a5d5e1
R 25 86 26 89
R 28 76 31 76
R 30 86 31 87
R 32 86 33 86
R 33 76 33 77
R 40 73 41 74
R 53 90 54 92
R 54 74 55 74
R 54 89 55 89
r 26 66
r 28 70
r 30 83
r 32 84
r 36 71
r 38 84
r 40 71
r 44 75
r 44 79
r 45 73
r 45 85
r 47 70
r 53 78
r 55 90
r 57 154
#b2b595
r 40 67
#b6cbd8
R 22 61 28 62
R 36 60 37 60
R 42 59 42 61
R 42 159 43 159
R 43 58 45 61
r 24 60
r 29 60
r 32 75
r 44 155
r 47 158
r 53 76
#dbd9db
r 45 75
// RegionID 4663
m 18 55
#4a706a
C 0 0 5 7
#527874
C 6 0 7 1
#668d8d
C 6 2 7 7
// RegionID 4664
m 18 56
#4a706a
C 0 0 5 7
C 6 2 7 7
#608786
C 6 0 7 1
// RegionID 4919
m 19 55
C 6 0 7 1
#668d8d
C 0 0 5 1
#8dbac1
C 6 2 7 7
#9dccd6
C 0 2 5 7
// RegionID 4920
m 19 56
#4a706a
C 0 2 7 7
#80aab0
C 6 0 7 1
#8dbac1
C 0 0 5 1
// RegionID 5689
m 22 57
#4a706a
C 0 4 7 7
// RegionID 5690
m 22 58
C 0 0 7 1
#64554b
C 0 2 7 7
// RegionID 5945
m 23 57
#4a706a
C 0 4 7 7
// RegionID 5948
m 23 60
#64554b
C 0 0 5 5
C 0 6 3 7
C 6 0 7 3
#b6cbd8
C 4 6 7 7
C 6 4 7 5
// RegionID 6201
m 24 57
#4a706a
C 0 4 3 7
// RegionID 6203
m 24 59
#4a718f
C 4 2 7 7
C 6 0 7 1
#64554b
C 0 0 3 7
C 4 0 5 1
// RegionID 6460
m 25 60
#4a718f
C 0 0 5 1
#553d75
C 0 2 7 5
C 4 6 7 7
C 6 0 7 1
#adc0cf
C 2 6 3 7
#b6cbd8
C 0 6 1 7
// RegionID 6710
m 26 54
#7fc7d9
C 0 0 7 3
// RegionID 6715
m 26 59
#4a718f
C 0 0 5 7
C 6 0 7 5
b 5
#4d020a
C 5 5 8 8
b 8
// RegionID 6966
m 27 54
#7fc7d9
C 0 0 7 3
// RegionID 6971
m 27 59
#553d75
C 2 6 7 7
#5e2423
C 0 6 1 7
#7fc7d9
C 0 0 7 3
// RegionID 7222
m 28 54
C 0 0 7 3
C 6 4 7 7
// RegionID 7223
m 28 55
C 6 0 7 7
// RegionID 7224
m 28 56
C 4 0 7 7
// RegionID 7227
m 28 59
#553d75
C 0 6 7 7
#7fc7d9
C 0 0 7 3
C 4 4 7 5
// RegionID 8508
m 33 60
#707b98
C 0 0 1 3
C 0 4 7 7
#838aa1
C 2 0 7 3
// RegionID 9007
m 35 47
#3b4f4d
C 2 6 5 7
#4a706a
C 0 0 7 5
C 0 6 1 7
C 6 6 7 7
// RegionID 9008
m 35 48
#384947
C 2 0 5 1
#4a706a
C 0 2 7 7
C 0 0 1 1
C 6 0 7 1
// RegionID 10029
m 39 45
#7fc7d9
C 0 0 7 1
// RegionID 10040
m 39 56
#6d758e
C 0 4 3 7
// RegionID 10044
m 39 60
#b6cbd8
C 0 6 7 7
// RegionID 10055
m 39 71
#a4d3df
C 0 0 1 7
#a5d5e1
C 2 0 7 7
// RegionID 10285
m 40 45
#7fc7d9
C 2 0 7 3
C 4 4 7 5
C 0 0 1 1
C 6 6 7 7
// RegionID 10555
m 41 59
#b6cbd8
C 6 0 7 7
// RegionID 10556
m 41 60
C 6 0 7 7
// RegionID 10810
m 42 58
#869dcc
C 0 0 3 3
#b6cbd8
C 0 4 7 7
C 4 0 7 3
// RegionID 10905
m 42 153
#332a15
C 2 0 5 5
C 6 0 7 1
#a9bbc6
C 6 2 7 3
#b3c7d3
C 0 2 1 5
C 2 6 5 7
C 6 4 7 5
#b6cbd8
C 0 0 1 1
C 0 6 1 7
C 6 6 7 7
// RegionID 11062
m 43 54
C 6 6 7 7
// RegionID 11065
m 43 57
#6d758e
C 0 0 7 3
#b6cbd8
C 0 4 7 7
// RegionID 11314
m 44 50
#89a4ab
C 0 4 7 7
// RegionID 11317
m 44 53
#b6cbd8
C 4 6 7 7
// RegionID 11318
m 44 54
C 0 4 1 7
C 2 2 7 7
C 4 0 7 1
// RegionID 11418
m 44 154
#332a15
C 0 0 7 3
#acbfcb
C 0 4 7 5
#b6cbd8
C 0 6 7 7
// RegionID 11675
m 45 155
C 0 0 1 7
// RegionID 11825
m 46 49
#7fc7d9
C 0 0 7 3
C 6 4 7 5
// RegionID 11830
m 46 54
#b6cbd8
C 6 0 7 5
// RegionID 11836
m 46 60
#1a1a1a
C 2 0 3 3
C 4 0 7 7
#b4c9d6
C 2 4 3 5
#b6cbd8
C 0 0 1 7
C 2 6 3 7
// RegionID 11837
m 46 61
#161616
C 6 0 7 9
#b6cbd8
C 0 0 5 9
// RegionID 12081
m 47 49
#7fc7d9
C 0 0 3 5
// RegionID 12086
m 47 54
#b6cbd8
C 0 0 1 5
// RegionID 12113
m 47 81
#4f4f4f
C 0 0 3 3
C 4 4 7 7
#543232
C 0 4 3 7
C 4 0 7 3
// RegionID 12181
m 47 149
#332a15
C 0 0 1 7
#b6cbd8
C 2 0 7 7
// RegionID 12339
m 48 51
#3e3e3e
C 0 6 7 7
// RegionID 12437
m 48 149
#332a15
C 2 0 7 7
#b6cbd8
C 0 0 1 7
// RegionID 12444
m 48 156
#332a15
C 0 0 7 3
// RegionID 12595
m 49 51
#3e3e3e
C 0 6 1 7
// RegionID 12596
m 49 52
C 0 0 1 5
// RegionID 12849
m 50 49
#40645d
C 0 0 5 7
#426660
C 6 0 7 7
// RegionID 13209
m 51 153
#3b3535
C 0 0 3 3
C 4 4 7 7
#543232
C 0 4 3 7
C 4 0 7 3
// RegionID 13210
m 51 154
#332a15
C 0 0 3 7
// RegionID 13618
m 53 50
#4a706a
C 4 0 7 7
#7fc7d9
C 0 0 3 7
// RegionID 13619
m 53 51
#4a706a
C 4 0 7 7
#7fc7d9
C 0 0 3 7
// RegionID 13620
m 53 52
#4a706a
C 2 0 7 7
// RegionID 13621
m 53 53
C 2 0 7 7
// RegionID 13622
m 53 54
C 4 0 7 7
// RegionID 13873
m 54 49
C 0 4 7 7
#7fc7d9
C 0 0 7 3
// RegionID 14129
m 55 49
#4a706a
C 0 4 7 7
// RegionID 14385
m 56 49
C 0 4 7 7
// RegionID 14641
m 57 49
#543232
C 0 4 7 7
// RegionID 14642
m 57 50
#4a706a
C 0 6 7 7
#543232
C 0 0 7 5
// RegionID 14651
m 57 59
#4a706a
C 0 0 7 3
#7fc7d9
C 0 4 7 7
// RegionID 14907
m 58 59
#323232
C 0 0 7 3
// RegionID 14908
m 58 60
#7fc7d9
C 0 0 3 7
// Demonic Gorillas
#1A2B2B
R 32 88 33 88
// POH
b 12
m29 89
// 29 89 p0 Basic wood
// 29 89 p1 Basic stone
// 29 89 p3 Fremennik-style wood
p 0 1 3
#8AD2DF
C 0 0 7 15
p 0
#251B09
c 3 0
c 0 3
c 2 3
c 4 3
c 6 3
c 7 4
p 1
#484840
c 3 0
c 0 3
c 2 3
c 4 3
c 6 3
c 7 4
// 29 89 p2 Whitewashed stone
p 2
#7fc7d9
C 0 0 7 15
p 2 3
#2C2C29
c 3 0
c 0 3
c 2 3
c 4 3
c 6 3
c 7 4
m30 89
// 30 89 p0 Tropical wood
p 0
#7fc7d9
C 0 0 7 15
// 30 89 p1 Fancy stone
// 30 89 p2 Deathly mansion
p 1 2
#8AD2DF
C 0 0 7 15
p 2
#8AD2DF
C 0 0 7 15
p all
#2C2C29
c 3 0
c 0 3
c 2 3
c 4 3
c 6 3
c 7 4

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2019 Abex
* 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.client.plugins.skybox;
import com.google.common.base.Strings;
import com.google.common.io.CharSource;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
@Slf4j
public class SkyboxTest
{
@Test
public void testLoadSimple() throws IOException
{
Skybox skybox = new Skybox(CharSource.wrap("bounds 0 0 100 100 #00F // R 0 0 100 100\r\nr 99 99").openStream(), "simple");
Assert.assertEquals(0, skybox.getColorForPoint(0, 0, 0, 1, null));
Assert.assertEquals(0x0000FF, skybox.getColorForPoint((99 * 64) + 32, (99 * 64) + 32, 0, 1, null));
}
@Test
public void testLoadActual() throws IOException
{
long start = System.nanoTime();
Skybox skybox = new Skybox(SkyboxPlugin.class.getResourceAsStream("skybox.txt"), "skybox.txt");
log.info("Parse took {}ms", (System.nanoTime() - start) / 1_000_000);
String skyboxFile = System.getProperty("skyboxExport");
if (!Strings.isNullOrEmpty(skyboxFile))
{
start = System.nanoTime();
BufferedImage img = skybox.render(1f, 0, 0, null);
long time = System.nanoTime() - start;
log.info("Map render took {}ms", time / 1_000_000);
log.info("Single render takes ~{}ns/frame", time / (img.getWidth() * img.getHeight()));
ImageIO.write(img, "png", new File(skyboxFile));
}
Assert.assertNotEquals(skybox.getColorForPoint(3232, 3232, 0, .9, null), 0); // Lumbridge will never be black
}
}

View File

@@ -169,6 +169,9 @@ public abstract class RSClientMixin implements RSClient
@Inject
private static boolean oldIsResized;
@Inject
static int skyboxColor;
@Inject
@Override
public Callbacks getCallbacks()
@@ -1381,4 +1384,18 @@ public abstract class RSClientMixin implements RSClient
return array;
}
@Inject
@Override
public void setSkyboxColor(int newSkyboxColor)
{
skyboxColor = newSkyboxColor;
}
@Inject
@Override
public int getSkyboxColor()
{
return skyboxColor;
}
}

View File

@@ -65,6 +65,9 @@ public abstract class RSSceneMixin implements RSScene
@Inject
private static int[] tmpX = new int[6];
@Shadow("skyboxColor")
static int skyboxColor;
@Inject
private static int[] tmpY = new int[6];
@@ -80,6 +83,22 @@ public abstract class RSSceneMixin implements RSScene
drawCallbacks.drawScene(cameraX, cameraY, cameraZ, cameraPitch, cameraYaw, plane);
}
final boolean isGpu = client.isGpu();
if (!isGpu)
{
if (skyboxColor != 0)
{
client.RasterizerFillRectangle(
client.getViewportXOffset(),
client.getViewportYOffset(),
client.getViewportWidth(),
client.getViewportHeight(),
skyboxColor
);
}
}
final int maxX = getMaxX();
final int maxY = getMaxY();
final int maxZ = getMaxZ();
@@ -87,7 +106,6 @@ public abstract class RSSceneMixin implements RSScene
final int minLevel = getMinLevel();
final RSTile[][][] tiles = getTiles();
final boolean isGpu = client.isGpu();
final int distance = isGpu ? rl$drawDistance : DEFAULT_DISTANCE;
if (cameraX < 0)

View File

@@ -794,6 +794,14 @@ public interface RSClient extends RSGameEngine, Client
@Override
void setOculusOrbNormalSpeed(int state);
@Import("lookingAtX")
@Override
int getOculusOrbFocalPointX();
@Import("lookingAtY")
@Override
int getOculusOrbFocalPointY();
RSItem getLastItemDespawn();
void setLastItemDespawn(RSItem lastItemDespawn);
@@ -896,6 +904,9 @@ public interface RSClient extends RSGameEngine, Client
@Import("graphicsPixelsHeight")
int getGraphicsPixelsHeight();
@Import("fillRectangle")
void RasterizerFillRectangle(int x, int y, int w, int h, int rgb);
@Import("startX")
int getStartX();