Da der Import einer Szene aus externen Tools zu Aufwendig ist, habe ich den internen Szenengraph um einen Debug-Modus ergänzt. Zudem konnte durch Optimierung der Vektor-Berechnungen etwa 6% und durch Umstellung der OpenGL Befehlsfolge noch einmal 4% Leistungsverbesserung erzielt werden. Die Berechnung und Darstellung einer Szene benötigt nun weniger Ressourcen als das Abspielen eines Internet-Videos.

Aufbau des mechanischen Orbits:

Hier experimentiere ich mit der Szenen-Beleuchtung:

It is now possible to use HI Devices like gamepads, joysticks for object or camera movement.

Here is a short demonstration of this feature:

    YAHIDAnimator* gamepad = [world createHIDAnimator];
    [gamepad addListener:[voxelCubeImp rotation] factor:90];

    YABasicAnimator* rotZ = [world createBasicAnimator];
    [rotZ setProgress:harmonic];
    [rotZ setInterval:0.5f];
    [rotZ addListener:[voxelCubeImp rotation] factor:5];
    [rotZ setInfluence:z];

This new animation class can be combined with any other animation method:

The iso-surface of a voxel map is now simultaneously updated with the change of voxel-densities. In this demo following code was used to produce a growing cube effect:

        const char growth = 120 / 10; 
        
        for(int i = 0; i <= 25; i++) {
            int xP = 10 + (rand() % (33 - 20));
            int zP = 10 + (rand() % (33 - 20));
            
            if( (xP > 12 && xP < 20) && (zP > 12 && zP < 20) )
                continue;
            
            int yP = 1;
            while(yP < 31 && [voxelMap getVoxel:xP :yP :zP] > 1 )
                yP++;
            
            if(yP < 15) {
                char d = [voxelMap getVoxel:xP :yP - 1 :zP];
                
                if(d < 120)
                    [voxelMap setVoxel:d + growth x:xP y:yP -1 z:zP];
                else 
                    [voxelMap setVoxel:growth x:xP y:yP z:zP];
            }
        }
        
        if(voxelMap.hasChanged) {
            [voxelMap updateIso];
            [voxelMapIngredient updateTriangles:[voxelMap vbo]];
        }

For each frame about 25 voxels are changed. The method updateIso is used to recalculate the mesh data. The voxel algorithm is fast enough to run exclusively on the CPU.

Voxel maps are now partially compressed (the inactive parts) and the borders of clusters are now stitched together. Im now using cubic projection for texture generation.

Here are some further examples:

A Cube

    YAVoxelMap* voxelMap = [[YAVoxelMap alloc] initDimensions:vMapDimensionX :vMapDimensionY :vMapDimensionZ];
    
    for (int x = 0; x < vMapDimensionX; x++)
        for (int y = 0; y < vMapDimensionY; y++)
            for (int z = 0; z < vMapDimensionZ; z++)
                if(x == 0 || x == (vMapDimensionX - 1) || y == 0 || y == (vMapDimensionY - 1) || z == 0 || z == (vMapDimensionZ - 1))
                    [voxelMap setVoxel: -1  x:x y:y z:z];
                else {
                    [voxelMap setVoxel: +1  x:x y:y z:z];
                    
                    if( (y % 5 > 1  && y % 5 < 4) && ( (x % 5 > 1  && x % 5 < 4) || (z % 5 > 1  && z % 5 < 4) )  )
                        [voxelMap setVoxel: -124  x:x y:y z:z];
                }
    
    [voxelMap calcIso];

A Ball

    YAVoxelMap* voxelMap = [[YAVoxelMap alloc] initDimensions:vMapDimensionX :vMapDimensionY :vMapDimensionZ];
    
    for (int x = 0; x < vMapDimensionX; x++)
        for (int y = 0; y < vMapDimensionY; y++)
            for (int z = 0; z < vMapDimensionZ; z++)
                if(x == 0 || x == (vMapDimensionX - 1) || y == 0 || y == (vMapDimensionY - 1) || z == 0 || z == (vMapDimensionZ - 1))
                    [voxelMap setVoxel: -125  x:x y:y z:z];
                else {
                    const float minDist = (1.0/32.0);
                    const float center = vMapDimensionX / 2;
                    const float dist = sqrt(pow(center - x, 2) + pow(center - y, 2) + pow(center - z, 2));
                    const float max = sqrt(pow(center - vMapDimensionX, 2) + pow(center - vMapDimensionY, 2) + pow(center - vMapDimensionZ, 2));
                    float distPercent = dist / max;
                    
                    char density = (distPercent < 0.5) ? 127 : -127;
                    
                    if(fabs(distPercent - 0.5) < minDist) {
                        float isoDensity = fabs(distPercent - 0.5)/minDist * 125.0;
                        if (distPercent < 0.5)
                            density = MAX(1, isoDensity);
                        else
                            density = MIN(-1,-isoDensity);
                    }
                    [voxelMap setVoxel:density x:x y:y z:z];
                }
    
    [voxelMap calcIso];

A simple terrain

I'm using a simple height map for this example. The values are than mapped like a "bar chart" in the voxel map.

        for(int z = 0; z < vMapDimensionZ; z++)
            for(int x = 0; x < vMapDimensionX; x++) {
                
                float zPercent = (float)z / (float)vMapDimensionZ;
                float xPercent = (float)x / (float)vMapDimensionX;
                
                float f1 = ( sinf( zPercent * 8 * M_PI ) * 0.4 + 0.5 ) * (blockResolution * 5) ;
                float f2 = ( sinf( xPercent * 10 * M_PI ) * 0.4 + 0.5 ) * (maxHeight / 5) ;
                float f3 = (cosf(xPercent * zPercent * 8 * M_PI ) * 0.4 + 0.5) * (maxHeight / 5) ;
                float f4 = ( sinf( pow(zPercent, xPercent) * 8 * M_PI ) * 0.4 + 0.5 ) * (blockResolution * 5) ;
                float f5 = ( cosf( M_PI / tanf(zPercent) * 8 * M_PI ) * 0.4 + 1.5 ) * (blockResolution * 5) ;

                float result = f5/5 + f1 + f2 - f3 + f4;
                field[x][z] = result;
            }

Here is a short video of the terrain:

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.

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.