lwjake2.render.lwjgl.Mesh.java Source code

Java tutorial

Introduction

Here is the source code for lwjake2.render.lwjgl.Mesh.java

Source

/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 * 
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package lwjake2.render.lwjgl;

import lwjake2.Defines;
import lwjake2.client.VID;
import lwjake2.client.entity_t;
import lwjake2.qcommon.qfiles;
import lwjake2.render.image_t;
import lwjake2.util.Math3D;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBMultitexture;
import org.lwjgl.opengl.GL11;

/**
 * Mesh
 *  
 * @author cwei
 */
public abstract class Mesh extends Light {

    // g_mesh.c: triangle model functions
    /*
    =============================================================
        
      ALIAS MODELS
        
    =============================================================
    */

    static final int NUMVERTEXNORMALS = 162;

    float[][] r_avertexnormals = Anorms.VERTEXNORMALS;
    float[] shadevector = { 0, 0, 0 };
    float[] shadelight = { 0, 0, 0 };

    // precalculated dot products for quantized angles
    static final int SHADEDOT_QUANT = 16;

    float[][] r_avertexnormal_dots = Anorms.VERTEXNORMAL_DOTS;

    float[] shadedots = r_avertexnormal_dots[0];

    /**
     * GL_LerpVerts
     * @param nverts
     * @param ov
     * @param verts
     * @param move
     * @param frontv
     * @param backv
     */
    void GL_LerpVerts(int nverts, int[] ov, int[] v, float[] move, float[] frontv, float[] backv) {
        FloatBuffer lerp = vertexArrayBuf;
        lerp.limit((nverts << 2) - nverts); // nverts * 3

        int ovv, vv;
        //PMM -- added RF_SHELL_DOUBLE, RF_SHELL_HALF_DAM
        if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE
                | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) {
            float[] normal;
            int j = 0;
            for (int i = 0; i < nverts; i++/* , v++, ov++, lerp+=4 */) {
                vv = v[i];
                normal = r_avertexnormals[(vv >>> 24) & 0xFF];
                ovv = ov[i];
                lerp.put(j, move[0] + (ovv & 0xFF) * backv[0] + (vv & 0xFF) * frontv[0]
                        + normal[0] * Defines.POWERSUIT_SCALE);
                lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF) * backv[1] + ((vv >>> 8) & 0xFF) * frontv[1]
                        + normal[1] * Defines.POWERSUIT_SCALE);
                lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF) * backv[2] + ((vv >>> 16) & 0xFF) * frontv[2]
                        + normal[2] * Defines.POWERSUIT_SCALE);
                j += 3;
            }
        } else {
            int j = 0;
            for (int i = 0; i < nverts; i++ /* , v++, ov++, lerp+=4 */) {
                ovv = ov[i];
                vv = v[i];

                lerp.put(j, move[0] + (ovv & 0xFF) * backv[0] + (vv & 0xFF) * frontv[0]);
                lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF) * backv[1] + ((vv >>> 8) & 0xFF) * frontv[1]);
                lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF) * backv[2] + ((vv >>> 16) & 0xFF) * frontv[2]);
                j += 3;
            }
        }
    }

    FloatBuffer colorArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 4);
    FloatBuffer vertexArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 3);
    FloatBuffer textureArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 2);
    boolean isFilled = false;
    float[] tmpVec = { 0, 0, 0 };
    float[][] vectors = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } // 3 mal vec3_t
    };

    // stack variable
    private final float[] move = { 0, 0, 0 }; // vec3_t      
    private final float[] frontv = { 0, 0, 0 }; // vec3_t
    private final float[] backv = { 0, 0, 0 }; // vec3_t

    /**
     * GL_DrawAliasFrameLerp
     * 
     * interpolates between two frames and origins
     * FIXME: batch lerp all vertexes
     */
    void GL_DrawAliasFrameLerp(qfiles.dmdl_t paliashdr, float backlerp) {
        qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame];

        int[] verts = frame.verts;

        qfiles.daliasframe_t oldframe = paliashdr.aliasFrames[currententity.oldframe];

        int[] ov = oldframe.verts;

        float alpha;
        if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0)
            alpha = currententity.alpha;
        else
            alpha = 1.0f;

        // PMM - added double shell
        if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE
                | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0)
            GL11.glDisable(GL11.GL_TEXTURE_2D);

        float frontlerp = 1.0f - backlerp;

        // move should be the delta back to the previous frame * backlerp
        Math3D.VectorSubtract(currententity.oldorigin, currententity.origin, frontv);
        Math3D.AngleVectors(currententity.angles, vectors[0], vectors[1], vectors[2]);

        move[0] = Math3D.DotProduct(frontv, vectors[0]); // forward
        move[1] = -Math3D.DotProduct(frontv, vectors[1]); // left
        move[2] = Math3D.DotProduct(frontv, vectors[2]); // up

        Math3D.VectorAdd(move, oldframe.translate, move);

        for (int i = 0; i < 3; i++) {
            move[i] = backlerp * move[i] + frontlerp * frame.translate[i];
            frontv[i] = frontlerp * frame.scale[i];
            backv[i] = backlerp * oldframe.scale[i];
        }

        // ab hier wird optimiert

        GL_LerpVerts(paliashdr.num_xyz, ov, verts, move, frontv, backv);

        //GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY );
        GL11.glVertexPointer(3, 0, vertexArrayBuf);

        // PMM - added double damage shell
        if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE
                | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) {
            GL11.glColor4f(shadelight[0], shadelight[1], shadelight[2], alpha);
        } else {
            GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
            GL11.glColorPointer(4, 0, colorArrayBuf);

            //
            // pre light everything
            //
            FloatBuffer color = colorArrayBuf;
            float l;
            int size = paliashdr.num_xyz;
            int j = 0;
            for (int i = 0; i < size; i++) {
                l = shadedots[(verts[i] >>> 24) & 0xFF];
                color.put(j, l * shadelight[0]);
                color.put(j + 1, l * shadelight[1]);
                color.put(j + 2, l * shadelight[2]);
                color.put(j + 3, alpha);
                j += 4;
            }
        }

        ARBMultitexture.glClientActiveTextureARB(GL_TEXTURE0);
        GL11.glTexCoordPointer(2, 0, textureArrayBuf);
        //GL11.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY);

        int pos = 0;
        int[] counts = paliashdr.counts;

        IntBuffer srcIndexBuf = null;

        FloatBuffer dstTextureCoords = textureArrayBuf;
        FloatBuffer srcTextureCoords = paliashdr.textureCoordBuf;

        int dstIndex = 0;
        int srcIndex = 0;
        int count;
        int mode;
        int size = counts.length;
        for (int j = 0; j < size; j++) {

            // get the vertex count and primitive type
            count = counts[j];
            if (count == 0)
                break; // done

            srcIndexBuf = paliashdr.indexElements[j];

            mode = GL11.GL_TRIANGLE_STRIP;
            if (count < 0) {
                mode = GL11.GL_TRIANGLE_FAN;
                count = -count;
            }
            srcIndex = pos << 1;
            srcIndex--;
            for (int k = 0; k < count; k++) {
                dstIndex = srcIndexBuf.get(k) << 1;
                dstTextureCoords.put(dstIndex, srcTextureCoords.get(++srcIndex));
                dstTextureCoords.put(++dstIndex, srcTextureCoords.get(++srcIndex));
            }

            GL11.glDrawElements(mode, srcIndexBuf);
            pos += count;
        }

        // PMM - added double damage shell
        if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE
                | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0)
            GL11.glEnable(GL11.GL_TEXTURE_2D);

        GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
    }

    private final float[] point = { 0, 0, 0 };

    /**
     * GL_DrawAliasShadow
     */
    void GL_DrawAliasShadow(qfiles.dmdl_t paliashdr, int posenum) {
        float lheight = currententity.origin[2] - lightspot[2];
        // qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame];
        int[] order = paliashdr.glCmds;
        float height = -lheight + 1.0f;

        int orderIndex = 0;
        int index = 0;

        // TODO shadow drawing with vertex arrays

        int count;
        while (true) {
            // get the vertex count and primitive type
            count = order[orderIndex++];
            if (count == 0)
                break; // done
            if (count < 0) {
                count = -count;
                GL11.glBegin(GL11.GL_TRIANGLE_FAN);
            } else
                GL11.glBegin(GL11.GL_TRIANGLE_STRIP);

            do {
                index = order[orderIndex + 2] * 3;
                point[0] = vertexArrayBuf.get(index);
                point[1] = vertexArrayBuf.get(index + 1);
                point[2] = vertexArrayBuf.get(index + 2);

                point[0] -= shadevector[0] * (point[2] + lheight);
                point[1] -= shadevector[1] * (point[2] + lheight);
                point[2] = height;
                GL11.glVertex3f(point[0], point[1], point[2]);

                orderIndex += 3;

            } while (--count != 0);

            GL11.glEnd();
        }
    }

    //   TODO sync with jogl renderer. hoz
    // stack variable
    private final float[] mins = { 0, 0, 0 };
    private final float[] maxs = { 0, 0, 0 };

    /**
     * R_CullAliasModel
    */
    boolean R_CullAliasModel(entity_t e) {
        qfiles.dmdl_t paliashdr = (qfiles.dmdl_t) currentmodel.extradata;

        if ((e.frame >= paliashdr.num_frames) || (e.frame < 0)) {
            VID.Printf(Defines.PRINT_ALL,
                    "R_CullAliasModel " + currentmodel.name + ": no such frame " + e.frame + '\n');
            e.frame = 0;
        }
        if ((e.oldframe >= paliashdr.num_frames) || (e.oldframe < 0)) {
            VID.Printf(Defines.PRINT_ALL,
                    "R_CullAliasModel " + currentmodel.name + ": no such oldframe " + e.oldframe + '\n');
            e.oldframe = 0;
        }

        qfiles.daliasframe_t pframe = paliashdr.aliasFrames[e.frame];
        qfiles.daliasframe_t poldframe = paliashdr.aliasFrames[e.oldframe];

        /*
        ** compute axially aligned mins and maxs
        */
        if (pframe == poldframe) {
            for (int i = 0; i < 3; i++) {
                mins[i] = pframe.translate[i];
                maxs[i] = mins[i] + pframe.scale[i] * 255;
            }
        } else {
            float thismaxs, oldmaxs;
            for (int i = 0; i < 3; i++) {
                thismaxs = pframe.translate[i] + pframe.scale[i] * 255;

                oldmaxs = poldframe.translate[i] + poldframe.scale[i] * 255;

                if (pframe.translate[i] < poldframe.translate[i])
                    mins[i] = pframe.translate[i];
                else
                    mins[i] = poldframe.translate[i];

                if (thismaxs > oldmaxs)
                    maxs[i] = thismaxs;
                else
                    maxs[i] = oldmaxs;
            }
        }

        /*
        ** compute a full bounding box
        */
        float[] tmp;
        for (int i = 0; i < 8; i++) {
            tmp = bbox[i];
            if ((i & 1) != 0)
                tmp[0] = mins[0];
            else
                tmp[0] = maxs[0];

            if ((i & 2) != 0)
                tmp[1] = mins[1];
            else
                tmp[1] = maxs[1];

            if ((i & 4) != 0)
                tmp[2] = mins[2];
            else
                tmp[2] = maxs[2];
        }

        /*
        ** rotate the bounding box
        */
        tmp = mins;
        Math3D.VectorCopy(e.angles, tmp);
        tmp[YAW] = -tmp[YAW];
        Math3D.AngleVectors(tmp, vectors[0], vectors[1], vectors[2]);

        for (int i = 0; i < 8; i++) {
            Math3D.VectorCopy(bbox[i], tmp);

            bbox[i][0] = Math3D.DotProduct(vectors[0], tmp);
            bbox[i][1] = -Math3D.DotProduct(vectors[1], tmp);
            bbox[i][2] = Math3D.DotProduct(vectors[2], tmp);

            Math3D.VectorAdd(e.origin, bbox[i], bbox[i]);
        }

        int f, mask;
        int aggregatemask = ~0; // 0xFFFFFFFF

        for (int p = 0; p < 8; p++) {
            mask = 0;

            for (f = 0; f < 4; f++) {
                float dp = Math3D.DotProduct(frustum[f].normal, bbox[p]);

                if ((dp - frustum[f].dist) < 0) {
                    mask |= (1 << f);
                }
            }

            aggregatemask &= mask;
        }

        if (aggregatemask != 0) {
            return true;
        }

        return false;
    }

    // bounding box
    float[][] bbox = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
            { 0, 0, 0 } };

    //   TODO sync with jogl renderer. hoz
    /**
     * R_DrawAliasModel
     */
    void R_DrawAliasModel(entity_t e) {
        if ((e.flags & Defines.RF_WEAPONMODEL) == 0) {
            if (R_CullAliasModel(e))
                return;
        }

        if ((e.flags & Defines.RF_WEAPONMODEL) != 0) {
            if (r_lefthand.value == 2.0f)
                return;
        }

        qfiles.dmdl_t paliashdr = (qfiles.dmdl_t) currentmodel.extradata;

        //
        // get lighting information
        //
        // PMM - rewrote, reordered to handle new shells & mixing
        // PMM - 3.20 code .. replaced with original way of doing it to keep mod authors happy
        //
        int i;
        if ((currententity.flags & (Defines.RF_SHELL_HALF_DAM | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_RED
                | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE)) != 0) {
            Math3D.VectorClear(shadelight);
            if ((currententity.flags & Defines.RF_SHELL_HALF_DAM) != 0) {
                shadelight[0] = 0.56f;
                shadelight[1] = 0.59f;
                shadelight[2] = 0.45f;
            }
            if ((currententity.flags & Defines.RF_SHELL_DOUBLE) != 0) {
                shadelight[0] = 0.9f;
                shadelight[1] = 0.7f;
            }
            if ((currententity.flags & Defines.RF_SHELL_RED) != 0)
                shadelight[0] = 1.0f;
            if ((currententity.flags & Defines.RF_SHELL_GREEN) != 0)
                shadelight[1] = 1.0f;
            if ((currententity.flags & Defines.RF_SHELL_BLUE) != 0)
                shadelight[2] = 1.0f;
        }

        else if ((currententity.flags & Defines.RF_FULLBRIGHT) != 0) {
            for (i = 0; i < 3; i++)
                shadelight[i] = 1.0f;
        } else {
            R_LightPoint(currententity.origin, shadelight);

            // player lighting hack for communication back to server
            // big hack!
            if ((currententity.flags & Defines.RF_WEAPONMODEL) != 0) {
                // pick the greatest component, which should be the same
                // as the mono value returned by software
                if (shadelight[0] > shadelight[1]) {
                    if (shadelight[0] > shadelight[2])
                        r_lightlevel.value = 150 * shadelight[0];
                    else
                        r_lightlevel.value = 150 * shadelight[2];
                } else {
                    if (shadelight[1] > shadelight[2])
                        r_lightlevel.value = 150 * shadelight[1];
                    else
                        r_lightlevel.value = 150 * shadelight[2];
                }
            }

            if (gl_monolightmap.string.charAt(0) != '0') {
                float s = shadelight[0];

                if (s < shadelight[1])
                    s = shadelight[1];
                if (s < shadelight[2])
                    s = shadelight[2];

                shadelight[0] = s;
                shadelight[1] = s;
                shadelight[2] = s;
            }
        }

        if ((currententity.flags & Defines.RF_MINLIGHT) != 0) {
            for (i = 0; i < 3; i++)
                if (shadelight[i] > 0.1f)
                    break;
            if (i == 3) {
                shadelight[0] = 0.1f;
                shadelight[1] = 0.1f;
                shadelight[2] = 0.1f;
            }
        }

        if ((currententity.flags & Defines.RF_GLOW) != 0) { // bonus items will pulse with time
            float scale;
            float min;

            scale = (float) (0.1f * Math.sin(r_newrefdef.time * 7));
            for (i = 0; i < 3; i++) {
                min = shadelight[i] * 0.8f;
                shadelight[i] += scale;
                if (shadelight[i] < min)
                    shadelight[i] = min;
            }
        }

        // =================
        // PGM   ir goggles color override
        if ((r_newrefdef.rdflags & Defines.RDF_IRGOGGLES) != 0
                && (currententity.flags & Defines.RF_IR_VISIBLE) != 0) {
            shadelight[0] = 1.0f;
            shadelight[1] = 0.0f;
            shadelight[2] = 0.0f;
        }
        // PGM   
        // =================

        shadedots = r_avertexnormal_dots[((int) (currententity.angles[1] * (SHADEDOT_QUANT / 360.0)))
                & (SHADEDOT_QUANT - 1)];

        float an = (float) (currententity.angles[1] / 180 * Math.PI);
        shadevector[0] = (float) Math.cos(-an);
        shadevector[1] = (float) Math.sin(-an);
        shadevector[2] = 1;
        Math3D.VectorNormalize(shadevector);

        //
        // locate the proper data
        //

        c_alias_polys += paliashdr.num_tris;

        //
        // draw all the triangles
        //
        if ((currententity.flags & Defines.RF_DEPTHHACK) != 0) // hack the depth range to prevent view model from poking into walls
            GL11.glDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin));

        if ((currententity.flags & Defines.RF_WEAPONMODEL) != 0 && (r_lefthand.value == 1.0f)) {
            GL11.glMatrixMode(GL11.GL_PROJECTION);
            GL11.glPushMatrix();
            GL11.glLoadIdentity();
            GL11.glScalef(-1, 1, 1);
            MYgluPerspective(r_newrefdef.fov_y, (float) r_newrefdef.width / r_newrefdef.height, 4, 4096);
            GL11.glMatrixMode(GL11.GL_MODELVIEW);

            GL11.glCullFace(GL11.GL_BACK);
        }

        GL11.glPushMatrix();
        e.angles[PITCH] = -e.angles[PITCH]; // sigh.
        R_RotateForEntity(e);
        e.angles[PITCH] = -e.angles[PITCH]; // sigh.

        image_t skin;
        // select skin
        if (currententity.skin != null)
            skin = currententity.skin; // custom player skin
        else {
            if (currententity.skinnum >= qfiles.MAX_MD2SKINS)
                skin = currentmodel.skins[0];
            else {
                skin = currentmodel.skins[currententity.skinnum];
                if (skin == null)
                    skin = currentmodel.skins[0];
            }
        }
        if (skin == null)
            skin = r_notexture; // fallback...
        GL_Bind(skin.texnum);

        // draw it

        GL11.glShadeModel(GL11.GL_SMOOTH);

        GL_TexEnv(GL11.GL_MODULATE);
        if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
            GL11.glEnable(GL11.GL_BLEND);
        }

        if ((currententity.frame >= paliashdr.num_frames) || (currententity.frame < 0)) {
            VID.Printf(Defines.PRINT_ALL,
                    "R_DrawAliasModel " + currentmodel.name + ": no such frame " + currententity.frame + '\n');
            currententity.frame = 0;
            currententity.oldframe = 0;
        }

        if ((currententity.oldframe >= paliashdr.num_frames) || (currententity.oldframe < 0)) {
            VID.Printf(Defines.PRINT_ALL, "R_DrawAliasModel " + currentmodel.name + ": no such oldframe "
                    + currententity.oldframe + '\n');
            currententity.frame = 0;
            currententity.oldframe = 0;
        }

        if (r_lerpmodels.value == 0.0f)
            currententity.backlerp = 0;

        GL_DrawAliasFrameLerp(paliashdr, currententity.backlerp);

        GL_TexEnv(GL11.GL_REPLACE);
        GL11.glShadeModel(GL11.GL_FLAT);

        GL11.glPopMatrix();

        if ((currententity.flags & Defines.RF_WEAPONMODEL) != 0 && (r_lefthand.value == 1.0F)) {
            GL11.glMatrixMode(GL11.GL_PROJECTION);
            GL11.glPopMatrix();
            GL11.glMatrixMode(GL11.GL_MODELVIEW);
            GL11.glCullFace(GL11.GL_FRONT);
        }

        if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
            GL11.glDisable(GL11.GL_BLEND);
        }

        if ((currententity.flags & Defines.RF_DEPTHHACK) != 0)
            GL11.glDepthRange(gldepthmin, gldepthmax);

        if (gl_shadows.value != 0.0f
                && (currententity.flags & (Defines.RF_TRANSLUCENT | Defines.RF_WEAPONMODEL)) == 0) {
            GL11.glPushMatrix();
            R_RotateForEntity(e);
            GL11.glDisable(GL11.GL_TEXTURE_2D);
            GL11.glEnable(GL11.GL_BLEND);
            GL11.glColor4f(0, 0, 0, 0.5f);
            GL_DrawAliasShadow(paliashdr, currententity.frame);
            GL11.glEnable(GL11.GL_TEXTURE_2D);
            GL11.glDisable(GL11.GL_BLEND);
            GL11.glPopMatrix();
        }
        GL11.glColor4f(1, 1, 1, 1);
    }
}