v0.4.2: lots of performance improvements to fog visuals

This commit is contained in:
Evan Debenham
2016-08-24 03:17:45 -04:00
committed by Evan Debenham
parent b325858644
commit 2bd1962b34
17 changed files with 170 additions and 53 deletions

View File

@@ -23,7 +23,9 @@ package com.shatteredpixel.shatteredpixeldungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Awareness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
@@ -656,8 +658,12 @@ public class Dungeon {
Rankings.INSTANCE.submit( true, cause );
}
public static void observe(){
observe( hero.viewDistance+1 );
}
public static void observe() {
public static void observe( int dist ) {
if (level == null) {
return;
@@ -668,19 +674,22 @@ public class Dungeon {
int cx = hero.pos % level.width();
int cy = hero.pos / level.width();
//Add one to account for hero's previous position
int viewDist = hero.viewDistance+1;
int ax = Math.max( 0, cx - viewDist );
int bx = Math.min( cx + viewDist, level.width() - 1 );
int ay = Math.max( 0, cy - viewDist );
int by = Math.min( cy + viewDist, level.height() - 1 );
int ax = Math.max( 0, cx - dist );
int bx = Math.min( cx + dist, level.width() - 1 );
int ay = Math.max( 0, cy - dist );
int by = Math.min( cy + dist, level.height() - 1 );
int len = bx - ax + 1;
int pos = ax + ay * level.width();
for (int y = ay; y <= by; y++, pos+=level.width()) {
BArray.or( level.visited, visible, pos, len, level.visited );
}
if (hero.buff(MindVision.class) != null || hero.buff(Awareness.class) != null)
GameScene.updateFog();
else
GameScene.updateFog(ax, ay, len, by-ay);
GameScene.afterObserve();
}

View File

@@ -20,14 +20,17 @@
*/
package com.shatteredpixel.shatteredpixeldungeon;
import android.graphics.Bitmap;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import android.opengl.GLES20;
import com.watabou.gltextures.SmartTexture;
import com.watabou.gltextures.TextureCache;
import com.watabou.glwrap.Texture;
import com.watabou.noosa.Image;
import com.watabou.utils.Rect;
import java.util.Arrays;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
public class FogOfWar extends Image {
@@ -36,13 +39,13 @@ public class FogOfWar extends Image {
private static final int MAPPED = 0xcc442211;
private static final int INVISIBLE = 0xFF000000;
private int[] pixels;
private int pWidth;
private int pHeight;
private int width2;
private int height2;
public Rect updated;
public FogOfWar( int mapWidth, int mapHeight ) {
@@ -65,59 +68,129 @@ public class FogOfWar extends Image {
width = width2 * size;
height = height2 * size;
texture( new FogTexture() );
texture( new FogTexture(width2, height2) );
scale.set(
DungeonTilemap.SIZE,
DungeonTilemap.SIZE );
x = y = -size / 2;
updated = new Rect(0, 0, pWidth, pHeight);
}
public void updateVisibility( boolean[] visible, boolean[] visited, boolean[] mapped ) {
if (pixels == null) {
pixels = new int[width2 * height2];
Arrays.fill( pixels, INVISIBLE );
}
for (int i=1; i < pHeight - 1; i++) {
int pos = (pWidth - 1) * i;
for (int j=1; j < pWidth - 1; j++) {
pos++;
int c = INVISIBLE;
if (visible[pos] && visible[pos - (pWidth - 1)] &&
visible[pos - 1] && visible[pos - (pWidth - 1) - 1]) {
c = VISIBLE;
if (updated.isEmpty())
return;
FogTexture fog = (FogTexture)texture;
for (int i=updated.top; i < updated.bottom; i++) {
int cell = (pWidth - 1) * i + updated.left;
fog.pixels.position((width2) * i + updated.left);
for (int j=updated.left; j < updated.right; j++) {
if (cell < pWidth || cell >= Dungeon.level.length()) {
fog.pixels.put(INVISIBLE);
} else
if (visited[pos] && visited[pos - (pWidth - 1)] &&
visited[pos - 1] && visited[pos - (pWidth - 1) - 1]) {
c = VISITED;
if (visible[cell] && visible[cell - (pWidth - 1)] &&
visible[cell - 1] && visible[cell - (pWidth - 1) - 1]) {
fog.pixels.put(VISIBLE);
} else
if (visited[cell] && visited[cell - (pWidth - 1)] &&
visited[cell - 1] && visited[cell - (pWidth - 1) - 1]) {
fog.pixels.put(VISITED);
}
else
if (mapped[pos] && mapped[pos - (pWidth - 1)] &&
mapped[pos - 1] && mapped[pos - (pWidth - 1) - 1]) {
c = MAPPED;
if (mapped[cell] && mapped[cell - (pWidth - 1)] &&
mapped[cell - 1] && mapped[cell - (pWidth - 1) - 1]) {
fog.pixels.put(MAPPED);
} else {
fog.pixels.put(INVISIBLE);
}
pixels[i * width2 + j] = c;
cell++;
}
}
texture.pixels( width2, height2, pixels );
if (updated.width() == pWidth && updated.height() == pHeight)
fog.update();
else
fog.update(updated.top, updated.bottom);
updated.setEmpty();
}
//provides a native intbuffer implementation because android.graphics.bitmap is too slow
//TODO perhaps should spin this off into something like FastEditTexture in SPD-classes
private class FogTexture extends SmartTexture {
private IntBuffer pixels;
public FogTexture() {
super( Bitmap.createBitmap( width2, height2, Bitmap.Config.ARGB_8888 ) );
public FogTexture(int w, int h) {
super();
width = w;
height = h;
pixels = ByteBuffer.
allocateDirect( w * h * 4 ).
order( ByteOrder.nativeOrder() ).
asIntBuffer();
filter( Texture.LINEAR, Texture.LINEAR );
TextureCache.add( FogOfWar.class, this );
}
@Override
public void reload() {
super.reload();
GameScene.afterObserve();
int[] ids = new int[1];
GLES20.glGenTextures( 1, ids, 0 );
id = ids[0];
filter( Texture.LINEAR, Texture.LINEAR );
update();
}
public void update(){
bind();
pixels.position(0);
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D,
0,
GLES20.GL_RGBA,
width,
height,
0,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
pixels );
}
//allows partially updating the texture
public void update(int top, int bottom){
bind();
pixels.position(top*width);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D,
0,
0,
top,
width,
bottom - top,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
pixels);
}
@Override
public void delete() {
super.delete();
}
}
@Override
public void draw() {
if (!updated.isEmpty()){
updateVisibility(Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped);
}
super.draw();
}
}

View File

@@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
public class Awareness extends FlavourBuff {
@@ -30,5 +31,6 @@ public class Awareness extends FlavourBuff {
public void detach() {
super.detach();
Dungeon.observe();
GameScene.updateFog();
}
}

View File

@@ -47,7 +47,7 @@ public class Light extends FlavourBuff {
@Override
public void detach() {
target.viewDistance = Dungeon.level.viewDistance;
Dungeon.observe();
Dungeon.observe(DISTANCE+1);
super.detach();
}

View File

@@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
public class MindVision extends FlavourBuff {
@@ -48,6 +49,7 @@ public class MindVision extends FlavourBuff {
public void detach() {
super.detach();
Dungeon.observe();
GameScene.updateFog();
}
@Override

View File

@@ -33,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GuardSprite;
import com.watabou.utils.Bundle;
import com.watabou.utils.Callback;
@@ -117,6 +118,7 @@ public class Guard extends Mob {
if (enemy == Dungeon.hero) {
Dungeon.hero.interrupt();
Dungeon.observe();
GameScene.updateFog();
}
}
}), -1);

View File

@@ -26,6 +26,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.watabou.noosa.Game;
import com.watabou.noosa.Visual;
@@ -92,6 +93,7 @@ public class Swap extends Actor {
if (ch1 == Dungeon.hero || ch2 == Dungeon.hero) {
Dungeon.observe();
GameScene.updateFog();
}
}
}

View File

@@ -77,6 +77,7 @@ public class RogueArmor extends ClassArmor {
Sample.INSTANCE.play( Assets.SND_PUFF );
Dungeon.level.press( target, curUser );
Dungeon.observe();
GameScene.updateFog();
curUser.spendAndNext( Actor.TICK );
}

View File

@@ -75,6 +75,7 @@ public class WarriorArmor extends ClassArmor {
curUser.move(dest);
Dungeon.level.press(dest, curUser);
Dungeon.observe();
GameScene.updateFog();
for (int i = 0; i < PathFinder.NEIGHBOURS8.length; i++) {
Char mob = Actor.findChar(curUser.pos + PathFinder.NEIGHBOURS8[i]);

View File

@@ -141,6 +141,7 @@ public class EtherealChains extends Artifact {
}));
affected.pos = newMobPos;
Dungeon.observe();
GameScene.updateFog();
curUser.spendAndNext(1f);
}
}));
@@ -178,6 +179,7 @@ public class EtherealChains extends Artifact {
curUser.spendAndNext(1f);
curUser.pos = newHeroPos;
Dungeon.observe();
GameScene.updateFog();
}
}));
}

View File

@@ -160,6 +160,7 @@ public class LloydsBeacon extends Artifact {
ScrollOfTeleportation.appear( hero, returnPos );
Dungeon.level.press( returnPos, hero );
Dungeon.observe();
GameScene.updateFog();
} else {
Buff buff = Dungeon.hero.buff(TimekeepersHourglass.timeFreeze.class);

View File

@@ -69,7 +69,7 @@ public class ScrollOfMagicMapping extends Scroll {
}
}
}
Dungeon.observe();
GameScene.updateFog();
GLog.i( Messages.get(this, "layout") );
if (noticed) {

View File

@@ -27,6 +27,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.tweeners.AlphaTweener;
@@ -69,6 +70,7 @@ public class ScrollOfTeleportation extends Scroll {
appear( hero, pos );
Dungeon.level.press( pos, hero );
Dungeon.observe();
GameScene.updateFog();
GLog.i( Messages.get(ScrollOfTeleportation.class, "tele") );

View File

@@ -125,7 +125,7 @@ public class WandOfPrismaticLight extends DamageWand {
if (noticed)
Sample.INSTANCE.play( Assets.SND_SECRET );
Dungeon.observe();
GameScene.updateFog();
}
@Override

View File

@@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Knuckles;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.TrapSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
@@ -55,7 +56,7 @@ public class DisarmingTrap extends Trap{
Dungeon.level.drop( item, cell ).seen = true;
for (int i : PathFinder.NEIGHBOURS9)
Dungeon.level.visited[cell+i] = true;
Dungeon.observe();
GameScene.updateFog();
Sample.INSTANCE.play(Assets.SND_TELEPORT);
CellEmitter.get(pos).burst(Speck.factory(Speck.LIGHT), 4);
@@ -77,7 +78,7 @@ public class DisarmingTrap extends Trap{
Dungeon.level.drop(weapon, cell).seen = true;
for (int i : PathFinder.NEIGHBOURS9)
Dungeon.level.visited[cell+i] = true;
Dungeon.observe();
GameScene.updateFog();
GLog.w( Messages.get(this, "disarm") );

View File

@@ -409,7 +409,7 @@ public class GameScene extends PixelScene {
super.update();
if (!freezeEmitters) water.offset( 0, -5 * Game.elapsed );
Actor.process();
if (Dungeon.hero.ready && Dungeon.hero.paralysed == 0) {
@@ -665,8 +665,8 @@ public class GameScene extends PixelScene {
public static void resetMap() {
if (scene != null) {
scene.tiles.map(Dungeon.level.map, Dungeon.level.width() );
}
updateFog();
}
public static void updateMap() {
@@ -691,11 +691,21 @@ public class GameScene extends PixelScene {
cancelCellSelector();
scene.addToFront( wnd );
}
public static void updateFog(){
if (scene != null)
scene.fog.updated.set(0, 0, Dungeon.level.width()+1, Dungeon.level.height()+1);
}
public static void updateFog(int x, int y, int w, int h){
if (scene != null) {
scene.fog.updated.union(x, y);
scene.fog.updated.union(x + w, y + h);
}
}
public static void afterObserve() {
if (scene != null) {
scene.fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped );
for (Mob mob : Dungeon.level.mobs) {
mob.sprite.visible = Dungeon.visible[mob.pos];
}