/* Copyright 2012 Jan Ove Saltvedt This file is part of KBot. KBot 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. KBot 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 KBot. If not, see <http://www.gnu.org/licenses/>. */ package com.kbotpro.scriptsystem.wrappers; import com.kbotpro.bot.BotEnvironment; import com.kbotpro.bot.RenderData; import com.kbotpro.hooks.Client; import com.kbotpro.hooks.Plane; import com.kbotpro.scriptsystem.Calculations; import com.kbotpro.scriptsystem.various.Point3D; import java.awt.*; /** * Created by IntelliJ IDEA. * User: Jan Ove Saltvedt * Date: Oct 15, 2009 * Time: 4:57:27 PM * To change this template use File | Settings | File Templates. */ public abstract class Model extends Wrapper { protected Model(BotEnvironment botEnv) { super(botEnv); } protected int[] xPoints; /** * Negated! If you want a height of 50 the value must be -50 */ protected int[] yPoints; protected int[] zPoints; protected short[] indices1; protected short[] indices2; protected short[] indices3; protected short minX; /** * Negated */ protected short minY; protected short minZ; protected short maxX; /** * Negated */ protected short maxY; protected short maxZ; /** * Used for faster calculations */ protected int[] boundsXPoints = new int[8]; protected int[] boundsYPoints = new int[8]; protected int[] boundsZPoints = new int[8]; protected int[] boundsScreenX = new int[8]; protected int[] boundsScreenY = new int[8]; protected int[] screenPointsX; protected int[] screenPointsY; protected int baseX; protected int baseY; protected int baseZ; /** * Draws the bounding polygon the RS client uses to match models when they are animated * @param g */ public void drawSimpleBounds(Graphics g){ calculateBoundingBox(); int minX = 9999999; int minY = 9999999; int maxX = -9999999; int maxY = -9999999; for(int i = 0; i < boundsScreenX.length; i++){ int x = boundsScreenX[i]; int y = boundsScreenY[i]; if(x < minX){ minX = x; } else if(x > maxX){ maxX = x; } if(y < minY){ minY = y; } else if(y > maxY){ maxY = y; } } g.drawLine(minX, minY, minX, maxY); g.drawLine(minX, maxY, maxX, maxY); g.drawLine(maxX, maxY, maxX, minY); g.drawLine(maxX, minY, minX, minY); } /** * Draws a bounding box on the Graphics object. * * @param g */ public void drawBoundingBox(Graphics g) { calculateBoundingBox(); /** * TOP * (MinX, MinY, MinZ) to (MaxX, MinY, MinZ) */ g.drawLine(boundsScreenX[0], boundsScreenY[0], boundsScreenX[1], boundsScreenY[1]); /** * (MaxX, MinY, MinZ) to (MaxX, MinY, MaxZ) */ g.drawLine(boundsScreenX[1], boundsScreenY[1], boundsScreenX[5], boundsScreenY[5]); /** * (MaxX, MinY, MaxZ) to (MinX, MinY, MaxZ) */ g.drawLine(boundsScreenX[5], boundsScreenY[5], boundsScreenX[4], boundsScreenY[4]); /** * (MinX, MinY, MaxZ) to (MinX, MinY, MinZ) */ g.drawLine(boundsScreenX[4], boundsScreenY[4], boundsScreenX[0], boundsScreenY[0]); /** * Bottom * (MinX, MaxY, MinZ) to (MaxX, MaxY, MinZ) */ g.drawLine(boundsScreenX[2], boundsScreenY[2], boundsScreenX[3], boundsScreenY[3]); /** * (MaxX, MaxY, MinZ) to (MaxX, MaxY, MaxZ) */ g.drawLine(boundsScreenX[3], boundsScreenY[3], boundsScreenX[7], boundsScreenY[7]); /** * (MaxX, MaxY, MaxZ) to (MinX, MaxY, MaxZ) */ g.drawLine(boundsScreenX[7], boundsScreenY[7], boundsScreenX[6], boundsScreenY[6]); /** * (MinX, MaxY, MaxZ) to (MinX, MaxY, MinZ) */ g.drawLine(boundsScreenX[6], boundsScreenY[6], boundsScreenX[2], boundsScreenY[2]); /** * Side 1 * (MinX, MaxY, MinZ) to (MinX, MinY, MinZ) */ g.drawLine(boundsScreenX[2], boundsScreenY[2], boundsScreenX[0], boundsScreenY[0]); /** * Side 2 * (MaxX, MaxY, MinZ) to (MaxX, MinY, MinZ) */ g.drawLine(boundsScreenX[3], boundsScreenY[3], boundsScreenX[1], boundsScreenY[1]); /** * Side 3 * (MaxX, MaxY, MaxZ) to (MaxX, MinY, MaxZ) */ g.drawLine(boundsScreenX[7], boundsScreenY[7], boundsScreenX[5], boundsScreenY[5]); /** * Side 4 * (MinX, MaxY, MaxZ) to (MinX, MinY, MaxZ) */ g.drawLine(boundsScreenX[6], boundsScreenY[6], boundsScreenX[4], boundsScreenY[4]); } public void drawWireframe(Graphics g) { calculateScreenPoints(); for (int i = 0; i < indices1.length; i++) { int index1 = indices1[i]; int index2 = indices2[i]; int index3 = indices3[i]; int point1X = screenPointsX[index1]; int point1Y = screenPointsY[index1]; int point2X = screenPointsX[index2]; int point2Y = screenPointsY[index2]; int point3X = screenPointsX[index3]; int point3Y = screenPointsY[index3]; g.drawLine(point1X, point1Y, point2X, point2Y); g.drawLine(point2X, point2Y, point3X, point3Y); g.drawLine(point3X, point3Y, point1X, point1Y); } } protected void calculateBoundingBox() { Client client = botEnv.client; RenderData renderData = botEnv.renderData; if (renderData == null) { return; } Plane[] planes = client.getPlaneArray(); if (planes == null) { return; } updateBasePos(); int curPlane = client.getCurrentPlane(); boundsXPoints[0] = minX; boundsYPoints[0] = minY; boundsZPoints[0] = minZ; boundsXPoints[1] = maxX; boundsYPoints[1] = minY; boundsZPoints[1] = minZ; boundsXPoints[2] = minX; boundsYPoints[2] = maxY; boundsZPoints[2] = minZ; boundsXPoints[3] = maxX; boundsYPoints[3] = maxY; boundsZPoints[3] = minZ; boundsXPoints[4] = minX; boundsYPoints[4] = minY; boundsZPoints[4] = maxZ; boundsXPoints[5] = maxX; boundsYPoints[5] = minY; boundsZPoints[5] = maxZ; boundsXPoints[6] = minX; boundsYPoints[6] = maxY; boundsZPoints[6] = maxZ; boundsXPoints[7] = maxX; boundsYPoints[7] = maxY; boundsZPoints[7] = maxZ; int xOff = renderData.xOff; int yOff = renderData.yOff; int zOff = renderData.zOff; int x1 = renderData.x1; int x2 = renderData.x2; int x3 = renderData.x3; int y1 = renderData.y1; int y2 = renderData.y2; int y3 = renderData.y3; int z1 = renderData.z1; int z2 = renderData.z2; int z3 = renderData.z3; int minX = renderData.minX; int minY = renderData.minY; //int maxX = renderer.getMaxX(); //int maxY = renderer.getMaxY(); byte[][][] groundSettings = client.getGroundSettingsArray(); boolean fixedMode = botEnv.client.getViewSettings().getLayout() == 1; for (int index = 0; index < 8; index++) { int xPoint = boundsXPoints[index] + baseX; int yPoint = -(boundsYPoints[index]); int zPoint = boundsZPoints[index] + baseZ; if(baseY == 0){ yPoint = getTileHeight(curPlane, xPoint, zPoint, planes, groundSettings) - yPoint; } else{ yPoint = baseY - yPoint; } final int distanceToPoint = zOff + (x3 * xPoint + y3 * yPoint + z3 * zPoint >> 14); // Here there should be done clipping if it was rendering but not needed for worldToScreen if (distanceToPoint == 0) { // We don't devide by zero! boundsScreenX[index] = -1; boundsScreenY[index] = -1; continue; } final int calcX = renderData.screenFactorX * (xOff + (x1 * xPoint + y1 * yPoint + z1 * zPoint >> 14)) / distanceToPoint; final int calcY = renderData.screenFactorY * (yOff + (x2 * xPoint + y2 * yPoint + z2 * zPoint >> 14)) / distanceToPoint; ///if (calcX >= minX && calcX <= maxX // && calcY >= minY && calcY <= maxY) { if(fixedMode){ // Fixed mode boundsScreenX[index] = calcX - minX + 4; boundsScreenY[index] = calcY - minY + 4; } else{ boundsScreenX[index] = calcX - minX; boundsScreenY[index] = calcY - minY; } /*} else{ boundsScreenX[index] = -1; boundsScreenY[index] = -1; }*/ } } /** * Note that this is not a precise check, but is the same as the client. * It is not 100% accurate for performance reaons! * @param pointX * @param pointY * @param x1 * @param y1 * @param x2 * @param y2 * @param x3 * @param y3 * @return */ private final boolean isPointWithinTriangle(int pointX, int pointY, int x1, int y1, int x2, int y2, int x3, int y3){ if(pointY < y1 && pointY < y2 && pointY < y3) return false; if(pointY > y1 && pointY > y2 && pointY > y3) return false; if(pointX < x1 && pointX < x2 && pointX < x3) return false; return pointX <= x1 || pointX <= x2 || pointX <= x3; } public boolean isPointOver(int pointX, int pointY){ calculateScreenPoints(); for (int i = 0; i < indices1.length; i++) { int index1 = indices1[i]; int index2 = indices2[i]; int index3 = indices3[i]; int point1X = screenPointsX[index1]; int point1Y = screenPointsY[index1]; int point2X = screenPointsX[index2]; int point2Y = screenPointsY[index2]; int point3X = screenPointsX[index3]; int point3Y = screenPointsY[index3]; if(isPointWithinTriangle(pointX, pointY, point1X, point1Y, point2X, point2Y, point3X, point3Y)){ return true; } } return false; } public boolean isPointOver(int pointX, int pointY, boolean justCheckBoundingBox){ calculateBoundingBox(); int minX = 9999999; int minY = 9999999; int maxX = -9999999; int maxY = -9999999; for(int i = 0; i < boundsScreenX.length; i++){ int x = boundsScreenX[i]; int y = boundsScreenY[i]; if(x < minX){ minX = x; } else if(x > maxX){ maxX = x; } if(y < minY){ minY = y; } else if(y > maxY){ maxY = y; } } if(pointX < minX || pointX > maxX || pointY < minY || pointY > maxX){ return false; } if(justCheckBoundingBox){ return true; } calculateScreenPoints(); for (int i = 0; i < indices1.length; i++) { int index1 = indices1[i]; int index2 = indices2[i]; int index3 = indices3[i]; int point1X = screenPointsX[index1]; int point1Y = screenPointsY[index1]; int point2X = screenPointsX[index2]; int point2Y = screenPointsY[index2]; int point3X = screenPointsX[index3]; int point3Y = screenPointsY[index3]; if(isPointWithinTriangle(pointX, pointY, point1X, point1Y, point2X, point2Y, point3X, point3Y)){ return true; } } return false; } /** * Gets a random point somewhere inside the model. (The actual point is on the surface of the model) * @return */ public Point3D createSeed(){ applyTransforms(); if(indices1.length == 0){ return new Point3D(0, 0, 0); } int i = Calculations.random(0, indices1.length); int index1 = indices1[i]; int index2 = indices2[i]; int index3 = indices3[i]; int point1X = xPoints[index1]; int point1Y = yPoints[index1]; int point1Z = zPoints[index1]; int point2X = xPoints[index2]-point1X; int point2Y = yPoints[index2]-point1Y; int point2Z = zPoints[index2]-point1Z; int point3X = xPoints[index3]-point1X; int point3Y = yPoints[index3]-point1Y; int point3Z = zPoints[index3]-point1Z; double a = Math.random(); double b = Math.random(); if(a+b > 1){ a = 1-a; b = 1-b; } double c = 1-a-b; return new Point3D(point1X+(int)(b*point2X+c*point3X), point1Y+(int)(b*point2Y+c*point3Y), point1Z+(int)(b*point2Z+c*point3Z)); } protected void calculateScreenPoints() { applyTransforms(); Client client = botEnv.client; RenderData renderData = botEnv.renderData; if (renderData == null) { return; } Plane[] planes = client.getPlaneArray(); if (planes == null) { return; } updateBasePos(); int curPlane = client.getCurrentPlane(); int xOff = renderData.xOff; int yOff = renderData.yOff; int zOff = renderData.zOff; int x1 = renderData.x1; int x2 = renderData.x2; int x3 = renderData.x3; int y1 = renderData.y1; int y2 = renderData.y2; int y3 = renderData.y3; int z1 = renderData.z1; int z2 = renderData.z2; int z3 = renderData.z3; int minX = renderData.minX; int minY = renderData.minY; //int maxX = renderer.getMaxX(); //int maxY = renderer.getMaxY(); byte[][][] groundSettings = client.getGroundSettingsArray(); boolean fixedMode = botEnv.client.getViewSettings().getLayout() == 1; int length = xPoints.length; if(yPoints.length < length){ length = yPoints.length; } if(zPoints.length < length){ length = zPoints.length; } for (int index = 0; index < length; index++) { int xPoint = xPoints[index] + baseX; int yPoint = -(yPoints[index]); int zPoint = zPoints[index] + baseZ; if(baseY == 0){ yPoint = getTileHeight(curPlane, xPoint, zPoint, planes, groundSettings) - yPoint; } else{ yPoint = baseY - yPoint; } final int distanceToPoint = zOff + (x3 * xPoint + y3 * yPoint + z3 * zPoint >> 14); // Here there should be done clipping if it was rendering but not needed for worldToScreen if (distanceToPoint == 0) { // We don't devide by zero! screenPointsX[index] = -1; screenPointsY[index] = -1; continue; } final int calcX = renderData.screenFactorX * (xOff + (x1 * xPoint + y1 * yPoint + z1 * zPoint >> 14)) / distanceToPoint; final int calcY = renderData.screenFactorY * (yOff + (x2 * xPoint + y2 * yPoint + z2 * zPoint >> 14)) / distanceToPoint; //if (calcX >= minX && calcX <= maxX // && calcY >= minY && calcY <= maxY) { if(fixedMode){ screenPointsX[index] = calcX - minX + 4; screenPointsY[index] = calcY - minY + 4; } else{ screenPointsX[index] = calcX - minX; screenPointsY[index] = calcY - minY; } /*} else{ screenPointsX[index] = -1; screenPointsY[index] = -1; }*/ } } /** * Optimized getTileHeight to minimize reflection usage in performance operations * * @param plane * @param X * @param Y * @param planes * @param groundSettings * @return */ protected int getTileHeight(int plane, int X, int Y, Plane[] planes, byte[][][] groundSettings) { if (planes == null) return 0; int x1 = X >> 9; int y1 = Y >> 9; if (x1 < 0 || y1 < 0 || x1 > 103 || y1 > 103) return 0; int x2 = X & 0x1ff; int y2 = Y & 0x1ff; int zIndex = plane; if (zIndex <= 3 && (groundSettings[1][x1][y1] & 2) == 2) zIndex++; final int[][] groundHeights = planes[zIndex].getTileHeights(); if (groundHeights == null) { return 0; } int height1 = ((groundHeights[x1][y1] * (512 - x2)) + (groundHeights[x1 + 1][y1] * x2)) >> 9; int height2 = ((groundHeights[x1][(y1 + 1)] * (512 - x2)) + (groundHeights[1 + x1][(y1 + 1)] * x2)) >> 9; return ((height1 * (512 - y2)) + (y2 * height2)) >> 9; } /** * Implement this method to update the base position for calculations. */ protected abstract void updateBasePos(); protected abstract void applyTransforms(); public Point3D getCenter(){ int deltaX = (maxX-minX) >> 1; int deltaY = (maxY-minY) >> 1; int deltaZ = (maxZ-minZ) >> 1; return new Point3D(minX+deltaX, minY+deltaY, minZ+deltaZ); } public void fillWireFrame(Graphics2D g){ calculateScreenPoints(); for (int i = 0; i < indices1.length; i++) { int index1 = indices1[i]; int index2 = indices2[i]; int index3 = indices3[i]; int[] xPoints = new int[3]; int[] yPoints = new int[3]; xPoints[0] = screenPointsX[index1]; yPoints[0] = screenPointsY[index1]; xPoints[1] = screenPointsX[index2]; yPoints[1] = screenPointsY[index2]; xPoints[2] = screenPointsX[index3]; yPoints[2] = screenPointsY[index3]; g.fillPolygon(xPoints, yPoints, 3); } } public int getHeight(){ return -minZ; } public int[] getXVertices() { applyTransforms(); return xPoints; } public int[] getYVertices() { applyTransforms(); return yPoints; } public int[] getZVertices() { applyTransforms(); return zPoints; } /** * Gets the screen points for a model * @return Returns a two dimensional array with the index 0 containing the x coords, and index 1 containing the y coords. (This is done for performance reasons) */ public int[][] getScreenPoints(){ calculateScreenPoints(); return new int[][]{screenPointsX, screenPointsY}; } }