123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- package me.hammerle.supersnuvi.rendering;
- import java.util.ArrayList;
- import me.hammerle.snuviengine.api.Shader;
- import me.hammerle.snuviengine.api.Texture;
- import me.hammerle.snuviengine.api.TextureRenderer;
- import me.hammerle.snuviengine.util.Rectangle;
- import me.hammerle.supersnuvi.Game;
- import me.hammerle.supersnuvi.entity.Entity;
- import me.hammerle.supersnuvi.gamelogic.Level;
- import me.hammerle.supersnuvi.gamelogic.LevelData;
- import me.hammerle.supersnuvi.tiles.Tile;
- import me.hammerle.supersnuvi.util.Utils;
- public class LevelRenderer implements IRenderer<Level>
- {
- public final static float ERROR = 1f / 65536f;
- public final static Texture TILES = new Texture("resources/tiles.png");
- private final static Texture GUI = new Texture("resources/gui.png");
- private final static TextureRenderer GUI_RENDERER = new TextureRenderer(60);
- private final static int MESH_SIZE = 16;
-
- private float cameraX = 0.0f;
- private float cameraY = 0.0f;
-
- private Level lastRenderedLevel = null;
- private int meshLayers = 0;
- private int meshWidth = 0;
- private int meshHeight = 0;
- private TextureRenderer[][][] meshes = null;
-
- @Override
- public void renderTick(Shader sh, float lag, Level level)
- {
- updateCamera(sh, lag, level);
- if(lastRenderedLevel != level)
- {
- updateMeshes(level);
- lastRenderedLevel = level;
-
- for(int i = 0; i < 32; i++)
- {
- level.getLight(i).markDirty();
- }
- }
- Shader.setAmbientLight(level.getAmbientRed(), level.getAmbientGreen(), level.getAmbientBlue());
- Shader.setLightEnabled(true);
- for(int i = 0; i < 1; i++)
- {
- Light l = level.getLight(i);
- if(l.isDirty())
- {
- sh.setLightColor(i, l.getRed(), l.getGreen(), l.getBlue());
- sh.setLightStrength(i, l.getStrength());
- l.clearDirtyFlag();
- }
- sh.setLightLocation(i,
- Utils.interpolate(l.getLastX(), l.getX(), lag) + cameraX,
- Utils.interpolate(l.getLastY(), l.getY(), lag) + cameraY);
- }
- renderTilesAndEntities(sh, lag, level);
- Shader.setLightEnabled(false);
- renderOverlay(sh, level);
- }
- private float getView(int screenSize, int levelSize, float pos)
- {
- pos -= screenSize >> 1;
- if(pos < 0)
- {
- return 0;
- }
- float max = levelSize * Tile.SIZE - screenSize;
- if(pos > max)
- {
- return max;
- }
- return pos;
- }
-
- private void updateCamera(Shader sh, float lag, Level level)
- {
- Entity hero = level.getHero();
-
- int levelWidth = level.getData().getWidth();
- int levelHeight = level.getData().getHeight();
-
- int screenWidth = sh.getViewWidth();
- int screenHeight = sh.getViewHeight();
-
- float halfHeroWidth = hero.getWidth() * 0.5f;
- float halfHeroHeight = hero.getHeight() * 0.5f;
-
- float oldCamX = -getView(screenWidth, levelWidth, hero.getLastX() + halfHeroWidth);
- float oldCamY = -getView(screenHeight, levelHeight, hero.getLastY() + halfHeroHeight);
- float camX = -getView(screenWidth, levelWidth, hero.getX() + halfHeroWidth);
- float camY = -getView(screenHeight, levelHeight, hero.getY() + halfHeroHeight);
- cameraX = Utils.interpolate(oldCamX, camX, lag);
- cameraY = Utils.interpolate(oldCamY, camY, lag);
- sh.translateTo(cameraX, cameraY);
- sh.updateMatrix();
- }
-
- private void updateMeshes(Level level)
- {
- if(meshes != null)
- {
- for(int l = 0; l < meshLayers; l++)
- {
- for(int mx = 0; mx < meshWidth; mx++)
- {
- for(int my = 0; my < meshHeight; my++)
- {
- meshes[l][mx][my].delete();
- }
- }
- }
- }
-
- meshLayers = level.getData().getLayers() - 1;
- meshWidth = (int) Math.ceil((double) level.getData().getWidth() / MESH_SIZE);
- meshHeight = (int) Math.ceil((double) level.getData().getHeight() / MESH_SIZE);
- meshes = new TextureRenderer[meshLayers][meshWidth][meshHeight];
- for(int l = 0; l < meshLayers; l++)
- {
- for(int mx = 0; mx < meshWidth; mx++)
- {
- for(int my = 0; my < meshHeight; my++)
- {
- meshes[l][mx][my] = new TextureRenderer(MESH_SIZE * MESH_SIZE * 2, false);
- }
- }
- }
- }
-
- private void drawMesh(Level level, int l, int tl, int mx, int my)
- {
- TextureRenderer tr = meshes[l][mx][my];
- if(!tr.isBuilt())
- {
- LevelData data = level.getData();
- int tsx = mx * MESH_SIZE;
- int tsy = my * MESH_SIZE;
- int tex = Math.min(tsx + MESH_SIZE, data.getWidth());
- int tey = Math.min(tsy + MESH_SIZE, data.getHeight());
- for(int x = tsx; x < tex; x++)
- {
- for(int y = tsy; y < tey; y++)
- {
- Tile t = Game.get().getTile(data.getTile(tl, x, y));
- if(t.shouldRender(x, y, level))
- {
- float minX = x * Tile.SIZE + t.getOffsetX();
- float minY = y * Tile.SIZE + t.getOffsetY();
- tr.addRectangle(minX, minY,
- minX + t.getWidth(), minY + t.getHeight(),
- t.getTextureMinX(x, y, level) + ERROR, t.getTextureMinY(x, y, level) + ERROR,
- t.getTextureMaxX(x, y, level) - ERROR, t.getTextureMaxY(x, y, level) - ERROR);
- }
- }
- }
- tr.build();
- }
- tr.draw();
- }
-
- private void renderTilesAndEntities(Shader sh, float lag, Level level)
- {
- int startX = (int) (-cameraX / (MESH_SIZE * Tile.SIZE));
- int startY = (int) (-cameraY / (MESH_SIZE * Tile.SIZE));
- int endX = (int) Math.ceil((-cameraX + sh.getViewWidth()) / (MESH_SIZE * Tile.SIZE));
- int endY = (int) Math.ceil((-cameraY + sh.getViewHeight()) / (MESH_SIZE * Tile.SIZE));
- startX = Math.min(Math.max(startX, 0), meshWidth);
- startY = Math.min(Math.max(startY, 0), meshHeight);
- endX = Math.min(Math.max(endX, 0), meshWidth);
- endY = Math.min(Math.max(endY, 0), meshHeight);
- // clear meshes which need an update
- if(level.getTileUpdater().shouldUpdateAll())
- {
- if(meshes != null)
- {
- for(int l = 0; l < meshLayers; l++)
- {
- for(int mx = 0; mx < meshWidth; mx++)
- {
- for(int my = 0; my < meshHeight; my++)
- {
- meshes[l][mx][my].clear();
- }
- }
- }
- }
- }
- else
- {
- level.getTileUpdater().forEach((layer, x, y) ->
- {
- if(layer == level.getData().getBackgroundIndex() + 1)
- {
- // do not update changes on entity layer
- return;
- }
- if(layer > level.getData().getBackgroundIndex())
- {
- layer--;
- }
- meshes[layer][x / MESH_SIZE][y / MESH_SIZE].clear();
- });
- }
- level.getTileUpdater().clear();
-
- // background
- sh.setColorEnabled(false);
- sh.setTextureEnabled(true);
- sh.setBlendingEnabled(true);
- TILES.bind();
-
- LevelData data = level.getData();
- int fromLayer = 0;
- int toLayer = level.getData().getBackgroundIndex() + 1;
- for(int l = fromLayer; l < toLayer; l++)
- {
- for(int mx = startX; mx < endX; mx++)
- {
- for(int my = startY; my < endY; my++)
- {
- drawMesh(level, l, l, mx, my);
- }
- }
- }
- // entities
- level.forEachEntity(entity -> entity.renderTick(lag));
- // foreground
- sh.setColorEnabled(false);
- sh.setTextureEnabled(true);
- sh.setBlendingEnabled(true);
- TILES.bind();
- fromLayer = toLayer + 1;
- toLayer = level.getData().getLayers();
- for(int l = fromLayer; l < toLayer; l++)
- {
- for(int mx = startX; mx < endX; mx++)
- {
- for(int my = startY; my < endY; my++)
- {
- drawMesh(level, l - 1, l, mx, my);
- }
- }
- }
- }
-
- public static String formatBottles(int bottles, int maxBottles)
- {
- char[] c = new char[5];
- if(bottles <= 9)
- {
- c[0] = '0';
- c[1] = (char) (bottles + '0');
- }
- else if(bottles > 99)
- {
- c[0] = 'X';
- c[1] = 'X';
- }
- else
- {
- c[0] = (char) ((bottles / 10) + '0');
- c[1] = (char) ((bottles % 10) + '0');
- }
- c[2] = '/';
-
- int currentMaxSouls = Math.max(bottles, maxBottles);
- if(currentMaxSouls <= 9)
- {
- c[3] = '0';
- c[4] = (char) (currentMaxSouls + '0');
- }
- else if(currentMaxSouls > 99)
- {
- c[3] = 'X';
- c[4] = 'X';
- }
- else
- {
- c[3] = (char) ((currentMaxSouls / 10) + '0');
- c[4] = (char) ((currentMaxSouls % 10) + '0');
- }
- return new String(c);
- }
-
- public static String formatTime(float time)
- {
- if(time == -1.0f)
- {
- return "-----";
- }
- else if(time >= 999.9f)
- {
- return "999.9";
- }
- return String.format("%05.1f", time);
- }
-
- private String[] split(String s)
- {
- ArrayList<String> list = new ArrayList<>();
- int old = 0;
- int index = 0;
- while(index < s.length())
- {
- switch(s.charAt(index))
- {
- case '\n':
- list.add(s.substring(old, index));
- list.add("\n");
- old = index + 1;
- break;
- case ' ':
- list.add(s.substring(old, index));
- old = index + 1;
- break;
- }
- index++;
- }
- if(old < s.length())
- {
- list.add(s.substring(old, index));
- }
- return list.toArray(new String[list.size()]);
- }
-
- private void renderMessage(Shader sh, Level level)
- {
- String message = level.getMessage();
- if(message != null)
- {
- float lineWidth = sh.getFontRenderer().getWidth();
- String[] messageParts = split(message);
- int index = 0;
- ArrayList<StringBuilder> list = new ArrayList<>();
- list.add(new StringBuilder());
- float currentWidth = 0;
- float w = sh.getViewWidth() - 26;
- for(String s : messageParts)
- {
- if(s.equals("\n"))
- {
- currentWidth = w;
- continue;
- }
- Rectangle rec = sh.getFontRenderer().getSize(s);
- // + lineWidth for the space
- if(currentWidth + rec.getWidth() + lineWidth < w)
- {
- currentWidth += rec.getWidth();
- StringBuilder sb = list.get(index);
- if(sb.length() == 0)
- {
- sb.append(s);
- }
- else
- {
- sb.append(" ");
- sb.append(s);
- currentWidth += lineWidth;
- }
- }
- else
- {
- StringBuilder sb = new StringBuilder();
- list.add(sb);
- index++;
- sb.append(s);
- currentWidth = rec.getWidth();
- }
- }
- float height = list.size() * sh.getFontRenderer().getHeight();
- sh.setColorEnabled(true);
- sh.setTextureEnabled(false);
- float messageY = sh.getViewHeight() - height - 26;
- sh.getColorRenderer().drawRectangle(0.0f, messageY, sh.getViewWidth(), sh.getViewHeight(), 0x77000000);
- messageY += 13;
- sh.setTextureEnabled(true);
- for(StringBuilder sb : list)
- {
- messageY = sh.getFontRenderer().drawString(13.0f, messageY, sb.toString());
- }
- sh.setColorEnabled(false);
- }
- }
-
- private void renderOverlay(Shader sh, Level level)
- {
- // menu rendering
- sh.translateTo(0.0f, 0.0f);
- sh.updateMatrix();
- // grey background of clock and bottles
- float lineHeight = sh.getFontRenderer().getHeight();
- float lineWidth = sh.getFontRenderer().getWidth();
- sh.setColorEnabled(true);
- sh.setTextureEnabled(false);
- sh.getColorRenderer().drawRectangle(0.0f, 0.0f, (lineWidth * 6.0f) + 10.0f, (lineHeight * 2.0f + 10.0f), 0x77000000);
- sh.setTextureEnabled(true);
- float y = 5.0f;
- y = sh.getFontRenderer().drawString(13.0f, y, formatBottles(level.getCurrentBottles(), level.getMaxBottles()));
- sh.getFontRenderer().drawString(13.0f, y, formatTime(level.getTime()));
- sh.setColorEnabled(false);
- // draw messages
- renderMessage(sh, level);
- GUI.bind();
- GUI_RENDERER.clear();
- int scale = sh.getViewScale();
- // bottles
- switch(scale)
- {
- case 1: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.0f, 0.046875f, 0.01171875f, 0.068359375f); break;
- case 2: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.01171875f, 0.046875f, 0.037109375f, 0.0859375f); break;
- default: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.037109375f, 0.046875f, 0.06640625f, 0.10546875f); break;
- }
- // clock
- switch(scale)
- {
- case 1: GUI_RENDERER.addRectangle(4.0f, y, 13.0f, y + 9.0f, 0.0f, 0.265625f, 0.017578125f, 0.283203125f); break;
- case 2: GUI_RENDERER.addRectangle(4.5f, y, 13.0f, y + 8.5f, 0.017578125f, 0.265625f, 0.05078125f, 0.298828125f); break;
- default: GUI_RENDERER.addRectangle(4.666666666f, y, 13.0f, y + 8.333333333f, 0.05078125f, 0.265625f, 0.099609375f, 0.314453125f); break;
- }
- float w = sh.getViewWidth();
- // gui background
- GUI_RENDERER.addRectangle(w - 111.0f, 0.0f, w - 44.0f, 24.0f, 0.0f, 0.0f, 0.130859375f, 0.046875f);
- GUI_RENDERER.addRectangle(w - 44.0f, 0.0f, w - 18.0f, 16.0f, 0.130859375f, 0.0f, 0.181640625f, 0.03125f);
- GUI_RENDERER.addRectangle(w - 76.0f, 24.0f, w - 45.0f, 57.0f, 0.068359375f, 0.046875f, 0.12890625f, 0.111328125f);
- // health mirror
- int healthFrame = (int) (level.getHero().getHealth().getHealthPercent() * 7);
- float leftMirror = (7 - healthFrame) * 0.0625f;
- GUI_RENDERER.addRectangle(w - 39.0f, 8.0f, w - 7.0f, 46.0f, leftMirror, 0.15625f, leftMirror + 0.0625f, 0.23046875f);
- // energy
- float energy = level.getHero().getEnergy().getEnergyPercent();
- float fullEnd = w - 109.0f + 64.0f * energy;
- GUI_RENDERER.addRectangle(w - 109.0f, 13.0f, fullEnd, 21.0f, 0.0f, 0.140625f, 0.125f * energy, 0.15625f);
- GUI_RENDERER.addRectangle(fullEnd, 13.0f, w - 45.0f, 21.0f, 0.125f * energy, 0.125f, 0.125f, 0.140625f);
- // gui foreground
- GUI_RENDERER.addRectangle(w - 49.0f, 0.0f, w, 64.0f, 0.201171875f, 0.0f, 0.296875f, 0.125f);
- GUI_RENDERER.addRectangle(w - 109.0f, 15.0f, w - 106.0f, 18.0f, 0.15625f, 0.03125f, 0.162109375f, 0.037109375f);
- GUI_RENDERER.addRectangle(w - 97.0f, 15.0f, w - 92.0f, 20.0f, 0.1796875f, 0.03125f, 0.189453125f, 0.041015625f);
- // health number overlay
- GUI_RENDERER.addRectangle(w - 30.0f, 53.0f, w - 12.0f, 62.0f, leftMirror, 0.23828125f, leftMirror + 0.03515625f, 0.255859375f);
- GUI_RENDERER.build();
- GUI_RENDERER.draw();
- // dynamic clock hand
- sh.setColorEnabled(true);
- sh.setTextureEnabled(false);
- switch(scale)
- {
- case 1: sh.translateTo(8.5f, y + 4.5f); break;
- case 2: sh.translateTo(8.75f, y + 4.25f); break;
- default: sh.translateTo(8.8333333333f, y + 4.16666667f); break;
- }
- sh.rotate(-level.getTime() * 72.0f);
- sh.updateMatrix();
- sh.getColorRenderer().drawRectangle(-0.5f / scale, -0.5f / scale, 0.5f / scale, 4.0f - 0.5f * scale, 0xFF000000);
- }
- }
|