/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "framework/DemoFile.h"
#include "renderer/tr_local.h"
#include "renderer/VertexCache.h"
#include "renderer/GuiModel.h"
/*
================
idGuiModel::idGuiModel
================
*/
idGuiModel::idGuiModel() {
indexes.SetGranularity( 1000 );
verts.SetGranularity( 1000 );
}
/*
================
idGuiModel::Clear
Begins collecting draw commands into surfaces
================
*/
void idGuiModel::Clear() {
surfaces.SetNum( 0, false );
indexes.SetNum( 0, false );
verts.SetNum( 0, false );
AdvanceSurf();
}
/*
================
idGuiModel::WriteToDemo
================
*/
void idGuiModel::WriteToDemo( idDemoFile *demo ) {
int i, j;
i = verts.Num();
demo->WriteInt( i );
for ( j = 0; j < i; j++ )
{
demo->WriteVec3( verts[j].xyz );
demo->WriteVec2( verts[j].st );
demo->WriteVec3( verts[j].normal );
demo->WriteVec3( verts[j].tangents[0] );
demo->WriteVec3( verts[j].tangents[1] );
demo->WriteUnsignedChar( verts[j].color[0] );
demo->WriteUnsignedChar( verts[j].color[1] );
demo->WriteUnsignedChar( verts[j].color[2] );
demo->WriteUnsignedChar( verts[j].color[3] );
}
i = indexes.Num();
demo->WriteInt( i );
for ( j = 0; j < i; j++ ) {
demo->WriteInt(indexes[j] );
}
i = surfaces.Num();
demo->WriteInt( i );
for ( j = 0 ; j < i ; j++ ) {
guiModelSurface_t *surf = &surfaces[j];
demo->WriteInt( (int&)surf->material );
demo->WriteFloat( surf->color[0] );
demo->WriteFloat( surf->color[1] );
demo->WriteFloat( surf->color[2] );
demo->WriteFloat( surf->color[3] );
demo->WriteInt( surf->firstVert );
demo->WriteInt( surf->numVerts );
demo->WriteInt( surf->firstIndex );
demo->WriteInt( surf->numIndexes );
demo->WriteHashString( surf->material->GetName() );
}
}
/*
================
idGuiModel::ReadFromDemo
================
*/
void idGuiModel::ReadFromDemo( idDemoFile *demo ) {
int i, j;
i = verts.Num();
demo->ReadInt( i );
verts.SetNum( i, false );
for ( j = 0; j < i; j++ )
{
demo->ReadVec3( verts[j].xyz );
demo->ReadVec2( verts[j].st );
demo->ReadVec3( verts[j].normal );
demo->ReadVec3( verts[j].tangents[0] );
demo->ReadVec3( verts[j].tangents[1] );
demo->ReadUnsignedChar( verts[j].color[0] );
demo->ReadUnsignedChar( verts[j].color[1] );
demo->ReadUnsignedChar( verts[j].color[2] );
demo->ReadUnsignedChar( verts[j].color[3] );
}
i = indexes.Num();
demo->ReadInt( i );
indexes.SetNum( i, false );
for ( j = 0; j < i; j++ ) {
demo->ReadInt(indexes[j] );
}
i = surfaces.Num();
demo->ReadInt( i );
surfaces.SetNum( i, false );
for ( j = 0 ; j < i ; j++ ) {
guiModelSurface_t *surf = &surfaces[j];
demo->ReadInt( (int&)surf->material );
demo->ReadFloat( surf->color[0] );
demo->ReadFloat( surf->color[1] );
demo->ReadFloat( surf->color[2] );
demo->ReadFloat( surf->color[3] );
demo->ReadInt( surf->firstVert );
demo->ReadInt( surf->numVerts );
demo->ReadInt( surf->firstIndex );
demo->ReadInt( surf->numIndexes );
surf->material = declManager->FindMaterial( demo->ReadHashString() );
}
}
/*
================
EmitSurface
================
*/
void idGuiModel::EmitSurface( guiModelSurface_t *surf, float modelMatrix[16], float modelViewMatrix[16], bool depthHack ) {
srfTriangles_t *tri;
if ( surf->numVerts == 0 ) {
return; // nothing in the surface
}
// copy verts and indexes
tri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *tri ) );
tri->numIndexes = surf->numIndexes;
tri->numVerts = surf->numVerts;
tri->indexes = (glIndex_t *)R_FrameAlloc( tri->numIndexes * sizeof( tri->indexes[0] ) );
memcpy( tri->indexes, &indexes[surf->firstIndex], tri->numIndexes * sizeof( tri->indexes[0] ) );
// we might be able to avoid copying these and just let them reference the list vars
// but some things, like deforms and recursive
// guis, need to access the verts in cpu space, not just through the vertex range
tri->verts = (idDrawVert *)R_FrameAlloc( tri->numVerts * sizeof( tri->verts[0] ) );
memcpy( tri->verts, &verts[surf->firstVert], tri->numVerts * sizeof( tri->verts[0] ) );
// move the verts to the vertex cache
tri->ambientCache = vertexCache.AllocFrameTemp( tri->verts, tri->numVerts * sizeof( tri->verts[0] ) );
// if we are out of vertex cache, don't create the surface
if ( !tri->ambientCache ) {
return;
}
renderEntity_t renderEntity;
memset( &renderEntity, 0, sizeof( renderEntity ) );
memcpy( renderEntity.shaderParms, surf->color, sizeof( surf->color ) );
viewEntity_t *guiSpace = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *guiSpace ) );
memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) );
memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) );
guiSpace->weaponDepthHack = depthHack;
// add the surface, which might recursively create another gui
R_AddDrawSurf( tri, guiSpace, &renderEntity, surf->material, tr.viewDef->scissor );
}
/*
====================
EmitToCurrentView
====================
*/
void idGuiModel::EmitToCurrentView( float modelMatrix[16], bool depthHack ) {
float modelViewMatrix[16];
myGlMultMatrix( modelMatrix, tr.viewDef->worldSpace.modelViewMatrix,
modelViewMatrix );
for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
EmitSurface( &surfaces[i], modelMatrix, modelViewMatrix, depthHack );
}
}
/*
================
idGuiModel::EmitFullScreen
Creates a view that covers the screen and emit the surfaces
================
*/
void idGuiModel::EmitFullScreen( void ) {
viewDef_t *viewDef;
if ( surfaces[0].numVerts == 0 ) {
return;
}
viewDef = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *viewDef ) );
// for gui editor
if ( !tr.viewDef || !tr.viewDef->isEditor ) {
viewDef->renderView.x = 0;
viewDef->renderView.y = 0;
viewDef->renderView.width = SCREEN_WIDTH;
viewDef->renderView.height = SCREEN_HEIGHT;
tr.RenderViewToViewport( &viewDef->renderView, &viewDef->viewport );
viewDef->scissor.x1 = 0;
viewDef->scissor.y1 = 0;
viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1;
viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1;
} else {
viewDef->renderView.x = tr.viewDef->renderView.x;
viewDef->renderView.y = tr.viewDef->renderView.y;
viewDef->renderView.width = tr.viewDef->renderView.width;
viewDef->renderView.height = tr.viewDef->renderView.height;
viewDef->viewport.x1 = tr.viewDef->renderView.x;
viewDef->viewport.x2 = tr.viewDef->renderView.x + tr.viewDef->renderView.width;
viewDef->viewport.y1 = tr.viewDef->renderView.y;
viewDef->viewport.y2 = tr.viewDef->renderView.y + tr.viewDef->renderView.height;
viewDef->scissor.x1 = tr.viewDef->scissor.x1;
viewDef->scissor.y1 = tr.viewDef->scissor.y1;
viewDef->scissor.x2 = tr.viewDef->scissor.x2;
viewDef->scissor.y2 = tr.viewDef->scissor.y2;
}
viewDef->floatTime = tr.frameShaderTime;
// qglOrtho( 0, 640, 480, 0, 0, 1 ); // always assume 640x480 virtual coordinates
viewDef->projectionMatrix[0] = 2.0f / 640.0f;
viewDef->projectionMatrix[5] = -2.0f / 480.0f;
viewDef->projectionMatrix[10] = -2.0f / 1.0f;
viewDef->projectionMatrix[12] = -1.0f;
viewDef->projectionMatrix[13] = 1.0f;
viewDef->projectionMatrix[14] = -1.0f;
viewDef->projectionMatrix[15] = 1.0f;
viewDef->worldSpace.modelViewMatrix[0] = 1.0f;
viewDef->worldSpace.modelViewMatrix[5] = 1.0f;
viewDef->worldSpace.modelViewMatrix[10] = 1.0f;
viewDef->worldSpace.modelViewMatrix[15] = 1.0f;
viewDef->maxDrawSurfs = surfaces.Num();
viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ) );
viewDef->numDrawSurfs = 0;
viewDef_t *oldViewDef = tr.viewDef;
tr.viewDef = viewDef;
// add the surfaces to this view
for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
EmitSurface( &surfaces[i], viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, false );
}
tr.viewDef = oldViewDef;
// add the command to draw this view
R_AddDrawViewCmd( viewDef );
}
/*
=============
AdvanceSurf
=============
*/
void idGuiModel::AdvanceSurf() {
guiModelSurface_t s;
if ( surfaces.Num() ) {
s.color[0] = surf->color[0];
s.color[1] = surf->color[1];
s.color[2] = surf->color[2];
s.color[3] = surf->color[3];
s.material = surf->material;
} else {
s.color[0] = 1;
s.color[1] = 1;
s.color[2] = 1;
s.color[3] = 1;
s.material = tr.defaultMaterial;
}
s.numIndexes = 0;
s.firstIndex = indexes.Num();
s.numVerts = 0;
s.firstVert = verts.Num();
surfaces.Append( s );
surf = &surfaces[ surfaces.Num() - 1 ];
}
/*
=============
SetColor
=============
*/
void idGuiModel::SetColor( float r, float g, float b, float a ) {
if ( !glConfig.isInitialized ) {
return;
}
if ( r == surf->color[0] && g == surf->color[1]
&& b == surf->color[2] && a == surf->color[3] ) {
return; // no change
}
if ( surf->numVerts ) {
AdvanceSurf();
}
// change the parms
surf->color[0] = r;
surf->color[1] = g;
surf->color[2] = b;
surf->color[3] = a;
}
/*
=============
DrawStretchPic
=============
*/
void idGuiModel::DrawStretchPic( const idDrawVert *dverts, const glIndex_t *dindexes, int vertCount, int indexCount, const idMaterial *hShader,
bool clip, float min_x, float min_y, float max_x, float max_y ) {
if ( !glConfig.isInitialized ) {
return;
}
if ( !( dverts && dindexes && vertCount && indexCount && hShader ) ) {
return;
}
// break the current surface if we are changing to a new material
if ( hShader != surf->material ) {
if ( surf->numVerts ) {
AdvanceSurf();
}
const_cast(hShader)->EnsureNotPurged(); // in case it was a gui item started before a level change
surf->material = hShader;
}
// add the verts and indexes to the current surface
if ( clip ) {
int i, j;
// FIXME: this is grim stuff, and should be rewritten if we have any significant
// number of guis asking for clipping
idFixedWinding w;
for ( i = 0; i < indexCount; i += 3 ) {
w.Clear();
w.AddPoint(idVec5(dverts[dindexes[i]].xyz.x, dverts[dindexes[i]].xyz.y, dverts[dindexes[i]].xyz.z, dverts[dindexes[i]].st.x, dverts[dindexes[i]].st.y));
w.AddPoint(idVec5(dverts[dindexes[i+1]].xyz.x, dverts[dindexes[i+1]].xyz.y, dverts[dindexes[i+1]].xyz.z, dverts[dindexes[i+1]].st.x, dverts[dindexes[i+1]].st.y));
w.AddPoint(idVec5(dverts[dindexes[i+2]].xyz.x, dverts[dindexes[i+2]].xyz.y, dverts[dindexes[i+2]].xyz.z, dverts[dindexes[i+2]].st.x, dverts[dindexes[i+2]].st.y));
for ( j = 0; j < 3; j++ ) {
if ( w[j].x < min_x || w[j].x > max_x ||
w[j].y < min_y || w[j].y > max_y ) {
break;
}
}
if ( j < 3 ) {
idPlane p;
p.Normal().y = p.Normal().z = 0.0f; p.Normal().x = 1.0f; p.SetDist( min_x );
w.ClipInPlace( p );
p.Normal().y = p.Normal().z = 0.0f; p.Normal().x = -1.0f; p.SetDist( -max_x );
w.ClipInPlace( p );
p.Normal().x = p.Normal().z = 0.0f; p.Normal().y = 1.0f; p.SetDist( min_y );
w.ClipInPlace( p );
p.Normal().x = p.Normal().z = 0.0f; p.Normal().y = -1.0f; p.SetDist( -max_y );
w.ClipInPlace( p );
}
int numVerts = verts.Num();
verts.SetNum( numVerts + w.GetNumPoints(), false );
for ( j = 0 ; j < w.GetNumPoints() ; j++ ) {
idDrawVert *dv = &verts[numVerts+j];
dv->xyz.x = w[j].x;
dv->xyz.y = w[j].y;
dv->xyz.z = w[j].z;
dv->st.x = w[j].s;
dv->st.y = w[j].t;
dv->normal.Set(0, 0, 1);
dv->tangents[0].Set(1, 0, 0);
dv->tangents[1].Set(0, 1, 0);
}
surf->numVerts += w.GetNumPoints();
for ( j = 2; j < w.GetNumPoints(); j++ ) {
indexes.Append( numVerts - surf->firstVert );
indexes.Append( numVerts + j - 1 - surf->firstVert );
indexes.Append( numVerts + j - surf->firstVert );
surf->numIndexes += 3;
}
}
} else {
int numVerts = verts.Num();
int numIndexes = indexes.Num();
verts.AssureSize( numVerts + vertCount );
indexes.AssureSize( numIndexes + indexCount );
surf->numVerts += vertCount;
surf->numIndexes += indexCount;
for ( int i = 0; i < indexCount; i++ ) {
indexes[numIndexes + i] = numVerts + dindexes[i] - surf->firstVert;
}
memcpy( &verts[numVerts], dverts, vertCount * sizeof( verts[0] ) );
}
}
/*
=============
DrawStretchPic
x/y/w/h are in the 0,0 to 640,480 range
=============
*/
void idGuiModel::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *hShader ) {
idDrawVert verts[4];
glIndex_t indexes[6];
if ( !glConfig.isInitialized ) {
return;
}
if ( !hShader ) {
return;
}
// clip to edges, because the pic may be going into a guiShader
// instead of full screen
if ( x < 0 ) {
s1 += ( s2 - s1 ) * -x / w;
w += x;
x = 0;
}
if ( y < 0 ) {
t1 += ( t2 - t1 ) * -y / h;
h += y;
y = 0;
}
if ( x + w > 640 ) {
s2 -= ( s2 - s1 ) * ( x + w - 640 ) / w;
w = 640 - x;
}
if ( y + h > 480 ) {
t2 -= ( t2 - t1 ) * ( y + h - 480 ) / h;
h = 480 - y;
}
if ( w <= 0 || h <= 0 ) {
return; // completely clipped away
}
indexes[0] = 3;
indexes[1] = 0;
indexes[2] = 2;
indexes[3] = 2;
indexes[4] = 0;
indexes[5] = 1;
verts[0].xyz[0] = x;
verts[0].xyz[1] = y;
verts[0].xyz[2] = 0;
verts[0].st[0] = s1;
verts[0].st[1] = t1;
verts[0].normal[0] = 0;
verts[0].normal[1] = 0;
verts[0].normal[2] = 1;
verts[0].tangents[0][0] = 1;
verts[0].tangents[0][1] = 0;
verts[0].tangents[0][2] = 0;
verts[0].tangents[1][0] = 0;
verts[0].tangents[1][1] = 1;
verts[0].tangents[1][2] = 0;
verts[1].xyz[0] = x + w;
verts[1].xyz[1] = y;
verts[1].xyz[2] = 0;
verts[1].st[0] = s2;
verts[1].st[1] = t1;
verts[1].normal[0] = 0;
verts[1].normal[1] = 0;
verts[1].normal[2] = 1;
verts[1].tangents[0][0] = 1;
verts[1].tangents[0][1] = 0;
verts[1].tangents[0][2] = 0;
verts[1].tangents[1][0] = 0;
verts[1].tangents[1][1] = 1;
verts[1].tangents[1][2] = 0;
verts[2].xyz[0] = x + w;
verts[2].xyz[1] = y + h;
verts[2].xyz[2] = 0;
verts[2].st[0] = s2;
verts[2].st[1] = t2;
verts[2].normal[0] = 0;
verts[2].normal[1] = 0;
verts[2].normal[2] = 1;
verts[2].tangents[0][0] = 1;
verts[2].tangents[0][1] = 0;
verts[2].tangents[0][2] = 0;
verts[2].tangents[1][0] = 0;
verts[2].tangents[1][1] = 1;
verts[2].tangents[1][2] = 0;
verts[3].xyz[0] = x;
verts[3].xyz[1] = y + h;
verts[3].xyz[2] = 0;
verts[3].st[0] = s1;
verts[3].st[1] = t2;
verts[3].normal[0] = 0;
verts[3].normal[1] = 0;
verts[3].normal[2] = 1;
verts[3].tangents[0][0] = 1;
verts[3].tangents[0][1] = 0;
verts[3].tangents[0][2] = 0;
verts[3].tangents[1][0] = 0;
verts[3].tangents[1][1] = 1;
verts[3].tangents[1][2] = 0;
DrawStretchPic( &verts[0], &indexes[0], 4, 6, hShader, false, 0.0f, 0.0f, 640.0f, 480.0f );
}
/*
=============
DrawStretchTri
x/y/w/h are in the 0,0 to 640,480 range
=============
*/
void idGuiModel::DrawStretchTri( idVec2 p1, idVec2 p2, idVec2 p3, idVec2 t1, idVec2 t2, idVec2 t3, const idMaterial *material ) {
idDrawVert tempVerts[3];
glIndex_t tempIndexes[3];
int vertCount = 3;
int indexCount = 3;
if ( !glConfig.isInitialized ) {
return;
}
if ( !material ) {
return;
}
tempIndexes[0] = 1;
tempIndexes[1] = 0;
tempIndexes[2] = 2;
tempVerts[0].xyz[0] = p1.x;
tempVerts[0].xyz[1] = p1.y;
tempVerts[0].xyz[2] = 0;
tempVerts[0].st[0] = t1.x;
tempVerts[0].st[1] = t1.y;
tempVerts[0].normal[0] = 0;
tempVerts[0].normal[1] = 0;
tempVerts[0].normal[2] = 1;
tempVerts[0].tangents[0][0] = 1;
tempVerts[0].tangents[0][1] = 0;
tempVerts[0].tangents[0][2] = 0;
tempVerts[0].tangents[1][0] = 0;
tempVerts[0].tangents[1][1] = 1;
tempVerts[0].tangents[1][2] = 0;
tempVerts[1].xyz[0] = p2.x;
tempVerts[1].xyz[1] = p2.y;
tempVerts[1].xyz[2] = 0;
tempVerts[1].st[0] = t2.x;
tempVerts[1].st[1] = t2.y;
tempVerts[1].normal[0] = 0;
tempVerts[1].normal[1] = 0;
tempVerts[1].normal[2] = 1;
tempVerts[1].tangents[0][0] = 1;
tempVerts[1].tangents[0][1] = 0;
tempVerts[1].tangents[0][2] = 0;
tempVerts[1].tangents[1][0] = 0;
tempVerts[1].tangents[1][1] = 1;
tempVerts[1].tangents[1][2] = 0;
tempVerts[2].xyz[0] = p3.x;
tempVerts[2].xyz[1] = p3.y;
tempVerts[2].xyz[2] = 0;
tempVerts[2].st[0] = t3.x;
tempVerts[2].st[1] = t3.y;
tempVerts[2].normal[0] = 0;
tempVerts[2].normal[1] = 0;
tempVerts[2].normal[2] = 1;
tempVerts[2].tangents[0][0] = 1;
tempVerts[2].tangents[0][1] = 0;
tempVerts[2].tangents[0][2] = 0;
tempVerts[2].tangents[1][0] = 0;
tempVerts[2].tangents[1][1] = 1;
tempVerts[2].tangents[1][2] = 0;
// break the current surface if we are changing to a new material
if ( material != surf->material ) {
if ( surf->numVerts ) {
AdvanceSurf();
}
const_cast(material)->EnsureNotPurged(); // in case it was a gui item started before a level change
surf->material = material;
}
int numVerts = verts.Num();
int numIndexes = indexes.Num();
verts.AssureSize( numVerts + vertCount );
indexes.AssureSize( numIndexes + indexCount );
surf->numVerts += vertCount;
surf->numIndexes += indexCount;
for ( int i = 0; i < indexCount; i++ ) {
indexes[numIndexes + i] = numVerts + tempIndexes[i] - surf->firstVert;
}
memcpy( &verts[numVerts], tempVerts, vertCount * sizeof( verts[0] ) );
}