/* =========================================================================== 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] ) ); }