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

Java tutorial

Introduction

Here is the source code for zildo.fwk.gfx.engine.SpriteEngine.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.HashMap;
import java.util.Map;

import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;
import org.newdawn.slick.TrueTypeFont;

import zildo.client.ClientEngineZildo;
import zildo.fwk.ZUtils;
import zildo.fwk.bank.SpriteBank;
import zildo.fwk.gfx.GFXBasics;
import zildo.fwk.gfx.PixelShaders.EngineFX;
import zildo.fwk.gfx.SpritePrimitive;
import zildo.monde.map.Point;
import zildo.monde.sprites.SpriteEntity;
import zildo.monde.sprites.SpriteModel;
import zildo.monde.sprites.SpriteStore;
import zildo.monde.sprites.desc.Outfit;
import zildo.monde.sprites.desc.ZildoOutfit;
import zildo.monde.sprites.elements.Element;
import zildo.resource.Constantes;
import zildo.server.SpriteManagement;

// SpriteEngine.cpp: implementation of the SpriteEngine class.
//
//////////////////////////////////////////////////////////////////////

public class SpriteEngine extends TextureEngine {

    // 3D Objects (vertices and indices per bank)
    SpritePrimitive meshSprites[] = new SpritePrimitive[Constantes.NB_SPRITEBANK];

    boolean pixelShaderSupported;

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

    public SpriteEngine() {
        super();

        pixelShaderSupported = isPixelShaderSupported();
    }

    public void init(SpriteStore p_spriteStore) {
        prepareSprites(p_spriteStore);
    }

    public void cleanUp() {
        for (SpritePrimitive sp : meshSprites) {
            sp.cleanUp();
        }
        for (int i = 0; i < n_Texture; i++) {
            int id = textureTab[i];
            cleanTexture(id);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // createTextureFromSpriteBank
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN: Bank to transform into texture
    ///////////////////////////////////////////////////////////////////////////////////////
    // Create a Direct3DTexture9 object from a sprite bank. Every sprite is added at the
    // right side of the previous one. If it is too long, we shift to next line, which is
    // calculated as the highest sprite on the line. And so on.
    // Here we use a GFXBasics object to gain in readability and comfort. This one will be deleted
    // at the end, indeed. So we can say this is a beautiful method.
    public void createTextureFromSpriteBank(SpriteBank sBank) {

        GFXBasics surfaceGfx = prepareSurfaceForTexture(true);

        surfaceGfx.StartRendering();
        surfaceGfx.box(0, 0, 320, 256, 32, new Vector4f(0, 0, 0, 0));

        int x = 0, y = 0, highestLine = 0;
        for (int n = 0; n < sBank.getNSprite(); n++) {
            SpriteModel spr = sBank.get_sprite(n);
            int longX = spr.getTaille_x();
            int longY = spr.getTaille_y();
            // Test de dpassement sur la texture
            if ((x + longX) > 256) {
                x = 0;
                y += highestLine;
                highestLine = 0;
            }
            // On stocke la position du sprite sur la texture
            spr.setTexPos_x(x);
            spr.setTexPos_y(y); //+1);
            // On place le sprite sur la texture
            short[] motif = sBank.getSpriteGfx(n);
            Vector4f replacedColor;
            for (int j = 0; j < longY; j++)

                for (int i = 0; i < longX; i++) {
                    int a = motif[i + j * longX];
                    if (a != 255) {
                        // Regular size
                        long modifiedColor = -1;
                        if (pixelShaderSupported) {
                            modifiedColor = sBank.modifyPixel(n, a);
                        }
                        replacedColor = modifiedColor == -1 ? null : createColor(modifiedColor);
                        surfaceGfx.pset(i + x, j + y, a, replacedColor);
                    }
                }

            // Next position
            x += longX;
            if (longY > highestLine) // Mark the highest sprite on the row
                highestLine = longY;
        }
        generateTexture();
    }

    /**
     * Create a new texture from a given one, and replace colors as specified by
     * the {@link Outfit} class.<br/>
     * 
     * @param p_originalTexture
     * @param p_replacements
     *            list of replacements : for a point (x,y), color-index <b>x</b>
     *            become color-index <b>y</b>.
     */
    public void createTextureFromAnotherReplacement(int p_originalTexture, Class<? extends Outfit> p_outfitClass) {

        GFXBasics surfaceGfx = prepareSurfaceForTexture(true);

        // 1) Store the color indexes once for all
        getTextureImage(textureTab[p_originalTexture]);
        Map<Integer, Integer> colorIndexes = new HashMap<Integer, Integer>();
        int i, j;
        for (j = 0; j < 256; j++) {
            for (i = 0; i < 256; i++) {
                Vector4f color = surfaceGfx.getPixel(i, j);
                if (color.w != 0) {
                    colorIndexes.put(j * 256 + i, surfaceGfx.getPalIndex(color));
                }
            }
        }

        // 2) Create all textures according to the outfits
        boolean textureReady = true;
        Outfit[] outfits = p_outfitClass.getEnumConstants();
        for (Outfit outfit : outfits) {
            Point[] replacements = outfit.getTransforms();
            if (replacements.length == 0) {
                continue; // No replacements
            }
            if (!textureReady) {
                surfaceGfx = prepareSurfaceForTexture(true);
            }
            surfaceGfx.StartRendering();
            for (j = 0; j < 256; j++) {
                for (i = 0; i < 256; i++) {
                    Integer palIndex = colorIndexes.get(j * 256 + i);
                    if (palIndex != null) {
                        for (Point p : replacements) {
                            if (palIndex == p.x) {
                                surfaceGfx.pset(i, j, p.y, null);
                            }
                        }
                    }
                }
            }

            generateTexture();
            textureReady = false;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // createTextureFromFontStyle
    ///////////////////////////////////////////////////////////////////////////////////////
    public void createTextureFromFontStyle(SpriteBank sprBank) {
        GFXBasics surfaceGfx = prepareSurfaceForTexture(true);

        TrueTypeFont ttFont = (TrueTypeFont) surfaceGfx.getFont();

        surfaceGfx.StartRendering();
        surfaceGfx.box(0, 0, 256, 8, 255, new Vector4f(0, 0, 0, 0));

        // Draw fonts on it

        String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-.,<>!?()'#$abcdefghijklmnopqrstuvwxyz";
        int posX = 0, posY = 0, maxHeight = 0;
        maxHeight = ttFont.getHeight();
        for (int i = 0; i < alphabet.length(); i++) {
            String a = alphabet.charAt(i) + "";
            int size = ttFont.getWidth(a);

            // Increase for outline effect
            size += 1;

            // We calculate the location for next font, then display it
            if ((posX + size) > 256) {
                posX = 0;
                posY += maxHeight;
                maxHeight = 0;
            }
            //if (size.bottom > maxHeight) {
            //   maxHeight=size.bottom;
            //}
            sprBank.addSpriteReference(posX, posY + 1, size, maxHeight);

            surfaceGfx.aff_texte(posX, posY + 1, a, 0xff0000ff, false);

            posX += size;

        }

        /*
        surfaceGfx.copyFromRenderedSurface();
        surfaceGfx.outlineBox(0,0,256,256,
        0xff0000ff,
        0xff00ff00,false);
        */
        generateTexture();

    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // prepareSprites
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN: Bank to transform into texture
    ///////////////////////////////////////////////////////////////////////////////////////
    // Prepare vertices and indices for drawing tiles
    void prepareSprites(SpriteStore p_spriteStore) {
        int i;

        // Allocate meshes
        for (i = 0; i < Constantes.NB_SPRITEBANK; i++) {
            if (i == SpriteBank.BANK_COPYSCREEN) {
                meshSprites[i] = new SpritePrimitive(4, 6, 512, 256);
            } else {
                meshSprites[i] = new SpritePrimitive(Constantes.NB_SPRITE_PER_PRIMITIVE * 4);
            }
        }
        // Load sprite banks
        for (i = 0; i < SpriteManagement.sprBankName.length; i++) {
            SpriteBank sprBank = p_spriteStore.getSpriteBank(i);

            // Create a DirectX9 texture based on the current tiles
            createTextureFromSpriteBank(sprBank);
        }

        // Create Zildo with all outfits
        if (!ClientEngineZildo.editing) {
            n_Texture = SpriteBank.BANK_ZILDOOUTFIT;
            createTextureFromAnotherReplacement(SpriteBank.BANK_ZILDO, ZildoOutfit.class);
        }

        // Prepare screen copy texture
        //textureTab[SpriteBank.BANK_COPYSCREEN]=generateTexture(0,64); //, 1024); //, Zildo.viewPortY);
        //n_Texture++;
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // addSprite
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN:  Entity to add into the primitive
    // OUT: Quad indice from the added entity
    ///////////////////////////////////////////////////////////////////////////////////////
    // Ajoute un sprite dans les IB/VB
    // *On considre que StartInitialization a dj t appel*
    public void addSprite(SpriteEntity entity) {
        float z = 0.0f;
        if (entity.getEntityType().isElement())
            z = ((Element) entity).z;

        SpriteModel spr = entity.getSprModel();
        entity.setLinkVertices(meshSprites[entity.getNBank()].addSprite(entity.getScrX(), entity.getScrY() - z,
                spr.getTexPos_x(), spr.getTexPos_y(), spr.getTaille_x(), spr.getTaille_y()));
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // synchronizeSprite
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN:  Entity to synchronize into the primitive
    ///////////////////////////////////////////////////////////////////////////////////////
    // *On considre que StartInitialization a dj t appel*
    public void synchronizeSprite(SpriteEntity entity) {

        float z = 0.0f;
        if (entity.getEntityType().isElement() || entity.getEntityType().isPerso())
            z = entity.z;

        // Reverse attribute
        int revX = entity.reverse.isHorizontal() ? -1 : 1;
        int revY = entity.reverse.isVertical() ? -1 : 1;

        SpriteModel spr = entity.getSprModel();
        entity.setLinkVertices(meshSprites[entity.getNBank()].synchronizeSprite(entity.getScrX(),
                entity.getScrY() - z, spr.getTexPos_x(), spr.getTexPos_y(), revX * spr.getTaille_x(),
                revY * spr.getTaille_y(), entity.repeatX, entity.repeatY));
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // endInitialization
    ///////////////////////////////////////////////////////////////////////////////////////
    public void endInitialization() {
        // Close meshes initialization
        for (int i = 0; i < Constantes.NB_SPRITEBANK; i++) {
            meshSprites[i].endInitialization();
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // startInitialization
    ///////////////////////////////////////////////////////////////////////////////////////
    public void startInitialization() {
        // Start meshes initialization
        for (int i = 0; i < Constantes.NB_SPRITEBANK; i++) {
            meshSprites[i].startInitialization();
        }
    }

    public void initRendering() {
        // Initialize mesh the first half-time for background
        // Because foreground display will continue rendering just after it.
        for (int i = 0; i < Constantes.NB_SPRITEBANK; i++)
            meshSprites[i].initRendering();
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // spriteRender
    ///////////////////////////////////////////////////////////////////////////////////////
    // Draw every sprite's primitives
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN: true=render BACKground
    //      false=render FOREground
    ///////////////////////////////////////////////////////////////////////////////////////
    public void render(boolean backGround) {

        // Display every sprites
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glEnable(GL11.GL_BLEND);
        float[] color = ZUtils.getFloat(GL11.GL_CURRENT_COLOR, 4);

        Vector3f ambient = ClientEngineZildo.ortho.getAmbientColor();
        if (ambient != null) {
            color[0] = ambient.x;
            color[1] = ambient.y;
            color[2] = ambient.z;
        }
        // Respect order from bankOrder
        boolean endSequence = false;
        int posBankOrder = 0;

        // Retrieve the sprite's order
        int[][] bankOrder = ClientEngineZildo.spriteDisplay.getBankOrder();

        int phase = (backGround) ? 0 : 1;
        while (!endSequence) {
            int numBank = bankOrder[phase][posBankOrder * 4];
            if (numBank == -1) {
                endSequence = true;
            } else {
                // Render the n sprites from this bank
                int nbQuads = bankOrder[phase][posBankOrder * 4 + 1];
                int iCurrentFX = bankOrder[phase][posBankOrder * 4 + 2];
                int alpha = bankOrder[phase][posBankOrder * 4 + 3];
                EngineFX currentFX = EngineFX.values()[iCurrentFX];
                int texId = textureTab[numBank];
                GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId);

                // Select the right pixel shader (if needed)
                if (pixelShaderSupported) {
                    switch (currentFX) {
                    case NO_EFFECT:
                        ARBShaderObjects.glUseProgramObjectARB(0);
                        break;
                    case PERSO_HURT:
                        // A sprite has been hurt
                        ARBShaderObjects.glUseProgramObjectARB(ClientEngineZildo.pixelShaders.getPixelShader(1));
                        ClientEngineZildo.pixelShaders.setParameter(1, "randomColor", new Vector4f(
                                (float) Math.random(), (float) Math.random(), (float) Math.random(), 1));
                        break;
                    default:
                        if (currentFX.needPixelShader()) {
                            // This is a color replacement, so get the right ones
                            Vector4f[] tabColors = ClientEngineZildo.pixelShaders
                                    .getConstantsForSpecialEffect(currentFX);

                            // And enable the 'color replacement' pixel shader
                            ARBShaderObjects
                                    .glUseProgramObjectARB(ClientEngineZildo.pixelShaders.getPixelShader(0));
                            ClientEngineZildo.pixelShaders.setParameter(0, "Color1", tabColors[2]);
                            ClientEngineZildo.pixelShaders.setParameter(0, "Color2", tabColors[3]);
                            ClientEngineZildo.pixelShaders.setParameter(0, "Color3",
                                    (Vector4f) new Vector4f(tabColors[0]).scale(color[0]));
                            ClientEngineZildo.pixelShaders.setParameter(0, "Color4",
                                    (Vector4f) new Vector4f(tabColors[1]).scale(color[0]));
                        } else {
                            ARBShaderObjects.glUseProgramObjectARB(0);
                        }
                    }
                }
                switch (currentFX) {
                case SHINY:
                    GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); // _MINUS_SRC_ALPHA);
                    GL11.glColor4f(1, (float) Math.random(), 0, (float) Math.random());
                    break;
                case QUAD:
                    GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
                    GL11.glColor4f(0.5f + 0.5f * (float) Math.random(), 0.5f * (float) Math.random(), 0, 1);
                    break;
                case FOCUSED:
                    GL11.glColor3f(1.0f, 1.0f, 1.0f);
                    break;
                default:
                    color[3] = alpha / 255.0f;
                    ZUtils.setCurrentColor(color);
                    GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
                }
                meshSprites[numBank].render(nbQuads);
                posBankOrder++;
            }
        }

        // Deactivate pixel shader
        if (pixelShaderSupported) {
            ARBShaderObjects.glUseProgramObjectARB(0);
        }
        GL11.glDisable(GL11.GL_BLEND);
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // buildIndexBuffers
    ///////////////////////////////////////////////////////////////////////////////////////
    // IN: quad order (sorted by texture, and by Y-position), number of quad on each one
    ///////////////////////////////////////////////////////////////////////////////////////
    // Call the homonym method on each sprite's primitive
    ///////////////////////////////////////////////////////////////////////////////////////
    public void buildIndexBuffers(int[][] quadOrder) {
        for (int i = 0; i < Constantes.NB_SPRITEBANK; i++) {
            int[] quadOrderForOneBank = quadOrder[i];
            if (quadOrderForOneBank != null) {
                meshSprites[i].buildIndexBuffer(quadOrderForOneBank);
            }
        }
    }

    /**
     * Capture screen before map scroll.
     */
    public void captureScreen() {
        saveScreen(textureTab[SpriteBank.BANK_COPYSCREEN]);
    }
}