[Python-ogre-commit] SF.net SVN: python-ogre:[827] trunk/python-ogre
Brought to you by:
andy_miller,
roman_yakovenko
From: <and...@us...> - 2008-12-05 07:18:32
|
Revision: 827 http://python-ogre.svn.sourceforge.net/python-ogre/?rev=827&view=rev Author: andy_miller Date: 2008-12-05 07:18:28 +0000 (Fri, 05 Dec 2008) Log Message: ----------- Initial add of Canvas wrapper and update for mygui Modified Paths: -------------- trunk/python-ogre/PythonOgreConfig_nt.py trunk/python-ogre/PythonOgreConfig_posix.py trunk/python-ogre/ReportVersion.py trunk/python-ogre/environment.py trunk/python-ogre/setup.py Added Paths: ----------- trunk/python-ogre/ThirdParty/canvas/ trunk/python-ogre/ThirdParty/canvas/Atlas.cpp trunk/python-ogre/ThirdParty/canvas/Atlas.h trunk/python-ogre/ThirdParty/canvas/Canvas.cpp trunk/python-ogre/ThirdParty/canvas/Canvas.h trunk/python-ogre/packages_2.5/ogre/gui/canvas/ trunk/python-ogre/packages_2.5/ogre/gui/canvas/__init__.py trunk/python-ogre/packages_2.5/ogre/gui/mygui/ trunk/python-ogre/packages_2.5/ogre/gui/mygui/__init__.py Modified: trunk/python-ogre/PythonOgreConfig_nt.py =================================================================== --- trunk/python-ogre/PythonOgreConfig_nt.py 2008-12-04 13:12:10 UTC (rev 826) +++ trunk/python-ogre/PythonOgreConfig_nt.py 2008-12-05 07:18:28 UTC (rev 827) @@ -84,6 +84,7 @@ PATH_hydrax = os.path.join(PATH_THIRDPARTY, 'Hydrax') PATH_hikari = os.path.join(PATH_THIRDPARTY, 'Hikari' ) #BASE_DIR, 'hikari', 'hikari') PATH_mygui = os.path.join(BASE_DIR, 'MyGUI_2.2.0_RC1_source' ) +PATH_canvas = os.path.join(PATH_THIRDPARTY, 'canvas') # it's time for the SDK version if SDK: @@ -132,6 +133,7 @@ PATH_LIB_opensteer = os.path.join(PATH_opensteer, 'win32','release') PATH_LIB_hikari = os.path.join(PATH_hikari ) # , 'lib') PATH_LIB_mygui = os.path.join(PATH_mygui, 'MyGUIEngine','lib','Release') +PATH_LIB_canvas = os.path.join(PATH_canvas ) PATH_INCLUDE_Ogre= os.path.join(PATH_Ogre,'OgreMain/include') PATH_INCLUDE_NEDMALLOC= os.path.join(PATH_Ogre,'OgreMain', 'src', 'nedmalloc') @@ -183,6 +185,7 @@ PATH_INCLUDE_hydrax= PATH_hydrax PATH_INCLUDE_hikari = os.path.join(PATH_hikari ) #, 'include') PATH_INCLUDE_mygui = os.path.join(PATH_mygui,'MyGUIEngine','include') +PATH_INCLUDE_canvas= PATH_canvas PATH_INCLUDE_OggVorbisTheora = [ os.path.join(BASE_DIR,'ogg','include') ,os.path.join(BASE_DIR, 'vorbis', 'include') Modified: trunk/python-ogre/PythonOgreConfig_posix.py =================================================================== --- trunk/python-ogre/PythonOgreConfig_posix.py 2008-12-04 13:12:10 UTC (rev 826) +++ trunk/python-ogre/PythonOgreConfig_posix.py 2008-12-05 07:18:28 UTC (rev 827) @@ -82,6 +82,7 @@ PATH_hydrax = os.path.join(PATH_THIRDPARTY, 'Hydrax') PATH_hikari = os.path.join(PATH_THIRDPARTY, 'Hikari' ) # BASE_DIR, 'hikari', 'hikari') PATH_mygui = os.path.join(BASE_DIR, 'MyGUI_2.2.0_RC1_source' ) +PATH_canvas = os.path.join(PATH_THIRDPARTY, 'canvas') ### ### these paths assume you've left all the directory structure as standard @@ -122,6 +123,7 @@ PATH_LIB_opensteer = os.path.join(LOCAL_LIB) PATH_LIB_hikari = os.path.join(PATH_hikari ) #, 'lib') PATH_LIB_mygui = os.path.join(PATH_mygui, 'MyGUIEngine','lib','Release') +PATH_LIB_canvas = os.path.join(PATH_canvas ) PATH_INCLUDE_Ogre= os.path.join(LOCAL_INCLUDE,'OGRE') # os.path.join(PATH_Ogre,'OgreMain/include') PATH_INCLUDE_Ogre_Dependencies = PATH_INCLUDE_Ogre # os.path.join( PATH_Ogre, 'Dependencies/include') @@ -194,4 +196,5 @@ PATH_INCLUDE_hydrax= PATH_hydrax PATH_INCLUDE_hikari = os.path.join(PATH_hikari ) #, 'include') PATH_INCLUDE_mygui = os.path.join(PATH_mygui,'MyGUIEngine','include') +PATH_INCLUDE_canvas= PATH_canvas Modified: trunk/python-ogre/ReportVersion.py =================================================================== --- trunk/python-ogre/ReportVersion.py 2008-12-04 13:12:10 UTC (rev 826) +++ trunk/python-ogre/ReportVersion.py 2008-12-05 07:18:28 UTC (rev 827) @@ -28,7 +28,7 @@ 'ogre.addons.noise', 'ogre.addons.watermesh', 'ogre.addons.particleuniverse', 'ogre.addons.cadunetree', 'ogre.renderer.ogrepcz', 'ogre.addons.hydrax', - 'ogre.gui.hikari' ) + 'ogre.gui.hikari','ogre.gui.canvas','ogre.gui.mygui' ) # bm.setupLogging("version.info") # options.logfilename) # logger = logging.getLogger('PythonOgre.ReportVersionInfo') Added: trunk/python-ogre/ThirdParty/canvas/Atlas.cpp =================================================================== --- trunk/python-ogre/ThirdParty/canvas/Atlas.cpp (rev 0) +++ trunk/python-ogre/ThirdParty/canvas/Atlas.cpp 2008-12-05 07:18:28 UTC (rev 827) @@ -0,0 +1,515 @@ +/* + This file is part of Canvas, a fast, lightweight 2D graphics engine for Ogre3D. + + Copyright (C) 2008 Adam J. Simmons + ajs...@gm... + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Atlas.h" +namespace CanvasNS +{ +/************************ +* TextureInfo +************************/ + +TextureInfo::TextureInfo() : isEmpty(true), width(0), height(0) +{ +} + +TextureInfo::TextureInfo(int atlasWidth, int atlasHeight, int x, int y, int width, int height) : isEmpty(false), width(width), height(height) +{ + texCoords.left = x / (float)atlasWidth; + texCoords.top = y / (float)atlasHeight; + texCoords.right = (x + width) / (float)atlasWidth; + texCoords.bottom = (y + height) / (float)atlasHeight; +} + +/************************ +* GlyphInfo +************************/ + +GlyphInfo::GlyphInfo() : bearingX(0), bearingY(0), advance(0) +{ +} + +GlyphInfo::GlyphInfo(Ogre::Real bearingX, Ogre::Real bearingY, Ogre::Real advance) + : bearingX(bearingX), bearingY(bearingY), advance(advance) +{ +} + +/************************ +* FontMetrics +************************/ + +FontMetrics::FontMetrics() : ascender(0), descender(0), height(0), maxAdvance(0) +{ +} + +FontMetrics::FontMetrics(Ogre::Real ascender, Ogre::Real descender, Ogre::Real height, Ogre::Real maxAdvance) + : ascender(ascender), descender(descender), height(height), maxAdvance(maxAdvance) +{ + +} + +/************************ +* CharCodeRange +************************/ + +const CharCodeRange CharCodeRange::BasicLatin = CharCodeRange(32, 166); +const CharCodeRange CharCodeRange::Latin1 = CharCodeRange(32, 255); +const CharCodeRange CharCodeRange::All = CharCodeRange(); + +CharCodeRange::CharCodeRange() +{ +} + +CharCodeRange::CharCodeRange(CharCode from, CharCode to) +{ + addRange(from, to); +} + +void CharCodeRange::addRange(CharCode from, CharCode to) +{ + ranges.push_back(std::pair<CharCode, CharCode>(from, to)); +} + +bool CharCodeRange::isWithinRange(CharCode code) const +{ + if(ranges.empty()) + return true; + + for(std::vector<std::pair<CharCode, CharCode> >::const_iterator i = ranges.begin(); i != ranges.end(); i++) + { + if(code >= i->first && code <= i->second) + return true; + } + + return false; +} + +/************************ +* ComputationRect +************************/ + +ComputationRect::ComputationRect(const Ogre::String& texFilename, const Ogre::String& resourceGroup) + : x(0), y(0), isPlaced(false), isFontGlyph(false), filename(texFilename) +{ + image.load(texFilename, resourceGroup); + width = (int)image.getWidth(); + height = (int)image.getHeight(); + area = width * height; +} + +ComputationRect::ComputationRect(const Ogre::String& texName, unsigned char* buffer, int width, int height) + : x(0), y(0), isPlaced(false), isFontGlyph(false), filename(texName) +{ + image.loadDynamicImage(buffer, width, height, 1, Ogre::PF_BYTE_BGRA, true); + this->width = (int)image.getWidth(); + this->height = (int)image.getHeight(); + area = this->width * this->height; +} + +ComputationRect::ComputationRect(const Ogre::String& fontFilename, Ogre::uint fontSize, CharCode charCode, unsigned char* buffer, int width, int height) + : x(0), y(0), isPlaced(false), isFontGlyph(true), filename(fontFilename), fontSize(fontSize), charCode(charCode) +{ + image.loadDynamicImage(buffer, width, height, 1, Ogre::PF_BYTE_LA, true); + this->width = (int)image.getWidth(); + this->height = (int)image.getHeight(); + area = this->width * this->height; +} + +/************************ +* FontFaceDefinition +************************/ + +FontFaceDefinition::FontFaceDefinition(const Ogre::String& filename, const CharCodeRange& codeRange, short renderType) : filename(filename), codeRange(codeRange), renderType(renderType) +{ +} + +void FontFaceDefinition::addSize(Ogre::uint fontSize) +{ + sizes.push_back(fontSize); +} + +/************************ +* FontFace +************************/ + +FontFace::FontFace() +{ +} + +FontFace::FontFace(const FontFaceDefinition& definition, const Ogre::String& resourceGroup, ComputationVector& renderContext) +{ + FT_Library library; + FT_Face face; + FT_Error error; + + error = FT_Init_FreeType(&library); + if(error) + OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "Could not load FreeType library.", "FontFace::FontFace"); + + Ogre::DataStreamPtr dataStream = Ogre::ResourceGroupManager::getSingleton().openResource(definition.filename, resourceGroup); + Ogre::MemoryDataStream stream(dataStream); + + error = FT_New_Memory_Face(library, stream.getPtr(), (FT_Long)stream.size(), 0, &face); + if(error) + OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "FreeType could not load a font-face.", "FontFace::FontFace"); + + + for(std::vector<Ogre::uint>::const_iterator i = definition.sizes.begin(); i != definition.sizes.end(); i++) + { + FT_ULong charCode = 0; + FT_UInt glyphIndex = 1; + + error = FT_Set_Pixel_Sizes(face, 0, (FT_UInt)*i); + if(error) + OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "FreeType could not set a font-size.", "FontFace::FontFace"); + + fontMetrics[*i] = FontMetrics(face->size->metrics.ascender / 64.0f, face->size->metrics.descender / 64.0f, + face->size->metrics.height / 64.0f, face->size->metrics.max_advance / 64.0f); + + for(charCode = FT_Get_First_Char(face, &glyphIndex); glyphIndex != 0; charCode = FT_Get_Next_Char(face, charCode, &glyphIndex)) + { + if(!definition.codeRange.isWithinRange(charCode)) + continue; + + if(definition.renderType == FontFaceDefinition::BetterContrast) + { + error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT); + if(error) + continue; + + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if(error) + continue; + } + else + { + error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_TARGET_LIGHT); + if(error) + continue; + + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LIGHT); + if(error) + continue; + } + + FT_Bitmap& bitmap = face->glyph->bitmap; + FT_Glyph_Metrics& metrics = face->glyph->metrics; + + if(!bitmap.buffer || (!bitmap.rows && !bitmap.width)) + continue; + + unsigned char* buffer = new unsigned char[bitmap.rows * bitmap.pitch * 2]; + + for(int idx = 0; idx < bitmap.rows * bitmap.pitch; idx++) + { + buffer[idx * 2] = 255; + buffer[idx * 2 + 1] = bitmap.buffer[idx]; + } + + fontSizes[*i][charCode] = GlyphInfo(metrics.horiBearingX / 64.0f, metrics.horiBearingY / 64.0f, metrics.horiAdvance / 64.0f); + + renderContext.push_back(new ComputationRect(definition.filename, (Ogre::uint)*i, charCode, buffer, bitmap.pitch, bitmap.rows)); + } + } +} + +/************************ +* Atlas +************************/ + +Atlas::Atlas(const std::vector<Ogre::String>& textureFilenames, const std::vector<FontFaceDefinition>& fonts, const Ogre::String& resourceGroup) +{ + Ogre::LogManager::getSingleton().logMessage("Loading an Atlas."); + Ogre::Timer timer; + + ComputationVector rectangles; + + for(std::vector<FontFaceDefinition>::const_iterator i = fonts.begin(); i != fonts.end(); i++) + { + fontFaces[i->filename] = FontFace(*i, resourceGroup, rectangles); + } + + for(std::vector<Ogre::String>::const_iterator i = textureFilenames.begin(); i != textureFilenames.end(); i++) + { + rectangles.push_back(new ComputationRect(*i, resourceGroup)); + } + + unsigned char* vcolBuffer = new unsigned char[16]; + memset(vcolBuffer, 255, 16); + + rectangles.push_back(new ComputationRect("VertexColor", vcolBuffer, 2, 2)); + + guessDimensions(rectangles); + pack(rectangles); + paint(rectangles); + + int glyphCount = 0; + int texCount = -1; // Subtract the default VertexColor texture + ComputationRect* rect; + + for(ComputationVector::iterator i = rectangles.begin(); i != rectangles.end(); i++) + { + rect = (*i); + + if(rect->isFontGlyph) + { + fontFaces[rect->filename].fontSizes[rect->fontSize][rect->charCode].texInfo = TextureInfo(dimensions.first, dimensions.second, rect->x, rect->y, rect->width, rect->height); + glyphCount++; + } + else + { + textures[rect->filename] = TextureInfo(dimensions.first, dimensions.second, rect->x, rect->y, rect->width, rect->height); + texCount++; + } + + delete rect; + } + + Ogre::LogManager::getSingleton().logMessage("Atlas loaded in " + Ogre::StringConverter::toString(timer.getMilliseconds() / 1000.0f) + + "s. Packed " + Ogre::StringConverter::toString(glyphCount) + " font glyphs and " + Ogre::StringConverter::toString(texCount) + + " textures into " + Ogre::StringConverter::toString(dimensions.first) + "x" + Ogre::StringConverter::toString(dimensions.second)); +} + +const std::pair<int, int>& Atlas::getDimensions() const +{ + return dimensions; +} + +const Ogre::String& Atlas::getMaterialName() const +{ + return materialName; +} + +const TextureInfo& Atlas::getTextureInfo(const Ogre::String& filename) const +{ + std::map<Ogre::String, TextureInfo>::const_iterator i = textures.find(filename); + static TextureInfo empty; + + if(i != textures.end()) + return i->second; + + return empty; +} + +const FontMetrics& Atlas::getFontMetrics(const Ogre::String& fontFilename, Ogre::uint fontSize) const +{ + std::map<Ogre::String, FontFace>::const_iterator font = fontFaces.find(fontFilename); + static FontMetrics empty; + + if(font == fontFaces.end()) + return empty; + + FontMetricsMap::const_iterator iter = font->second.fontMetrics.find(fontSize); + + if(iter == font->second.fontMetrics.end()) + return empty; + + return iter->second; +} + +const GlyphMap& Atlas::getGlyphMap(const Ogre::String& fontFilename, Ogre::uint fontSize) const +{ + std::map<Ogre::String, FontFace>::const_iterator font = fontFaces.find(fontFilename); + static GlyphMap empty; + + if(font == fontFaces.end()) + return empty; + + FontSizeMap::const_iterator size = font->second.fontSizes.find(fontSize); + + if(size == font->second.fontSizes.end()) + return empty; + + return size->second; +} + +const GlyphInfo& Atlas::getGlyphInfo(const Ogre::String& fontFilename, Ogre::uint fontSize, CharCode charCode) const +{ + std::map<Ogre::String, FontFace>::const_iterator font = fontFaces.find(fontFilename); + static GlyphInfo empty; + + if(font == fontFaces.end()) + return empty; + + FontSizeMap::const_iterator size = font->second.fontSizes.find(fontSize); + + if(size == font->second.fontSizes.end()) + return empty; + + GlyphMap::const_iterator glyph = size->second.find(charCode); + + if(glyph == size->second.end()) + return empty; + + return glyph->second; +} + +void Atlas::guessDimensions(const ComputationVector& rectangles) +{ + int totalArea = 0; + int maxWidth = 0; + int maxHeight = 0; + + for(ComputationVector::const_iterator i = rectangles.begin(); i != rectangles.end(); i++) + { + totalArea += (*i)->area; + maxWidth = std::max(maxWidth, (*i)->width); + maxHeight = std::max(maxHeight, (*i)->height); + } + + int squareRoot = (int)sqrt((double)totalArea); + + dimensions.first = Ogre::Bitwise::firstPO2From(std::max(squareRoot, maxWidth)); + dimensions.second = Ogre::Bitwise::firstPO2From(std::max(squareRoot, maxHeight)); +} + +void Atlas::pack(ComputationVector& rectangles) +{ + struct compare { bool operator()(ComputationRect* a, ComputationRect* b){ return(a->area > b->area); }}; + std::sort(rectangles.begin(), rectangles.end(), compare()); + + while(true) + { + for(ComputationVector::iterator i = rectangles.begin(); i != rectangles.end(); i++) + { + (*i)->isPlaced = false; + (*i)->x = -1; + (*i)->y = -1; + } + + int successCount = 0; + fill(rectangles, 0, 0, dimensions.first - 1, dimensions.second - 1, successCount); + + if(successCount == (int)rectangles.size()) + break; + + static bool toggle = false; + toggle = !toggle; + + if(toggle) + dimensions.first = Ogre::Bitwise::firstPO2From(dimensions.first + 1); + else + dimensions.second = Ogre::Bitwise::firstPO2From(dimensions.second + 1); + } +} + +void Atlas::fill(ComputationVector& rectangles, int x1, int y1, int x2, int y2, int& count) +{ + ComputationVector::iterator iter; + + for(iter = rectangles.begin(); iter != rectangles.end(); iter++) + { + if((!(*iter)->isPlaced) && (x2 - x1 + 1 >= (*iter)->width) && (y2 - y1 + 1 >= (*iter)->height)) + break; + } + + if(iter == rectangles.end()) + return; + + ComputationRect* rect = *iter; + + rect->x = x1; + rect->y = y1; + rect->isPlaced = true; + count++; + + int aRight = (x2 - x1 + 1 - rect->width) * rect->height; + int aDown = (y2 - y1 + 1 - rect->height) * rect->width; + if(aRight < aDown) + { + if(y1 + rect->height < y2) + fill(rectangles, x1, y1 + rect->height, x2, y2, count); + + if((x1 + rect->width < x2) && (y1 < y1 + rect->height - 1)) + fill(rectangles, x1 + rect->width, y1, x2, y1 + rect->height - 1, count); + } + else + { + if(x1 + rect->width < x2) + fill(rectangles, x1 + rect->width, y1, x2, y2, count); + + if((y1 + rect->height < y2) && (x1 < x1 + rect->width - 1)) + fill(rectangles, x1, y1 + rect->height, x1 + rect->width - 1, y2, count); + } +} + +void Atlas::paint(const ComputationVector& rectangles) +{ + static unsigned int count = 0; + Ogre::String texName = "AtlasTexture_" + Ogre::StringConverter::toString(count); + materialName = "AtlasMaterial_" + Ogre::StringConverter::toString(count); + + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual( + texName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, dimensions.first, dimensions.second, 0, Ogre::PF_BYTE_BGRA, + Ogre::TU_STATIC_WRITE_ONLY); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + size_t dstBpp = Ogre::PixelUtil::getNumElemBytes(pixelBox.format); + size_t dstPitch = pixelBox.rowPitch * dstBpp; + + Ogre::uint8* dstData = static_cast<Ogre::uint8*>(pixelBox.data); + + for(ComputationVector::const_iterator i = rectangles.begin(); i != rectangles.end(); i++) + { + unsigned char* conversionBuf = 0; + Ogre::PixelBox srcPixels = (*i)->image.getPixelBox(); + + if((*i)->image.getFormat() != Ogre::PF_BYTE_BGRA) + { + conversionBuf = new unsigned char[(*i)->image.getWidth() * (*i)->image.getHeight() * dstBpp]; + Ogre::PixelBox convPixels(Ogre::Box(0, 0, (*i)->width, (*i)->height), Ogre::PF_BYTE_BGRA, conversionBuf); + Ogre::PixelUtil::bulkPixelConversion((*i)->image.getPixelBox(), convPixels); + srcPixels = convPixels; + } + + size_t srcPitch = srcPixels.rowPitch * dstBpp; + Ogre::uint8* srcData = static_cast<Ogre::uint8*>(srcPixels.data); + + for(size_t row = 0; row < (*i)->image.getHeight(); row++) + { + for(size_t col = 0; col < srcPitch; col++) + { + dstData[((row + (*i)->y) * dstPitch) + ((*i)->x * dstBpp) + col] = srcData[(row * srcPitch) + col]; + } + } + + if(conversionBuf) + delete[] conversionBuf; + } + + pixelBuffer->unlock(); + + Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(materialName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::Pass* pass = material->getTechnique(0)->getPass(0); + pass->setDepthCheckEnabled(false); + pass->setDepthWriteEnabled(false); + pass->setLightingEnabled(false); + pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); + + Ogre::TextureUnitState* texUnit = pass->createTextureUnitState(texName); + texUnit->setTextureFiltering(Ogre::FO_NONE, Ogre::FO_NONE, Ogre::FO_NONE); + + count++; +} +} // namespace Added: trunk/python-ogre/ThirdParty/canvas/Atlas.h =================================================================== --- trunk/python-ogre/ThirdParty/canvas/Atlas.h (rev 0) +++ trunk/python-ogre/ThirdParty/canvas/Atlas.h 2008-12-05 07:18:28 UTC (rev 827) @@ -0,0 +1,270 @@ +/* + This file is part of Canvas, a fast, lightweight 2D graphics engine for Ogre3D. + + Copyright (C) 2008 Adam J. Simmons + ajs...@gm... + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __Atlas__ +#define __Atlas__ + +#include "Ogre.h" +#include "OgreBitwise.h" +#include <map> +#include <vector> +#include <algorithm> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H + +namespace CanvasNS +{ +/** +* TextureInfo represents a texture within an Atlas instance. +* Contains the actual dimensions of a texture and its location within the atlas. +*/ +struct TextureInfo +{ + bool isEmpty; + Ogre::FloatRect texCoords; + int width, height; + + TextureInfo(); + TextureInfo(int atlasWidth, int atlasHeight, int x, int y, int width, int height); +}; + +/** +* GlyphInfo represents a glyph within an Atlas instance. +* Contains glyph metrics which can be used for text layout purposes. +* See: http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-3.html +*/ +struct GlyphInfo +{ + Ogre::Real bearingX; + Ogre::Real bearingY; + Ogre::Real advance; + + TextureInfo texInfo; + + GlyphInfo(); + GlyphInfo(Ogre::Real bearingX, Ogre::Real bearingY, Ogre::Real advance); +}; + +/** +* FontMetrics represents the scaled global metrics for a certain font size. +* Contains font metrics which can be used for text layout purposes. +* +* <ul> +* <li>ascender - The typographic ascender of the face, expressed in Real pixels. +* <li>descender - The typographic descender of the face, expressed in Real pixels. +* <li>height - The height is the vertical distance between two consecutive baselines, expressed in Real pixels. +* <li>maxAdvance - The maximal advance width, in Real pixels, for all glyphs in this face. This can be used to make word wrapping computations faster. +* </ul> +*/ +struct FontMetrics +{ + Ogre::Real ascender; + Ogre::Real descender; + Ogre::Real height; + Ogre::Real maxAdvance; + + FontMetrics(); + FontMetrics(Ogre::Real ascender, Ogre::Real descender, Ogre::Real height, Ogre::Real maxAdvance); +}; + +typedef Ogre::uint32 CharCode; + +/** +* CharCodeRange is used to specify the ranges of characters that a font should load. +* For convenience, two common unicode ranges (BasicLatin & Latin1) have been already defined. +*/ +struct CharCodeRange +{ + std::vector<std::pair<CharCode, CharCode> > ranges; + + CharCodeRange(); + CharCodeRange(CharCode from, CharCode to); + + /** + * Add a range of characters to the definition. + * + * @param from The beginning of the range, inclusive. + * @param to The end of the range, inclusive. + */ + void addRange(CharCode from, CharCode to); + + /** + * Test if a character code is within this CharCodeRange. + * + * @return True if a character code is within any of the ranges, false otherwise. + */ + bool isWithinRange(CharCode code) const; + + static const CharCodeRange BasicLatin; + static const CharCodeRange Latin1; + static const CharCodeRange All; +}; + +/** +* Defines a font-face for Atlas initialization purposes. +*/ +struct FontFaceDefinition +{ + Ogre::String filename; + std::vector<Ogre::uint> sizes; + CharCodeRange codeRange; + short renderType; + + /** + * Specifies the type of rendering that this font should use. + * <ul> + * <li>BetterContrast - Sharper text, more like Windows' font rendering. + * <li>BetterShape - Smoother text, more like MacOSX's font rendering. + * </ul> + */ + enum RenderType + { + BetterContrast, + BetterShape + }; + + /** + * Constructs a FontFaceDefinition + * + * @param filename The filename of the font-face. + * @param codeRange The range of CharCode's to render for this font-face. + * @param renderType Optional; the type of rendering to use for this font-face. + */ + FontFaceDefinition(const Ogre::String& filename, const CharCodeRange& codeRange = CharCodeRange::BasicLatin, short renderType = FontFaceDefinition::BetterContrast); + + /** + * Adds a font-size (in px) to this font-face definition. + */ + void addSize(Ogre::uint fontSize); +}; + +struct ComputationRect +{ + int width, height; + int x, y; + int area; + bool isPlaced; + + Ogre::String filename; + bool isFontGlyph; + Ogre::uint fontSize; + CharCode charCode; + + Ogre::Image image; + + ComputationRect(const Ogre::String& texFilename, const Ogre::String& resourceGroup); + ComputationRect(const Ogre::String& texName, unsigned char* buffer, int width, int height); + ComputationRect(const Ogre::String& fontFilename, Ogre::uint fontSize, CharCode charCode, unsigned char* buffer, int width, int height); + +}; + +typedef std::vector<ComputationRect*> ComputationVector; + +typedef std::map<CharCode, GlyphInfo> GlyphMap; +typedef std::map<Ogre::uint, GlyphMap> FontSizeMap; +typedef std::map<Ogre::uint, FontMetrics> FontMetricsMap; + +struct FontFace +{ + FontSizeMap fontSizes; + FontMetricsMap fontMetrics; + + FontFace(); + + FontFace(const FontFaceDefinition& definition, const Ogre::String& resourceGroup, ComputationVector& renderContext); +}; + +/** +* The work-horse of Canvas; Atlas is a programmatic texture-atlas that can hold textures and font-glyphs. +*/ +class Atlas +{ + std::map<Ogre::String, FontFace> fontFaces; + std::map<Ogre::String, TextureInfo> textures; + std::pair<int, int> dimensions; + Ogre::String materialName; + + void guessDimensions(const ComputationVector& rectangles); + void pack(ComputationVector& rectangles); + void fill(ComputationVector& rectangles, int x1, int y1, int x2, int y2, int& count); + void paint(const ComputationVector& rectangles); + +public: + /** + * Constructs an Atlas. + * + * @param textureFilenames The filenames of the textures to load into this atlas. + * @param fonts The fonts to load into this atlas. + * @param resourceGroup The name of the resource group where the textures and fonts can be found. + */ + Atlas(const std::vector<Ogre::String>& textureFilenames, const std::vector<FontFaceDefinition>& fonts, const Ogre::String& resourceGroup); + + /** + * Retrieve the dimensions of this atlas, in pixels. + */ + const std::pair<int, int>& getDimensions() const; + + /** + * Retrieve the name of the internal material + */ + const Ogre::String& getMaterialName() const; + + /** + * Retrieve info about a certain texture within this atlas. + * + * @note If the filename is not found, the returned TextureInfo's member "isEmpty" will be true. + * + * @param filename The filename of the texture to look up. + */ + const TextureInfo& getTextureInfo(const Ogre::String& filename) const; + + /** + * Retrieve the metrics for a certain font size. + * + * @param fontFilename The filename of the font to look up. + * @param fontSize The font size to look up. + */ + const FontMetrics& getFontMetrics(const Ogre::String& fontFilename, Ogre::uint fontSize) const; + + /** + * Retrieve the GlyphMap for a certain font size. + * + * @param fontFilename The filename of the font to look up. + * @param fontSize The font size to look up. + */ + const GlyphMap& getGlyphMap(const Ogre::String& fontFilename, Ogre::uint fontSize) const; + + /** + * Retrieve info about a certain font glyph within this atlas. + * + * @param fontFilename The filename of the font-face. + * @param fontSize The size of the font. + * @param charCode The CharCode to look up. + * + * @note If the filename, size, or CharCode is not found, the returned GlyphInfo will contain a TextureInfo with + * the member "isEmpty" set to true. + */ + const GlyphInfo& getGlyphInfo(const Ogre::String& fontFilename, Ogre::uint fontSize, CharCode charCode) const; +}; +} //namespace +#endif Added: trunk/python-ogre/ThirdParty/canvas/Canvas.cpp =================================================================== --- trunk/python-ogre/ThirdParty/canvas/Canvas.cpp (rev 0) +++ trunk/python-ogre/ThirdParty/canvas/Canvas.cpp 2008-12-05 07:18:28 UTC (rev 827) @@ -0,0 +1,692 @@ +/* + This file is part of Canvas, a fast, lightweight 2D graphics engine for Ogre3D. + + Copyright (C) 2008 Adam J. Simmons + ajs...@gm... + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Canvas.h" + +using namespace Ogre; + +namespace CanvasNS +{ +/************************ +* Fill +************************/ + +Fill::Fill() : isEmpty(true) +{ +} + +Fill::Fill(const Ogre::ColourValue& color) : isEmpty(false), atlasKey("VertexColor") +{ + coloring.colors.first = color; + coloring.hasGradient = false; +} + +Fill::Fill(const Ogre::ColourValue& gradientColor1, const Ogre::ColourValue& gradientColor2, short orientation) : isEmpty(false), atlasKey("VertexColor") +{ + coloring.colors.first = gradientColor1; + coloring.colors.second = gradientColor2; + coloring.hasGradient = true; + coloring.orientation = orientation; +} + +Fill::Fill(const Ogre::String& texture, const Ogre::ColourValue& color) : isEmpty(false), atlasKey(texture) +{ + coloring.colors.first = color; + coloring.hasGradient = false; +} + +Fill::Fill(const Ogre::String& texture, const Ogre::ColourValue& gradientColor1, const Ogre::ColourValue& gradientColor2, short orientation) : isEmpty(false), atlasKey(texture) +{ + coloring.colors.first = gradientColor1; + coloring.colors.second = gradientColor2; + coloring.hasGradient = true; + coloring.orientation = orientation; +} + +/************************ +* Border +************************/ + +Border::Border() : isEmpty(true) +{ +} + +Border::Border(int width, const Ogre::ColourValue& color) : widths(width, width, width, width), colors(color, color, color, color), isEmpty(false) +{ +} + +Border::Border(const WidthRect& widths, const ColorRect& colors) : widths(widths), colors(colors), isEmpty(false) +{ +} + +/************************ +* Canvas +************************/ + +Canvas::Canvas(Atlas* atlas, Ogre::Viewport* viewport) : atlas(atlas), viewport(viewport), vertexData(0), indexData(0), + bufferSize(100), renderQueueID(Ogre::RENDER_QUEUE_OVERLAY), isDirty(false), visibility(true) +{ + viewport->getTarget()->addListener(this); + material = Ogre::MaterialManager::getSingleton().getByName(atlas->getMaterialName()); + setUseIdentityProjection(true); + setUseIdentityView(true); + + resizeBuffers(); + clearClip(); +} + + +Canvas::~Canvas() +{ + destroyBuffers(); + viewport->getTarget()->removeListener(this); +} + +void Canvas::drawRectangle(int x, int y, int width, int height, const Fill& fill, const Border& border) +{ + PixelRect rect(x, y, x + width, y + height); + + if(border.isEmpty) + { + if(isOutsideClip(rect)) + return; + } + else + { + if(isOutsideClip(PixelRect(rect.left - border.widths.left, rect.top - border.widths.top, rect.right + border.widths.right, rect.bottom + border.widths.bottom))) + return; + } + + if(!fill.isEmpty) + { + TextureInfo texInfo = atlas->getTextureInfo(fill.atlasKey); + if(texInfo.isEmpty) + return; + + // Draw a simple rectangle with the normal texture-coordinates at each corner + if(fill.atlasKey == "VertexColor" || (width == texInfo.width && height == texInfo.height)) + { + drawQuad(rect, texInfo.texCoords, fill.coloring); + } + else // Draw a tiled rectangle, may contain multiple quads to give the illusion that the texture is "tiling" + { + Ogre::Real xMax = width / (double)texInfo.width; + Ogre::Real yMax = height / (double)texInfo.height; + + Ogre::Real right = 0; + Ogre::Real bottom = 0; + + for(Ogre::Real left = 0; left < Ogre::Math::Ceil(xMax); left++) + { + for(Ogre::Real top = 0; top < Ogre::Math::Ceil(yMax); top++) + { + if(left + 1 > xMax) + right = xMax; + else + right = left + 1; + + if(top + 1 > yMax) + bottom = yMax; + else + bottom = top + 1; + + PixelRect tile(x + (left * texInfo.width), y + (top * texInfo.height), x + (right * texInfo.width), y + (bottom * texInfo.height)); + + if(isOutsideClip(tile)) + continue; + + Ogre::FloatRect texCoords = texInfo.texCoords; + texCoords.right = texCoords.left + (right - left) * (texCoords.width()); + texCoords.bottom = texCoords.top + (bottom - top) * (texCoords.height()); + + Coloring coloring = fill.coloring; + if(coloring.hasGradient) + { + Ogre::Real amount1, amount2; + + if(coloring.orientation == Coloring::Vertical) + { + amount1 = top / yMax; + amount2 = bottom / yMax; + } + else + { + amount1 = left / xMax; + amount2 = right / xMax; + } + + coloring.colors.first = (fill.coloring.colors.first * (1 - amount1)) + (fill.coloring.colors.second * amount1); + coloring.colors.second = (fill.coloring.colors.first * (1 - amount2)) + (fill.coloring.colors.second * amount2); + } + + drawQuad(tile, texCoords, coloring); + } + } + } + } + + // Draw the four sides of the border, if present + if(!border.isEmpty) + { + Ogre::FloatRect vColCoords = atlas->getTextureInfo("VertexColor").texCoords; + Corners<Ogre::Vector2> corners; + + PixelRect bRect(rect.left - border.widths.left, rect.top - border.widths.top, rect.right + border.widths.right, rect.bottom + border.widths.bottom); + + // Left Border + if(!isOutsideClip(PixelRect(bRect.left, bRect.top, rect.left, bRect.bottom))) + { + corners.topLeft = Ogre::Vector2(bRect.left, bRect.top); + corners.bottomLeft = Ogre::Vector2(bRect.left, bRect.bottom); + corners.bottomRight = Ogre::Vector2(rect.left, rect.bottom); + corners.topRight = Ogre::Vector2(rect.left, rect.top); + + drawQuad(corners, vColCoords, border.colors.left); + } + + // Bottom Border + if(!isOutsideClip(PixelRect(bRect.left, rect.bottom, bRect.right, bRect.bottom))) + { + corners.topLeft = Ogre::Vector2(rect.left, rect.bottom); + corners.bottomLeft = Ogre::Vector2(bRect.left, bRect.bottom); + corners.bottomRight = Ogre::Vector2(bRect.right, bRect.bottom); + corners.topRight = Ogre::Vector2(rect.right, rect.bottom); + + drawQuad(corners, vColCoords, border.colors.bottom); + } + + // Right Border + if(!isOutsideClip(PixelRect(rect.right, bRect.top, bRect.right, bRect.bottom))) + { + corners.topLeft = Ogre::Vector2(rect.right, rect.top); + corners.bottomLeft = Ogre::Vector2(rect.right, rect.bottom); + corners.bottomRight = Ogre::Vector2(bRect.right, bRect.bottom); + corners.topRight = Ogre::Vector2(bRect.right, bRect.top); + + drawQuad(corners, vColCoords, border.colors.right); + } + + // Top Border + if(!isOutsideClip(PixelRect(bRect.left, bRect.top, bRect.right, rect.top))) + { + corners.topLeft = Ogre::Vector2(bRect.left, bRect.top); + corners.bottomLeft = Ogre::Vector2(rect.left, rect.top); + corners.bottomRight = Ogre::Vector2(rect.right, rect.top); + corners.topRight = Ogre::Vector2(bRect.right, bRect.top); + + drawQuad(corners, vColCoords, border.colors.top); + } + } +} + +void Canvas::drawGlyph(const GlyphInfo& glyph, int x, int y, int width, int height, const Ogre::ColourValue& color) +{ + if(glyph.texInfo.isEmpty) + return; + + PixelRect rect(x, y, x + width, y + height); + + if(isOutsideClip(rect)) + return; + + Coloring coloring; + coloring.colors.first = color; + coloring.hasGradient = false; + + drawQuad(rect, glyph.texInfo.texCoords, coloring); +} + +void Canvas::clear() +{ + quadList.clear(); + isDirty = true; +} + +void Canvas::setClip(int left, int top, int right, int bottom) +{ + clip.left = left; + clip.top = top; + clip.right = right; + clip.bottom = bottom; +} + +void Canvas::clearClip() +{ + clip.left = 0; + clip.top = 0; + clip.right = viewport->getActualWidth(); + clip.bottom = viewport->getActualHeight(); +} + +const Ogre::MaterialPtr& Canvas::getMaterial() const +{ + return material; +} + +void Canvas::getRenderOperation(Ogre::RenderOperation& op) +{ + op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; + + op.vertexData = vertexData; + op.vertexData->vertexStart = 0; + op.vertexData->vertexCount = quadList.size() * 4; + + op.useIndexes = true; + op.indexData = indexData; + op.indexData->indexStart = 0; + op.indexData->indexCount = quadList.size() * 6; +} + +void Canvas::getWorldTransforms(Ogre::Matrix4* xform) const +{ + xform[0] = this->_getParentNodeFullTransform(); +} + +const Ogre::Quaternion& Canvas::getWorldOrientation() const +{ + return this->getParentNode()->_getDerivedOrientation(); +} + +const Ogre::Vector3& Canvas::getWorldPosition() const +{ + return this->getParentNode()->_getDerivedPosition(); +} + +Ogre::Real Canvas::getSquaredViewDepth(const Ogre::Camera* cam) const +{ + Ogre::Node* node = this->getParentNode(); + assert(node); + return node->getSquaredViewDepth(cam); +} + +const Ogre::LightList& Canvas::getLights() const +{ + return this->queryLights(); +} + +const Ogre::String& Canvas::getMovableType() const +{ + static Ogre::String typeName("Canvas"); + + return typeName; +} + +const Ogre::AxisAlignedBox& Canvas::getBoundingBox() const +{ + static Ogre::AxisAlignedBox box; + box.setInfinite(); + + return box; +} + +Ogre::Real Canvas::getBoundingRadius() const +{ + return 2.0; +} + +void Canvas::_updateRenderQueue(Ogre::RenderQueue* queue) +{ + resizeBuffers(); + updateGeometry(); + + queue->addRenderable(this, renderQueueID); +} + +void Canvas::setVisible(bool visible) +{ + mVisible = visibility = visible; +} + +bool Canvas::isVisible() const +{ + if (!visibility || mBeyondFarDistance || mRenderingDisabled) + return false; + + SceneManager* sm = Root::getSingleton()._getCurrentSceneManager(); + if (sm && !(mVisibilityFlags & sm->_getCombinedVisibilityMask())) + return false; + + return true; +} + +void Canvas::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) +{ +} + +void Canvas::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) +{ +} + +void Canvas::preViewportUpdate(const Ogre::RenderTargetViewportEvent& evt) +{ + if(evt.source == viewport && mVisible) + visibility = true; +} + +void Canvas::postViewportUpdate(const Ogre::RenderTargetViewportEvent& evt) +{ + if(evt.source == viewport && mVisible) + visibility = false; +} + +void Canvas::viewportAdded(const Ogre::RenderTargetViewportEvent& evt) +{ +} + +void Canvas::viewportRemoved(const Ogre::RenderTargetViewportEvent& evt) +{ +} + +void Canvas::destroyBuffers() +{ + if(vertexData) + { + delete vertexData; + vertexData = 0; + + buffer.setNull(); + } + + if(indexData) + { + delete indexData; + indexData = 0; + } +} + +void Canvas::resizeBuffers() +{ + if(bufferSize < quadList.size()) + { + bufferSize = quadList.size() * 2; + destroyBuffers(); + } + + if(!vertexData) + { + vertexData = new Ogre::VertexData(); + vertexData->vertexStart = 0; + vertexData->vertexCount = bufferSize * 4; + + Ogre::VertexDeclaration* decl = vertexData->vertexDeclaration; + Ogre::VertexBufferBinding* binding = vertexData->vertexBufferBinding; + + size_t offset = 0; + decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3); + decl->addElement(0, offset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + offset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR); + decl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0); + + buffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + decl->getVertexSize(0), vertexData->vertexCount, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + binding->setBinding(0, buffer); + } + + if(!indexData) + { + indexData = new Ogre::IndexData(); + indexData->indexStart = 0; + indexData->indexCount = bufferSize * 6; + + indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( + Ogre::HardwareIndexBuffer::IT_16BIT, indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); + + unsigned short* indexBuffer = (unsigned short*)indexData->indexBuffer->lock(0, indexData->indexBuffer->getSizeInBytes(), Ogre::HardwareBuffer::HBL_DISCARD); + + // Indexes are generated here because we know that we will only be rendering quads + // This means that we only have to handle updating the vertex buffer in Canvas::updateGeometry + for(size_t indexIdx, vertexIdx, quadIdx = 0; quadIdx < bufferSize; quadIdx++) + { + indexIdx = quadIdx * 6; + vertexIdx = quadIdx * 4; + + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 0); + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 2); + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 1); + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 1); + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 2); + indexBuffer[indexIdx++] = (unsigned short)(vertexIdx + 3); + } + + indexData->indexBuffer->unlock(); + } +} + +bool Canvas::isOutsideClip(const PixelRect& rect) +{ + if(rect.left > clip.right) + return true; + else if(rect.right < clip.left) + return true; + else if(rect.top > clip.bottom) + return true; + else if(rect.bottom < clip.top) + return true; + + return false; +} + +void Canvas::localize(Ogre::Vector2& vertex) +{ + Ogre::Real xTexel = Ogre::Root::getSingleton().getRenderSystem()->getHorizontalTexelOffset(); + Ogre::Real yTexel = Ogre::Root::getSingleton().getRenderSystem()->getVerticalTexelOffset(); + + vertex.x = ((vertex.x + xTexel) / (Ogre::Real)viewport->getActualWidth()) * 2 - 1; + vertex.y = ((vertex.y + yTexel) / (Ogre::Real)viewport->getActualHeight()) * -2 + 1; +} + +void Canvas::drawQuad(const PixelRect& rect, const Ogre::FloatRect& texCoords, const Coloring& coloring) +{ + PixelRect clipped; + Ogre::FloatRect clippedTexCoords(texCoords); + Coloring clippedColoring = coloring; + + if(rect.left > clip.left) + { + clipped.left = rect.left; + } + else + { + clipped.left = clip.left; + Ogre::Real delta = (clipped.left - rect.left) / (float)rect.width(); + clippedTexCoords.left += delta * texCoords.width(); + + if(coloring.hasGradient && coloring.orientation == Coloring::Horizontal) + clippedColoring.colors.first = (coloring.colors.first * (1 - delta)) + (coloring.colors.second * delta); + } + + if(rect.top > clip.top) + { + clipped.top = rect.top; + } + else + { + clipped.top = clip.top; + Ogre::Real delta = (clipped.top - rect.top) / (float)rect.height(); + clippedTexCoords.top += delta * texCoords.height(); + + if(coloring.hasGradient && coloring.orientation == Coloring::Vertical) + clippedColoring.colors.first = (coloring.colors.first * (1 - delta)) + (coloring.colors.second * delta); + } + + if(rect.right < clip.right) + { + clipped.right = rect.right; + } + else + { + clipped.right = clip.right; + Ogre::Real delta = (clipped.right - rect.right) / (float)rect.width(); + clippedTexCoords.right += delta * texCoords.width(); + + if(coloring.hasGradient && coloring.orientation == Coloring::Horizontal) + clippedColoring.colors.second = (coloring.colors.first * (-delta)) + (coloring.colors.second * (1 + delta)); + } + + if(rect.bottom < clip.bottom) + { + clipped.bottom = rect.bottom; + } + else + { + clipped.bottom = clip.bottom; + Ogre::Real delta = (clipped.bottom - rect.bottom) / (float)rect.height(); + clippedTexCoords.bottom += delta * texCoords.height(); + + if(coloring.hasGradient && coloring.orientation == Coloring::Vertical) + clippedColoring.colors.second = (coloring.colors.first * (-delta)) + (coloring.colors.second * (1 + delta)); + } + + Canvas::Quad quad; + quad.vertices = Corners<Ogre::Vector2>(Ogre::Vector2(clipped.left, clipped.top), Ogre::Vector2(clipped.left, clipped.bottom), + Ogre::Vector2(clipped.right, clipped.bottom), Ogre::Vector2(clipped.right, clipped.top)); + + localize(quad.vertices.topLeft); + localize(quad.vertices.bottomLeft); + localize(quad.vertices.bottomRight); + localize(quad.vertices.topRight); + + quad.texCoords.topLeft = Ogre::Vector2(clippedTexCoords.left, clippedTexCoords.top); + quad.texCoords.bottomLeft = Ogre::Vector2(clippedTexCoords.left, clippedTexCoords.bottom); + quad.texCoords.bottomRight = Ogre::Vector2(clippedTexCoords.right, clippedTexCoords.bottom); + quad.texCoords.topRight = Ogre::Vector2(clippedTexCoords.right, clippedTexCoords.top); + + if(clippedColoring.hasGradient) + { + if(clippedColoring.orientation == Coloring::Vertical) + quad.colors = Corners<Ogre::ColourValue>(clippedColoring.colors.first, clippedColoring.colors.second, clippedColoring.colors.second, clippedColoring.colors.first); + else + quad.colors = Corners<Ogre::ColourValue>(clippedColoring.colors.first, clippedColoring.colors.first, clippedColoring.colors.second, clippedColoring.colors.second); + } + else + { + quad.colors = Corners<Ogre::ColourValue>(clippedColoring.colors.first); + } + + quadList.push_back(quad); + isDirty = true; +} + +void Canvas::drawQuad(const Corners<Ogre::Vector2>& corners, const Ogre::FloatRect& texCoords, const Ogre::ColourValue& color) +{ + Corners<Ogre::Vector2> clippedCorners; + clippedCorners.topLeft.x = corners.topLeft.x > clip.left ? corners.topLeft.x : clip.left; + clippedCorners.topLeft.y = corners.topLeft.y > clip.top ? corners.topLeft.y : clip.top; + clippedCorners.bottomLeft.x = corners.bottomLeft.x > clip.left ? corners.bottomLeft.x : clip.left; + clippedCorners.bottomLeft.y = corners.bottomLeft.y < clip.bottom ? corners.bottomLeft.y : clip.bottom; + clippedCorners.bottomRight.x = corners.bottomRight.x < clip.right ? corners.bottomRight.x : clip.right; + clippedCorners.bottomRight.y = corners.bottomRight.y < clip.bottom ? corners.bottomRight.y : clip.bottom; + clippedCorners.topRight.x = corners.topRight.x < clip.right ? corners.topRight.x : clip.right; + clippedCorners.topRight.y = corners.topRight.y > clip.top ? corners.topRight.y : clip.top; + + Canvas::Quad quad; + quad.vertices = clippedCorners; + + localize(quad.vertices.topLeft); + localize(quad.vertices.bottomLeft); + localize(quad.vertices.bottomRight); + localize(quad.vertices.topRight); + + quad.texCoords.topLeft = Ogre::Vector2(texCoords.left, texCoords.top); + quad.texCoords.bottomLeft = Ogre::Vector2(texCoords.left, texCoords.bottom); + quad.texCoords.bottomRight = Ogre::Vector2(texCoords.right, texCoords.bottom); + quad.texCoords.topRight = Ogre::Vector2(texCoords.right, texCoords.top); + + quad.colors = Corners<Ogre::ColourValue>(color); + + quadList.push_back(quad); + isDirty = true; +} + +void Canvas::updateGeometry() +{ + if(!isDirty) + return; + + float* vBuffer = (float*)buffer->lock(0, quadList.size() * buffer->getVertexSize() * 4, Ogre::HardwareBuffer::HBL_DISCARD); + + Ogre::RGBA color; + Ogre::RGBA* vBufferCol = 0; + + for(std::vector<Canvas::Quad>::iterator i = quadList.begin(); i != quadList.end(); i++) + { + // Top-Left Vertex + *vBuffer++ = i->vertices.topLeft.x; + *vBuffer++ = i->vertices.topLeft.y; + *vBuffer++ = 0; + + vBufferCol = (Ogre::RGBA*)vBuffer; + Ogre::Root::getSingleton().convertColourValue(i->colors.topLeft, &color); + *vBufferCol++ = color; + + vBuffer = (float*)vBufferCol; + + *vBuffer++ = i->texCoords.topLeft.x; + *vBuffer++ = i->texCoords.topLeft.y; + + // Top-Right Vertex + *vBuffer++ = i->vertices.topRight.x; + *vBuffer++ = i->vertices.topRight.y; + *vBuffer++ = 0; + + vBufferCol = (Ogre::RGBA*)vBuffer; + Ogre::Root::getSingleton().convertColourValue(i->colors.topRight, &color); + *vBufferCol++ = color; + + vBuffer = (float*)vBufferCol; + + *vBuffer++ = i->texCoords.topRight.x; + *vBuffer++ = i->texCoords.topRight.y; + + // Bottom-Left Vertex + *vBuffer++ = i->vertices.bottomLeft.x; + *vBuffer++ = i->vertices.bottomLeft.y; + *vBuffer++ = 0; + + vBufferCol = (Ogre::RGBA*)vBuffer; + Ogre::Root::getSingleton().convertColourValue(i->colors.bottomLeft, &color); + *vBufferCol++ = color; + + vBuffer = (float*)vBufferCol; + + *vBuffer++ = i->texCoords.bottomLeft.x; + *vBuffer++ = i->texCoords.bottomLeft.y; + + // Bottom-Right Vertex + *vBuffer++ = i->vertices.bottomRight.x; + *vBuffer++ = i->vertices.bottomRight.y; + *vBuffer++ = 0; + + vBufferCol = (Ogre::RGBA*)vBuffer; + Ogre::Root::getSingleton().convertColourValue(i->colors.bottomRight, &color); + *vBufferCol++ = color; + + vBuffer = (float*)vBufferCol; + + *vBuffer++ = i->texCoords.bottomRight.x; + *vBuffer++ = i->texCoords.bottomRight.y; + } + + buffer->unlock(); + isDirty = false; +} +} // namespace Added: trunk/python-ogre/ThirdParty/canvas/Canvas.h =================================================================== --- trunk/python-ogre/ThirdParty/canvas/Canvas.h (rev 0) +++ trunk/python-ogre/ThirdParty/canvas/Canvas.h 2008-12-05 07:18:28 UTC (rev 827) @@ -0,0 +1,278 @@ +/* + This file is part of Canvas, a fast, lightweight 2D graphics engine for Ogre3D. + + Copyright (C) 2008 Adam J. Simmons + ajs...@gm... + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __Canvas_H__ +#define __Canvas_H__ + +#include <vector> +#include "Atlas.h" + +namespace CanvasNS +{ +class Canvas; + +/** +* An internal templated helper class. +*/ +template<typename T> struct Corners +{ + T topLeft, bottomLeft, bottomRight, topRight; + + Corners() + { + } + + Corners(const T& all) : topLeft(all), bottomLeft(all), bottomRight(all), topRight(all) + { + } + + Corners(const T& topLeft, const T& bottomLeft, const T& bottomRight, const T& topRight) : + topLeft(topLeft), bottomLeft(bottomLeft), bottomRight(bottomRight), topRight(topRight) + { + } +}; + +/** +* Internal structure, used to define solid fill colors/gradients. +*/ +struct Coloring +{ + std::pair<Ogre::ColourValue, Ogre::ColourValue> colors; + bool hasGradient; + short orientation; + + enum GradientOrientation + { + Vertical, + Horizontal + }; +}; + +/** +* Defines the "fill" (color, texture, gradient) for a shape drawn with Canvas. +*/ +struct Fill +{ + bool isEmpty; + Ogre::String atlasKey; + Coloring coloring; + + /** + * Create an empty fill. + */ + Fill(); + + /** + * Create a pure-color fill. + */ + Fill(const Ogre::ColourValue& color); + + /** + * Create a gradient fill. + * + * @param gradientColor1 In a vertical gradient, the top color, and in a horizontal gradient, the left color. + * @param gradientColor2 In a vertical gradient, the bottom color, and in a horizontal gradient, the right color. + * @param orientation The orientation of the gradient, can be either Coloring::Vertical or Coloring::Horizontal. + */ + Fill(const Ogre::ColourValue& gradientColor1, const Ogre::ColourValue& gradientColor2, short orientation = CanvasNS::Coloring::Vertical); + + /** + * Create a texture fill (must be loaded in current atlas) with an optional multiplied color. + * + * @param texture The filename of the texture, should be loaded in the current atlas. + * @param color The optional color to multiply the texture with. + */ + Fill(const Ogre::String& texture, const Ogre::ColourValue& color = Ogre::ColourValue::White); + + /** + * Create a texture fill (must be loaded in current atlas) with a multiplied gradient. + * + * @param texture The filename of the texture, should be loaded in the current atlas. + * @param gradientColor1 In a vertical gradient, the top color, and in a horizontal gradient, the left color. + * @param gradientColor2 In a vertical gradient, the bottom color, and in a horizontal gradient, the right color. + * @param orientation The orientation of the gradient, can be either Coloring::Vertical or Coloring::Horizontal. + */ + Fill(const Ogre::String& texture, const Ogre::ColourValue& gradientColor1, const Ogre::ColourValue& gradientColor2, short orientation = CanvasNS::Coloring::Vertical); +}; + +typedef Ogre::TRect<int> WidthRect; +typedef Ogre::TRect<int> ClipRect; +typedef Ogre::TRect<int> PixelRect; +typedef Ogre::TRect<Ogre::ColourValue> ColorRect; + +/** +* Defines the "border" for a rectangle drawn with Canvas. +*/ +struct Border +{ + bool isEmpty; + WidthRect widths; + ColorRect colors; + + /** + * Create an empty border. + */ + Border(); + + /** + * Create a border with a uniform pixel width and uniform color + * + * @param width The width, in pixels, for every side of the border. + * @param color The color for every side of the border. + */ + Border(int width, const Ogre::ColourValue& color); + + /** + * Create a border with custom pixel widths and colors for each side. + * + * @param widths The widths, in pixels, to use for each side. + * @param colors The colors to use for each side. + */ + Border(const WidthRect& widths, const ColorRect& colors); +}; + +/** +* The Canvas. +*/ +class Canvas : public Ogre::Renderable, public Ogre::MovableObject, public Ogre::RenderTargetListener +{ + struct Quad + { + Corners<Ogre::Vector2> vertices; + Corners<Ogre::Vector2> texCoords; + Corners<Ogre::ColourValue> colors; + }; + + Atlas* atlas; + std::vector<Canvas::Quad> quadList; + Ogre::HardwareVertexBufferSharedPtr buffer; + Ogre::VertexData* vertexData; + Ogre::IndexData* indexData; + size_t bufferSize; + Ogre::MaterialPtr material; + Ogre::Viewport* viewport; + Ogre::uint8 renderQueueID; + ClipRect clip; + bool isDirty; + bool visibility; + +public: + /** + * Constructs the canvas. + * + * @param atlas The texture atlas to use for this canvas. + * @param viewport The viewport that this canvas will display in. + */ + Canvas(Atlas* atlas, Ogre::Viewport* viewport); + + /** + * Destroys the canvas. + */ + ~Canvas(); + + /** + * Draws a rectangle on the canvas. + * + * @param x The x-coordinate of the origin, in pixels. + * @param y The y-coordinate of the origin, in pixels. + * @param width The width of the rectangle, in pixels. + * @param height The height of the rectangle, in pixels. + * @param fill The fill to use. + * @param border The optional border. + */ + void drawRectangle(int x, int y, int width, int height, const Fill& fill, const Border& border = Border()); + + /** + * Draws a glyph on the canvas. + * + * @param glyph The GlyphInfo of the glyph to draw (obtained from Atlas). + * @param x The x-coordinate of the origin, in pixels. + * @param y The y-coordinate of the origin, in pixels. + * @param width The width of the rectangle, in pixels. + * @param height The height of the rectangle, in pixels. + * @param color The color of the glyph. + */ + void drawGlyph(const GlyphInfo& glyph, int x, int y, int width, int height, const Ogre::ColourValue& color); + + /** + * Clears the canvas. + */ + void clear(); + + /** + * Sets the current clipping boundaries to use for subsequent draw calls. + * + * @param left The left-boundary, in pixels. + * @param top The top-boundary, in pixels. + * @param right The right-boundary, in pixels. + * @param bottom The bottom-boundary, in pixels. + */ + void setClip(int left, int top, int right, int bottom); + + /** + * Resets the current clipping boundaries to the dimensions of the viewport. + */ + void clearClip(); + + // Inherited from Ogre::Renderable + const Ogre::MaterialPtr& getMaterial() const; + void getRenderOperation(Ogre::RenderOperation& op); + void getWorldTransforms(Ogre::Matrix4* xform) const; + const Ogre::Quaternion& getWorldOrientation() const; + const Ogre::Vector3& getWorldPosition() const; + Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const; + const Ogre::LightList& getLights() const; + + // Inherited from Ogre::MovableObject + const Ogre::String& getMovableType() const; + const Ogre::AxisAlignedBox& getBoundingBox() const; + Ogre::Real getBoundingRadius() const; + void _updateRenderQueue(Ogre::RenderQueue* queue); + void setVisible(bool visible); + bool isVisible() const; + + // Inherited from Ogre::RenderTargetListener + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void preViewportUpdate(const Ogre::RenderTargetViewportEvent& evt); + void postViewportUpdate(const Ogre::RenderTargetViewportEvent& evt); + void viewportAdded(const Ogre::RenderTargetViewportEvent& evt); + void viewportRemoved(const Ogre::RenderTargetViewportEvent& evt); + +protected: + + void destroyBuffers(); + + void resizeBuffers(); + + void localize(Ogre::Vector2& vertex); + + bool isOutsideClip(const PixelRect& rect); + + void drawQuad(const PixelRect& rect, const Ogre::FloatRect& texCoords, const Coloring& coloring); + + void drawQuad(const Corners<Ogre::Vector2>& corners, const Ogre::FloatRect& texCoords, const Ogre::ColourValue& color); + + void updateGeometry(); +}; + +} // namespace +#endif Modified: trunk/python-ogre/environment.py =================================================================== --- trunk/python-ogre/environment.py 2008-12-04 13:12:10 UTC (rev 826) +++ trunk/python-ogre/environment.py 2008-12-05 07:18:28 UTC (rev 827) @@ -1863,6 +1863,28 @@ descText = "MyGUI Interface System" descLink = "http://sourceforge.net/projects/my-gui/" +class canvas: + active = False + pythonModule = True + version="1.0" + name='canvas' + parent="ogre/gui" + cflags = "" + include_dirs = [ Config.PATH_Boost, + Config.PATH_INCLUDE_canvas + , Config.PATH_INCLUDE_Ogre + ,Config.PATH_INCLUDE_Ogre_Dependencies + ] + lib_dirs = [Config.PATH_LIB_Boost + ,Config.PATH_LIB_Ogre_OgreMain + , Config.PATH_LIB_Ogre_Dependencies + ] + CheckIncludes=[] + libs=[ boost.lib, 'OgreMain', 'freetype235'] + ModuleName="canvas" + descText = "Canvas GUI System" + descLink = "http://www.ogre3d.org/phpBB2/viewtopic.php?t=41365&postdays=0&postorder=asc&start=0&sid=6578000180a935734beb03d548b900a4" + #######################################################################... [truncated message content] |