Archive for the ‘glgeom’ Category
GLGeom v0.0.6 Released!
GLGeom v0.0.6 has been released!
GLGeom is the header-only C++ template math library developed in conjunction with LxEngine, modeled after and extending the GLM mathematics library. It provides strongly typed point, vector, and color classes, polygonal mesh and primitives classes (spheres, cylinders, etc.), bounding objects, intersection calculations, and more.
Release 0.0.6 contains many improvements and changes. The most significant changes may be to the glgeom_extension_primitive_buffer module, a module designed to provide a slightly higher-level, CPU-side take on a vertex array object: it stores a mesh (quads, triangles, lines, points) as well as optional arrays for face properties and vertex properties like normals, colors, and UVs. It now also includes some basic adjacency information. All of these properties can be set manually or, when possible, generated automatically.
The full release notes which includes download links, API documentation, and code diffs are located here:
As it will be for some time, it’s still a very early release but feedback is greatly appreciated. Leave a comment on the blog!
Example Code
// Use a utility method to create a basic shape glgeom::primitive_buffer primitive = glgeom::create_sphere(); // Automatically compute face normals on the sphere glgeom::compute_face_normals(primitive); // // Generate some UV coordinates // // Use a spherical mapper with a scale transform of 10. // glgeom::compute_uv_mapping( primitive, 0, [](const glgeom::point3f& position, glgeom::vector3f& normal) -> glgeom::point2f { glgeom::point2f baseUV = glgeom::mapper_spherical(position); return glgeom::scale( baseUV, glgeom::vector2f(10, 10) ); });
And a look at the primitive buffer functions…
Obviously a little incomplete, but we’re still at 0.0.6!
//===========================================================================// // GEOMETRY CREATION //===========================================================================// inline primitive_buffer create_cube (void); inline primitive_buffer create_sphere (void); inline primitive_buffer create_cylinder (void); inline primitive_buffer create_cone (void); inline primitive_buffer create_torus (void); inline primitive_buffer create_vertex_normals_mesh (primitive_buffer& mesh); inline primitive_buffer create_face_normals_mesh (primitive_buffer& mesh); //===========================================================================// // ITERATORS //===========================================================================// inline void iterate_indices (primitive_buffer& primitive, std::function<void (size_t faceIndex, glgeom::uint16* vertexIndices)> f); //===========================================================================// // BOUNDS //===========================================================================// inline void compute_bounds (primitive_buffer& primitive, glgeom::abbox3f& bbox); inline void compute_bounds (primitive_buffer& primitive, glgeom::bsphere3f& bsphere); inline void compute_bounds (primitive_buffer& primitive, glgeom::abbox3f& bbox, glgeom::bsphere3f& bsphere); inline void compute_bounding_box (primitive_buffer& primitive); inline void compute_bounding_sphere (primitive_buffer& primitive); inline void compute_bounds (primitive_buffer& primitive); //===========================================================================// // NORMALS //===========================================================================// inline void compute_face_normals (primitive_buffer& primitive); inline void compute_vertex_normals (primitive_buffer& primitive); inline void compute_uv_mapping (primitive_buffer& primitive, size_t channel, std::function<glgeom::point2f (const glgeom::point3f&, const glgeom::vector3f&)> f); //===========================================================================// // ADJACENCY //===========================================================================// inline void compute_adjacency_vertex_to_faces (primitive_buffer& primitive); inline bool verify_adjacency_vertex_to_faces (primitive_buffer& primitive, bool report = true); //===========================================================================// // MISC //===========================================================================// inline void reverse_winding_order (primitive_buffer& primitive);
Today’s Updates
What I’ve been working on today…
Created a stub for lxcore.lib
Currently, LxEngine is composed of the following:
- glgeom (header-only library of math functions)
- lxengine.lib (where the vast majority of the code is)
- lxengineapp.exe (a small front-end that drives the code in lxengine.lib)
What I’d like it to be is:
- glgeom (same as before)
- lxcore.lib (low-level, standalone functions and data types from lxengine.lib)
- lxframework.lib (the higher-level framework of Engine, Document, View, Element, classes)
- lxengine.exe (slightly larger front-end that drives the framework in a flexible, configurable form)
That’s a bit of an over simplification since there’s also extension libraries like lxrasterizer.lib and plug-ins like soundAL.dll, which exist now and will continue to exist.
The general point however is that I want make the framework “leaner.” Right now it has too many specific features (mostly from early development when things weren’t very modular) and it should really be a rather bare but flexible MVC framework. Those features belong in plug-ins and extensions. Even the utility stand-alone functions belong elsewhere (lxcore.lib). The end goal is a smaller framework is much better: frameworks are useful – right up until the point they become bloated and hard to understand (and thus defect prone and with steep learning curves).
GLGeom Error Handling
Long story short: all this time, I’ve been using “assert(0)” as an error handling mechanism in GLGeom to keep from tying in any dependencies. That’s not a very valid approach…
So I’m added a simple, but flexible error handling mechanism instead: docs here.
GLGeom Compute Primitive Buffer Adjacency Info
Still a work-in-progress, I’m trying to port over some old code that is pretty fast at computing adjacency from a polygon soup. This is also stressing GLGeom from a new angle, which is helping me fill in the blanks on some of the functionality in this currently-version-0.0.5-library.
Better get back to working on it…
GLGeom UV generators
Per Dave’s request, I’ve recently added a new function to generate UV coordinates on a glgeom::primitive_buffer. The function name is glgeom::compute_uv_mapping().
The principle is simple: it’s a callback-based function that iterates over all the vertices and calls the callback to generate a UV coordinate. The existing glgeom_extension_mappers module already provides worker functions to use in the callback argument (e.g. mapper_cube, mapper_spherical).
Images
Above is a spherical mapper with a 10x scale applied to the terrain geometry.
Above is a cube mapper with a 10x scale applied to the terrain geometry.
Code
The syntax for generating a planar XY mapping is as follows:
glgeom::compute_uv_mapping(primitive, 0, [](const glgeom::point3f& p, const glgeom::vector3f& n) -> glgeom::point2f { return glgeom::scale( glgeom::mapper_planar_xy(p), glgeom::vector2f(10, 10)); });
Using namespace glgeom, it looks like this:
compute_uv_mapping(primitive, 0, [](const point3f& p, const vector3f& n) -> point2f { return scale( mapper_planar_xy(p), vector2f(10, 10)); });
For anyone living in the dark ages of pre-C++11, the above code reads as follows:
- Compute a UV mapping for “primitive”
- Store the computed UVs in channel 0
- Generate a lambda function as callback…
- …which takes in a 3d position and a normal and returns a 2d point (i.e. the UV coordinate)
- …which uses the GLGeom built-in “mapper_planar_xy” function to generate the UV
- …and lastly applies a scale factor of 10 to both the u and v coordinates
API Design Commentary
I have to admit, I’ve not thoroughly satisfied with the resulting code above. It’s a bit verbose and has a very high syntactical-boilerplate-to-content ratio. However, this is the best compromise I’ve come up with yet.
What I don’t like about this design
- The lamdba notation is heavy-weight: there’s almost as much code to define the lambda signature as there is in the lambda body
- It’s possibly too general: in an ideal design, everything should have an immediately obvious place. Mappers transformations (scaling and rotation) as quite common so I’d be nice to have a “built-in” argument for these rather than relying on the user adding them manually into the lambda (and possibly introducing a typo into the multiplication order)
- Is a UV coordinate a “point”? In theory yes, in practice UVs are often treated as generic tuples, making the glgeom::point2t type occasionally cumbersome
Why not allow function pointers to be passed in directly?
I’d like it better if syntax like this worked:
glgeom::compute_uv_mapping(primitive, glgeom::mapper_cube);
But this doesn’t work well. I want to support mapper functions that require positions and normals (e.g. mapper_cube) as well as those that depend only on position (e.g. mapper_spherical). I also want to support lambda functions for inlined custom mappers. Overloading a compute_uv_mapping template method to automatically choose the right variation did not seem to work (VS2010, at least, doesn’t seem to handle overloading well with lambdas of varying type). The compiler doesn’t seem to automatically resolve to the right template overload in a way that provides the syntax I’d like.
With this approach, I ended up with compiler complaints about ambiguous type resolution.
Why not provide an explicit set of compute_uv_* methods?
The ugly boilerplate syntax would go away if a series of functions where provided:
glgeom::compute_uv_cube_mapping(primitive, uv_transform); glgeom::compute_uv_spherical_mapping(primitive, uv_transform); glgeom::compute_uv_planar_xy_mapping(primitive, uv_transform); ...
I like this approach that (a) syntax doesn’t get in the way and (b) it leads to about as self-documenting code as is possible.
What I don’t like is (a) it requires a mirror function for every mapper defined in the glgeom_extension_mappers and (b) creates a dependency between the glgeom_extension_mappers module and the glgeom_extension_primitive_buffer module.
So…?
I much prefer the principle that a generic function to apply an mapper exists alongside and independent of the set of mappers that can be applied (i.e. something a bit more along the lines of functional programming, thank you Javascript and JQuery for teaching me how nice this approach can be!). With that principle in mind, I’ve ended up with the syntactically verbose lambda based approach.
What’s Next?
Hopefully, generating tangents and bi-tangents. I wrote a bit before about bump mapping and generating those values in the GLSL shader, but it would be nice to compute such data easily on the CPU side as well.
In any case, this will require me to add some functions to compute mesh adjacency information for a primitive buffer – a feature I’ve been meaning to add anyway.
glgeom::create_sphere()
Per the first ever user request (thanks Dave), GLGeom has a new feature:
glgeom::primitive_buffer glgeom::create_sphere();
Implementation
The code, located here, works as follows:
- Creates a unit cube
- Moves vertices onto the surface of a unit sphere
- Split each quad into four quads via introducing a quad midpoint
- Move the new vertex onto the surface of the unit sphere
- Repeat steps 3 & 4 for several iterations
Why add this?
While LxEngine does already support loading meshes from Blender files, I do like the idea of providing some basic shapes for the GLGeom library. It does have practical application: especially when throwing together a quick test or maybe implementing a research project, it can be nice to create a zero-dependencies executable that creates rather than loads the geometry it needs. I see this as a feature useful for the small samples and tests use cases.
Image
The result is a (fairly) evenly divided sphere:
…and yes, the “snow” from the previous post is still in the image – I lazily hacked the new functionality into Tutorial 5 for testing
And also, since the rasterizer is OpenGL 3.2 based, it renders using triangles rather than (the now deprecated) quads. Thus, I mention “quads” above, yet the wireframe image displays a sphere composed of triangles. The conversion takes place at the LxEngine rasterization level so I’ve ceased to think too much about such conversions!
Details
The create_sphere() function creates a glgeom::primitive_buffer with the following properties:
primitive.type = "quads"; primitive.vertex.positions = /*vector filled with the unit sphere positions*/; primitive.vertex.normals = /*vector filled with normals for each vertex*/; primitive.indices = /*four 16-bit indices per quad in the sphere*/; primitive.bbox = /*unit bounding box*/; primitive.bsphere = /*unit bounding sphere*/;
GLGeom v0.0.5 Released!
GLGeom v0.0.5 has been released!
GLGeom is the C++ template math library developed in conjunction with LxEngine, modeled after and extending the GLM mathematics library. It provides strongly typed point, vector, and color classes, primitives classes (spheres, cylinders, etc.), bounding objects, intersection calculations, and more.
This release is most notable for the time that’s passed since the last release!
Almost four months has seen a hodgepodge of changes and improvements to the extent that it’s hard to coherently, yet briefly summarize them. A more orderly development and release schedule would certainly be preferable, but so it goes with pre-v1.0 releases of hobby projects.
The full release notes which includes download links, API documentation, and code diffs are located here:
.
As it will be for some time, it’s still a very early release but feedback is always welcome!
Example Code
Swizzle and vec operator
glgeom::vector3f v (1, 2, 3), u(4, 5, 6); glgeom::color3f c; glgeom::point3f p; glm::vec3 q; // Supports swizzle operators v.zxy = u.xyz; // v now contains (6, 5, 4) c.rgb = 1; // set c to white (1, 1, 1) // Strict types // v = c; Compiler error! No implicit conversion from color to vector // p = v; Compiler error! No implicit conversion from vector to point // Use "vec" operator to treat the type as a generic GLM vec2/3/4 q = v.vec; p.vec = q;



