OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username:
Password:
CapsLock is on.
Remember me (for 1 year)
Log in
Home
Tutorials
Tutorials Home
Basic Tutorials
Intermediate Tutorials
Mad Marx Tutorials
In Depth Tutorials
Older Tutorials
External Tutorials
Cookbook
Cookbook Home
CodeBank
Snippets
Experiences
Ogre Articles
Libraries
Libraries Home
Alternative Languages
Assembling A Toolset
Development Tools
OGRE Libraries
List of Libraries
Tools
Tools Home
DCC Tools
DCC Tutorials
DCC Articles
DCC Resources
Assembling a production pipeline
Development
Development Home
Roadmap
Building Ogre
Installing the Ogre SDK
Setting Up An Application
Ogre Wiki Tutorial Framework
Frequently Asked Questions
Google Summer Of Code
Help Requested
Ogre Core Articles
Community
Community Home
Projects Using Ogre
Recommended Reading
Contractors
Wiki
Immediate Wiki Tasklist
Wiki Ideas
Wiki Guidelines
Article Writing Guidelines
Wiki Styles
Wiki Page Tracker
Ogre Wiki Help
Ogre Wiki Help Overview
Help - Basic Syntax
Help - Images
Help - Pages and Structures
Help - Wiki Plugins
Toolbox
Freetags
Categories
List Pages
Structures
Trackers
Statistics
Rankings
List Galleries
Ogre Lexicon
Comments
History: Render to SVG
View page
Source of version: 3
(current)
I like to explain how to render a picture to a svg file ([http://en.wikipedia.org/wiki/Scalable_Vector_Graphics]). Before we start, please read ((Projecting 3D position and size to 2D)) and ((Raycasting to the polygon level)). The main idea of using svg is that you can scale the picture without loosing quality. You can also write directly svg images in HTML files, which is very helpful, for example, to include images into doxygen. The svg code can easily be written into the documentation tag, you don’t need a binary file. Writing a screenshot to a binary file is something like {CODE(wrap="1", colors="c++")}mWindow->writeContentsToFile(name + ".png");{CODE} Creating a svg file is much more work. We have to detect all visible polygons transform their coordinates to screen coordinates and write them to a text file. Example from ((Ogre Procedural Geometry Library)): PNG: {img fileId="2142" thumb="y" rel="box[g]"} SVG: {img fileId="2143" thumb="y" rel="box[g]"} {CODE(wrap="1", colors="xml")} <?xml version="1.0" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"> <g style="stroke:black;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"> <path style="fill:#ffffff" d="M 15,45.861 81.5962,136.867 142.869,15 z" /> <path style="fill:#ffffff" d="M 81.5962,136.867 15,45.861 94.3075,248.919 z" /> <path style="fill:#ffffff" d="M 94.3075,248.919 15,45.861 40.9194,152.754 z" /> <path style="fill:#ffffff" d="M 142.869,15 81.5962,136.867 242.271,74.1127 z" /> <path style="fill:#ffffff" d="M 81.5962,136.867 94.3075,248.919 242.271,74.1127 z" /> <path style="fill:#ffffff" d="M 242.271,74.1127 94.3075,248.919 209.245,184.948 z" /> </g> </svg> {CODE} First we have to add a few objects to the header: {CODE(wrap="1", colors="c++")}RaySceneQuery* mRaySceneQuery; PixelBox* mRenderWindowPixelBox; void GetMeshInformation(Entity *entity, size_t &vertex_count, Ogre::Vector3* &vertices, size_t &index_count, Ogre::uint32* &indices, const Ogre::Vector3 &position, const Ogre::Quaternion &orient, const Ogre::Vector3 &scale); struct SVGPATH { std::vector<Vector2> points; // Points of the path ColourValue color; // Fill color Real distance; // Distance to camera bool operator<(SVGPATH rhs) { return distance > rhs.distance; } bool operator>(SVGPATH rhs) { return distance < rhs.distance; } }; {CODE} As you can see I use the GetMeshInformation function from ((Raycasting to the polygon level)). I'll skip that code in this article. In the beginning we have to initialize the ray query and the PixelBox object {CODE(wrap="1", colors="c++")} // Init ray query & PixelBox mRaySceneQuery = mSceneMgr->createRayQuery(Ogre::Ray(), Ogre::SceneManager::WORLD_GEOMETRY_TYPE_MASK); if (mRaySceneQuery == NULL) return; mRaySceneQuery->setSortByDistance(true); mRenderWindowPixelBox = new PixelBox (mWindow->getWidth(), mWindow->getHeight(), 1, PF_R8G8B8); mRenderWindowPixelBox->data = new BYTE[mRenderWindowPixelBox->getConsecutiveSize()]; {CODE} The next step is to define a lot of local variables in your export function {CODE(wrap="1", colors="c++")} // Local variables unsigned int width = mWindow->getWidth(); // screen width unsigned int height = mWindow->getHeight(); // screen height Real fw = 0.5f * width; // scale width Real fh = -0.5f * height; // scale height Real tw = (Real)width; // translate x direction Real th = (Real)height; // translate y direction std::vector<SVGPATH> pathList; // list of path elements std::ofstream svgfile; // output file Ray ray; // ray to detect polygon RaySceneQueryResult query_result; // ray query result Real closest_distance = -1.0f; // polygon distance to camera size_t vertex_count; // number of vertices size_t index_count; // number of indecies Vector3 *vertices; // pointer to vertex buffer Vector3 eyeSpacePos[3]; // variable for a triangle uint32 *indices; // pointer to index buffer Ogre::Image img; // image for colour detection Real dx = 1.0f / (Real)width; // ray query step size in x direction Real dy = 1.0f / (Real)height; // ray query step size in y direction {CODE} {QUOTE()}The color detection is not working now but I show a useful code fragment.{QUOTE} To get the color of a pixel we have to copy the rendered image to a buffer. {CODE(wrap="1", colors="c++")} // Copy image mWindow->copyContentsToMemory(*mRenderWindowPixelBox); img.loadDynamicImage(static_cast<Ogre::uchar*>(mRenderWindowPixelBox->data),width, height, PF_R8G8B8); img.flipAroundX(); {CODE} The simplest way (maybe not most efficient way) to get all visible polygons is to create a ray for every pixel on sceen and do a search {CODE(wrap="1", colors="c++")} // Create rays for every pixel on screen for(Real x = 0.0f; x <= 1.0f; x += dx) for(Real y = 0.0f; y <= 1.0f; y += dy) { mCamera->getCameraToViewportRay(x, y, &ray); mRaySceneQuery->setRay(ray); if (mRaySceneQuery->execute().size() <= 0) continue; SVGPATH closest_result; {CODE} The next code part is a copy of ((Raycasting to the polygon level)) {CODE(wrap="1", colors="c++")} // Search for triangles closest_distance = -1.0f; query_result = mRaySceneQuery->getLastResults(); for (size_t qr_idx = 0; qr_idx < query_result.size(); qr_idx++) { if ((closest_distance >= 0.0f) && (closest_distance < query_result[qr_idx].distance)) break; if ((query_result[qr_idx].movable != NULL) && (query_result[qr_idx].movable->getMovableType().compare("Entity") == 0)) { Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[qr_idx].movable); GetMeshInformation( pentity, vertex_count, vertices, index_count, indices, pentity->getParentNode()->_getDerivedPosition(), pentity->getParentNode()->_getDerivedOrientation(), pentity->getParentNode()->_getDerivedScale()); bool new_closest_found = false; for (int i = 0; i < static_cast<int>(index_count); i += 3) { std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, vertices[indices[i]], vertices[indices[i+1]], vertices[indices[i+2]], true, false); if (hit.first) { if ((closest_distance < 0.0f) || (hit.second < closest_distance)) { closest_distance = hit.second; {CODE} In the next step, the coordinates of the detected triangles are transformed to screen coordinates. Also tried to detect the color of the pixel to set the fill color. A valid triangle must in front of the camera and must not have any dark color. {CODE(wrap="1", colors="c++")} // Get triangle vetices and transform coordinates to screen system closest_result.points.clear(); for(int j = 0; j < 3; j++) { eyeSpacePos[j] = mCamera->getViewMatrix(true) * vertices[indices[i+j]]; Vector3 p = mCamera->getProjectionMatrix() * eyeSpacePos[j]; closest_result.points.push_back(Vector2(p.x * fw, p.y * fh)); } closest_result.distance = closest_distance; /* +Bugfix needed closest_result.color = img.getColourAt((size_t)(x * (Real)width), (size_t)(x * (Real)width), 0); -Bugfix needed */ closest_result.color = ColourValue::White; // Use white color instead of image color because it's not working correct now // Check is result is in front of the camera new_closest_found = (eyeSpacePos[0].z < 0.0f && eyeSpacePos[1].z < 0.0f && eyeSpacePos[2].z < 0.0f && closest_result.color.r > 0.4f && closest_result.color.g > 0.4f && closest_result.color.b > 0.4f); {CODE} The last code part of the triangle detection is used to add the closest triangle to a path list (each triangle is a path in svg) and detect the left top corner of the image. {CODE(wrap="1", colors="c++")} // # } } } delete[] vertices; delete[] indices; // Check for a valid triangle and a valid distance if (new_closest_found && closest_distance >= 0.0f) { // Check if triangle already exists bool inlist = false; for(std::vector<SVGPATH>::iterator iter = pathList.begin(); iter != pathList.end(); iter++) if((*iter).points[0] == closest_result.points[0] && (*iter).points[1] == closest_result.points[1] && (*iter).points[2] == closest_result.points[2]) { inlist = true; break; } if(!inlist) { // Triangle does not exist pathList.push_back(closest_result); for(int j = 0; j < 3; j++) { // Find left top corner of the image tw = std::min(tw, closest_result.points[j].x); th = std::min(th, closest_result.points[j].y); } } } } } } {CODE} In the end we have to write out all path elements to a file {CODE(wrap="1", colors="c++")} // Write SVG file if(pathList.size() > 0) { std::sort(pathList.begin(), pathList.end()); tw = -1.0f * tw + 15.0f; // Create a translation in x direction with a border th = -1.0f * th + 15.0f; // Create a translation in y direction with a border svgfile.open(name + ".svg"); svgfile << "<?xml version=\"1.0\" standalone=\"no\"?>" << std::endl; svgfile << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" << width << "\" height=\"" << height << "\">" << std::endl; svgfile << " <g style=\"stroke:black;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\">" << std::endl; char color[8]; for(std::vector<SVGPATH>::iterator iter = pathList.begin(); iter != pathList.end(); iter++) { #if _MSC_VER > 1310 sprintf_s(color, 8, #else sprintf(color, #endif "#%02x%02x%02x", (BYTE)(iter->color.r * 255.0f), (BYTE)(iter->color.g * 255.0f), (BYTE)(iter->color.b * 255.0f)); svgfile << " <path style=\"fill:" << color << "\" d=\"M"; for(std::vector<Vector2>::iterator pt = iter->points.begin(); pt != iter->points.end(); pt++) svgfile << " " << (pt->x + tw) << "," << (pt->y + th); svgfile << " z\" />" << std::endl; } svgfile << " </g>" << std::endl; svgfile << "</svg>" << std::endl; } {CODE} Discuss here: [http://www.ogre3d.org/forums/viewtopic.php?f=5&t=71647]
Search by Tags
Search Wiki by Freetags
Latest Changes
Minimal Ogre Collision
Artifex Terra
OpenMB
Advanced Mogre Framework
MogreSocks
Critter AI
Mogre Add-ons
MOGRE
Mogre MyGUI wrapper
MOGRE Editable Terrain Manager
...more
Search
Find
Advanced
Search Help
Online Users
58 online users