rendered paste bodyIndex: src/common/effects/pseffectobjlabel.h===================================================================--- src/common/effects/pseffectobjlabel.h (revision 0)+++ src/common/effects/pseffectobjlabel.h (revision 436)@@ -0,0 +1,82 @@+/*+ * Author: Roland Schulz+ *+ * Copyright (C) 2007 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)+ *+ *+ * 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 (version 2 of the License)+ * 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.+ *+ */++#ifndef PS_EFFECT_OBJ_LABEL_HEADER+#define PS_EFFECT_OBJ_LABEL_HEADER++#include "pseffectobj.h"+#include <imesh/genmesh.h>+#include <csutil/cscolor.h>+#include "pseffectobjtextable.h"++struct iGraphics3D;+struct iGraphics2D;+struct iTextureManager;+struct iTextureWrapper;+struct iFont;+struct iParticle;+class psEffect2DRenderer;++class psEffectObjLabel : public psEffectObj, public psEffectObjTextable+{+private:+ // needed managers+ csRef<iTextureManager> txtmgr;+ csRef<iGeneralFactoryState> facState;+ csRef<iGeneralMeshState> genState;++ // font+ csString sizeFileName;+ csArray<uint16> xpos;+ csArray<uint16> ypos;+ csArray<uint16> width;++public:+ psEffectObjLabel(iView *parentView, psEffect2DRenderer * renderer2d);+ virtual ~psEffectObjLabel();++ /** Creates a material that will fit an array of text elements and draws those elements.+ */+ virtual bool SetText(const csArray<psEffectTextElement> & elements);+ virtual bool SetText(const csArray<psEffectTextRow> & rows);+ virtual bool SetText(int rows, ...);++ bool Load(iDocumentNode * node);+ void LoadGlyphs(csString name);+ psEffectObj * Clone() const;+ // inherited function overloads+ virtual bool Render(const csVector3 &up);+ virtual bool Update(csTicks elapsed);+ virtual void CloneBase(psEffectObj * newObj) const;++protected:+ /** performs the post setup (after the effect obj has been loaded).+ * Things like create mesh factory, etc are initialized here.+ */+ bool PostSetup();++ void DrawTextElement(const psEffectTextElement & element);++ /** Creates a mesh factory for this quad.+ */+ virtual bool CreateMeshFact();++};++#endifIndex: src/common/effects/pseffect.cpp===================================================================--- src/common/effects/pseffect.cpp (revision 435)+++ src/common/effects/pseffect.cpp (revision 436)@@ -45,6 +45,7 @@ #include "pseffectobjtrail.h" #include "pseffectobjdecal.h" #include "pseffectobjtext2d.h"+#include "pseffectobjlabel.h" #include "util/log.h" #include "util/pscssetup.h"@@ -184,6 +185,10 @@ { obj = new psEffectObjText(parentView, renderer2d); }+ else if (type == "label")+ {+ obj = new psEffectObjLabel(parentView, renderer2d);+ } else if (type == "trail") { obj = new psEffectObjTrail(parentView, renderer2d);Index: src/common/effects/pseffectobjlabel.cpp===================================================================--- src/common/effects/pseffectobjlabel.cpp (revision 0)+++ src/common/effects/pseffectobjlabel.cpp (revision 436)@@ -0,0 +1,436 @@+/*+* Author: Roland Schulz+*+* Copyright (C) 2007 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)+*+*+* 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 (version 2 of the License)+* 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 <ctype.h>+#include <psconfig.h>++#include <csutil/xmltiny.h>+#include <iengine/engine.h>+#include <iengine/material.h>+#include <ivideo/material.h>+#include <iengine/mesh.h>+#include <iengine/movable.h>+#include <iengine/camera.h>+#include <cstool/csview.h>+#include <iengine/region.h>+#include <imesh/objmodel.h>+#include <csutil/flags.h>+#include <csgeom/tri.h>++#include "effects/pseffectobjlabel.h"+#include "effects/pseffectanchor.h"+#include "effects/pseffect2drenderer.h"++#include "util/log.h"+#include "util/pscssetup.h"++#define NUM_GLYPHS 256++psEffectObjLabel::psEffectObjLabel(iView * parentView, psEffect2DRenderer * renderer2d)+ : psEffectObj(parentView, renderer2d)+{+ meshFact = 0;+ xpos.SetSize(NUM_GLYPHS,0);+ ypos.SetSize(NUM_GLYPHS,0);+ width.SetSize(NUM_GLYPHS,64);+ for(int i=0; i<256; i++)+ {+ xpos[i] = (i%16) * 64;+ ypos[i] = (i/16-2) * 64;+ }+}++psEffectObjLabel::~psEffectObjLabel()+{+ if( meshFact )+ {+ engine->RemoveObject(meshFact);+ }+ if( mesh )+ {+ engine->RemoveObject(mesh);+ }++}++bool psEffectObjLabel::Load(iDocumentNode *node)+{+ // get the attributes+ name = "";+ materialName = "";+ csRef<iDocumentAttributeIterator> attribIter = node->GetAttributes();+ while (attribIter->HasNext())+ {+ csRef<iDocumentAttribute> attr = attribIter->Next();+ csString attrName = attr->GetName();++ attrName.Downcase();+ if (attrName == "name")+ name = attr->GetValue();+ else if (attrName == "material")+ materialName = attr->GetValue();+ else if (attrName == "sizefile")+ sizeFileName = attr->GetValue();+ }+ if (name == "")+ {+ csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Attempting to create an effect obj with no name.\n");+ return false;+ }+ if (materialName == "")+ {+ csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Attempting to create an effect obj with no material.\n");+ return false;+ }+ if (sizeFileName == "")+ {+ LoadGlyphs(sizeFileName);+ } + + if (!psEffectObj::Load(node))+ {+ return false;+ }+ return PostSetup();+}+++void psEffectObjLabel::LoadGlyphs(csString filename)+{+ csRef<iVFS> vfs = csQueryRegistry<iVFS > ( psCSSetup::object_reg );+ csRef<iDocumentSystem> xml = csQueryRegistry<iDocumentSystem> (psCSSetup::object_reg);+ csRef<iDocument> doc = xml->CreateDocument();+ csRef<iDataBuffer> buf (vfs->ReadFile (filename));+ if (!buf || !buf->GetSize ())+ {+ return ;+ }+ const char* error = doc->Parse( buf );+ if ( error )+ {+ printf("Error loading glyphs file: %s\n", error);+ return ;+ }++ csRef<iDocumentNodeIterator> iter = doc->GetRoot()->GetNode("glyphs")->GetNodes();+ int number;++ while ( iter->HasNext() )+ {+ csRef<iDocumentNode> child = iter->Next();++ if ( child->GetType() != CS_NODE_ELEMENT )+ continue;+ sscanf(child->GetValue(), "glyph%d", &number);+ if (number < 0 || number >= NUM_GLYPHS)+ continue;+ width[number] = child->GetAttributeValueAsInt("width");+ xpos[number] = child->GetAttributeValueAsInt("x");+ ypos[number] = child->GetAttributeValueAsInt("y");+ }+}+++bool psEffectObjLabel::PostSetup()+{+// if (!CreateMeshFact())+// return false;++ return true;+}++bool psEffectObjLabel::CreateMeshFact()+{+ static unsigned int uniqueID = 0;+ csString facName = "effect_label_fac_";+ facName += uniqueID++;+ + meshFact = engine->CreateMeshFactory ("crystalspace.mesh.object.genmesh", facName.GetData());+ region->Add(meshFact->QueryObject());+ + iMeshObjectFactory * fact = meshFact->GetMeshObjectFactory();+ facState = scfQueryInterface<iGeneralFactoryState> (fact);+ if (!facState)+ {+ csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Couldn't create genmesh: %s\n", name.GetData());+ return false;+ }++ // setup the material+ csRef<iMaterialWrapper> mat = region->FindMaterial(materialName);+ if (mat)+ {+ fact->SetMaterialWrapper(mat);+ } else {+ csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "No material for label mesh: %s\n", materialName.GetData());+ }+ +#if 0+ facState->SetVertexCount(4);++ // we have to set the vertices so that the quad actually gets rendered+ facState->GetVertices()[0].Set( 0.5f, 0, 0.5f);+ facState->GetVertices()[1].Set(-0.5f, 0, 0.5f);+ facState->GetVertices()[2].Set(-0.5f, 0, -0.5f);+ facState->GetVertices()[3].Set( 0.5f, 0, -0.5f);+ facState->GetTexels()[0].Set(5.0/8,4.0/16);+ facState->GetTexels()[1].Set(4.0/8,4.0/16);+ facState->GetTexels()[2].Set(4.0/8,5.0/16);+ facState->GetTexels()[3].Set(5.0/8,5.0/16);+ facState->GetNormals()[0].Set(0,1,0);+ facState->GetNormals()[1].Set(0,1,0);+ facState->GetNormals()[2].Set(0,1,0);+ facState->GetNormals()[3].Set(0,1,0);++ facState->SetTriangleCount(4);++ facState->GetTriangles()[0].Set(0, 2, 1);+ facState->GetTriangles()[1].Set(0, 2, 3);+ facState->GetTriangles()[2].Set(2, 0, 1);+ facState->GetTriangles()[3].Set(2, 0, 3);+#endif+ return true;+}+++bool psEffectObjLabel::Render(const csVector3 &up)+{+ if(!CreateMeshFact())+ return false;++ objUp = up;++ static unsigned long nextUniqueID = 0;+ csString effectID = "effect_label_";+ effectID += nextUniqueID++;++ // create a mesh wrapper from the factory we just created+ mesh = engine->CreateMeshWrapper(meshFact, effectID.GetData());+ if (!mesh)+ {+ csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Couldn't create meshwrapper in label effect\n");+ return false;+ }++ // do the up vector+// csReversibleTransform rt;+// rt.LookAt(csVector3(up.x, up.z, up.y), csVector3(0,2,1));+// matUp = rt.GetT2O();+// matBase = matUp;++ // common flags+ mesh->GetFlags().Set(CS_ENTITY_NOHITBEAM);+ mesh->GetFlags().Set(CS_ENTITY_NODECAL);+ // we can have labels overlay geometry, or we can have them be occluded by it like normal objects+ //priority = engine->GetAlphaRenderPriority();+ // zFunc = CS_ZBUF_NONE;+ priority = engine->GetObjectRenderPriority();+ zFunc = CS_ZBUF_USE;+ mixmode = CS_FX_COPY;+ //printf("zbufmode: %d pri: %d mixmode: %d\n", zFunc, priority, mixmode);+ mesh->SetZBufMode(zFunc);+ mesh->SetRenderPriority(priority);++ // disable culling+ csRef<iStringSet> globalStringSet = csQueryRegistryTagInterface<iStringSet> + (psCSSetup::object_reg, "crystalspace.shared.stringset");+ csStringID viscull_id = globalStringSet->Request ("viscull");+ mesh->GetMeshObject()->GetObjectModel()->SetTriangleData(viscull_id, 0);++ genState = scfQueryInterface<iGeneralMeshState> (mesh->GetMeshObject());+ genState->SetLighting(false);++ // obj specific++ if (mixmode != CS_FX_ALPHA)+ mesh->GetMeshObject()->SetMixMode(mixmode); // to check+ // genState->SetMixMode(mixmode); //was not working with latest CS++ return true;+}++bool psEffectObjLabel::Update(csTicks elapsed)+{+ if (!anchor || !anchor->IsReady()) // wait for anchor to be ready+ return true;++ if (!psEffectObj::Update(elapsed))+ return false;++ return true;+}++void psEffectObjLabel::CloneBase(psEffectObj * newObj) const+{+ psEffectObj::CloneBase(newObj);++ psEffectObjLabel * newLabelObj = dynamic_cast<psEffectObjLabel *>(newObj);++ newLabelObj->materialName = materialName;+}++psEffectObj * psEffectObjLabel::Clone() const+{+ psEffectObjLabel * newObj = new psEffectObjLabel(view, renderer2d);+ CloneBase(newObj);+ + return newObj;+}++bool psEffectObjLabel::SetText(const csArray<psEffectTextElement> & elements)+{+ printf("settext <array> not supported\n");+ return false;+}++bool psEffectObjLabel::SetText(const csArray<psEffectTextRow> & rows)+{+ printf("settext <textrow> not supported\n");+ return false;+}++bool psEffectObjLabel::SetText(int rows, ...)+{+ size_t lettercount = 0;++ static csArray<psEffectTextElement> elemBuffer;+ psEffectTextElement newElem;+ psEffectTextRow * row = 0;+ int y = 0;++ // calculate dimensions of text area+ int maxWidth = 0;+ int maxHeight = 0;++ elemBuffer.Empty();++ va_list arg;+ va_start(arg, rows);++ // Loop through all rows+ for (int a=0; a<rows; a++)+ {+ row = va_arg(arg, psEffectTextRow *);++ // Text and Formatting+ newElem.colour = row->colour;+ newElem.hasOutline = row->hasOutline;+ newElem.hasShadow = row->hasShadow;+ newElem.shadowColour = row->shadowColour;+ newElem.outlineColour = row->outlineColour;+ newElem.text = row->text;+ newElem.align = row->align;+ newElem.width = 0;+ for(uint b=0; b<newElem.text.Length(); b++)+ {+ newElem.width += width[newElem.text[b]];+ }+ // Positioning+ //font->GetDimensions(newElem.text, newElem.width, newElem.height);+ newElem.height = 64;+ newElem.x = 0;+ newElem.y = y;+ elemBuffer.Push(newElem);+ y += newElem.height;+ lettercount += newElem.text.Length();++ int right = newElem.x + newElem.width;+ int bottom = newElem.y + newElem.height;++ if (right > maxWidth)+ maxWidth = right;+ if (bottom > maxHeight)+ maxHeight = bottom;+ }+ va_end(arg);++ CS_ASSERT(facState!=0);+ iMeshObjectFactory * fact = meshFact->GetMeshObjectFactory();+ int mw,mh;+ fact->GetMaterialWrapper()->GetMaterial()->GetTexture()->GetOriginalDimensions(mw, mh);++ facState->SetVertexCount( lettercount * 4 );+ facState->SetTriangleCount( lettercount * 2 );++ size_t elementCount = elemBuffer.GetSize();+ int cp = 0;++ for(size_t i=0; i<elementCount; i++)+ {+ int x = elemBuffer[i].width;+ + switch (elemBuffer[i].align)+ {+ case ETA_LEFT:+ x = maxWidth;+ break;+ case ETA_CENTER:+ x = (maxWidth-x)/2;+ break;+ case ETA_RIGHT:+ x = maxWidth-x;+ break;+ }+ int y = elemBuffer[i].y;+ const char * text = elemBuffer[i].text;+ for(size_t j=0; j<strlen(text); j++)+ {+ uint c =text[j];+ float fx1, fy1, fx2, fy2;+ fx1 = (float)x / maxWidth*3 - 1.5;+ fy1 = (float)y / maxWidth*3;+ fx2 = (float)(x+width[c]) / maxWidth*3 - 1.5;+ fy2 = (float)(y+64) / maxWidth*3;+ //printf("rendering char %d pos %d,%d - %f %f %f %f\n", cp/4, x, y, fx1, fy1, fx2, fy2);+ facState->GetVertices()[cp ].Set(fx2,0,fy1); + facState->GetVertices()[cp+1].Set(fx1,0,fy1); + facState->GetVertices()[cp+2].Set(fx1,0,fy2); + facState->GetVertices()[cp+3].Set(fx2,0,fy2);+ // texture is assumed to be 8x8, each cell 64x64+ float fracx1 = (float)xpos[c] / mw;+ float fracy1 = (float)ypos[c] / mh;+ float fracx2 = (float)(xpos[c]+width[c]) / mw;+ float fracy2 = (float)(ypos[c]+64) / mh;+ //printf("texels %c %d,%d - %f %f %f %f\n", text[j], xpos[c], ypos[c], fracx1, fracy1, fracx2, fracy2);+ facState->GetTexels()[cp ].Set( fracx2, fracy2 );+ facState->GetTexels()[cp+1].Set( fracx1, fracy2 );+ facState->GetTexels()[cp+2].Set( fracx1, fracy1 );+ facState->GetTexels()[cp+3].Set( fracx2, fracy1 );+ facState->GetNormals()[0].Set(0,1,0);+ facState->GetNormals()[1].Set(0,1,0);+ facState->GetNormals()[2].Set(0,1,0);+ facState->GetNormals()[3].Set(0,1,0);+ //facState->GetTriangles()[(cp/2) ].Set( cp , cp+2, cp+1);+ facState->GetTriangles()[(cp/2) ].Set( cp , cp+2, cp+3);+ facState->GetTriangles()[(cp/2)+1].Set( cp+2, cp , cp+1);+ //facState->GetTriangles()[cp+3].Set( cp+2, cp , cp+3);+ cp += 4;+ x += 64;+ }+ csColor color((float)((newElem.colour>>16) & 255)/255.0F,+ (float)((newElem.colour>> 8) & 255)/255.0F,+ (float)((newElem.colour ) & 255)/255.0F);+ mesh->GetMeshObject()->SetColor(color);+ }+// facState->CalculateNormals();+// facState->SetColor(csColor(1.0,1.0,1.0));+// facState->SetLighting(false);+// fact->SetMixMode(CS_FX_ADD);+// Render2(objUp);+ return true;+}Index: data/effects/misc/labelfontglyphs.xml===================================================================--- data/effects/misc/labelfontglyphs.xml (revision 0)+++ data/effects/misc/labelfontglyphs.xml (revision 436)@@ -0,0 +1,3 @@+<glyphs>+ <glyph032 width=20 x=0 y=256></glyph032>+</glyphs>Index: data/effects/misc/label.eff===================================================================--- data/effects/misc/label.eff (revision 0)+++ data/effects/misc/label.eff (revision 436)@@ -0,0 +1,28 @@+<?xml version="1.0" encoding="utf-8" ?> +<library>+ <textures>+ <texture name="labelfonttable">+ <file>/this/art/effects/character_set_myriad.dds</file>+ <alpha><binary/></alpha>+ </texture>+ </textures>+ <materials>+ <material name="mat_labelfonttable">+ <texture>labelfonttable</texture>+ </material>+ </materials>+ <addon plugin="PSEffects">+ <effect name="label">+ <anchor type="basic" name="pos">+ <dir>none</dir>+ </anchor>+ <obj type="label" name="label" material="mat_labelfonttable"+ sizefile="/this/data/effects/misc/labelfontglyphs.xml">+ <mixmode>alpha</mixmode>+ <attach>pos</attach>+ <death>none</death>+ <dir>billboard</dir>+ </obj>+ </effect>+ </addon>+</library>Index: src/client/entitylabels.h===================================================================--- src/client/entitylabels.h (revision 447)+++ src/client/entitylabels.h (working copy)@@ -138,9 +138,9 @@ void CreateLabelOfObject( GEMClientObject* object ); /**- * Shows or hides label of given entity (only sets visibility, doesn't create/destruct it)- * This is used when visibility mode is OnMouseOver - labels are created for all entities,- * but only one is set visible (or none at all).+ * Shows or hides label of given entity, and creates the label if it does not exist+ * and needs to be shown, this is used for the mouseover setting, to create+ * labels just before they are viewed for the first time */ void ShowLabelOfObject( GEMClientObject* object, bool show ); Index: src/client/entitylabels.cpp===================================================================--- src/client/entitylabels.cpp (revision 447)+++ src/client/entitylabels.cpp (working copy)@@ -224,8 +224,7 @@ csString guild( actor->GetGuildName() ); csString guild2( psengine->GetGuildName() ); - if ( guild.Length() )- if ( guild.Length() )+ if ( guild2.Length() && guild.Length() == guild2.Length() ) { // If same guild, indicate with color if (guild == guild2)@@ -276,6 +275,19 @@ void psEntityLabels::CreateLabelOfObject(GEMClientObject *object) {+ if (!object)+ {+ Debug1( LOG_ANY, 0, "NULL object passed to psEntityLabels::CreateLabelOfObject" );+ return;+ }++ // Don't make labels for the player or action locations+ if (object->GetEntity() == celClient->GetMainActor() || object->GetObjectType() == GEM_ACTION_LOC)+ return;++ psEffectManager* effectMgr = psengine->GetEffectManager();+ CS_ASSERT_MSG("Effects Manager must exist before loading entity labels!", effectMgr );+ iMeshWrapper* mesh = object->pcmesh->GetMesh(); // Has it got a mesh to attach to?@@ -285,16 +297,13 @@ // Get the height of the model const csBox3& boundBox = mesh->GetMeshObject()->GetObjectModel()->GetObjectBoundingBox(); - psEffectManager* effectMgr = psengine->GetEffectManager();- // Create the effect- unsigned int id = effectMgr->RenderEffect( "entitylabel", + unsigned int id = effectMgr->RenderEffect( "label", csVector3(0.0f,boundBox.Max(1) + 0.25f,0.0f), mesh ); psEffect* effect = effectMgr->FindEffect(id); object->SetEntityLabel(effect);- effectMgr->ShowEffect(id,false); // Hide until told to show // Update text SetObjectText(object);@@ -315,21 +324,10 @@ void psEntityLabels::OnObjectArrived( GEMClientObject* object ) {- CS_ASSERT_MSG("Effects Manager must exist before loading entity labels!", psengine->GetEffectManager() );-- if (!object)- {- Debug1( LOG_ANY, 0, "NULL object passed to psEntityLabels::OnObjectArried" );+ if (visibility == LABEL_NEVER || visibility == LABEL_ONMOUSE) return;- } - // Action location?- if (object->GetObjectType() == GEM_ACTION_LOC)- return;-- DeleteLabelOfObject(object); // just to be sure- if ( object->GetEntity() != celClient->GetMainActor() )- CreateLabelOfObject( object );+ CreateLabelOfObject( object ); } @@ -473,9 +471,20 @@ inline void psEntityLabels::ShowLabelOfObject(GEMClientObject* object, bool show) {- psEffect* effect = object->GetEntityLabel();- if (effect != NULL)- psengine->GetEffectManager()->ShowEffect(effect->GetUniqueID(),show);+ if (object->GetEntityLabel() == NULL)+ {+ // If the label is not created, don't bother creating and hiding it, it won't be visible+ if (!show)+ return;++ CreateLabelOfObject(object);+ }++ psEffect* entityLabel = object->GetEntityLabel();+ if (entityLabel != NULL)+ {+ psengine->GetEffectManager()->ShowEffect(entityLabel->GetUniqueID(),show);+ } } void psEntityLabels::RepaintAllLabels()Index: src/client/psengine.cpp===================================================================--- src/client/psengine.cpp (revision 447)+++ src/client/psengine.cpp (working copy)@@ -826,8 +826,10 @@ { UnmuteAllSounds(); }- if (IsGameLoaded())- celclient->GetEntityLabels()->RepaintAllLabels();+ // RS: this causes a freeze switching back to the client window+ // for up to a few seconds, and seems very much unnecessary+ //if (IsGameLoaded())+ // celclient->GetEntityLabels()->RepaintAllLabels(); } else if (ev.Name == event_focusgained) {@@ -1056,7 +1058,7 @@ { } -inline void psEngine::FinishFrame()+void psEngine::FinishFrame() { g3d->FinishDraw (); g3d->Print (NULL);