Showing posts with label NCCA. Show all posts
Showing posts with label NCCA. Show all posts

Friday, 21 November 2014

Using Qt Library templates

The following video shows how to configure a Qt project to create a static library to use in your own projects, the main code for this can be found on github here

The main things you will need to do are in the .pro file as follows

TEMPLATE = lib
CONFIG+=staticlib

If you omit the staticlib it will create a dynamic library and the runtime linker will need to be told where to find your lib.

In the project you intend to use the library in you need to set the LIBS Qt variable passing in the -L[path to lib] and -l lib(s) to link


 For more examples of this see this blog post




Friday, 21 March 2014

Using emscripten to port NGL to the Web

Introduction

I have been using the ngl:: library for many years as part of teaching various graphic programming courses. I decided recently it would be interesting to port the core library and many of the demos to work interactively on the web using WebGL so started investigating a number of ways to do this. The main library is written in C++ and uses either Qt or SDL to create the OpenGL context. 
I had a number of choices as to which approach to take, I could learn Java Script and  three.js however this would mean porting all my codebase to Java Script which seemed like too much work.
In the end I came across the emscripten system which is a LLVM-to-JavaScript Compiler which can convert my C++ code into LLVM and then into JavaScript and then into asm.js the process was quite a steep learning curve, however I manage to get quite a lot of demos ported very quickly which can be seen here the rest of the blog will outline the process and how the webngl system was developed.

Installing Emscripten

My main development environment is a mac, however I have also tested the files under linux and it also works well. To get started I followed the tutorial here and all worked first time. The next stage was to try a simple WebGL demo that is provided with the examples. There are several WebGL demos using different libraries for OpenGL context creation however as I'm most familiar with SDL I chose to use this as the basis of the framework.

A Simple SDL demo program

The following code is a simple SDL program (very similar to a normal SDL program) the only difference is the call to emscripten_set_main_loop.

#include "SDL.h"
#include <GLES2/gl2.h>
#define GL_GLEXT_PROTOTYPES 1
#include <GLES2/gl2ext.h>
#include <emscripten.h>
#include <iostream>
#include <cstdlib>


void process()
{
  // as we don't have a timer we need to do something here
  // using a static to update at an interval
  static int t=0;
  if(++t > 100)
  {
    float r=(double)rand() / ((double)RAND_MAX + 1);
    float g=(double)rand() / ((double)RAND_MAX + 1);
    float b=(double)rand() / ((double)RAND_MAX + 1);
  
    glClearColor(r,g,b,1);
    t=0;
  }
  glClear(GL_COLOR_BUFFER_BIT);
  // this is where we draw
  SDL_GL_SwapBuffers();
}

int main(int argc, char *argv[])
{

 SDL_Surface *screen;

 // Init SDL
 if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) 
 {
  std::cerr<<"Unable to initialize SDL: "<<SDL_GetError();
  return EXIT_FAILURE;
 }

 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

 screen = SDL_SetVideoMode( 720, 576, 16, SDL_OPENGL  | SDL_RESIZABLE); 
 if ( !screen ) 
 {
  std::cerr<<"Unable to set video mode: "<<SDL_GetError();
  return EXIT_FAILURE;
 }

 glEnable(GL_DEPTH_TEST);

 // let emscripten process something then 
 // give control back to the browser
 emscripten_set_main_loop (process, 0, true);

 SDL_Quit();
 return EXIT_SUCCESS;
}

The emscripten_set_main_loop function is explained very well here basically we create a function that is called asynchronously to allow the browser to regain control after every iteration of the function. It is important that this function does exit else the browser will hang up and I have had several times when I get a complete lockup of the system.

Compiling the program

To compile the program we use the following command line (in this case I'm using c++ )
em++ -s FULL_ES2=1 SDL1.cpp -o SDL1.html
The flag -s FULL_ES2 tells emscripten to use full OpenGL ES 2 specification when compiling the code, the -o SDL1.html will generate an html file as well as the javascript file for the canvas to use. The html file can now be opened in the browser an in this case you will see a screen that changes colour every 100 cycles of the main loop. In the next post I will begin to discuss how I ported the rest of ngl to use emscripten.

Wednesday, 13 November 2013

Designing a Software System

This is the first guest post on my Blog, this one comes from a PhD student (and previously an MSc at Bournemouth) Mathieu Sanchez.

This post is specifically aimed at the design of complex software systems and is in general feedback on how to so the initial design required for some of our assignments. I was really impressed with this email and decided to share it with everyone.

Design often takes experience, which is why it is so difficult to teach, but there are some "half rules".
  • Write down your concepts, keywords and actions on a piece of paper, there is no need to draw anything. Verbs often translate to a relation and/or a class method (important). Nouns often refer to a class. ( see this and this)
  • Classes are most likely a singular noun. If it is not then maybe you are doing something wrong. In this case, check your multiplicity, and try to find a better name. It can happen that you have "container" classes. Don't name them by what they contain. An example would be a container for several wolves. If you name it wolves, it is unclear, and will lead to confusion. Do you mean a "pack of wolves"? Naming is very important, and will help you get a clearer vision, and help the markers (which is always good).
  • Classes ending in -ER  are a warning flag. It can happen, but if it does, you need to be sure of yourself. Once again, naming might be the issue, not the actual design. (see here here and here)
  • Don't build crazy associations everywhere. Data can travel along an association and should have a strong meaning such as "owns" "directs" etc...
  • Triangle relations are nasty. If you have class A, B and C, there should not be a link between each of them with each other. 
  • If you find that there are many ways to achieve the same task, then there might be some inheritance in there. For instance, shadows in rendering can be done through shadow mapping or shadow feelers. This is crying for inheritance. Make sure you have a look at Liskov substitution if you have inheritance.
  • Many of the common problems are solved with (famous) patterns. For instance, if there are two ways of doing one operations, but can also be combined together to achieve a better result, then there is a pattern for that.
  • When I read a diagram, I first look for a point of entry. And so should you. A point of entry for me is where everything starts, the class that controls the entire system. If it is unclear to you where it starts, then you might have missed something.
  • Think at a higher level at first, don't go straight for details, in fact implementation details such as acceleration structures only come last. The first diagram gives an overview of the system. If the interfaces are correctly made, adding spatial data structures is a piece of cake.
  • Think about extensibility. What if someone else wants to extend your library/system? If I have to jump into your code to add some if/else and edit your interface, then it is just plain wrong. Hence inheritance and factories.

Finally, once you have a draft, you need to review your diagram. The only thing I do when I try to help you is ask you my famous "what is its responsibility, in one and only sentence?". 

Do it yourself. Once the diagram is under your eyes, you can see if one of the classes overlaps with another, or worse, there is no class for a particular class. Then, double check your multiplicity. Read it out loud if needed. You should have two sentences to read. Let s say we have  [ A ] 1 ------- * [ B ], then it would read "A has many B, and B belongs to one and only one A". Multiplicity can be: 0..1 (at most one), 1 (one and only one), 0..* (any number), 1..* (one or more). Your inheritance should be solid if it respects the Liskov substitution principle.


Don't leave your diagrams without explanation, and stand your ground. Design is all about making decisions and trade-offs. I want to know those, because this is what lets me know if you actually worked on this, and are an able analyst, or if you are just fighting to just get a working system.

Wednesday, 2 October 2013

Introduction to using the bash shell (NCCA Version)

So it's the first week of term, and many new students have arrived to start their lab sessions only to find that we don't use windows!

There are many reasons for this, however the main one is the our main output industry is VFX and the majority of (large) VFX houses use linux / unix. This is due to a number of reasons, however mainly stability, scalability and ease of customisation.

Learning to use the linux command line and linux in general is quite a daunting task for a new user, and many people often wonder why they should bother when there is a perfectly good GUI. However as I shall explain in the following videos it will make you a more efficient / better operator if you can master some of the basic shell commands.

Also it has the added benefit that once you have learnt them, most of them will also be available in both shell scripts, python programs and C / C++ programs so you are learning valuable transferable skills all in one go.

Whilst these videos will concentrate on the university lab setup they will also be relevant to other linux distributions and linux in general and are mainly a repetition of what we do in the introduction lab sessions.

There is a link to a workbook and other resources here


 

(for more info on ssh see here)









 
alias ls='ls --color'
alias ll='ls -al'
alias rm='rm -i'

some useful information on the prompt

Wednesday, 7 December 2011

Getting Started with Alembic

I've just started looking at the Alembic interchange format which looks like a really promising system for passing baked animation data plus a whole lot more around. With big players like ILM, Sony Image Works, The Foundry etc all using it I thought it would be good to get started with implementing my own wrapper for the ngl:: library and just have a general look at what it can do so I can do a lecture or two on it.

Installation is fairly straight forward, you need boost++,  HDF5 and cmake and the OpenEXR framework libraries ( ILMBase which has IMath etc).  On the mac the latest version get placed in /usr/local/alembic-1.0.3 and this will have the static libs, headers and maya plugins needed to get started.

I'm not going to dwell too long on the structures etc of Alembic at present and just have a look at getting started on some very basic loading of meshes etc. A good overview can be found here.

Getting some geometry

To get some sample geometry to test I decided to generate a very simple scene of primitives in maya


I have named each of the primitives to I can see how they fit into the alembic structure, also as transforms are present within the alembic structure, I decided to duplicate and transform some of the elements to see how this worked.

To export the data we can use the maya exported which comes with Alembic. This is called AbcExport and can be run from the Mel script window. I've not really managed to figure out how selection / grouping works with the exporter as yet, and it seems best to make each individual element in the scene a root and export in turn. This can be done with the following command.

AbcExport -v -j " -ws -root pasted__Platonic -root pasted__Helix -root pasted__Pipe -root pasted__Solid -root pasted__Torus -root pasted__Cone -root pasted__Cylinder -root pasted__Sphere -root pasted__Plane -root Platonic -root Helix -root Pipe -root Solid -root Torus -root Cone -root Cylinder -root Sphere -root Plane -file /Users/jmacey/teaching/AlembicTests/MayaTest.abc";

You will see that the -j option is basically a command string that specifies the job, in this case we are exporting world space root nodes (-ws) where -root  indicates each of the objects to store.

Finally I'm writing to the file MayaTest.abc with an absolute directory.


Basic Qt Project

To get started I've created a basic Qt project based on the installation directory  /usr/local/alembic-1.0.3 to build and link agains the libs using a qt program can be done using the following

INCLUDEPATH+=/usr/local/alembic-1.0.3/include/
INCLUDEPATH+=/usr/local/include/OpenEXR
LIBS+=-L/usr/local/alembic-1.0.3/lib/static/
LIBS+=-lAlembicAbc
LIBS+=-lAlembicAbcGeom
LIBS+=-lAlembicAbcCoreAbstract
LIBS+=-lAlembicUtil
LIBS+=-lAlembicAbcCoreHDF5
LIBS+=-lImath
LIBS+=-lHalf
LIBS+=-lIex
LIBS+=-lhdf5
LIBS+=-lhdf5_hl

You will notice that we need to include both the hdf5 and ILM libs as well as the static alembic libraries.

Basic File read / echo
The first program I wrote was a basic traversal of the structure looking for nodes / meshes.  From initial reading of the docs It seems that the internal structure of an alembic file closely resembles as unix file system. With a root node / then nodes following down a tree structure. Each of these nodes is a distinct object that we can access and gather attribute values from.

In most of the sample code / demos a recursive visitor pattern seems to have been used for initial test I wanted a simple iterative solution for quick testing and debugging so I decided to use static loops instead. 

The following code is the basic opening of a Alembic archive.
#include <Alembic/AbcGeom/All.h>
#include <Alembic/AbcCoreAbstract/All.h>
#include <Alembic/AbcCoreHDF5/All.h>
#include <Alembic/Abc/ErrorHandler.h>

using namespace Alembic::AbcGeom; 
...
IArchive  archive( Alembic::AbcCoreHDF5::ReadArchive(),argv[1] );

One of the key design philosophies behind Alembic is that the API is split into In and Out classes similar to iostream. This is summed up well by the following quote in the documents

"Because Alembic is intended as a caching system, and not a live scenegraph, the API is split into two roughly symmetrical halves: one for writing, and one for reading. At the Abc and AbcGeom layers, classes that start with ‘O’ are for writing (or “output”), and classes that start with ‘I’ are for reading (or “input”). This is analogous to the C++ iostreams conceptual separation of istreams and ostreams."

So in the above we are opening an input (I) archive.

The next section of code will get the top of the archive and see how many children there are 
std::cout<<"traversing archive for elements\n";
IObject obj=archive.getTop();
unsigned int numChildren=obj.getNumChildren();
std::cout<< "found "<<numChildren<<" children in file\n";
Once we have the number of children we can iterate for each child in the node and traverse the tree (it must be noted in this example I know the tree is of a set depth, in reality we would need to traverse using recursion / visitor pattern to cope with more complex scene / geometry data however for a proof of concept this is fine)

for(int i=0; i<numChildren; ++i)
{
 std::cout<<obj.getChildHeader(i).getFullName()<<"\n";
 IObject child(obj,obj.getChildHeader(i).getName());
 
 std::cout<<"Children "<<child.getNumChildren()<<"\n";
 const MetaData &md = child.getMetaData();
 std::cout<<md.serialize() <<"\n";
 
 for(int x=0; x<child.getNumChildren(); x++)
 {
  IObject child2(child,child.getChildHeader(x).getName());
  const MetaData &md2 = child2.getMetaData();
  if( IPolyMeshSchema::matches( md2 ) || ISubDSchema::matches( md2 ))
  {
   std::cout<<"Found a mesh "<<child2.getName()<<"\n"; 
  }
 }
}

The code above grabs the child header of the current object and prints out the name. We then create a new IObject from the current object branch (getChildHeader(i).getName() ), this is the next object in the tree and we can then traverse this.

To access all of the data we can grab the meta data and call the serialize method, followed by traversing the data to see if we have a mesh and seeing what the name is. The output on the scene above looks like the following ( a partial listing )


traversing archive for elements
found 18 children in file

/Plane
Children 1
schema=AbcGeom_Xform_v3;schemaObjTitle=AbcGeom_Xform_v3:.xform
Found a mesh plane
/Platonic
Children 1
schema=AbcGeom_Xform_v3;schemaObjTitle=AbcGeom_Xform_v3:.xform
Found a mesh icosa
/Solid
Children 1
schema=AbcGeom_Xform_v3;schemaObjTitle=AbcGeom_Xform_v3:.xform
Found a mesh buckyball
/Sphere
Children 1
schema=AbcGeom_Xform_v3;schemaObjTitle=AbcGeom_Xform_v3:.xform
Found a mesh sphere
/Torus
Children 1
schema=AbcGeom_Xform_v3;schemaObjTitle=AbcGeom_Xform_v3:.xform
Found a mesh torus


Getting to the Points
Once I have the ability to grab the mesh in the file structure I can now look at accessing the point data and rendering it. This is a two stage process with the maya output as we have a top level transform node and then the point data. We need to get the transform matrix, then we need to multiply the points by it to get them in the correct world space.

Using the previous code frame work, we can grab the transform using the following

IXform x( child, kWrapExisting );
XformSample xs;
x.getSchema().get( xs );
M44d mat=xs.getMatrix();

You will see that this returns a M44d which is from the IMath library and is a 4x4 Matrix. We can also access the XformSample elements in a number of ways such as getTranslation, get[X/Y/Z]Rotation, getScale which will actually fit in well with the ngl::Transform class once I get to that stage. Initially I'm just going to use the raw matrix and use IMath to do the multiplication with the point.

We now do the check to see if we have a mesh as outlined above, If we do we can built a MeshObject and access the data as show below
// we have a mesh so build a mesh object
IPolyMesh mesh(child,child2.getName());
// grab the schema for the object
IPolyMeshSchema schema=mesh.getSchema();

// now grab the time sample data for the object

IPolyMeshSchema::Sample mesh_samp;
schema.get( mesh_samp );
// get how many points (positions) there are
uint32_t size=mesh_samp.getPositions()->size();

Another important concept of Alembic is the idea of sampling, again for the documentation

"Because Alembic is a sampling system, and does not natively store things like animation curves or natively provide interpolated values, there is a rich system for recording and recovering the time information associated with the data stored in it. In the classes AbcA::TimeSamplingType and AbcA::TimeSampling, you will find that interface."


We access the mesh sample at the current time value (0 as we've not set any frame data yet) and we can get the positions (and size) at this temporal sample.

The following code will now loop for each of the positions in the sample and multiply them by the current transform. I then save each in my own format Vec3
for(uint32_t m=0; m<size; ++m)
{
  // get the current point
  V3f p= mesh_samp.getPositions()->get()[m];
  // multiply by transform
  p=p*mat;
  // store for later use
  data.push_back(ngl::Vec3(p.x,p.y,p.z));
}
In the case of this demo I just create a point cloud into a Vertex Array object and draw using my ngl:: framework. This can be seen in the following video

A lot of this post is supposition /  basic rantings about what I've done so far, as this is a very new API and there is very little solid documentation at the moment, I think this post may well be updated / superseded very soon. It is my intention to fully integrate the mesh and possibly lights / cameras as much as I can into ngl, and we also intend to use this as a core data exchange format at the NCCA, so I hope to have much more detail about all this very soon.