Archive for the ‘modeling’ tag

BlendReader Class

without comments

A prototype quality BlendReader class has been integrated into LxEngine.   The interface is small and simple. Part of the core vision for LxEngine is excellent usability for the development team during development. This means removing steps from the development process that technically can be automated.  Direct support for the Blender file format is an exciting addition in this regard.  No more export step: simply save the updated file and run the application.

I call the code ‘prototype quality’ at this stage because I know it won’t work correctly on .blend files from 32-bit systems or from big endian systems.  Both these issues will be trivial to solve, but have not yet been addressed.   There’s also likely a bit of room for optimization, but for the most part, I doubt that matters for anything but massive scenes (in which case, it’ll likely be better not to be loading a .blend file directly, but rather some leaner format).

If nothing else, the .blend file format is fairly interesting in itself.

Usage

The BlendReader interface is trivial to use:

  1. Create the BlendReader object
  2. Call reader.open(std::string filename)
    1. This call opens the file, reads the blend file’s “DNA” structure and build indices so objects can be read out of the file easily
  3. Call reader.getBlocksByType(std::string type)
    1. This returns a list of the info about the blocks in the file of the given type.  For example, “Mesh” or “Scene”.
  4. Call reader.readObject(address)
    1. This takes the address of a block and reads it in as a typed object.  It’s not a C++ type, but rather a wrapper on the block that lets the user grab the fields by name without any error-prone pointer arithmetic.
  5. Call obj.field<type>(name, index)
    1. Reads a particular named field out of the object and casts it to the given type.  Of course the caller needs to get the type right – but this is inevitable as at some point the opaque chunk of binary data needs to be cast into native C++ types

Example Code

Here’s the chunk of prototype code that loads the blend files into an LxEngine document (using the LxEngine Mesh structure):

    float normalizeShort (short s)
    {
        return float (s) / float(std::numeric_limits<short>::max());
    }
 
 
    Mesh*
    load_blend (std::string filename)
    {
        BlendReader reader;
        if ( reader.open(filename) )
        {
            Mesh* pMesh = new Mesh;
 
            auto meshBlocks = reader.getBlocksByType("Mesh");
 
            if (meshBlocks.size() != 1)
            {
                lx_warn("More than one mesh found in .blend file.  Processing only the "
                        "first one that is found.");
            }
 
            auto spObj = reader.readObject(meshBlocks[0]->address);
            auto numVerts = spObj->field<int>("totvert");
            auto numFaces = spObj->field<int>("totface");
 
            pMesh->mVertices.reserve(numVerts);
            pMesh->mFaces.reserve(numFaces);
 
            pMesh->mFlags.mVertexNormals = true;
 
            auto spVerts = reader.readObject(spObj->field<unsigned __int64>("mvert"));
            for (int i = 0; i < numVerts; ++i)
            {
                Mesh::Vertex v;
                v.position = spVerts->field<point3>("co", 0);
 
                // Normals are encoded as shorts
                v.normal.x = normalizeShort( spVerts->field<short>("no", 0) );
                v.normal.y = normalizeShort( spVerts->field<short>("no", 1) );
                v.normal.z = normalizeShort( spVerts->field<short>("no", 2) );
 
                pMesh->mVertices.push_back(v);
                spVerts->next();
            }
            spVerts.reset();
 
            auto spFaces = reader.readObject(spObj->field<unsigned __int64>("mface"));
            for (int i = 0; i < numFaces; ++i)
            {
                Mesh::Quad q;
                q.index[0] = spFaces->field<int>("v1");
                q.index[1] = spFaces->field<int>("v2");
                q.index[2] = spFaces->field<int>("v3");
                q.index[3] = spFaces->field<int>("v4");
 
                pMesh->mFaces.push_back(q);
                spFaces->next();
            }
            spFaces.reset();
 
            return pMesh;
        }
        else
        {
            lx_error("Could not open file '%s'", filename.c_str());
            return nullptr;
        }
    }

 

Full Source Code

The latest code (assuming future submissions haven’t moved it) is available here on github.

And some related links on the .blend file format:

UV Unwrapping

without comments

I continued the exercise from the other day, this time aiming to create a “real” UV texture map rather than applying a seamless texture map.  The exercise this time was to create a simple box mesh representing a tiny shed of sorts and create a UV texture map for it.   The goal is a trivial box building not unlike one from the old game Bard’s Tale…

Screenshot from the old Apple IIgs game
The Bard’s Tale

Mesh
The mesh itself is incredibly simple: start with a box, extrude to the top.  Join the vertices at either end of the top extrusion – and done.

UV Unwrapping
Given the simple mesh, choosing the seams for the unwrapping was not difficult either.

Texture Map
From there, I then needed to create the actual texture map.  This is where the work started.

First problem: Blender 2.5 Alpha 2 appears to have an Export UV Layout feature which exports the UV Layout as an SVG file.  GIMP can open SVG files so no problem…except the Export UV Layout command didn’t seem to actually save anything in Blender 2.5 Alpha 2.  A little searching on Google and the Blender Artists forums convinced there was at least a strong enough probability that this was a bug, not a user error on my part, that I decided to look for a workaround.  The workaround: stretch the Blender UV layout window, ALT+PrintScreen, paste from the clipboard into GIMP, crop and resize to 512×512.   Not a perfect solution, but then again, this is an exercise, not production work.

The next problem was making a decent image.  I went to Google Images, searched for an old door and came across an image to use.  (Note: I did look and could not find copyright, licensing, or even original author information for the image.  Technically, I believe this means I should not have used the image…but I did.  In the future I should try to avoid this by either using my own photos are starting points or using images from someplace like flickr where images usually come with associated licensing info.)  The base image I used was from here, with a lower res copy below:

Base stock photograph used for
the texture map

The using the GIMP with a lot of resizing, rotating, cropping, smudging, cloning, and a little dodge and burn, I managed to create a texture map:

Custom texture map made from a highly sliced
and diced stock photograph

Result
The result was what I was aiming for.  Not overly impressive – and definitely could be improved even in its simple state, but it is in fact a custom texture mapped model of a simple shed, which is pretty close to what I had envisioned.

Final Model

Not perfect – but was what I was aiming for.

Written by arthur

July 7th, 2010 at 3:39 pm

Blender 2.5 and UV Unwrapping

without comments

I spent some time attempting to teach myself a bit more about 3D modeling since I’m taking a break from graphics programming, but have a hard time giving up learning more about graphics entirely…

Blender 2.5

I’m using Blender 2.5 Alpha 2.  The new UI in Blender is amazingly improved.  I don’t think I’ve ever seen such a vast improvement in a free software application before.  I can’t compliment the team enough: this is a tremendous step forward.   As a poignant example, I have very limited Blender experience (see one of my earlier blog posts) but can say that – for the first time – in this program’s vast set of capabilities, I was often able to find what I was looking for myself without resulting to Google.  Finding tools in the UI without Google may sound like an “obvious” requirement for good/decent/acceptable software, but for an application of the level of complexity of Blender, I think it’s fair to say it’s rarely the reality that finding the desired functionality is to any degree intuitive to beginners.  Again, kudos to the Blender team.

(Warning: there are some stability problems still if you’re giving 2.5 a try.  This is an Alpha 2 release, after all.)

Modeling Tutorial

I also happened across an excellent tutorial on KatsBits.com that takes the reader from start to finish on a static model blender model.  (Note: the tutorial uses a pre-Blender 2.5 release, the UI is very different.  Again though, with only minimal help from Google, I was able to find the 2.5 equivalents of everything being done.)

It begins with a cube, shows how to do basic cuts and extrudes, then moves on to setting up a UV map for the model and applying a texture.  The reason I enjoyed this tutorial so much was that (to a non-modeler beginner like me) it gave the best, concise explanation of UV Unwrapping.

I highly recommend reading the whole tutorial, as both the original author deserves that and the images give essential context to explain it more fully, but here’s are core idea that triggered the insight for me:

The principle involved here is the same as if you were to cut a cardboard box down one side, laying the resulting top, bottom and sides flat on the ground so all the parts of it were spread out. A mesh is treated much the same way where-ever possible, edges are placed around a mesh to facilitate a similar end result. 

In other words, a seam is a cutting line and the non-seams are where the Blender unwrapping algorithm will attempt to “unfold” the model.  For example, to unfold the top of a box (but to keep it connected to the box as a whole), three of the four top edges should be marked seems and the top will unfold along the unmarked seam.  Any non-planar collection of faces not adjacent to a seam will be more or less flattened or squashed down onto plane.

The second insight is that more seams means (likely) a more linear, clean unfolding but the trade-off is the UVs across each seam are discontinuous across that seam.

I suspect this was likely obvious to anyone who has spent any time 3D modelling before, but for a beginner like me it was good to finally “get it” about UV Unwrapping.

Results

The results?  Nothing fancy: just a chair similar to the one from the above tutorial.

Note: the wood texture was adapted in GIMP from a wood photograph available here (thanks, bittbox).  Using the GIMP Offset command, a bit of the Clone tool, and some color level adjustments, I managed to make a acceptable quality seamless texture out of the photograph.   I should also note that there’s no real need for a seamless texture in the final model given that the UV unwrapping; making the texture seamless (and with more desirable results than the Make Seamless built-in script) was a different, separate exercise.

Written by arthur

July 7th, 2010 at 12:00 am

Status Update

without comments

While most updates thus far has come at the completion of a sub-feature of sorts, this update is more of a general status report (largely due to my lack of time of late to complete a noteworthy sub-feature).

Project Setup
The lxengine code now compiles in Release builds.  This required a short Perl script to generate the Visual Studio vcproj files since the code previously only built correctly in Debug.  The long and short of it is that I wanted to minimize the amount of time spent in project setup and it seems that Visual Studio is more tailored for creating a new project than adapting it to an existing one. With this project, I intend to have many small sub-projects with identical build settings (link to the same libraries, produce output files in a similar directory structure, reference the same data files) as well as desire flexibility to easily relocate project source and destination files (without updating dozens of project files).  It seemed easier to write a short script to auto-generate near skeleton vcproj files and work with the Visual Studio Properties (vsprops) files to manage all the common settings.

In any case, I now have a profile-able Release build of the lxengine code and hopefully will need to do minimal project setup to add new samples and tests.  If it turns out I do, it seems that CMake is a growing favorite in the project build tools these days.  That may be the best eventual destination.

Performance
With the Release build, I experimented with several optimizations that I should do a write up on: namely, discovering several ways not to do transform caches in a ray-tracer (the uncached version beat both my naive transformation caches), and that if you plan to squeeze every bit of performance out of the Microsoft VC 9.0 compiler, be prepared to use the __forceinline compiler-specific keyword (the C++ keyword inline may not be enough to sway cl.exe’s internal algorithm to actually inline the code).

This project, as all projects do, is reminding me of how features compound upon features.  In this case, the ray tracer is (in my mind) already “too slow” (despite performance not being a primary objective of this project) and soon I’ll need to get interested in spatial indexing structures, fully optimized intersection algorithms, multi-threading, and other performance-oriented functionality.  Or this might be the time to make the leap to progressive rendering using hybrid rasterization / ray tracing techniques…

Modeling
I would like to add some experiments with procedural geometry to the project.  Procedural geometry at the object level for entire meshes (e.g. a tree) as well as geometry and maps for materials (e.g. wood bark) would be interesting.  I’ve given a look into Perlin noise as well as an algorithm for natural rock procedural geometry and textures, though I haven’t experimented yet with either.

For now, with the bit of time I’ve had, I’ve been a bit distracted by polygonal mesh modeling with Blender.  This isn’t an area I have any expertise in.  I did stumble upon the page about the newest blender open move project, Durian; it will be interesting to see the results.  As for myself, I’m still pecking at the keyboard trying to learn the Blender interface to construct the building facade that I’ve begun:

I found what seems to be an excellent set of freely available Blender video tutorials by David Allen Ward (homepage).  I don’t think many tutorial writers appreciate the value in actually watching someone work.  A fundamental part of the process includes an appreciation for how much tweaking goes into it, what actions are done over and over, and all the details that are not seen in the final product.  Anyone who has learned a task by literally looking over someone else’s shoulder as they work knows that there are all sorts of practical tips, tricks, and general information that get picked up and otherwise would likely never be shared.

Written by arthur

December 22nd, 2009 at 7:46 pm

Posted in lxengine

Tagged with , , ,