507 lines
13 KiB
Java
507 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2016-2017, 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.mixins;
|
|
|
|
import java.awt.Shape;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import net.runelite.api.Model;
|
|
import net.runelite.api.Perspective;
|
|
import net.runelite.api.hooks.DrawCallbacks;
|
|
import net.runelite.api.mixins.Copy;
|
|
import net.runelite.api.mixins.Inject;
|
|
import net.runelite.api.mixins.MethodHook;
|
|
import net.runelite.api.mixins.Mixin;
|
|
import net.runelite.api.mixins.Replace;
|
|
import net.runelite.api.mixins.Shadow;
|
|
import net.runelite.api.model.Jarvis;
|
|
import net.runelite.api.model.Triangle;
|
|
import net.runelite.api.model.Vertex;
|
|
import net.runelite.rs.api.RSAnimation;
|
|
import net.runelite.rs.api.RSClient;
|
|
import net.runelite.rs.api.RSFrames;
|
|
import net.runelite.rs.api.RSModel;
|
|
import net.runelite.rs.api.RSSkeleton;
|
|
|
|
@Mixin(RSModel.class)
|
|
public abstract class RSModelMixin implements RSModel
|
|
{
|
|
@Shadow("client")
|
|
private static RSClient client;
|
|
|
|
@Inject
|
|
private int rl$sceneId;
|
|
|
|
@Inject
|
|
private int rl$bufferOffset;
|
|
|
|
@Inject
|
|
private int rl$uvBufferOffset;
|
|
|
|
@Inject
|
|
private float[] rl$faceTextureUVCoordinates;
|
|
|
|
@Inject
|
|
private int[] rl$vertexNormalsX;
|
|
|
|
@Inject
|
|
private int[] rl$vertexNormalsY;
|
|
|
|
@Inject
|
|
private int[] rl$vertexNormalsZ;
|
|
|
|
@MethodHook(value = "<init>", end = true)
|
|
@Inject
|
|
public void rl$init(RSModel[] models, int length)
|
|
{
|
|
if (getFaceTextures() != null)
|
|
{
|
|
int count = getTrianglesCount();
|
|
float[] uv = new float[count * 6];
|
|
int idx = 0;
|
|
|
|
for (int i = 0; i < length; ++i)
|
|
{
|
|
RSModel model = models[i];
|
|
if (model != null)
|
|
{
|
|
float[] modelUV = model.getFaceTextureUVCoordinates();
|
|
|
|
if (modelUV != null)
|
|
{
|
|
System.arraycopy(modelUV, 0, uv, idx, model.getTrianglesCount() * 6);
|
|
}
|
|
|
|
idx += model.getTrianglesCount() * 6;
|
|
}
|
|
}
|
|
|
|
setFaceTextureUVCoordinates(uv);
|
|
}
|
|
|
|
vertexNormals();
|
|
}
|
|
|
|
@Override
|
|
@Inject
|
|
public List<Vertex> getVertices()
|
|
{
|
|
int[] verticesX = getVerticesX();
|
|
int[] verticesY = getVerticesY();
|
|
int[] verticesZ = getVerticesZ();
|
|
|
|
List<Vertex> vertices = new ArrayList<Vertex>(getVerticesCount());
|
|
|
|
for (int i = 0; i < getVerticesCount(); ++i)
|
|
{
|
|
Vertex v = new Vertex(
|
|
verticesX[i],
|
|
verticesY[i],
|
|
verticesZ[i]
|
|
);
|
|
vertices.add(v);
|
|
}
|
|
|
|
return vertices;
|
|
}
|
|
|
|
@Override
|
|
@Inject
|
|
public List<Triangle> getTriangles()
|
|
{
|
|
int[] trianglesX = getTrianglesX();
|
|
int[] trianglesY = getTrianglesY();
|
|
int[] trianglesZ = getTrianglesZ();
|
|
|
|
List<Vertex> vertices = getVertices();
|
|
List<Triangle> triangles = new ArrayList<>(getTrianglesCount());
|
|
|
|
for (int i = 0; i < getTrianglesCount(); ++i)
|
|
{
|
|
int triangleX = trianglesX[i];
|
|
int triangleY = trianglesY[i];
|
|
int triangleZ = trianglesZ[i];
|
|
|
|
Triangle triangle = new Triangle(
|
|
vertices.get(triangleX),
|
|
vertices.get(triangleY),
|
|
vertices.get(triangleZ)
|
|
);
|
|
triangles.add(triangle);
|
|
}
|
|
|
|
return triangles;
|
|
}
|
|
|
|
@Copy("contourGround")
|
|
@Replace("contourGround")
|
|
@SuppressWarnings("InfiniteRecursion")
|
|
public Model copy$contourGround(int[][] tileHeights, int packedX, int height, int packedY, boolean copy, int contouredGround)
|
|
{
|
|
// With contouredGround >= 0 lighted models are countoured, so we need to copy uvs
|
|
Model model = copy$contourGround(tileHeights, packedX, height, packedY, copy, contouredGround);
|
|
if (model != null && model != this)
|
|
{
|
|
RSModel rsModel = (RSModel) model;
|
|
rsModel.setVertexNormalsX(rl$vertexNormalsX);
|
|
rsModel.setVertexNormalsY(rl$vertexNormalsY);
|
|
rsModel.setVertexNormalsZ(rl$vertexNormalsZ);
|
|
rsModel.setFaceTextureUVCoordinates(rl$faceTextureUVCoordinates);
|
|
}
|
|
return model;
|
|
}
|
|
|
|
@Copy("drawFace")
|
|
@Replace("drawFace")
|
|
public void copy$drawFace(int face)
|
|
{
|
|
DrawCallbacks callbacks = client.getDrawCallbacks();
|
|
if (callbacks == null || !callbacks.drawFace(this, face))
|
|
{
|
|
copy$drawFace(face);
|
|
}
|
|
}
|
|
|
|
@MethodHook("buildSharedModel")
|
|
@Inject
|
|
public void rl$buildSharedModel(boolean refTransparencies, Model sharedModel, byte[] transparencyBuffer)
|
|
{
|
|
// Animated models are usually a shared Model instance that is global
|
|
RSModel rsModel = (RSModel) sharedModel;
|
|
rsModel.setVertexNormalsX(rl$vertexNormalsX);
|
|
rsModel.setVertexNormalsY(rl$vertexNormalsY);
|
|
rsModel.setVertexNormalsZ(rl$vertexNormalsZ);
|
|
rsModel.setFaceTextureUVCoordinates(rl$faceTextureUVCoordinates);
|
|
}
|
|
|
|
@Inject
|
|
public void interpolateFrames(RSFrames frames, int frameId, RSFrames nextFrames, int nextFrameId, int interval, int intervalCount)
|
|
{
|
|
if (getVertexGroups() != null)
|
|
{
|
|
if (frameId != -1)
|
|
{
|
|
RSAnimation frame = frames.getFrames()[frameId];
|
|
RSSkeleton skeleton = frame.getSkeleton();
|
|
RSAnimation nextFrame = null;
|
|
if (nextFrames != null)
|
|
{
|
|
nextFrame = nextFrames.getFrames()[nextFrameId];
|
|
if (nextFrame.getSkeleton() != skeleton)
|
|
{
|
|
nextFrame = null;
|
|
}
|
|
}
|
|
|
|
client.setAnimOffsetX(0);
|
|
client.setAnimOffsetY(0);
|
|
client.setAnimOffsetZ(0);
|
|
|
|
interpolateFrames(skeleton, frame, nextFrame, interval, intervalCount);
|
|
resetBounds();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Inject
|
|
public void interpolateFrames(RSSkeleton skin, RSAnimation frame, RSAnimation nextFrame, int interval, int intervalCount)
|
|
{
|
|
if (nextFrame == null || interval == 0)
|
|
{
|
|
// if there is no next frame or interval then animate the model as we normally would
|
|
for (int i = 0; i < frame.getTransformCount(); i++)
|
|
{
|
|
int type = frame.getTransformTypes()[i];
|
|
animate(skin.getTypes()[type], skin.getList()[type], frame.getTranslatorX()[i],
|
|
frame.getTranslatorY()[i], frame.getTranslatorZ()[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int transformIndex = 0;
|
|
int nextTransformIndex = 0;
|
|
for (int i = 0; i < skin.getCount(); i++)
|
|
{
|
|
boolean frameValid = false;
|
|
if (transformIndex < frame.getTransformCount()
|
|
&& frame.getTransformTypes()[transformIndex] == i)
|
|
{
|
|
frameValid = true;
|
|
}
|
|
boolean nextFrameValid = false;
|
|
if (nextTransformIndex < nextFrame.getTransformCount()
|
|
&& nextFrame.getTransformTypes()[nextTransformIndex] == i)
|
|
{
|
|
nextFrameValid = true;
|
|
}
|
|
if (frameValid || nextFrameValid)
|
|
{
|
|
int staticFrame = 0;
|
|
int type = skin.getTypes()[i];
|
|
if (type == 3 || type == 10)
|
|
{
|
|
staticFrame = 128;
|
|
}
|
|
int currentTranslateX = staticFrame;
|
|
int currentTranslateY = staticFrame;
|
|
int currentTranslateZ = staticFrame;
|
|
if (frameValid)
|
|
{
|
|
currentTranslateX = frame.getTranslatorX()[transformIndex];
|
|
currentTranslateY = frame.getTranslatorY()[transformIndex];
|
|
currentTranslateZ = frame.getTranslatorZ()[transformIndex];
|
|
transformIndex++;
|
|
}
|
|
int nextTranslateX = staticFrame;
|
|
int nextTranslateY = staticFrame;
|
|
int nextTranslateZ = staticFrame;
|
|
if (nextFrameValid)
|
|
{
|
|
nextTranslateX = nextFrame.getTranslatorX()[nextTransformIndex];
|
|
nextTranslateY = nextFrame.getTranslatorY()[nextTransformIndex];
|
|
nextTranslateZ = nextFrame.getTranslatorZ()[nextTransformIndex];
|
|
nextTransformIndex++;
|
|
}
|
|
int translateX;
|
|
int translateY;
|
|
int translateZ;
|
|
if (type == 2)
|
|
{
|
|
int deltaX = nextTranslateX - currentTranslateX & 0xFF;
|
|
int deltaY = nextTranslateY - currentTranslateY & 0xFF;
|
|
int deltaZ = nextTranslateZ - currentTranslateZ & 0xFF;
|
|
if (deltaX >= 128)
|
|
{
|
|
deltaX -= 256;
|
|
}
|
|
if (deltaY >= 128)
|
|
{
|
|
deltaY -= 256;
|
|
}
|
|
if (deltaZ >= 128)
|
|
{
|
|
deltaZ -= 256;
|
|
}
|
|
translateX = currentTranslateX + deltaX * interval / intervalCount & 0xFF;
|
|
translateY = currentTranslateY + deltaY * interval / intervalCount & 0xFF;
|
|
translateZ = currentTranslateZ + deltaZ * interval / intervalCount & 0xFF;
|
|
}
|
|
else if (type == 5)
|
|
{
|
|
// don't interpolate alpha transformations
|
|
// alpha
|
|
translateX = currentTranslateX;
|
|
translateY = 0;
|
|
translateZ = 0;
|
|
}
|
|
else
|
|
{
|
|
translateX = currentTranslateX + (nextTranslateX - currentTranslateX) * interval / intervalCount;
|
|
translateY = currentTranslateY + (nextTranslateY - currentTranslateY) * interval / intervalCount;
|
|
translateZ = currentTranslateZ + (nextTranslateZ - currentTranslateZ) * interval / intervalCount;
|
|
}
|
|
// use interpolated translations to animate
|
|
animate(type, skin.getList()[i], translateX, translateY, translateZ);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@Inject
|
|
public Shape getConvexHull(int localX, int localY, int orientation, int tileHeight)
|
|
{
|
|
int[] x2d = new int[getVerticesCount()];
|
|
int[] y2d = new int[getVerticesCount()];
|
|
|
|
Perspective.modelToCanvas(client, getVerticesCount(), localX, localY, tileHeight, orientation, getVerticesX(), getVerticesZ(), getVerticesY(), x2d, y2d);
|
|
|
|
return Jarvis.convexHull(x2d, y2d);
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int getSceneId()
|
|
{
|
|
return rl$sceneId;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setSceneId(int sceneId)
|
|
{
|
|
rl$sceneId = sceneId;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int getBufferOffset()
|
|
{
|
|
return rl$bufferOffset;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setBufferOffset(int bufferOffset)
|
|
{
|
|
rl$bufferOffset = bufferOffset;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int getUvBufferOffset()
|
|
{
|
|
return rl$uvBufferOffset;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setUvBufferOffset(int bufferOffset)
|
|
{
|
|
rl$uvBufferOffset = bufferOffset;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public float[] getFaceTextureUVCoordinates()
|
|
{
|
|
return rl$faceTextureUVCoordinates;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setFaceTextureUVCoordinates(float[] faceTextureUVCoordinates)
|
|
{
|
|
rl$faceTextureUVCoordinates = faceTextureUVCoordinates;
|
|
}
|
|
|
|
@Inject
|
|
public void vertexNormals()
|
|
{
|
|
if (rl$vertexNormalsX == null)
|
|
{
|
|
int verticesCount = getVerticesCount();
|
|
|
|
rl$vertexNormalsX = new int[verticesCount];
|
|
rl$vertexNormalsY = new int[verticesCount];
|
|
rl$vertexNormalsZ = new int[verticesCount];
|
|
|
|
int[] trianglesX = getTrianglesX();
|
|
int[] trianglesY = getTrianglesY();
|
|
int[] trianglesZ = getTrianglesZ();
|
|
int[] verticesX = getVerticesX();
|
|
int[] verticesY = getVerticesY();
|
|
int[] verticesZ = getVerticesZ();
|
|
|
|
for (int i = 0; i < getTrianglesCount(); ++i)
|
|
{
|
|
int var9 = trianglesX[i];
|
|
int var10 = trianglesY[i];
|
|
int var11 = trianglesZ[i];
|
|
|
|
int var12 = verticesX[var10] - verticesX[var9];
|
|
int var13 = verticesY[var10] - verticesY[var9];
|
|
int var14 = verticesZ[var10] - verticesZ[var9];
|
|
int var15 = verticesX[var11] - verticesX[var9];
|
|
int var16 = verticesY[var11] - verticesY[var9];
|
|
int var17 = verticesZ[var11] - verticesZ[var9];
|
|
|
|
int var18 = var13 * var17 - var16 * var14;
|
|
int var19 = var14 * var15 - var17 * var12;
|
|
|
|
int var20;
|
|
for (var20 = var12 * var16 - var15 * var13; var18 > 8192 || var19 > 8192 || var20 > 8192 || var18 < -8192 || var19 < -8192 || var20 < -8192; var20 >>= 1)
|
|
{
|
|
var18 >>= 1;
|
|
var19 >>= 1;
|
|
}
|
|
|
|
int var21 = (int) Math.sqrt(var18 * var18 + var19 * var19 + var20 * var20);
|
|
if (var21 <= 0)
|
|
{
|
|
var21 = 1;
|
|
}
|
|
|
|
var18 = var18 * 256 / var21;
|
|
var19 = var19 * 256 / var21;
|
|
var20 = var20 * 256 / var21;
|
|
|
|
rl$vertexNormalsX[var9] += var18;
|
|
rl$vertexNormalsY[var9] += var19;
|
|
rl$vertexNormalsZ[var9] += var20;
|
|
|
|
rl$vertexNormalsX[var10] += var18;
|
|
rl$vertexNormalsY[var10] += var19;
|
|
rl$vertexNormalsZ[var10] += var20;
|
|
|
|
rl$vertexNormalsX[var11] += var18;
|
|
rl$vertexNormalsY[var11] += var19;
|
|
rl$vertexNormalsZ[var11] += var20;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int[] getVertexNormalsX()
|
|
{
|
|
return rl$vertexNormalsX;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setVertexNormalsX(int[] vertexNormalsX)
|
|
{
|
|
rl$vertexNormalsX = vertexNormalsX;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int[] getVertexNormalsY()
|
|
{
|
|
return rl$vertexNormalsY;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setVertexNormalsY(int[] vertexNormalsY)
|
|
{
|
|
rl$vertexNormalsY = vertexNormalsY;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public int[] getVertexNormalsZ()
|
|
{
|
|
return rl$vertexNormalsZ;
|
|
}
|
|
|
|
@Inject
|
|
@Override
|
|
public void setVertexNormalsZ(int[] vertexNormalsZ)
|
|
{
|
|
rl$vertexNormalsZ = vertexNormalsZ;
|
|
}
|
|
} |