I have been working on a dynamic LOD system of terrain rendering based on using cubic Bézier patches.
I have done similar previous work on PC (http://jj.iamjunkie.net/pc.php). Now I am trying to create an entire terrain engine system from the ground up for PSP. The model for which I'm basing my terrain engine off of is the method described here:
http://www.gamasutra.com/view/feature/3 ... hp?print=1
I have been making some progress. These are just showing off the terrain being rendered. No specific LOD has been used yet. Just showing off that the ability to subdivide any patch at a certain level is there.
The problem comes now. I have a camera system setup for FPS style of viewing and movement, but in order to have the effect of walking on the terrain I need to get a height value from a patch at any given camera_x, camera_z.
The way I had done cubic Bézier patches on PC was using a basis function:
Code: Select all
P0*t^3 + P1*3t^2(1-t) + P2*3t(1-t)^2 + P3*(1-t)^3
Code: Select all
sceGuPatchDivide(u, v);
sceGumDrawBezier(GU_VERTEX_32BITF|GU_TRANSFORM_3D, 4, 4, 0, bpatchArray[i].nodes);
My problem is, the way the GU strips together the geometry is abstracted away and I seem to be having an issue. I can render the landscape just fine, but the height lookup is incorrect.
Code: Select all
float
getHeightAt(float a_x, float a_z)
{
/* account for map scaling and centering */
a_x /= SCALE_X;
a_z /= SCALE_Z;
a_x += mapCenter;
a_z += mapCenter;
/* calculate the patch lookup index */
/* get current patch corresponding to a_x and a_z */
int i = (int)((floor(a_z / 3) * (mapWidth / 3)) + floor(a_x / 3));
/* 4 control nodes per patch width & height */
a_x /= 4;
a_z /= 4;
float u = a_x - floor(a_x);
float v = a_z - floor(a_z);
pspDebugScreenSetXY(0, 4);
pspDebugScreenPrintf("patch lookup index: %d ", i);
pspDebugScreenSetXY(0, 5);
pspDebugScreenPrintf("num patches: %d ", numPatches);
/* basis function */
/* P0*t^3 + P1*3t^2(1-t) + P2*3t(1-t)^2 + P3*(1-t)^3 */
float result_y = 0.0f;
float u1 = 1.0f - u;
float v1 = 1.0f - v;
float bu[4] = {u*u*u, 3*u1*u*u, 3*u1*u1*u, u1*u1*u1};
float bv[4] = {v*v*v, 3*v1*v*v, 3*v1*v1*v, v1*v1*v1};
int s, t;
for(s = 0; s < 4; s++)
{
for(t = 0; t < 4; t++)
{
result_y += bpatchArray[i].nodes[(s * 4) + t].y * bu[s] * bv[t];
}
}
return result_y;
}
Perhaps there is some stupid error that I am overlooking (I really hope this is the case). I have been continuing to debug this for quite a few days and I am rather stuck. Perhaps some fresh eyes could spot some errors in my methodology. Thanks in advance! :)
bpatch.h
Code: Select all
#ifndef _BPATCH_H_
#define _BPATCH_H_
/* ____________________ S T R U C T S ____________________ */
struct Vertex
{
float x, y, z;
};
struct BPatch
{
struct Vertex nodes[16];
};
#endif
terrain.h
Code: Select all
#ifndef _TERRAIN_H_
#define _TERRAIN_H_
/* ____________________ D E F I N E S ____________________ */
#define SCALE_X 4.0f
#define SCALE_Y 0.5f
#define SCALE_Z 4.0f
/* __________ P U B L I C F U N C T I O N S __________ */
int
loadMap(const char *a_filename);
float
getHeightAt(float a_x, float a_z);
void
renderTerrain(unsigned int a_renderMode);
int
destroyTerrain(void);
#endif
Code: Select all
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <pspgu.h>
#include <pspgum.h>
#include <pspdebug.h>
#include "terrain.h"
#include "bpatch.h"
/* __________ P R I V A T E V A R I A B L E S __________ */
static unsigned int mapWidth, mapHeight, mapSize;
static float mapCenter;
static unsigned int numPatches;
static struct BPatch *bpatchArray;
/* __________ P R I V A T E F U N C T I O N S __________ */
static int
createPatches(unsigned char *a_map);
/* __________ F U N C T I O N S __________ */
int
loadMap(const char *a_filename)
{
FILE * pFile = NULL;
if (!a_filename)
{
return -1;
fclose(pFile);
}
pFile = fopen(a_filename, "rb");
if (!pFile)
{
printf("ERROR: Can't find map file!");
fclose(pFile);
return -1;
}
fseek(pFile, 0, SEEK_END);
mapSize = ftell(pFile);
mapWidth = (int)sqrtf(mapSize);
mapHeight = mapWidth;
mapCenter = (int)(mapWidth / 2);
unsigned char *heightmapData;
heightmapData = malloc(sizeof(unsigned char)*mapSize);
if (heightmapData == NULL)
{
fclose(pFile);
free(heightmapData);
return -1;
}
fseek(pFile, 0, SEEK_SET);
fread(heightmapData, 1, mapSize, pFile );
int result = ferror(pFile);
if (result)
{
printf("ERROR: Failed to get data!");
fclose(pFile);
free(heightmapData);
return -1;
}
fclose(pFile);
numPatches = (int)(mapWidth / 3) * (int)(mapHeight / 3);
//printf("num patches: %d\n", numPatches);
bpatchArray = malloc(sizeof(struct BPatch) * numPatches);
int errorCheck = createPatches(heightmapData);
if (errorCheck != 1)
{
fclose(pFile);
free(heightmapData);
free(bpatchArray);
return -1;
}
free(heightmapData);
return 1;
}
static int
createPatches(unsigned char * a_map)
{
int u, v, x, z, patchIndex = 0, nodeIndex = 0;
for (u = 0; u < mapHeight - 3; u += 3)
{
//printf("u: %d\n", u);
for (v = 0; v < mapWidth - 3; v += 3)
{
//printf("v: %d\n", v);
//patchIndex = (u * mapWidth) + v;
nodeIndex = 0;
for (x = u; x < u + 4; x++)
{
//printf("z: %d\n", z);
for(z = v; z < v + 4; z++)
{
//printf("x: %d\n", x);
bpatchArray[patchIndex].nodes[nodeIndex].x = (float)((x - mapCenter) * SCALE_X);
bpatchArray[patchIndex].nodes[nodeIndex].y = (float)(a_map[(x * mapWidth) + z] * SCALE_Y);
bpatchArray[patchIndex].nodes[nodeIndex].z = (float)((z - mapCenter) * SCALE_Z);
nodeIndex++;
}
}
/*
if (patchIndex == 0)
{
int temp = 0;
for (temp = 0; temp < 16; temp++)
{
printf("patch: %d\n", patchIndex);
printf("node: %d\n", temp);
printf("x: %f\n", bpatchArray[patchIndex].nodes[temp].x);
printf("y: %f\n", bpatchArray[patchIndex].nodes[temp].y);
printf("y: %f\n\n", bpatchArray[patchIndex].nodes[temp].z);
}
}
*/
//printf("patchIndex: %d\n", patchIndex);
//printf("nodeIndex: %d\n\n", nodeIndex);
patchIndex++;
}
}
return 1;
}
float
getHeightAt(float a_x, float a_z)
{
/* account for map scaling and centering */
a_x /= SCALE_X;
a_z /= SCALE_Z;
a_x += mapCenter;
a_z += mapCenter;
/* calculate the patch lookup index */
/* get current patch corresponding to a_x and a_z */
int i = (int)((floor(a_z / 3) * (mapWidth / 3)) + floor(a_x / 3));
/* 4 control nodes per patch width & height */
a_x /= 4;
a_z /= 4;
float u = a_x - floor(a_x);
float v = a_z - floor(a_z);
pspDebugScreenSetXY(0, 4);
pspDebugScreenPrintf("patch lookup index: %d ", i);
pspDebugScreenSetXY(0, 5);
pspDebugScreenPrintf("num patches: %d ", numPatches);
/* basis function */
/* P0*t^3 + P1*3t^2(1-t) + P2*3t(1-t)^2 + P3*(1-t)^3 */
float result_y = 0.0f;
float u1 = 1.0f - u;
float v1 = 1.0f - v;
float bu[4] = {u*u*u, 3*u1*u*u, 3*u1*u1*u, u1*u1*u1};
float bv[4] = {v*v*v, 3*v1*v*v, 3*v1*v1*v, v1*v1*v1};
int s, t;
for(s = 0; s < 4; s++)
{
for(t = 0; t < 4; t++)
{
result_y += bpatchArray[i].nodes[(s * 4) + t].y * bu[s] * bv[t];
}
}
return result_y;
}
void
renderTerrain(unsigned int a_renderMode)
{
//sceGuPatchDivide(4, 4);
if (a_renderMode == 0)
sceGuPatchPrim(GU_POINTS);
else if (a_renderMode == 1)
sceGuPatchPrim(GU_LINE_STRIP);
else if (a_renderMode == 2)
sceGuPatchPrim(GU_TRIANGLE_STRIP);
//sceGuColor(0xffffffff);
/* just some coloring stuff and strip stuff at different subdivide levels for testing */
int i, colorIndex = 0, u = 4, v = 4;
for (i = 0; i < numPatches; i++)
{
if (colorIndex == 0) /* red */
{
u = 8;
v = 8;
sceGuColor(0x000000ff);
}
if (colorIndex == 1) /* green */
{
u = 4;
v = 4;
sceGuColor(0x0000ff00);
}
if (colorIndex == 2) /* blue */
{
u = 6;
v = 6;
sceGuColor(0x00ff0000);
}
sceGuPatchDivide(u, v);
sceGumDrawBezier(GU_VERTEX_32BITF|GU_TRANSFORM_3D, 4, 4, 0, bpatchArray[i].nodes);
if (colorIndex == 2)
{
colorIndex = -1;
}
colorIndex++;
}
}
int
destroyTerrain(void)
{
free(bpatchArray);
return 1;
}