I tried another approach, thinking that my array data wasn't being returned by the method I tried earlier. What I did was I first made float* groundDataIn into a class member, so now it's in my @interface declaration, like this:
Code: Select all
@interface RocketPenguinTestView : Isgl3dBasic3DView {
...
float* groundDataIn;
}
EDIT: I changed my implementation with the following methods
New init: method:
Code: Select all
- (id) init {
if ((self = [super init])) {
_physicsObjects = [[NSMutableArray alloc] init];
_lastStepTime = [[NSDate alloc] init];
srandom(time(NULL));
// Create and configure touch-screen camera controller
_cameraController = [[Isgl3dDemoCameraController alloc] initWithCamera:self.camera andView:self];
_cameraController.orbit = 16;
_cameraController.theta = 30;
_cameraController.phi = 30;
_cameraController.doubleTapEnabled = NO;
// Enable shadow rendering
[Isgl3dDirector sharedInstance].shadowRenderingMethod = Isgl3dShadowPlanar;
[Isgl3dDirector sharedInstance].shadowAlpha = 0.4;
// Create physics world with discrete dynamics
_collisionConfig = new btDefaultCollisionConfiguration();
_broadphase = new btDbvtBroadphase();
_collisionDispatcher = new btCollisionDispatcher(_collisionConfig);
_constraintSolver = new btSequentialImpulseConstraintSolver;
_discreteDynamicsWorld = new btDiscreteDynamicsWorld(_collisionDispatcher, _broadphase, _constraintSolver, _collisionConfig);
_discreteDynamicsWorld->setGravity(btVector3(0,-10,0));
_physicsWorld = [[Isgl3dPhysicsWorld alloc] init];
[_physicsWorld setDiscreteDynamicsWorld:_discreteDynamicsWorld];
[self.scene addChild:_physicsWorld];
// Create textures
_beachBallMaterial = [[Isgl3dTextureMaterial alloc] initWithTextureFile:@"BeachBall.png" shininess:0.9 precision:TEXTURE_MATERIAL_MEDIUM_PRECISION repeatX:NO repeatY:NO];
_isglLogo = [[Isgl3dTextureMaterial alloc] initWithTextureFile:@"cardboard.jpg" shininess:0.9 precision:TEXTURE_MATERIAL_MEDIUM_PRECISION repeatX:NO repeatY:NO];
// The next is for the terrain heightmap.
Isgl3dTextureMaterial * textureMaterial = [[Isgl3dTextureMaterial alloc] initWithTextureFile:@"RaceTrack1Terrain_1024.png" shininess:0 precision:TEXTURE_MATERIAL_MEDIUM_PRECISION repeatX:NO repeatY:NO];
float radius = 1.0;
float width = 2.0;
_sphereMesh = [[Isgl3dSphere alloc] initWithGeometry:radius longs:16 lats:16];
_cubeMesh = [[Isgl3dCube alloc] initWithGeometry:width height:width depth:width nx:2 ny:2];
// Create two nodes for the different meshes
_cubesNode = [[_physicsWorld createNode] retain];
_spheresNode = [[_physicsWorld createNode] retain];
// Create the terrain mesh
Isgl3dTerrainMesh * terrainMesh = [[Isgl3dTerrainMesh alloc] initWithTerrainDataFile:@"RaceTrack1Path_512.png" channel:2 width:32 depth:32 height:10 nx:32 nz:32];
[self createTerrainWithMesh:terrainMesh andMaterial:textureMaterial];
// Add light
_light = [[Isgl3dShadowCastingLight alloc] initWithHexColor:@"111111" diffuseColor:@"FFFFFF" specularColor:@"FFFFFF" attenuation:0.003];
[self.scene addChild:_light];
[_light setTranslation:10 y:20 z:10];
_light.planarShadowsNode = _terrainPhysicsObject.node;
[self setSceneAmbient:@"666666"];
// Schedule updates
[self schedule:@selector(tick:)];
}
return self;
}
New createTerrainWithMesh: method:
Code: Select all
-(void)createTerrainWithMesh:(Isgl3dTerrainMesh*)mesh andMaterial:(Isgl3dTextureMaterial*)material {
// Create UIImage
UIImage * terrainDataImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"RaceTrack1Path_512" ofType:@"png"]];
//Get the raw data of the material image.
/*
* Note we specify 4 bytes per pixel here even though we ignore the
* alpha value; you can't specify 3 bytes per-pixel.
*/
unsigned int imageWidth = terrainDataImage.size.width;
unsigned int imageHeight = terrainDataImage.size.height;
unsigned char * pixelData = (unsigned char*)malloc(imageWidth * imageHeight * 4);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixelData, imageWidth, imageHeight, 8, imageWidth * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextClearRect(context, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), terrainDataImage.CGImage);
CGContextRelease(context);
// Create array of heights
groundDataIn = (float*)malloc(imageWidth * imageHeight * sizeof(float));
// Iterate once to get all terrain data needed in a simple array
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
groundDataIn[j * imageWidth + i] = pixelData[(j * imageWidth + i) * 4 + 2] / 255.0;
printf("Data at index %d: %f \n", j * imageWidth + i, groundDataIn[j * imageWidth + i]);
}
}
free (pixelData);
//Add the mesh to the physics world
Isgl3dMeshNode * node = [_terrain createNodeWithMesh:mesh andMaterial:material];
//Create the collision shape
btCollisionShape* groundShape = new btHeightfieldTerrainShape(512,512, groundDataIn, 1.0f, 0.0f, 10.0f, 2, PHY_FLOAT, false);
[self createPhysicsObject:node shape:groundShape mass:0 restitution:0.6 isFalling:NO];
node.enableShadowCasting = YES;
_terrain = [_physicsWorld createNodeWithMesh:mesh andMaterial:material];
}
The printf statement confirmed that the groundDataIn array of floats has perfectly valid values in it. Also, the following line used have groundDataIn typecast as a float when being passed in, but a look at the TerrainDemo example showed that it's unnecessary.
Code: Select all
btCollisionShape* groundShape = new btHeightfieldTerrainShape(512,512, groundDataIn, 1.0f, 0.0f, 10.0f, 2, PHY_FLOAT, false);
However, there's still no collision detection between the terrain and anything else. What am I doing wrong? I'm at a loss and can't figure this out at all. Argh!
Original code
Adding it as a class method without merging the code from my getPixelDataFromImage: method didn't help matters, so I suspected that my array wasn't being returned properly. Therefore, I took the code from my method and added it to my init: method, like so:
Code: Select all
//Test new approach to populating terrain collision object array
UIImage *terrainImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"RaceTrack1Path_512" ofType:@"png"]];
[terrainImage retain];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
/*
* Note we specify 4 bytes per pixel here even though we ignore the
* alpha value; you can't specify 3 bytes per-pixel.
*/
unsigned int imageWidth = terrainImage.size.width;
unsigned int imageHeight = terrainImage.size.height;
size_t bytesPerRow = terrainImage.size.width * 4;
unsigned char * imgData = (unsigned char*)malloc(terrainImage.size.height*bytesPerRow);
CGContextRef context = CGBitmapContextCreate(imgData, terrainImage.size.width, terrainImage.size.height, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextClearRect(context, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight),
terrainImage.CGImage);
CGContextRelease(context);
groundDataIn = (float*)malloc(imageWidth * imageHeight * sizeof(float));
// Iterate once to get all terrain data needed in a simple array
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
groundDataIn[j * imageWidth + i] = imgData[(j * imageWidth + i) * 4 + 2] / 255.0;
}
}
free (imgData);
[terrainImage release];
btCollisionShape* groundShape = new btHeightfieldTerrainShape(512,512, (float*)groundDataIn, 1.0f, 0.0f, 10.0f, 2, PHY_FLOAT, false);
Unfortunately, the height field still doesn't have a working collision mesh. Other objects, such as balls and boxes, simply pass through my heightfield without colliding. Where did I go wrong? Is my array interator wrong? Did I mess up the terrain image stuff? Is there an error somewhere else in the code that I didn't think of?
BTW, I tried looking through the TerrainDemo, but I didn't get much out of it since it doesn't load a heightmap from an image file like I'm trying to do.