Render to SVG        

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

mWindow->writeContentsToFile(name + ".png");

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:
Primitive Box
SVG:

<?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>


First we have to add a few objects to the header:

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; }
};


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

// 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()];


The next step is to define a lot of local variables in your export function

// 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
The color detection is not working now but I show a useful code fragment.

To get the color of a pixel we have to copy the rendered image to a buffer.

// Copy image
    mWindow->copyContentsToMemory(*mRenderWindowPixelBox);
    img.loadDynamicImage(static_cast<Ogre::uchar*>(mRenderWindowPixelBox->data),width, height, PF_R8G8B8);
    img.flipAroundX();

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

// 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;

The next code part is a copy of Raycasting to the polygon level

// 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;

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.

// 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);

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.

// #
                            }
                        }
                    }
 
                    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);
                            }
                        }
                    }
                }
            }
        }

In the end we have to write out all path elements to a file

// 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;
    }


Discuss here: http://www.ogre3d.org/forums/viewtopic.php?f=5&t=71647