Voxel Maps with UV texture

The basic voxel algorithm is now implemented and I can leave the “painful” field of byte-arithmetic and countless lookup tables. Back in my library it is possible to reuse the standard classes for projection and texturing.

The first simple approach for texturing is the use of UV maps. For each sample a “virtual cell” is created with the dimension [0..1][0..1]. The endpoints of an Isosur-Face lie within this cell. The resulting coordinates are mapped to a uv texture:

example of uv map debugging

UV map debugging

This is an example for the object definition with an arbitrary texture:

    NSString* voxelMapIngredientName = @"voxelMap";
    YAIngredient* voxelMapIngredient = [world createIngredient:voxelMapIngredientName];
    [voxelMapIngredient setFlavour:Terrain];
    [voxelMapIngredient setTexture:@"terrainTex.png"];
    [voxelMapIngredient createModelfromTriangles:voxelData];

The random cell is now textured:

unit test texture mapping

The samples of a voxel-map are accessible like in a multidimensional array. By definition samples with negative density are outside of a body, samples with positive density are “inside”. Here is an example of cube with holes:

    const int vMapDimension = 32 * 2;
    YAVoxelMap* voxelMap = [[YAVoxelMap alloc] initDimensions:vMapDimension :vMapDimension :vMapDimension];

    for (int x = 0; x < vMapDimension; x++)
        for (int y = 0; y < vMapDimension; y++)
            for (int z = 0; z < vMapDimension; z++)
                if(x == 0 || x == (vMapDimension - 1) || y == 0 || y == (vMapDimension - 1) || z == 0 || z == (vMapDimension - 1))
                    [voxelMap setVoxel: -10  x:x y:y z:z];
                else {
                    if( ((x % 10 > 2 && x % 10 < 8) && ( y  % 10 > 2 && y  % 10 < 8)) ||
                        ((z % 10 > 2 && z % 10 < 8) && ( y  % 10 > 2 && y  % 10 < 8)) ||
                        ((x % 10 > 2 && x % 10 < 8) && ( z  % 10 > 2 && z  % 10 < 8))
                       )
                        [voxelMap setVoxel: -10 x:x y:y z:z];
                    else
                        [voxelMap setVoxel: +10 x:x y:y z:z];
                }
    [voxelMap calcIso];

This voxel map consists of 262144 samples and looks like this:

Big Voxel Map

Big Voxel Map

Obviously the mapping quality (depth) can be improved. Instead of ads, deferred shading with lightning, shadows and reflections of a HDR image can produce better results but this was already discussed in a previous post.

It is still possible to render this image with 60fps and it is therefore suitable for realtime applications:

YAOgl: Engine Improvements

Here is an example of an voxel cell with random samples:

Voxel Cell with random samples

It is now possible to change the projection parameter.
With the following commands you can change the camera frustum:

    [[[world transformer] projectionInfo] setZFar:1000.0f];
    [[world transformer] recalcCam];

YAOGL: Voxel Cell

A quick example of a voxel cell consisting of 32.000 samples rendered in real-time:

For reasons of simplicity the cell was sliced over the z-axis.

The voxel cell is rendered in a separate coordinate system and can be manipulated like any other object.

YAOgl: Animation with Interpolation

You can now use interpolation to animate objects and bones. This means that you simply add your target position for a vector or quaternion for a specific point in time. The actual position or rotation is than calculated between the last and next target. Additionally all capabilities from the basic animation class are inherited:

Engine status

The Framework now compiles with the latest LLVM version. The memory management was reimplemented with ARC. I’m actually working on improved methods for user interactions and object modeling.

Using microsofts kinect device for motion tracking

The kinect device was originally developed for gesture recognition in games. Because of its capabilities to record 3D body poses in real-time I checked the possibilities for motion tracking. Therefore I developed a small program written in Scala. My recorded data looks promising but at the moment certain work has to be done in post production to create a professional skeletal animation. There are at the moment much better solutions available like Brekel Kinect.

Using a kinect is very straightforward with the latest OpenNI library. Here is a Scala example:

package de.yousry.motiontracker

import scala.actors.Actor
import org.OpenNI._
import de.yousry.motiontracker.observer._
import scala.collection.mutable.HashMap

object OpenNIHandler extends Actor {

  var quit = false;
  var listeners = List[OpenNIListener]()

  var context: Option[Context] = None
  var depthGen: Option[DepthGenerator] = None
  var userGenerator: Option[UserGenerator] = None
  var skeletonCap: Option[SkeletonCapability] = None

  var width = 0
  var height = 0

  val activeUsers: HashMap[Int, MetaFile] = new HashMap[Int, MetaFile]()

  init

  def act = loopWhile(!quit) {
    react {
      case ReceptorDimensions => reply { ReceptionResult(width, height) }
      case ConnectOpenNI(listener) => listeners ::= listener
      case DisconnectOpenNI(listener) => listeners = listeners.filter(x => x != listener)
      case Quit => {
        println("Quit OpenNI handler")
        context.get.stopGeneratingAll
        context.get.release

        activeUsers.values.foreach(_.out.close)

        quit = true
      }
      case Loop => loop
    }
  }

  def init = {
    println("Initializing Openni")
    try {
      val SAMPLE_XML_FILE = "../../data/OpenNIConfig.xml"
      val scriptNode = new OutArg[ScriptNode]();
      context = Some(Context.createFromXmlFile(SAMPLE_XML_FILE, scriptNode))

      depthGen = Some(DepthGenerator.create(context.get))
      val depthMD = depthGen.get.getMetaData()
      width = depthMD.getFullXRes()
      height = depthMD.getFullYRes()

      userGenerator = Some(UserGenerator.create(context.get))
      skeletonCap = Some(userGenerator.get.getSkeletonCapability())
      val poseDetectionCap = userGenerator.get.getPoseDetectionCapability();

      val calibPose = skeletonCap.get.getSkeletonCalibrationPose()

      userGenerator.get.getNewUserEvent().addObserver(new NewUserObserver(skeletonCap.get, poseDetectionCap, calibPose));

      userGenerator.get.getLostUserEvent().addObserver(new LostUserObserver(activeUsers));
      skeletonCap.get.getCalibrationCompleteEvent().addObserver(new CalibrationCompleteObserver(calibPose, activeUsers, skeletonCap.get, poseDetectionCap))

      poseDetectionCap.getPoseDetectedEvent().addObserver(new PoseDetectedObserver(poseDetectionCap, skeletonCap.get));

      skeletonCap.get.setSkeletonProfile(SkeletonProfile.ALL);

      context.get.startGeneratingAll
    } catch {
      case ex: Exception => println("Could not read OpenNI Config:  " + ex.getCause()); sys.exit
    }

    OpenNIHandler ! Loop
  }

  def loop = {

    if (!listeners.isEmpty) {
      try {
        context.get.waitAnyUpdateAll

        val depthMD = depthGen.get.getMetaData()
        val sceneMD = userGenerator.get.getUserPixels(0)

        val oniUsers = userGenerator.get.getUsers.toList.par
          .filter(Id => skeletonCap.get.isSkeletonTracking(Id))
          .map(userId => new OniUser(userId, skeletonCap.get, depthGen.get, activeUsers.get(userId).get))

        listeners.par.foreach(_.updateNI(depthMD, sceneMD, oniUsers.toList))

      } catch {
        case ex: Exception => println("OpenNI Hanlder loop: " + ex.getMessage())
      }
    } else {
      Thread.sleep(100)
    }

    OpenNIHandler ! Loop
  }

}

trait OpenNIListener {
  def updateNI(depthMD: DepthMetaData, sceneMD: SceneMetaData, users: List[OniUser])
}

case class ConnectOpenNI(listener: OpenNIListener)
case class DisconnectOpenNI(listener: OpenNIListener)
case object Loop
case object Quit
case object ReceptorDimensions
case class ReceptionResult(width: Int, height: Int)
case class MetaFile(userId: Int, startTime: Long, out: java.io.FileWriter)

Update: Kungle Status

The Kungle Database evolved as expected. The loss of the free online translation service from Google was a drawback but I’m currently working on an alternative solution.

The Stats:

Number of processed articles

4.807.518

Articles from other directories were removed.

Multilanguage dictionary size

32.961.074

Latest recorded term

Workbag

Recorded: 2012-01-01T04:02:53.000Z

Topic: Technology

Area: usa

I picked up my workbag in my right hand, pretending to fumble in the outer pocket with my left, and turned away from the chair.

Latest recorded location

kyoto-shijo

One of the cutest boutique hotels in Kyoto, the Hotel MyStays Kyoto-Shijo’s thoroughly modern premises are just a short train ride away from some of Kyoto’s top tourist attractions.

Latest recorded person

Carolina D.

“Rotem S., a 23-year-old Israeli tourist, was charged Saturday with starting the fire, said Carolina D., a spokeswoman for the office of prosecutor in the city of Puerto Natales, in a telephone interview.”

(I have obfuscated the family name.)

Most frequent used term

People: 5.092.034

The: (the to of and in) list is ignored.

Availability

About 25% of all articles are now unavailable online.
About 70% / 40%  of all articles aren’t findable by major search engines.

The query was calculated in Q2/2011 with a subset of 500 articles: The results were generated in germany and could differ from your location and search history.

The test program is available via F/OSS license. It was originally designed to identify plagiarism and index quality.

Update 1/1: Missing link added.

YAOGL Nature Scene


This scene was hacked in several hours. Only the YAOGL default shaders were used. The grass is a 10 layer billboard:

Consisting of simple repetitive patterns:

The video should now be viewable in HD:

The tree was created with a “l-system” generator.

Update: Leafs are now rendered without backface-culling

YAOGL Realtime Kinematics with Bones

It is now possible to add kinematics to skeletal-data:

YAKinematics *kinematics = [[YAKinematics alloc] initWithJoints:[TentacleImp joints]];
[kinematics createKinematics];

and use it with:

[kinematics reset];
[kinematics setJointOrientation:@"Bone.Head"
                    quaternion:[[YAQuaternion alloc] euler: 25 pitch:0  roll:  0 ]];

Here is an example(with some material variations):

YAOGL Bones and Joints

You can now optionally add skeletal-data to your model. Bones (represented as matrices in modelspace) and Joints (as axis with head pitch and roll) are handled in vertex and geometry units of the GPU.

NSString *modelFile = @"armatureProbe";
NSString *ingredientName = @"probe";
YAIngredient *ingredient = [world createIngredient:ingredientName];
[ingredient setModel:modelFile];
[world addShapeShifter:modelFile];

The shape-shifter template will than connect itself to every instantiation of the inhered model, creating a copy of the default pose.
For example to access your “shape-shifting” data as joints use:

NSMutableDictionary* joints = [probeImp joints];
YAShaper* upperBone = [joints objectForKey:@"upperBone"];

Movements can be created (as usual) by animation-objects or with a json stream (for example from kinect or openSim)

    {
      "Name": "upperBone",
      "Id": 1,
      "Parent": 0,
      "Joint": [ 0.000000, -0.000000,  1.015570 ],
      "Quaternion": [ 0.000000,  0.000000,  0.000000,  1.000000],
      "Bone": [ 1.000000,  0.000000,  0.000000,  0.000000,
                0.000000,  1.000000,  0.000000,  0.000000,
                0.000000,  0.000000,  1.000000,  0.000000,
                0.000000,  0.000000, -0.000000,  1.000000
      ]
    }

Here is an unrelaistic example from my unit tests where the pitch and roll of a joint are simultaneously modified.

YAOGL Text

The YAOGL-Text function is an implementation of the algorithm described in the paper “Improved Alpha-Tested Magnification for Vector Textures and Special Effects” by Chris Green. Basis of this algorithm is a texture-atlas. The alpha channel of this texture is used as depth map to reduce aliasing effects (taking advantage of the fact that a fragment isn’t always a pixel).
Since a text ingredient isn’t bases on a 3D model but rather created by a string, you don’t need to explicitly define it. It is created by the method createImpersonatorFromText. If you want to update it later on (change text, etc) you can simply ask the impersonator for the reference (I use the terms ingredient/impersonator because I want a precise distinction to program objects respectively generics or templates).

- (int) createImpersonatorFromText: (NSString*) text
{
    [YALog debug:TAG message:@"createImpersonatorFromText"];

    NSString* ingredientName = [NSString stringWithFormat:@"YAText2D_%d", uniqueId()];
    YAIngredient* ingredient = [self createIngredient:ingredientName];
    [ingredient setFlavour:Text];
    [ingredient setText:text];

    [ingredient setModel:ingredientName]; 

    YAImpersonator* impersonator = [[YAImpersonator alloc] initWithIngredient: ingredientName];
    [impersonators setObject:impersonator forKey:[NSNumber  numberWithInt: [impersonator identifier]]];
    return [impersonator identifier];
}

All other features remain unchanged. Here is an example:

- (void)setupScene
{
    [world setMultiSampling: true];

    YAIngredient* ingredient = [world createIngredient:@"ball"];
    [ingredient setModel:@"smoothBall"]; /

    int impersonatorId = [world createImpersonator: @"ball"];
    YAImpersonator* impersonator = [world getImpersonator:impersonatorId];
    [[impersonator translation] setZ:8.0f];

    int textImpersonatorId = [world createImpersonatorFromText: @"Hallo,\nWelt!"];
    YAImpersonator* textImpersonator = [world getImpersonator:textImpersonatorId];
    [[textImpersonator translation] setZ:7.0f];
    [[textImpersonator size] setVector: [[YAVector3f alloc] initVals: 0.5f :0.5f :0.5f]];
    [world setDrawScene:true];
}

It creates the following result:

YAOGL Text Example

YAOGL 2D Text

Since your text is a real part of the Scene, you can apply any transformation or animation.
For example you can join a text with a elastic spring to a moving object.

But now to something completely different.

YAOGL Realtime PCF Shadows

YAOGL realtime shadows

YAOGL Realtime shadows

The principle is simple. An additional camera is used to render the scene to a new frame buffer object. The task of this fbo is to calculate the depth information from an imaginary light source to all shadow casters. The depth information in the fbo can be used to decide if a fragment (pixel or part of a pixel) is shadowed or not.

Complicated is the configuration of the OGL 3 core state-machine. Most documents about this subject are outdated or simply wrong. Also the OpenGL specification is interpreted differently among the hardware vendors.

The benefits of the efforts is a burning fast 3d graphics running on your mac, pad or phone.