Thursday, June 5, 2014

Done!! & Belated Conclusions

I've been meaning to upload the final presentation (apologies for the delay). Now that things have settled down a bit, here are some notes on the finished plug-in. Here's the GitHub repo for those who are interested in the code: https://github.com/aliceyang/HydroTerrain



On the whole, HydroTerrain is mostly feature complete for river generation, and missing some features for terrain generation. Below are some screen shots of the plug-in running just river generation functionality.




From the postmortem, the task division for the plug-in is listed below. 

    ALICE:
1. Build Framework
1.1. Implement user interface in MEL [1d]
1.2. Create source images (river slope, terrain slope) [1d]
1.3. Implement command plug­in framework in C++ [1d]
1.3.1. Create code stubs for plug­in initialization, compute, etc.   
2. River network generation
2.1. Create data structures  
2.1.1. Individual river nodes with priority index (Horton­Strahler’s number),  position and elevation [3d]
2.1.2. River node graph [1d]
2.2. Convert source images to usable data [2d]
2.3. Implement L­system expansion with initial candidate nodes for hierarchical drainage network growth. [6d]  
2.4. Implement probabilistic rule application to determine river branch types [4d]
2.5. Add compatibility checks for newly created nodes [3d]
2.6. Integrate voronoi partitioning library [3d]
2.7. Classify river type for each river node and edge (dependant on 3.1) [3d]
3.3. Create River primitives [3d]

    CHENG:
3. Terrain system generation
3.1. Take expanded nodes and implement voronoi partitioning [4d]
3.1.1. Connect the points for each voronoi cell to get a patch.  
3.2. Assign height values to voronoi cells [1d]  
3.3.1. Define river junction algorithm
3.3.2. Connect river junctions
3.4. Implement Poisson distribution to cover terrain patch with terrain primitives. [4d]
4. CSG­Tree like datastructure (Cheng) [7d]  
4.1. Implement tree structure. [1­2 d]
4.2. Define different terrain features (hills, valleys, etc.) [1­2 d]
4.3. Implement blending, adding, subtracting, carving. [1­3 d]


CSG (constructive solid geometry) data structure and terrain primitives are currently incomplete, as my partner was not able to implement them in time. Since it is the minimum requirement for carving a basic river from the terrain, we were only able to demo river generation. You can see some attempts at CSG intersections at the end of the video where the terrain is interspersed with cylinders. 

Despite some setbacks, I'm quite happy with how the rest of the project turned out. The RiverNetworkNode updates/regenerates in real time, so every time the river contour (CV curve) is changed, all the nodes are recalculated, enabling the artist to make changes on the fly. You'll notice in the video that vertically lowering a river mouth results in more rivers branches flowing towards it, which adheres to the natural phenomenon as water always flows downwards. 

I was able to learn a lot about writing plug-in's for Maya through this project (not least of which is that it's sometimes a painful and tedious process, hah). I also picked up workarounds for using CGAL with Maya—for example, a node containing the CGAL library will only load properly in Maya when it has been compiled in release, rather than debug, mode. It will compile absolutely fine in either mode, which can throw a lot of people off. Might I note that coming to this discovery was a long and frustrating process. 

All in all, I'm glad I got to work on HydroTerrain. While I don't plan on continuing this particular project, I gained a lot from the experience. From a technical point of view, I learned about the procedural process of generating river networks as well as a bit about image processing and STL structures through the CImg and tree.hpp libraries. In terms of project management and planning, I learned how to divide a large task into discrete modular chunks and stick to a development schedule. In terms of Maya integration, I learned how to take general algorithms and translate them into Maya-specific functions. If anyone wants to actually run the plug-in, I'm happy to get in touch and go over the setup instructions (it's quite involved with CGAL installations and path variables which is why I'm not including it here). Cheers!



Saturday, April 19, 2014

Gradient look-up

Implemented gradient look-up from the river slope map this week, which lets us determine where a newly generated node should be positioned. I also added a horizontal jitter to handle the case where a parent node splits off into two children nodes with the same starting point and gradient vector. The sample river slope map I'm working off of at the moment is a simple radial gradient (lighter values designate higher elevation).




Nodes seem to be coming along nicely. Next steps are to implement geometry compatibility checks to determine when expansion should terminate. After that I'll be working on defining different river types and adding that to the final output.

Monday, April 7, 2014

Trouble with bounding boxes

Currently I'm stuck on querying the bounding box for the CV curve. This will be needed to fit the river slope image map onto the curve in order to retrieve the slope value at a specific position.

In my compute() function, I have my MFnNurbsCurve object, via
MObject curve = inputCurveData.asNurbsCurve();
MFnNurbsCurve curveFn (curve, &returnStatus); // Can also set as MItCurveCV
McheckErr(returnStatus, "ERROR creating curve function set\n");

And I've tried directly calling boundingBox() on it (which should work since MFnNurbsCurve inherits from MFnDagNode), but I get a return status code of 1 (kFailure), and internal status code of 6 (kNotImplemented), which isn't very helpful. I still don't know why it's not working.

An alternate method I tried was to query the bounding box attribute, as follows:
MObject boundingBoxObj = curveFn.attribute(MString("boundingBox"), &returnStatus); 
I get the same error with this method.

I know the bounding box is definitely queryable because the MEL command getAttr curve1.boundingBoxMin returns a valid value. Though I would rather not resort to that if I don't have to.

EDIT:
Per this answer, it seems like the reason why boundingbox() has been failing is because it will only work if the MFnNurbsCurve is attached to a dagNode, rather than curve data coming through the DG. I'm just going to manually calculate the bounding box.

Tuesday, April 1, 2014

Updates 4/1: Expansion Algorithm

Currently I'm still working on implementing the expansion algorithm, which is described as a grammar-like rewriting process. The system uses two non-terminal symbols to denote the candidate node and an instantiated node, and a terminal symbol that represents a node that has been added to the graph and that cannot be expanded.


While stepping through the algorithm, I realized that while the paper indicated how to calculate the elevation (y) of each newly created node, there was nothing for their (x, z) positions. I emailed one of the authors, Dr. Bene, and was told that the main direction is given by the direction to the previous node and is modified by the gradient of the river slope and other factors. I'm still using dummy constants for the river slope values, but it looks like I will either have to find an image processing library that covers gradients, or roll it out myself (which someone recommended in conjunction with using stb_img.c to read in the slope map).

Here is what one step 2.2 in the expansion chart looks like (this is with dummy slope and gradient values. You can tell by how the river pretty much aims straight to the origin)

Bird's eye view down y-axis

Breaking the next few weeks down into chunks, I have the following tasks left:

  • Implement function that reads in an image map describing the slope values in grey scale and scales it to fit the bounding box of the CV curve in order to determine the elevation of every node.
  • Calculate the gradient of the river slope map to determine expansion direction of newly instantiated nodes. 
  • Finish implementing expansion with symmetric Horton-Strahler junction creation 
  • Implement expansion with asymmetric Horton-Strahler junction creation
  • Implement expansion with just river growth
  • Implement compatibility check, which determines if a node is able to be expanded
  • Design and implement expansion loop
  • Calculate distance between edge and all other edges to avoid collisions in the graph
  • Calculate distance between a river node (point) and the contour curve
  • Check if a point lies within a curve (this is to ensure no nodes are created beyond the river contour)
  • Create a user interface in MEL

Saturday, March 15, 2014

Working Node!

Turns out changing inputData.asNurvsCurveTransformed() to inputData.asNurbsCurve() fixed the error I was getting. I now have a very basic node that puts placeholder geometry on the CV points of a curve. Next step is to implement the node expansion algorithm.


Attributes and Such

I'm testing the RiverNetworkNode right now and running into a few issues. First off, I realized I don't actually know which attribute of the curve I need to connect to the node. For polygons in our previous assignments it was the .matrix attribute. I took a look through Maya's connection editor, and it seems like only borderConnections is able to connect to the node's inputCurve attribute.

RiverNetworkNode takes in a CV curve and outputs a set of points to be fed into an instancer

However, when compute runs, I get an error creating the curve function set. Currently trying to debug this; will update with findings later.

The MEL commands I'm currently using are as follows:
// Create a CV curve using the CV curve tool
createNode RiverNetworkNode
connectAttr -f curve1.borderConnections[0] RiverNetworkNode1.inputCurve;
polyCube;
instancer;
connectAttr pCube1.matrix instancer1.inputHierarchy[0];
connectAttr RiverNetworkNode1.outputPoints instancer1.inputPoints;
EDIT: using connectAttr -f curve1.worldSpace[0] RiverNetworkNode1.inputCurve; got me further in the compute function. Now running into an error retrieving the CV points on the curve. Will investigate further tomorrow.

Friday, March 14, 2014

Pi Day Update

Happy Pi Day!

Last week I emailed our previous lab director Joe for advice on the tool implementation, as I had some questions about the Maya API as well as uncertainties on how best to approach it. I'll summarize some takeaways here:

The authoring tool will most likely be a collection of nodes and commands, where each node represents a modular portion of the overall task. This piece of advice was really helpful, since I originally envisioned the tool to be one monolithic node. He also clarified that nodes add pieces to the underlying Maya engine in the dependency graph, while commands make new features available to the command engine (namely calling nodes and issuing updates right away), so the tool will most likely require a combination of both. Previously I had thought nodes and commands were an either-or case, which it clearly is not.

I also asked about the viability of storing an overall state for a collection of nodes (for my river node network), and was told that while it could be done, the general paradigm in Maya is to propagate information down. Taking that into consideration, I'm currently working on creating a new RiverNetwork Maya Node. It will take in as input a CV curve as the river contour, and output an array of position values for the generated set of river nodes. These can be visualized with a simple sphere connected to an instancer.

One thing I'm still not sure how to handle is how to represent the parent-child relation between the generated output (this information will be needed to visualize the river graph part). To my [limited] knowledge, Maya nodes can only output objects that are MFnData, and it might take some extra work to figure out how to make one of the following data types work:

kInvalid Invalid value.
kNumeric Numeric, use MFnNumericData extract the node data.
kPlugin Plugin Blind Data, use MFnPluginData to extract the node data.
kPluginGeometry Plugin Geometry, use MFnGeometryData to extract the node data.
kString String, use MFnStringData to extract the node data.
kMatrix Matrix, use MFnMatrixData to extract the node data.
kStringArray String Array, use MFnStringArrayData to extract the node data.
kDoubleArray Double Array, use MFnDoubleArrayData to extract the node data.
kIntArray Int Array, use MFnIntArrayData to extract the node data.
kPointArray Point Array, use MFnPointArrayData to extract the node data.
kVectorArray Vector Array, use MFnVectorArrayData to extract the node data.
kComponentList Component List, use MFnComponentListData to extract the node data.
kMesh Mesh, use MFnMeshData to extract the node data.
kLattice Lattice, use MFnLatticeData to extract the node data.
kNurbsCurve Nurbs Curve, use MFnNurbsCurveData to extract the node data.
kNurbsSurface Nurbs Surface, use MFnNurbsSurfaceData to extract the node data.
kSphere Sphere, use MFnSphereData to extract the node data.
kDynArrayAttrs ArrayAttrs, use MFnArrayAttrsData to extract the node data.
kDynSweptGeometry SweptGeometry, use MFnDynSweptGeometryData to extract the node data. This data node is in OpenMayaFX which must be linked to.
kSubdSurface Subdivision Surface, use MFnSubdData to extract the node data.
kNObject nObject data, use MFnNObjectData to extract node data
kLast Last value. It does not represent real data, but can be used to loop on all possible types

Wednesday, March 12, 2014

Self-Reminders On Setting Up New Maya Plug-in Projects

Been running into a few issues when I tried setting up additional projects for Maya plug-ins in my project solution. Figured it might be helpful to keep track of what fixes I used here:
  1. Make sure the path to Maya has been set to environment variable, $(MAYA_PATH) in my case, in project properties -> C/C++ and Linker sections. This is mainly for collaborating with people who have different versions of Maya (my partner has 2013 and I 2012)

  2. In Build -> Configuration Manager, verify that the settings are "Debug" for configuration and "x64" for Active Solution Platform (and all the other solution platforms)

  3. If the project throws error MSB4044: The "CppClean" task was not given a value for the required parameter "FoldersToClean", make sure the Output Directory name contains $(Configuration) and not $(ConfigurationName) (via EatMyRandom)

  4. Always set the MTypeId value in the .cpp file, otherwise the project'll throw an error. 

Friday, March 7, 2014

Progress Report 3/6/14

The past week involved mostly research, ramp-up, and setup.

Tasks I finished
1.2: Create source images- we now have two simple, ellipse-based image maps that define the river and terrain slopes.
1.3: Set up framework- I set up a running solution based off of homework 2 that exports a .mll plugin for use in Maya.

Tasks I'm Working On
I'm still working on creating the necessary river node data structures. The will inherit from a base node class that my partner is still in the middle of implementing.

Roadblocks and Time Sinks
A lot of time went into ramping up on git and figuring out how to collaborate with my partner on github. One of the first problems was figuring out which files to exclude so we wouldn't overwrite each other's data with our user-specific files generated by Visual Studio. After we spent a fair amount of time doing trial and error (there were instances where my partner's project wouldn't build, but then we'd discover it was because he had set his debug platform to win32 instead of x64).

In the end, I found a standard list of file types to include in .gitignore for visual studio projects. It can be found here

Another issue I encountered was with the Github for Windows program. While I was committing changes to my local repository located in my 660 folder, I noticed that the Windows interface was not picking up any of my changes. Further inspection led me to find that the program clones another copy of the project within a folder called GitHub under My Documents.

Finally, my partner and I found out Windows for Github handles merge conflicts very poorly by throwing a fairly generic-sounding error. When I took a closer look, it seemed that git didn't have a built in merge tool beyond command line editing, and I spent a few hours figuring out how to set it up. Just for posterity, here are the steps to set tortoisemerge as git's default merge tool:
  1. Download TortoiseGit
  2. While installing, select OpenSSH and NOT Putty as the preferred ssh tool
  3. Open your .gitconfig file and add the following lines
    [merge]
        tool = tortoisemerge
    [mergetool "toirtoisemerge"]
        cmd = \"C:/Program Files/TortoiseGit/bin/TortoiseGitMerge.exe\" -base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"
    [merge]
        tool = tortoisemerge
    [mergetool "tortoisemerge"]
        path = C:\\Program Files\\TortoiseGit\\bin\\tortoisegitmerge.exe
  4. References for above: devstuffs and stackoverflow
  5. Read the thread for fixing merge conflicts in git on stackoverflow. 
While working on the tool UI in MEL (Task 1.1) I realized that, since we are creating a maya node, I don't know how I would consolidate the UI values with the node attributes. Maybe instead of having a MEL-based UI, we should make all the fields shown in the mockup node attributes? When creating the design doc, we had not considered how to combine these two inputs into one source. It's something I plan on looking further into this week. 

Another task I've been looking into (that was not accounted for in the design doc because we thought it was trivial but have since found that it's not) is how to read in initial user inputs. We had originally assumed that the user will create a CV curve to define the river contour, however I haven't been able to find a way to translate this into a concrete demarcation in the plug-in. I think I might have to look into implementing our own algorithm to check whether or not a point lies within an enclosed curve. For the interest of time, we might simplify our approach by starting with circles as curves and auto-generating river-mouths instead of taking in user input. 

Monday, March 3, 2014

CIS 660 Advanced Topics in Computer Graphics and Animation: Authoring Tool Project

The authoring tool project is a semester-long endeavor where students in the class pair up and implement a SIGGRAPH paper from the past 6 years. The paper my partner and I chose is from SIGGRAPH 2013 and titled "Terrain Generation Using Procedural Models Based On Hydrology". Procedural generation has always been an interest of mine, and I think it'll be fun to take a break from buildings and focus on something a bit more organic.

We're naming our tool HydroTerrain (creative, isn't it). And we broke down the paper into two main components: river network generation and terrain generation. I will be mostly responsible for the river networks and my partner the terrain. Our complete design document can be seen below, and our progress can be tracked here.

2