/* * Author: Andrew Robberts * * Copyright (C) 2003 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "effects/pseffectobjtext.h" #include "effects/pseffectanchor.h" #include "util/pscssetup.h" #include "util/log.h" #define SCALE_X 500.0 #define SCALE_Y 500.0 psEffectObjText::psEffectObjText(iView *parentView) : psEffectObj(parentView) { } psEffectObjText::~psEffectObjText() { if (!staticText && meshFact) engine->RemoveObject(meshFact); if (mat) engine->RemoveObject(mat); if (tex) engine->RemoveObject(tex); } bool psEffectObjText::SetText(const char * newText) { if (!sprite) return false; bool ret = false; if (!staticText) { ret = UnprotectedSetText(newText); CalcTextureCoords(); mesh->GetMeshObject()->SetMaterialWrapper(mat); } return ret; } bool psEffectObjText::SetText(int rows, ...) { if (!sprite || staticText) return false; csArray textElems; psEffectObjText::EffectTextElement newElem; int x = 5; int y = 5; psEffectObjText::EffectTextRow* row = NULL; va_list arg; va_start(arg, rows); // Loop through all rows for(int i = 0; i < rows; i++) { row = va_arg( arg, EffectTextRow*); // 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; // Positioning font->GetDimensions(newElem.text, newElem.width, newElem.height); newElem.x = x; newElem.y = y; textElems.Push(newElem); y += newElem.height; } va_end(arg); //now we have our elements.. if ( RenderText(textElems) ) { CalcTextureCoords(); mesh->GetMeshObject()->SetMaterialWrapper(mat); return true; } return false; } bool psEffectObjText::IsStaticText() const { return staticText; } bool psEffectObjText::Load(iDocumentNode *node) { // get the attributes name = ""; materialName = ""; fontName = "/this/data/ttf/verdana.ttf"; fontSize = 20; text = ""; staticText = true; textColour = csColor(1,1,1); antiAlias = false; growUpwards = false; yOffset = 0.00f; csRef attribIter = node->GetAttributes(); while (attribIter->HasNext()) { csRef attr = attribIter->Next(); csString attrName = attr->GetName(); attrName.Downcase(); if (attrName == "name") name = attr->GetValue(); else if (attrName == "font") fontName = attr->GetValue(); else if (attrName == "fontquality") fontSize = attr->GetValueAsInt(); else if (attrName == "static") staticText = attr->GetValueAsBool(); else if (attrName == "antialias") antiAlias = attr->GetValueAsBool(); else if (attrName == "growupwards") growUpwards = attr->GetValueAsBool(); } if (name == "") { csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Attempting to create an effect obj with no name.\n"); return false; } csRef dataNode = node->GetNode("text"); if (dataNode) { text = dataNode->GetContentsValue(); } dataNode = node->GetNode("textColour"); if (dataNode) { textColour = csColor(dataNode->GetAttributeValueAsFloat("r")/255.0f, dataNode->GetAttributeValueAsFloat("g")/255.0f, dataNode->GetAttributeValueAsFloat("b")/255.0f); } if (!psEffectObj::Load(node)) return false; return PostSetup(); } bool psEffectObjText::Render(const csVector3 &up) { static unsigned long nextUniqueID = 0; csString effectID = "effect_text_"; effectID += nextUniqueID++; // if we don't have static text, then we need a separate mesh for each instance if (!staticText) { CreateMesh(); UnprotectedSetText(text); } // create a mesh wrapper from the factory we just created mesh = engine->CreateMeshWrapper(meshFact, effectID.GetData()); // do the up vector objUp = up; 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->SetZBufMode(zFunc); mesh->SetRenderPriority(priority); // disable culling mesh->GetMeshObject()->GetObjectModel()->SetPolygonMeshViscull(0); // obj specific // create the sprite sprite = SCF_QUERY_INTERFACE(mesh->GetMeshObject(), iSprite2DState); lastScale = 1.0f; lastSpin = 0.0f; CalcTextureCoords(); mesh->GetMeshObject()->SetMaterialWrapper(mat); particle = SCF_QUERY_INTERFACE(mesh->GetMeshObject(), iParticle); return true; } bool psEffectObjText::Update(csTicks elapsed) { if (!anchor || !anchor->IsReady()) // wait for anchor to be ready return true; life += (float)elapsed; while (life > animLength && killTime <= 0) life -= animLength; if (life >= birth && !isAlive) isAlive = true; if (isAlive && anchorMesh && anchor->IsReady()) { mesh->GetMovable()->SetSector(anchorMesh->GetMovable()->GetSectors()->Get(0)); mesh->GetMovable()->SetPosition(anchorMesh->GetMovable()->GetFullPosition()); if (dir == DT_NONE) matBase = anchorMesh->GetMovable()->GetFullTransform().GetT2O(); } if (keyFrames->Length() == 0) { mesh->GetMovable()->SetPosition(anchorMesh->GetMovable()->GetFullPosition()); } else { currKeyFrame = FindKeyFrameByTime(life); nextKeyFrame = currKeyFrame + 1; if (nextKeyFrame >= keyFrames->Length()) nextKeyFrame = 0; // scale ///////// float lerpScale = LERP_KEY(KA_SCALE); particle->ScaleBy(lerpScale / lastScale); lastScale = lerpScale; // position //////////// csVector3 objOffset = LERP_VEC_KEY(KA_POS); csVector3 lerpRot = LERP_VEC_KEY(KA_ROT); objOffset = csZRotMatrix3(lerpRot.z) * csYRotMatrix3(lerpRot.y) * csXRotMatrix3(lerpRot.x) * csVector3(-objOffset.x, objOffset.y, -objOffset.z); if (growUpwards) { objOffset += csVector3(0.f, yOffset, 0.f); } mesh->GetMovable()->SetPosition(anchorMesh->GetMovable()->GetFullPosition() + objOffset); // spin //////// float lerpSpin = LERP_KEY(KA_SPIN_Y); particle->Rotate(lerpSpin-lastSpin); lastSpin = lerpSpin; // colour ////////// csVector3 lerpColour = LERP_VEC_KEY(KA_COLOUR); mesh->GetMeshObject()->SetColor(csColor(lerpColour.x, lerpColour.y, lerpColour.z)); } mesh->GetMovable()->UpdateMove(); if (killTime <= 0) return true; // this effect obj will last forever until some divine intervention // age the effect obj killTime -= (int)elapsed; if (killTime <= 0) return false; return true; } psEffectObj * psEffectObjText::Clone() const { psEffectObjText * newObj = new psEffectObjText(view); CloneBase(newObj); // text specific newObj->font = font; newObj->fontName = fontName; newObj->fontSize = fontSize; newObj->staticText = staticText; newObj->textColour = textColour; newObj->text = text; newObj->antiAlias = antiAlias; newObj->growUpwards = growUpwards; newObj->mat = mat; newObj->g3d = g3d; newObj->g2d = g2d; newObj->txtmgr = txtmgr; newObj->texWidth = texWidth; newObj->texHeight = texHeight; newObj->paddingWidth = paddingWidth; newObj->paddingHeight = paddingHeight; return newObj; } bool psEffectObjText::CreateMesh() { static unsigned int uniqueID = 0; csString facName = "effect_text_fac_"; facName += uniqueID++; meshFact = engine->CreateMeshFactory ("crystalspace.mesh.object.sprite.2d", facName.GetData()); region->Add(meshFact->QueryObject()); return true; } bool psEffectObjText::PostSetup() { g3d = CS_QUERY_REGISTRY(psCSSetup::object_reg, iGraphics3D); if (!g3d) { csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Couldn't get iGraphics3D plugin!"); return false; } g2d = g3d->GetDriver2D(); txtmgr = g3d->GetTextureManager(); if (!txtmgr) { csReport(psCSSetup::object_reg, CS_REPORTER_SEVERITY_ERROR, "planeshift_effects", "Couldn't get iTextureManager!"); return false; } // load the font font = g2d->GetFontServer()->LoadFont(fontName, fontSize); // if we have static text, then lets create the mesh and set the text here if (staticText) { CreateMesh(); UnprotectedSetText(text); } animLength = 10; if (keyFrames->Length() > 0) animLength += keyFrames->Get(keyFrames->Length()-1)->time; return true; } bool psEffectObjText::UnprotectedSetText(const char * newText) { text = newText; csArray textElems; // build the list of text elements size_t a=0; psEffectObjText::EffectTextElement newElem; int textIndex = 0; int x = 5; int y = 5; csArray colourStack; colourStack.Push(g2d->FindRGB((int)(textColour.red*255), (int)(textColour.green*255), (int)(textColour.blue*255))); int maxWidth = 5; csArray shadowStack; csArray outlineStack; csArray alignStack; alignStack.Push(ETA_LEFT); char tag[512] = ""; char tagParam[512] = ""; size_t len = text.Length(); while (a < len) { bool drawElem = false; bool newLine = false; newElem.colour = colourStack.Top(); newElem.hasShadow = (shadowStack.Length() > 0); if (newElem.hasShadow) newElem.shadowColour = shadowStack.Top(); newElem.hasOutline = (outlineStack.Length() > 0); if (newElem.hasOutline) newElem.outlineColour = outlineStack.Top(); newElem.align = alignStack.Top(); if (newText[a] == '[' && newText[a+1] != '[') { // it's a tag ++a; int tagIndex = 0; bool hasTagParam = false; tag[0] = '\0'; tagParam[0] = '\0'; while (newText[a] != ']' && newText[a+1] != '\0') { if (newText[a] == ':') { hasTagParam = true; tag[tagIndex] = '\0'; tagIndex = 0; } else if (!hasTagParam) { tag[tagIndex] = newText[a]; ++tagIndex; } else { tagParam[tagIndex] = newText[a]; ++tagIndex; } ++a; } if (!hasTagParam) tag[tagIndex] = '\0'; if (strcmp(tag, "center") == 0) { alignStack.Push(ETA_CENTER); drawElem = true; newLine = false; } else if (strcmp(tag, "/center") == 0) { if (alignStack.Top() == ETA_CENTER) { alignStack.Pop(); drawElem = true; newLine = true; } } else if (strcmp(tag, "left") == 0) { alignStack.Push(ETA_LEFT); drawElem = true; newLine = false; } else if (strcmp(tag, "/left") == 0) { if (alignStack.Top() == ETA_LEFT && alignStack.Length() > 1) { alignStack.Pop(); drawElem = true; newLine = true; } } else if (strcmp(tag, "right") == 0) { alignStack.Push(ETA_RIGHT); drawElem = true; newLine = false; } else if (strcmp(tag, "/right") == 0) { if (alignStack.Top() == ETA_RIGHT) { alignStack.Pop(); drawElem = true; newLine = true; } } else if (strcmp(tag, "br /") == 0 || strcmp(tag, "br/") == 0) { drawElem = true; newLine = true; } else if (strcmp(tag, "colour") == 0) { int r = (HexToInt(tagParam[0]) << 4) + HexToInt(tagParam[1]); int g = (HexToInt(tagParam[2]) << 4) + HexToInt(tagParam[3]); int b = (HexToInt(tagParam[4]) << 4) + HexToInt(tagParam[5]); colourStack.Push(g2d->FindRGB(r, g, b)); drawElem = true; newLine = false; } else if (strcmp(tag, "/colour") == 0) { if (colourStack.Length() > 1) colourStack.Pop(); drawElem = true; newLine = false; } else if (strcmp(tag, "shadow") == 0) { int r = 0, g = 0, b = 0; if (tagParam[0] != '\0') { r = (HexToInt(tagParam[0]) << 4) + HexToInt(tagParam[1]); g = (HexToInt(tagParam[2]) << 4) + HexToInt(tagParam[3]); b = (HexToInt(tagParam[4]) << 4) + HexToInt(tagParam[5]); } shadowStack.Push(g2d->FindRGB(r, g, b)); drawElem = true; newLine = false; } else if (strcmp(tag, "/shadow") == 0) { if (shadowStack.Length() > 0) shadowStack.Pop(); drawElem = true; newLine = false; } else if (strcmp(tag, "outline") == 0) { int r = 0, g = 0, b = 0; if (tagParam[0] != '\0') { r = (HexToInt(tagParam[0]) << 4) + HexToInt(tagParam[1]); g = (HexToInt(tagParam[2]) << 4) + HexToInt(tagParam[3]); b = (HexToInt(tagParam[4]) << 4) + HexToInt(tagParam[5]); } outlineStack.Push(g2d->FindRGB(r, g, b)); drawElem = true; newLine = false; } else if (strcmp(tag, "/outline") == 0) { if (outlineStack.Length() > 0) outlineStack.Pop(); drawElem = true; newLine = false; } } else { if (newText[a] == '[' && newText[a+1] == '[') ++a; newElem.text += newText[a]; ++textIndex; } ++a; if (a == len) { drawElem = true; newLine = true; } if (drawElem) { newElem.text += '\0'; int start = 0, end; while (newElem.text.GetAt(start) == 9 || newElem.text.GetAt(start) == 10 || newElem.text.GetAt(start) == 13 || newElem.text.GetAt(start) == 32) ++start; end = start; int c=start; while (newElem.text.GetData()[c] != '\0') { if (newElem.text.GetAt(c) != 9 && newElem.text.GetAt(c) != 10 && newElem.text.GetAt(c) != 13 && newElem.text.GetAt(c) != 32) end = c; ++c; } //newElem.text.SetAt(end+1, '\0'); newElem.text.DeleteAt(0,start); textIndex = 0; newElem.x = x; newElem.y = y; font->GetDimensions(newElem.text, newElem.width, newElem.height); if (newElem.width > maxWidth) maxWidth = newElem.width; textElems.Push(newElem); if (newLine) { y += newElem.height; x = 5; } else x += newElem.width; } } return RenderText(textElems); } /*bool psEffectObjText::PrepareText() { static unsigned int uniqueID = 0; csString matName = "effect_text_mat_"; matName += uniqueID++; // setup the material if (mat) engine->RemoveObject(mat); if (tex) engine->RemoveObject(tex); //calculate dimensions int y = 0; int maxWidth = 0; // create the material maxWidth += 10; y += 5; texWidth = ToPowerOf2(maxWidth); texHeight = ToPowerOf2(y); paddingWidth = texWidth - maxWidth; paddingHeight = texHeight - y; csColor transp(51/255.0F,51/255.0F,254/255.0F); tex = engine->CreateBlackTexture(matName, texWidth, texHeight, &transp, CS_TEXTURE_3D); tex->Register(txtmgr); mat = engine->CreateMaterial(matName, tex); g3d->SetRenderTarget(mat->GetMaterial()->GetTexture()); g3d->BeginDraw(CSDRAW_2DGRAPHICS); g2d->DrawBox(0, 0, texWidth, texHeight, g2d->FindRGB (51, 51, 254)); return true; }*/ bool psEffectObjText::ApplyRow(const EffectTextElement* row) { // draw the text elements int textX = row->x; if (row->align == ETA_LEFT) { textX = 5; } else if (row->align == ETA_CENTER) { textX = (texWidth-paddingWidth) / 2 - row->width / 2; } else if (row->align == ETA_RIGHT) { textX = texWidth - paddingWidth - row->width - 5; } const char * drawText = row->text; if (row->hasOutline) { // so, um, change this g2d->Write(font, textX, row->y-2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX+2, row->y-2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX+2, row->y, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX+2, row->y+2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX, row->y+2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX-2, row->y+2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX-2, row->y, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX-2, row->y-2, row->outlineColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); } if (row->hasShadow) g2d->Write(font, textX+2, row->y+2, row->shadowColour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); g2d->Write(font, textX, row->y, row->colour, -1, drawText, (antiAlias ? 0 : CS_WRITE_NOANTIALIAS)); return true; } //Renders an array of text elements to the label's texture bool psEffectObjText::RenderText(const csArray& textElems) { static unsigned int uniqueID = 0; csString matName = "effect_text_mat_"; matName += uniqueID++; // setup the material if (mat) engine->RemoveObject(mat); if (tex) engine->RemoveObject(tex); //calculate dimensions int height = 0; int maxWidth = 0; yOffset = 0.00f; for (size_t a=0; awidth>maxWidth?elem->width:maxWidth; height += elem->height; if (a > 0) yOffset += (float) elem->height; } yOffset /= SCALE_Y; maxWidth += 10; //some magic padding numbers height += 5; // create the material texWidth = ToPowerOf2(maxWidth); texHeight = ToPowerOf2(height); paddingWidth = texWidth - maxWidth; paddingHeight = texHeight - height; csColor transp(51/255.0F,51/255.0F,254/255.0F); tex = engine->CreateBlackTexture(matName, texWidth, texHeight, &transp, CS_TEXTURE_3D); tex->Register(txtmgr); mat = engine->CreateMaterial(matName, tex); g3d->SetRenderTarget(mat->GetMaterial()->GetTexture()); g3d->BeginDraw(CSDRAW_2DGRAPHICS); g2d->DrawBox(0, 0, texWidth, texHeight, g2d->FindRGB (51, 51, 254)); // draw the text elements for (size_t a=0; aFinishDraw(); return true; } void psEffectObjText::CalcTextureCoords() { iColoredVertices* vertices = sprite->GetVertices(); vertices->SetSize(4); float w = ((float)texWidth - (float)paddingWidth); float h = ((float)texHeight - (float)paddingHeight); float umax = w / (float)texWidth; float vmax = h / (float)texHeight; // w = w / (float)(texWidth > texHeight ? texWidth : texHeight) * 0.5f; // h = h / (float)(texWidth > texHeight ? texWidth : texHeight) * 0.5f; //Scaling factor chosen only because it makes the name "Harnquist" about as wide as Harnquist. w = w/SCALE_X; h = h/SCALE_Y; vertices->Get(0).pos.x = -w; vertices->Get(0).pos.y = h; vertices->Get(1).pos.x = w; vertices->Get(1).pos.y = h; vertices->Get(2).pos.x = w; vertices->Get(2).pos.y = -h; vertices->Get(3).pos.x = -w; vertices->Get(3).pos.y = -h; vertices->Get(0).u = 0; vertices->Get(0).v = 0; vertices->Get(1).u = umax; vertices->Get(1).v = 0; vertices->Get(2).u = umax; vertices->Get(2).v = vmax; vertices->Get(3).u = 0; vertices->Get(3).v = vmax; vertices->Get(0).color_init.Set(1, 1, 1); vertices->Get(1).color_init.Set(1, 1, 1); vertices->Get(2).color_init.Set(1, 1, 1); vertices->Get(3).color_init.Set(1, 1, 1); }