zildo.fwk.gfx.engine.TileEngine.java Source code

Java tutorial

Introduction

Here is the source code for zildo.fwk.gfx.engine.TileEngine.java

Source

/**
 * Legend of Zildo
 * Copyright (C) 2006-2011 Evariste Boussaton
 * Based on original Zelda : link to the past (C) Nintendo 1992
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package zildo.fwk.gfx.engine;

import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector3f;

import zildo.client.ClientEngineZildo;
import zildo.fwk.bank.MotifBank;
import zildo.fwk.gfx.GFXBasics;
import zildo.fwk.gfx.TilePrimitive;
import zildo.fwk.gfx.effect.CloudGenerator;
import zildo.monde.map.Area;
import zildo.monde.map.Case;
import zildo.monde.map.Point;
import zildo.monde.map.Tile;
import zildo.monde.sprites.Reverse;
import zildo.resource.Constantes;

// V1.0
// --------------------------------------------
// 4 vertices ---> 2 triangles ---> 1 tile
// 6 vertices ---> 4 triangles ---> 2 tiles
// 8 vertices ---> 6 triangles ---> 3 tiles
// (...)
// 42 vertices --> 40 triangles ---> 20 tiles

// x----x----x----x ... x----x            a=TILEENGINE_WIDTH
// |0   |1   |2   | ... |a-1 |a
// |    |    |    | ... |    |
// |    |    |    | ... |    |
// x----x----x----x ... x----x
// |a+1 |a+2 |a+3 | ... |2a  |2a+1

// Indices : (0,a+2,a+1) - (0,1,a+2)
//          (1,a+3,a+2) - (1,2,a+3)
//                (...)
//           (a-1,2a+1,2a) - (a-1,a,2a+1)

// V2.0
// --------------------------------------------
// 4 vertices ---> 2 triangles ---> 1 tile
// 8 vertices ---> 4 triangles ---> 2 tiles
// 12 vertices --> 6 triangles ---> 3 tiles
// (...)
// 80 vertices --> 40 triangles --> 20 tiles

// x----x x----x x----x ... x----x            a=TILEENGINE_WIDTH
// |0  1| |2  3| |4  5| ... |2a-2|2a-1
// |    | |    | |    | ... |    |
// |2a  | |2a+2| |2a+4| ... |4a-2|
// x----x x----x x----x ... x----x
//   2a+1   2a+3   2a+5       4a-1
// x----x x----x x----x ... x----x
// |4a  | |4a+2| |4a+4| ... |6a-2|6a-1

// Indices : (0,2a+1,2a)   - (0,1,2a+1)
//          (2,2a+3,2a+2) - (2,3,2a+3)

public class TileEngine extends TextureEngine {

    // /////////////////////
    // Variables
    // /////////////////////
    private int cameraX;
    private int cameraY;

    // 3D Objects (vertices and indices per bank)
    protected TilePrimitive[] meshFORE;
    protected TilePrimitive[] meshBACK;

    private boolean initialized = false;
    List<MotifBank> motifBanks;
    public int texCloudId;

    static public String[] tileBankNames = { "foret1", "village", "maison", "grotte", "foret2", "foret3", "foret4",
            "palais1", "palais2" };

    // ////////////////////////////////////////////////////////////////////
    // Construction/Destruction
    // ////////////////////////////////////////////////////////////////////

    public TileEngine() {
        super();

        cameraX = -1;
        cameraY = -1;

        meshFORE = new TilePrimitive[Constantes.NB_MOTIFBANK];
        meshBACK = new TilePrimitive[Constantes.NB_MOTIFBANK];

        // Load graphs
        motifBanks = new ArrayList<MotifBank>();
        this.charge_tous_les_motifs();

        loadTiles();

        createCloudTexture();
    }

    public void cleanUp() {
        for (TilePrimitive tp : meshFORE) {
            if (tp != null) {
                tp.cleanUp();
            }
        }
        for (TilePrimitive tp : meshBACK) {
            if (tp != null) {
                tp.cleanUp();
            }
        }

        initialized = false;
    }

    // /////////////////////////////////////////////////////////////////////////////////////
    // charge_tous_les_motifs
    // /////////////////////////////////////////////////////////////////////////////////////
    // Load every tile banks
    // /////////////////////////////////////////////////////////////////////////////////////
    void charge_tous_les_motifs() {
        for (String bankName : tileBankNames) {
            this.charge_motifs(bankName.toUpperCase());
        }
    }

    // /////////////////////////////////////////////////////////////////////////////////////
    // charge_motifs
    // /////////////////////////////////////////////////////////////////////////////////////
    // IN:filename to load as a tile bank
    // /////////////////////////////////////////////////////////////////////////////////////
    void charge_motifs(String filename) {
        MotifBank motifBank = new MotifBank();

        motifBank.charge_motifs(filename);

        motifBanks.add(motifBank);
    }

    public MotifBank getMotifBank(int n) {
        return motifBanks.get(n);
    }

    public void loadTiles() {
        // Create a texture based on the current tiles
        for (int i = 0; i < tileBankNames.length; i++) {
            MotifBank motifBank = getMotifBank(i);
            this.createTextureFromMotifBank(motifBank);
        }
    }

    // Prepare vertices and indices for drawing tiles
    public void prepareTiles(Area theMap) {

        int i, x, y;

        for (i = 0; i < Constantes.NB_MOTIFBANK; i++) {
            meshFORE[i] = new TilePrimitive(Constantes.TILEENGINE_MAXPOINTS);
            meshBACK[i] = new TilePrimitive(Constantes.TILEENGINE_MAXPOINTS);
            meshFORE[i].startInitialization();
            meshBACK[i].startInitialization();
        }

        // Prepare vertices and indices for background map (tiles displayed
        // UNDER sprites)
        // For each tile bank
        for (y = 0; y < Constantes.TILEENGINE_HEIGHT; y++) {
            for (x = 0; x < Constantes.TILEENGINE_WIDTH; x++) {
                // Get corresponding case on the map
                Case mapCase = theMap.get_mapcase(x, y + 4);
                if (mapCase != null) {
                    Tile back = mapCase.getBackTile();
                    int xTex = (back.index % 16) * 16;
                    int yTex = (back.index / 16) * 16 + 1;
                    meshBACK[back.bank].addTile((16 * x), (16 * y), xTex, yTex);
                }
            }
        }

        // Prepare vertices and indices for foreground map (tiles displayed ON
        // sprites)
        // For each tile bank
        for (y = 0; y < Constantes.TILEENGINE_HEIGHT; y++) {
            for (x = 0; x < Constantes.TILEENGINE_WIDTH; x++) {
                // Get corresponding foreground case on the map
                Case mapCase = theMap.get_mapcase(x, y + 4);
                if (mapCase != null && mapCase.getForeTile() != null) {
                    Tile fore = mapCase.getForeTile();
                    int xTex = (fore.index % 16) * 16;
                    int yTex = (fore.index / 16) * 16 + 1;
                    meshFORE[fore.bank].addTile(16 * x, 16 * y, xTex, yTex);

                }
            }
        }
        for (i = 0; i < Constantes.NB_MOTIFBANK; i++) {
            meshFORE[i].endInitialization();
            meshBACK[i].endInitialization();
        }

        initialized = true;
    }

    public void createTextureFromMotifBank(MotifBank mBank) {

        GFXBasics surface = prepareSurfaceForTexture(true);

        // Display tiles on it
        int x = 0, y = 0;
        for (int n = 0; n < mBank.getNb_motifs(); n++) {
            short[] motif = mBank.get_motif(n);
            for (int j = 0; j < 16; j++) {
                for (int i = 0; i < 16; i++) {
                    int a = motif[i + j * 16];
                    if (a != 255) {
                        surface.pset(i + x, j + y, a, null);
                    }
                }
            }
            // Next position
            x += 16;
            if (x >= 256) {
                x = 0;
                y += 16;
            }
        }

        generateTexture();
    }

    public void createCloudTexture() {
        prepareSurfaceForTexture(false);

        CloudGenerator cGen = new CloudGenerator(scratch);
        cGen.generate();

        generateTexture();
        texCloudId = textureTab[n_Texture - 1];
    }

    public void render(boolean backGround) {

        if (initialized) {
            Vector3f ambient = ClientEngineZildo.ortho.getAmbientColor();
            if (ambient != null) {
                GL11.glColor3f(ambient.x, ambient.y, ambient.z);
            }

            // Small optimization: do not draw invisible faces ! (counterclock
            // wise vertices)
            // pD3DDevice9.SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
            if (backGround) {
                // Display BACKGROUND
                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
                GL11.glEnable(GL11.GL_BLEND);
                for (int i = 0; i < Constantes.NB_MOTIFBANK; i++) {
                    if (meshBACK[i].getNPoints() > 0) {
                        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureTab[i]);
                        meshBACK[i].render();
                    }
                }
                GL11.glDisable(GL11.GL_BLEND);
            } else {
                // Display FOREGROUND
                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
                GL11.glEnable(GL11.GL_BLEND);

                for (int i = 0; i < Constantes.NB_MOTIFBANK; i++) {
                    if (meshFORE[i].getNPoints() > 0) {
                        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureTab[i]);
                        meshFORE[i].render();
                    }
                }
                GL11.glDisable(GL11.GL_BLEND);
            }

            // GL11.glColor3f(1f, 1f, 1f);

        }
    }

    // Redraw all tiles with updating VertexBuffers.
    // No need to access particular tile, because we draw every one.
    // **BUT THIS COULD BE DANGEROUS WHEN A TILE SWITCHES FROM ONE BANK TO
    // ANOTHER**
    public void updateTiles(Point cameraNew, Area[] p_areas, int compteur_animation) {

        if (initialized && cameraX != -1 && cameraY != -1) {

            int i;
            for (i = 0; i < Constantes.NB_MOTIFBANK; i++) {
                meshBACK[i].startInitialization();
                meshFORE[i].startInitialization();
            }
            for (Area theMap : p_areas) {
                if (theMap == null) {
                    break;
                }
                Point offset = theMap.getOffset();
                for (int y = 0; y < Constantes.TILEENGINE_HEIGHT; y++) {
                    for (int x = 0; x < Constantes.TILEENGINE_WIDTH; x++) {
                        // Get corresponding case on the map
                        Case mapCase = theMap.get_mapcase(x, y + 4);
                        if (mapCase != null) {
                            Tile back = mapCase.getBackTile();
                            int n_motif = mapCase.getAnimatedMotif(compteur_animation);
                            int bank = back.bank;
                            if (bank < 0 || bank >= Constantes.NB_MOTIFBANK) {
                                throw new RuntimeException("We got a big problem");
                            }
                            updateTilePrimitive(meshBACK[bank], x, y, cameraNew, offset, n_motif, back.reverse);

                            Tile back2 = mapCase.getBackTile2();
                            if (back2 != null) {
                                n_motif = back2.index;
                                updateTilePrimitive(meshBACK[back2.bank], x, y, cameraNew, offset, n_motif,
                                        back2.reverse);
                            }

                            Tile fore = mapCase.getForeTile();
                            if (fore != null) {
                                n_motif = fore.index;
                                updateTilePrimitive(meshFORE[fore.bank], x, y, cameraNew, offset, fore.index,
                                        fore.reverse);

                            }
                        }
                    }
                }
            }
            for (i = 0; i < Constantes.NB_MOTIFBANK; i++) {
                meshBACK[i].endInitialization();
                meshFORE[i].endInitialization();
            }
        }

        cameraX = cameraNew.x;
        cameraY = cameraNew.y;

    }

    private void updateTilePrimitive(TilePrimitive tp, int x, int y, Point cameraNew, Point offset, int n_motif,
            Reverse reverse) {
        int xTex = (n_motif % 16) * 16;
        int yTex = (n_motif / 16) * 16; // +1;

        tp.updateTile((16 * x) - cameraNew.x + offset.x, (16 * y) - cameraNew.y + offset.y, xTex, yTex, reverse);
    }

    /**
     * Return the bank's index in loaded ones from a given name
     * 
     * @param p_name
     * @return int
     */
    public static int getBankFromName(String p_name) {
        int i = 0;
        for (String s : tileBankNames) {
            if (s.equalsIgnoreCase(p_name)) {
                return i;
            }
            i++;
        }
        throw new RuntimeException("Bank " + p_name + " doesn't exist.");
    }

    /**
     * Return the bank's name by index
     * 
     * @param nBank
     * @return String
     */
    public String getBankNameFromInt(int nBank) {
        return tileBankNames[nBank];
    }
}