Skip to content

Commit 09536c4

Browse files
committed
refactor: create CameraTileVisibility and use in scene
1 parent 808b7f4 commit 09536c4

File tree

3 files changed

+157
-127
lines changed

3 files changed

+157
-127
lines changed

src/main/java/org/runejs/client/frame/ScreenController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public static void setBounds() {
108108
}
109109

110110

111-
Game.currentScene.computeTileVisibilityMaps(500, 800, frameMode == ScreenMode.FIXED ? 512 : drawWidth, frameMode == ScreenMode.FIXED ? 334 : drawHeight, is);
111+
Game.currentScene.precalculateTileVisibility(500, 800, frameMode == ScreenMode.FIXED ? 512 : drawWidth, frameMode == ScreenMode.FIXED ? 334 : drawHeight, is);
112112
Game.gameCanvas.setSize(ScreenController.frameMode == ScreenMode.FIXED ? 512 : ScreenController.drawWidth, ScreenController.frameMode == ScreenMode.FIXED ? 334 : ScreenController.drawHeight);
113113

114114
if (Game.gameStatusCode <= 35 && Game.gameStatusCode >= 30) {
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package org.runejs.client.scene;
2+
3+
import org.runejs.client.scene.camera.CameraRotation;
4+
5+
/**
6+
* A class to hold the precomputed tile visibility information.
7+
*/
8+
public class CameraTileVisibility {
9+
private int viewportWidth;
10+
private int viewportWidthMidpoint;
11+
private int viewportHeight;
12+
private int viewportHeightMidpoint;
13+
14+
private int drawDistance;
15+
16+
public final boolean[][][][] visibilityInfo;
17+
18+
public CameraTileVisibility(int viewportWidth, int viewportHeight, int drawDistance, int minHeight, int maxHeight, int[] heightsForPitch) {
19+
this.viewportWidth = viewportWidth;
20+
this.viewportHeight = viewportHeight;
21+
this.viewportWidthMidpoint = viewportWidth / 2;
22+
this.viewportHeightMidpoint = viewportHeight / 2;
23+
24+
this.drawDistance = drawDistance;
25+
26+
visibilityInfo = new boolean[8][32][(drawDistance * 2) + 1][(drawDistance * 2) + 1];
27+
28+
this.computeTileVisibilityMaps(minHeight, maxHeight, heightsForPitch);
29+
}
30+
31+
/**
32+
* Precomputes visibility information for the game world's tiles based on possible camera positions and orientations.
33+
*
34+
* This helps to quickly determine which tiles need to be rendered based on the camera's current position and orientation.
35+
*
36+
* It then interpolates this data, ensuring that the nearby tiles around visible tiles are also visible.
37+
* This interpolation helps to prevent sudden changes in tile visiblity as the camera moves or rotates.
38+
*
39+
* @param minHeight The minimum height to check for visibility.
40+
* @param maxHeight The maximum height to check for visibility.
41+
* @param width The width of the viewport.
42+
* @param height The height of the viewport.
43+
* @param arg0 An array containing height values for each camera pitch angle.
44+
*/
45+
private void computeTileVisibilityMaps(int minHeight, int maxHeight, int[] arg0) {
46+
final int cameraAngles = 9;
47+
final int rotationsPerCircle = 32;
48+
49+
boolean[][][][] visibilityMaps = new boolean[cameraAngles][rotationsPerCircle][(this.drawDistance * 2) + 3][(this.drawDistance * 2) + 3];
50+
51+
// Iterating over different camera pitch angles (from 128 to 384)
52+
for (int pitch = 128; pitch <= 384; pitch += 32) {
53+
// Iterating over different camera yaw angles (from 0 to 2048)
54+
for (int yaw = 0; yaw < 2048; yaw += 64) {
55+
CameraRotation camera = new CameraRotation(yaw, pitch);
56+
57+
int pitchIndex = (pitch - 128) / 32;
58+
int yawIndex = yaw / 64;
59+
60+
// Iterating over different tile positions around the camera
61+
for (int tileX = -26; tileX <= 26; tileX++) {
62+
for (int tileY = -26; tileY <= 26; tileY++) {
63+
int absoluteTileX = tileX * 128;
64+
int absoluteTileY = tileY * 128;
65+
boolean isVisible = false;
66+
67+
// Checking visibility at different heights
68+
for (int h = -minHeight; h <= maxHeight; h += 128) {
69+
if (isPointVisibleOnScreen(absoluteTileX, arg0[pitchIndex] + h, absoluteTileY, camera)) {
70+
isVisible = true;
71+
break;
72+
}
73+
}
74+
visibilityMaps[pitchIndex][yawIndex][tileX + this.drawDistance + 1][tileY + this.drawDistance + 1] = isVisible;
75+
}
76+
}
77+
}
78+
}
79+
80+
// smooth out any gaps in the visibility maps by checking adjacent values
81+
final int maxPitchIndex = 8;
82+
final int maxYawIndex = 32;
83+
84+
for (int pitchIndex = 0; pitchIndex < maxPitchIndex; pitchIndex++) {
85+
for (int yawIndex = 0; yawIndex < maxYawIndex; yawIndex++) {
86+
for (int tileX = -this.drawDistance; tileX < this.drawDistance; tileX++) {
87+
for (int tileY = -this.drawDistance; tileY < this.drawDistance; tileY++) {
88+
boolean isVisible = false;
89+
90+
checkVisibility:
91+
for (int xOffset = -1; xOffset <= 1; xOffset++) {
92+
for (int yOffset = -1; yOffset <= 1; yOffset++) {
93+
// Check visibility at the current pitch and yaw, adjusted by the offset
94+
if (visibilityMaps[pitchIndex][yawIndex][tileX + xOffset + this.drawDistance + 1][tileY + yOffset + this.drawDistance + 1]) {
95+
isVisible = true;
96+
break checkVisibility;
97+
}
98+
// Check visibility at the current pitch and next yaw, adjusted by the offset
99+
if (visibilityMaps[pitchIndex][(yawIndex + 1) % 31][tileX + xOffset + this.drawDistance + 1][tileY + yOffset + this.drawDistance + 1]) {
100+
isVisible = true;
101+
break checkVisibility;
102+
}
103+
// Check visibility at the next pitch and current yaw, adjusted by the offset
104+
if (visibilityMaps[pitchIndex + 1][yawIndex][tileX + xOffset + this.drawDistance + 1][tileY + yOffset + this.drawDistance + 1]) {
105+
isVisible = true;
106+
break checkVisibility;
107+
}
108+
// Check visibility at the next pitch and next yaw, adjusted by the offset
109+
if (visibilityMaps[pitchIndex + 1][(yawIndex + 1) % 31][tileX + xOffset + this.drawDistance + 1][tileY + yOffset + this.drawDistance + 1]) {
110+
isVisible = true;
111+
break checkVisibility;
112+
}
113+
}
114+
}
115+
visibilityInfo[pitchIndex][yawIndex][tileX + this.drawDistance][tileY + this.drawDistance] = isVisible;
116+
}
117+
}
118+
}
119+
}
120+
}
121+
122+
/**
123+
* Checks if a point in 3D space projects onto the screen, after rotation and perspective transformation.
124+
*
125+
* @param x The x-coordinate of the point in 3D space.
126+
* @param y The y-coordinate of the point in 3D space.
127+
* @param z The z-coordinate of the point in 3D space.
128+
* @return Returns true if the projected point falls within the screen boundaries; otherwise false.
129+
*/
130+
public boolean isPointVisibleOnScreen(int x, int y, int z, CameraRotation cameraRotation) {
131+
// Rotate around the X axis
132+
int rotatedX = z * cameraRotation.yawSine + x * cameraRotation.yawCosine >> 16;
133+
int rotatedZ = z * cameraRotation.yawCosine - x * cameraRotation.yawSine >> 16;
134+
135+
// Rotate around the Y axis
136+
int rotatedY = y * cameraRotation.pitchSine + rotatedZ * cameraRotation.pitchCosine >> 16;
137+
int finalZ = y * cameraRotation.pitchCosine - rotatedZ * cameraRotation.pitchSine >> 16;
138+
139+
// Check if the point is behind the near clipping plane (too close to the camera)
140+
if (rotatedY < 50/* || rotatedY > 3500*/) {
141+
return false;
142+
}
143+
144+
// Apply perspective division and translate to screen space
145+
int screenX = this.viewportWidthMidpoint + (rotatedX << 9) / rotatedY;
146+
int screenY = this.viewportHeightMidpoint + (finalZ << 9) / rotatedY;
147+
148+
// Check if the point is within the screen bounds
149+
return screenX >= 0 && screenX <= this.viewportWidth && screenY >= 0 && screenY <= this.viewportHeight;
150+
}
151+
}

src/main/java/org/runejs/client/scene/Scene.java

Lines changed: 5 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public class Scene {
3333

3434

3535
private static final int TILE_DRAW_DISTANCE = 75;
36-
public static boolean[][][][] TILE_VISIBILITY_MAPS = new boolean[8][32][(TILE_DRAW_DISTANCE * 2) + 1][(TILE_DRAW_DISTANCE * 2) + 1];
3736

3837

3938
public static boolean lowMemory = true;
@@ -110,107 +109,17 @@ public class Scene {
110109
private int currentCameraTileX;
111110
private int currentCameraZ;
112111

112+
private CameraTileVisibility tileVisibilityInfo;
113+
113114
public Scene(int[][][] heightMap) {
114115
tileArray = new SceneTile[mapSizeZ][mapSizeX][mapSizeY];
115116
tileOcclusionCycles = new int[mapSizeZ][mapSizeX + 1][mapSizeY + 1];
116117
this.heightMap = heightMap;
117118
initToNull();
118119
}
119120

120-
/**
121-
* Precomputes visibility information for the game world's tiles based on possible camera positions and orientations.
122-
*
123-
* This helps to quickly determine which tiles need to be rendered based on the camera's current position and orientation.
124-
*
125-
* It then interpolates this data, ensuring that the nearby tiles around visible tiles are also visible.
126-
* This interpolation helps to prevent sudden changes in tile visiblity as the camera moves or rotates.
127-
*
128-
* @param minHeight The minimum height to check for visibility.
129-
* @param maxHeight The maximum height to check for visibility.
130-
* @param width The width of the viewport.
131-
* @param height The height of the viewport.
132-
* @param arg0 An array containing height values for each camera pitch angle.
133-
*/
134-
public void computeTileVisibilityMaps(int minHeight, int maxHeight, int width, int height, int[] arg0) {
135-
drawWidth = width;
136-
drawHeight = height;
137-
drawWidthMidpoint = width / 2;
138-
drawHeightMidpoint = height / 2;
139-
140-
final int cameraAngles = 9;
141-
final int rotationsPerCircle = 32;
142-
143-
boolean[][][][] visibilityMaps = new boolean[cameraAngles][rotationsPerCircle][(TILE_DRAW_DISTANCE * 2) + 3][(TILE_DRAW_DISTANCE * 2) + 3];
144-
145-
// Iterating over different camera pitch angles (from 128 to 384)
146-
for (int pitch = 128; pitch <= 384; pitch += 32) {
147-
// Iterating over different camera yaw angles (from 0 to 2048)
148-
for (int yaw = 0; yaw < 2048; yaw += 64) {
149-
CameraRotation camera = new CameraRotation(yaw, pitch);
150-
151-
int pitchIndex = (pitch - 128) / 32;
152-
int yawIndex = yaw / 64;
153-
154-
// Iterating over different tile positions around the camera
155-
for (int tileX = -26; tileX <= 26; tileX++) {
156-
for (int tileY = -26; tileY <= 26; tileY++) {
157-
int absoluteTileX = tileX * 128;
158-
int absoluteTileY = tileY * 128;
159-
boolean isVisible = false;
160-
161-
// Checking visibility at different heights
162-
for (int h = -minHeight; h <= maxHeight; h += 128) {
163-
if (isPointVisibleOnScreen(absoluteTileX, arg0[pitchIndex] + h, absoluteTileY, camera)) {
164-
isVisible = true;
165-
break;
166-
}
167-
}
168-
visibilityMaps[pitchIndex][yawIndex][tileX + TILE_DRAW_DISTANCE + 1][tileY + TILE_DRAW_DISTANCE + 1] = isVisible;
169-
}
170-
}
171-
}
172-
}
173-
174-
// smooth out any gaps in the visibility maps by checking adjacent values
175-
final int maxPitchIndex = 8;
176-
final int maxYawIndex = 32;
177-
178-
for (int pitchIndex = 0; pitchIndex < maxPitchIndex; pitchIndex++) {
179-
for (int yawIndex = 0; yawIndex < maxYawIndex; yawIndex++) {
180-
for (int tileX = -TILE_DRAW_DISTANCE; tileX < TILE_DRAW_DISTANCE; tileX++) {
181-
for (int tileY = -TILE_DRAW_DISTANCE; tileY < TILE_DRAW_DISTANCE; tileY++) {
182-
boolean isVisible = false;
183-
184-
checkVisibility:
185-
for (int xOffset = -1; xOffset <= 1; xOffset++) {
186-
for (int yOffset = -1; yOffset <= 1; yOffset++) {
187-
// Check visibility at the current pitch and yaw, adjusted by the offset
188-
if (visibilityMaps[pitchIndex][yawIndex][tileX + xOffset + TILE_DRAW_DISTANCE + 1][tileY + yOffset + TILE_DRAW_DISTANCE + 1]) {
189-
isVisible = true;
190-
break checkVisibility;
191-
}
192-
// Check visibility at the current pitch and next yaw, adjusted by the offset
193-
if (visibilityMaps[pitchIndex][(yawIndex + 1) % 31][tileX + xOffset + TILE_DRAW_DISTANCE + 1][tileY + yOffset + TILE_DRAW_DISTANCE + 1]) {
194-
isVisible = true;
195-
break checkVisibility;
196-
}
197-
// Check visibility at the next pitch and current yaw, adjusted by the offset
198-
if (visibilityMaps[pitchIndex + 1][yawIndex][tileX + xOffset + TILE_DRAW_DISTANCE + 1][tileY + yOffset + TILE_DRAW_DISTANCE + 1]) {
199-
isVisible = true;
200-
break checkVisibility;
201-
}
202-
// Check visibility at the next pitch and next yaw, adjusted by the offset
203-
if (visibilityMaps[pitchIndex + 1][(yawIndex + 1) % 31][tileX + xOffset + TILE_DRAW_DISTANCE + 1][tileY + yOffset + TILE_DRAW_DISTANCE + 1]) {
204-
isVisible = true;
205-
break checkVisibility;
206-
}
207-
}
208-
}
209-
TILE_VISIBILITY_MAPS[pitchIndex][yawIndex][tileX + TILE_DRAW_DISTANCE][tileY + TILE_DRAW_DISTANCE] = isVisible;
210-
}
211-
}
212-
}
213-
}
121+
public void precalculateTileVisibility(int viewportWidth, int viewportHeight, int minHeight, int maxHeight, int[] heightsForPitch) {
122+
tileVisibilityInfo = new CameraTileVisibility(viewportWidth, viewportHeight, TILE_DRAW_DISTANCE, minHeight, maxHeight, heightsForPitch);
214123
}
215124

216125
public static int adjustLightness(int hsl, int lightness) {
@@ -223,36 +132,6 @@ public static int adjustLightness(int hsl, int lightness) {
223132
return (hsl & 0xff80) + lightness;
224133
}
225134

226-
/**
227-
* Checks if a point in 3D space projects onto the screen, after rotation and perspective transformation.
228-
*
229-
* @param x The x-coordinate of the point in 3D space.
230-
* @param y The y-coordinate of the point in 3D space.
231-
* @param z The z-coordinate of the point in 3D space.
232-
* @return Returns true if the projected point falls within the screen boundaries; otherwise false.
233-
*/
234-
public boolean isPointVisibleOnScreen(int x, int y, int z, CameraRotation cameraRotation) {
235-
// Rotate around the X axis
236-
int rotatedX = z * cameraRotation.yawSine + x * cameraRotation.yawCosine >> 16;
237-
int rotatedZ = z * cameraRotation.yawCosine - x * cameraRotation.yawSine >> 16;
238-
239-
// Rotate around the Y axis
240-
int rotatedY = y * cameraRotation.pitchSine + rotatedZ * cameraRotation.pitchCosine >> 16;
241-
int finalZ = y * cameraRotation.pitchCosine - rotatedZ * cameraRotation.pitchSine >> 16;
242-
243-
// Check if the point is behind the near clipping plane (too close to the camera)
244-
if (rotatedY < 50/* || rotatedY > 3500*/) {
245-
return false;
246-
}
247-
248-
// Apply perspective division and translate to screen space
249-
int screenX = drawWidthMidpoint + (rotatedX << 9) / rotatedY;
250-
int screenY = drawHeightMidpoint + (finalZ << 9) / rotatedY;
251-
252-
// Check if the point is within the screen bounds
253-
return screenX >= 0 && screenX <= drawWidth && screenY >= 0 && screenY <= drawHeight;
254-
}
255-
256135

257136
public void createOccluder(int z, int searchMask, int lowestX, int highestX, int lowestY, int highestY, int highestZ, int lowestZ) {
258137
SceneCluster sceneCluster = new SceneCluster();
@@ -422,7 +301,7 @@ public void render(Camera camera, int plane) {
422301
currentPitchCosine = Model.COSINE[pitch];
423302
currentYawSine = Model.SINE[yaw];
424303
currentYawCosine = Model.COSINE[yaw];
425-
currentTileVisibilityMap = TILE_VISIBILITY_MAPS[(pitch - 128) / 32][yaw / 64];
304+
currentTileVisibilityMap = tileVisibilityInfo.visibilityInfo[(pitch - 128) / 32][yaw / 64];
426305
currentCameraX = cameraPosX;
427306
currentCameraZ = cameraPosZ;
428307
currentCameraY = cameraPosY;

0 commit comments

Comments
 (0)