Extending Quake Limits - Part 3: Alias Models

Post tutorials on how to do certain tasks within game or engine code here.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Post by revelator »

some experiments i done seems to work quite ok.

this was modified from old pqer source for and easy way to implement vertex arrays.

Code: Select all

// fenix@io.com: vertex arrays

/*
Copyright (C) 1996-1997 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.

*/

#include "quakedef.h"
#include "varrays.h"

#define R_MIN_VERTEX_COUNT 128

vavertex_t* global_vertex_array;
unsigned*   global_indice_array;
texcoord_t* global_lightmap_texcoords;

int global_vertex_array_size;
int global_indice_array_size;

int mode_table[] =
{
    /* R_POINTS         */ GL_POINTS,
    /* R_LINES          */ GL_LINES,
    /* R_LINE_STRIP     */ GL_LINE_STRIP,
    /* R_LINE_LOOP      */ GL_LINE_LOOP,
    /* R_TRIANGLES      */ GL_TRIANGLES,
    /* R_TRIANGLE_STRIP */ GL_TRIANGLE_STRIP,
    /* R_TRIANGLE_FAN   */ GL_TRIANGLE_FAN,
    /* R_QUADS          */ GL_QUADS,
    /* R_QUAD_STRIP     */ GL_QUAD_STRIP,
    /* R_POLYGON        */ GL_POLYGON
};

vavertex_t* current_vertex_array;
unsigned    current_vertex_fields;
texcoord_t* current_lightmap_texcoords;

/* R_InitVertexArrays
 ****************************************************************************/
void R_InitVertexArrays(void)
{
    int pnum = COM_CheckParm("-varraysize");

    if (pnum != 0)
    {
        global_vertex_array_size = atoi(com_argv[pnum + 1]);

        if (global_vertex_array_size < R_MIN_VERTEX_COUNT)
        {
            global_vertex_array_size = R_MIN_VERTEX_COUNT;
        }
    }
    else
    {
        global_vertex_array_size = R_MIN_VERTEX_COUNT;
    }
    global_indice_array_size = global_vertex_array_size;

    global_vertex_array = Heap_ZAlloc(sizeof(vavertex_t) * global_vertex_array_size);
    global_indice_array = Heap_ZAlloc(sizeof(vavertex_t) * global_indice_array_size);

    global_lightmap_texcoords = Heap_ZAlloc(sizeof(texcoord_t) * global_vertex_array_size);
}

/* R_VertexArray
 ****************************************************************************/
void R_VertexArray(vavertex_t* varray, texcoord_t* texcoords, unsigned fields)
{
    current_lightmap_texcoords = texcoords;
    current_vertex_array  = varray;
    current_vertex_fields = 0;

    if (fields & R_VERTEX3)
    {
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, sizeof(vavertex_t), &varray->x);
        current_vertex_fields |= R_VERTEX3;
    }
    else if (fields & R_VERTEX2)
    {
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(2, GL_FLOAT, sizeof(vavertex_t), &varray->x);
        current_vertex_fields |= R_VERTEX2;
    }
    else
    {
        glDisableClientState(GL_VERTEX_ARRAY);
    }

    if (fields & R_COLOR4)
    {
        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vavertex_t), &varray->r);
        current_vertex_fields |= R_COLOR4;
    }
    else if (fields & R_COLOR3)
    {
        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(vavertex_t), &varray->r);
        current_vertex_fields |= R_COLOR3;
    }
    else
    {
        glDisableClientState(GL_COLOR_ARRAY);
    }

    if (fields & R_TEXCOORD2)
    {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(2, GL_FLOAT, sizeof(vavertex_t), &varray->s);
        current_vertex_fields |= R_TEXCOORD2;
    }
    else
    {
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }
}

/* R_DrawArray
 ****************************************************************************/
void R_DrawArray(R_MODE mode, int first, size_t count)
{
    glDrawArrays(mode_table[mode], first, count);
}

/* R_DrawElements
 ****************************************************************************/
void R_DrawElements(R_MODE mode, size_t count, unsigned* indices)
{
    glDrawElements(mode_table[mode], count, GL_UNSIGNED_INT, indices);
}

/* R_DrawRangeElements
 ****************************************************************************/
void R_DrawRangeElements(R_MODE mode, unsigned start, unsigned end, size_t count, unsigned* indices)
{
    glDrawRangeElements(mode_table[mode], start, end, count, GL_UNSIGNED_INT, indices);
}


and the header

Code: Select all

// fenix@io.com: vertex arrays

#ifndef GL_RVA_INCLUDED
#define GL_RVA_INCLUDED

/*
Copyright (C) 1996-1997 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.

*/

typedef struct vavertex_s
{
    float         x, y, z;       // vertex
    float         s, t;          // texcoord
    signed char   i, j, k;       // normal
    unsigned char n;             // normal index
    unsigned char r, g, b, a;    // color
} vavertex_t;

typedef struct texcoord_s
{
    float s, t;
} texcoord_t;

typedef enum
{
    R_COLOR3    = 1,
    R_COLOR4    = 2,
    R_TEXCOORD2 = 4,
    R_VERTEX2   = 8,
    R_VERTEX3   = 16
} R_VAFIELD;

typedef enum
{
    R_POINTS,
    R_LINES,
    R_LINE_STRIP,
    R_LINE_LOOP,
    R_TRIANGLES,
    R_TRIANGLE_STRIP,
    R_TRIANGLE_FAN,
    R_QUADS,
    R_QUAD_STRIP,
    R_POLYGON
} R_MODE;

extern vavertex_t* global_vertex_array;
extern unsigned*   global_indice_array;
extern int global_vertex_array_size;
extern int global_indice_array_size;
extern texcoord_t* global_lightmap_texcoords;
extern texcoord_t* current_lightmap_texcoords;

void R_InitVertexArrays(void);
void R_VertexArray(vavertex_t* varray, texcoord_t* texcoords, unsigned fields);
void R_DrawArray(R_MODE mode, int first, size_t count);
void R_DrawElements(R_MODE mode, size_t count, unsigned* indices);
void R_DrawRangeElements(R_MODE, unsigned start, unsigned end, size_t count, unsigned* indices);

#endif
atm i use it for the parts of realm that was not yet drawn with arrays gl_draw.c etc.

heres an example of use.

Code: Select all

void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
{
	int				v, u, c;
	unsigned		trans[64*64], *dest;
	byte			*src;
	int				p;
    vavertex_t      *varray;

	GL_Bind (translate_texture);

	c = pic->width * pic->height;

	dest = trans;
	for (v=0 ; v<64 ; v++, dest += 64)
	{
		src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width];
		for (u=0 ; u<64 ; u++)
		{
			p = src[(u*pic->width)>>6];
			if (p == 255)
				dest[u] = p;
			else
				dest[u] =  d_8to24table[translation[p]];
		}
	}

	glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glColor3f (1,1,1);

    R_VertexArray(global_vertex_array, R_VERTEX2|R_TEXCOORD2);

    varray = global_vertex_array;

    varray->s = 0;
    varray->t = 0;
    varray->x = x;
    varray->y = y;
    varray++;

    varray->s = 1;
    varray->t = 0;
    varray->x = x + pic->width;
    varray->y = y;
    varray++;

    varray->s = 1;
    varray->t = 1;
    varray->x = x + pic->width;
    varray->y = y + pic->height;
    varray++;

    varray->s = 0;
    varray->t = 1;
    varray->x = x;
    varray->y = y + pic->height;

    R_DrawArray(R_QUADS, 0, 4);
}
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Post by mankrip »

The software renderer has limits hard-coded into the x86 ASM code. I've tried to extend the limit of vertexes in it once, but since I know pretty much nothing about x86 ASM I've got a lot of weird errors.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Extending Quake Limits - Part 3: Alias Models

Post by mh »

Vertex cache optimization (read all about it) for the glDrawElements version of the above code:

Code: Select all

	// optimize the model
	{
		int hunkmark = Hunk_LowMark ();

		int numtris = hdr->numindexes / 3;
		unsigned short *newindexes = (unsigned short *) Hunk_Alloc (hdr->numindexes * sizeof (unsigned short));
		DWORD *optresult = (DWORD *) Hunk_Alloc ((hdr->numverts > numtris ? hdr->numverts : numtris) * sizeof (DWORD));
		aliasmesh_t *newvertexes = (aliasmesh_t *) Hunk_Alloc (hdr->numverts * sizeof (aliasmesh_t));
		DWORD *remap = (DWORD *) Hunk_Alloc (hdr->numverts * sizeof (DWORD));

		D3DXOptimizeFaces (indexes, numtris, hdr->numverts, FALSE, optresult);

		for (int i = 0; i < numtris; i++)
		{
			int src = optresult[i] * 3;
			int dst = i * 3;

			newindexes[dst + 0] = indexes[src + 0];
			newindexes[dst + 1] = indexes[src + 1];
			newindexes[dst + 2] = indexes[src + 2];
		}

		D3DXOptimizeVertices (newindexes, numtris, hdr->numverts, FALSE, optresult);

		for (int i = 0; i < hdr->numverts; i++)
		{
			memcpy (&newvertexes[i], &hunkmesh[optresult[i]], sizeof (aliasmesh_t));
			remap[optresult[i]] = i;
		}

		// copy the optimized version back out
		for (int i = 0; i < hdr->numindexes; i++)
			indexes[i] = remap[newindexes[i]];

		for (int i = 0; i < hdr->numverts; i++)
		{
			hunkmesh[i].vertindex = newvertexes[i].vertindex;
			hunkmesh[i].st[0] = newvertexes[i].st[0];
			hunkmesh[i].st[1] = newvertexes[i].st[1];
		}

		Hunk_FreeToLowMark (hunkmark);
	}
Plonk this at the end of your new GL_MakeAliasModelDisplayLists.

You'll need to remove some C++isms (like the "int i" in the for loops) as well as pass pheader as an additional param to the original function (I called it "hdr" here).

This also needs to include "d3dx9.h" and link to "d3dx9.lib" but it works perfectly in OpenGL too; I just put it into RMQ and got very measurable performance improvements in cases where MDL drawing was the primary bottleneck in the scene.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Extending Quake Limits - Part 3: Alias Models

Post by revelator »

Long time past i last toyed with this, but there is a small bug id like to report.
Torch models go ballistic with this code (basically they dissapear), so in retrospect since last time i used it it was on a
modified tochris source, i made sure it was not due to any of my tinkering by applaying the code to an unmodified quake source and it still does it :oops:
Productivity is a state of mind.
Post Reply