[SOLVED]Bézier Patches on PSP

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
User avatar
Calv!n
Posts: 10
Joined: Mon Aug 13, 2007 2:18 am
Contact:

[SOLVED]Bézier Patches on PSP

Post by Calv!n »

Greetings everyone.

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.
Image
Image

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
The PSP has a built in render call for drawing bpatches.

Code: Select all

sceGuPatchDivide(u, v);
sceGumDrawBezier(GU_VERTEX_32BITF|GU_TRANSFORM_3D, 4, 4, 0, bpatchArray[i].nodes);
The first call will tell the GU to what level to subdivide the patch. The second function does the rendering of the patch. 4x4 nodes per patch.

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&#40;s = 0; s < 4; s++&#41;
	&#123;
		for&#40;t = 0; t < 4; t++&#41;
		&#123;
			result_y += bpatchArray&#91;i&#93;.nodes&#91;&#40;s * 4&#41; + t&#93;.y * bu&#91;s&#93; * bv&#91;t&#93;;
		&#125;
	&#125;
	return result_y;
&#125;
I used this same basis function for PC for stripping together my vertices, and I used the first order derivative of this basis function for normals (lighting) calculations and it all worked out fine on PC.

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
&#123;
	float x, y, z;
&#125;;

struct BPatch
&#123;
	struct Vertex nodes&#91;16&#93;;
&#125;;

#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&#40;const char *a_filename&#41;;

float
getHeightAt&#40;float a_x, float a_z&#41;;

void
renderTerrain&#40;unsigned int a_renderMode&#41;;

int
destroyTerrain&#40;void&#41;;

#endif
terrain.c

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&#40;unsigned char *a_map&#41;;


/* __________ F U N C T I O N S __________ */
int
loadMap&#40;const char *a_filename&#41;
&#123;
	FILE * pFile = NULL;
	
	if &#40;!a_filename&#41;
	&#123;
		return -1;
		fclose&#40;pFile&#41;;
	&#125;
	
	pFile = fopen&#40;a_filename, "rb"&#41;;
	if &#40;!pFile&#41;
	&#123;
		printf&#40;"ERROR&#58; Can't find map file!"&#41;;
		fclose&#40;pFile&#41;;
		return -1;
	&#125;
	
	fseek&#40;pFile, 0, SEEK_END&#41;;
	mapSize = ftell&#40;pFile&#41;;
	
	mapWidth = &#40;int&#41;sqrtf&#40;mapSize&#41;;
	mapHeight = mapWidth;
	mapCenter = &#40;int&#41;&#40;mapWidth / 2&#41;;
	
	unsigned char *heightmapData;
	heightmapData = malloc&#40;sizeof&#40;unsigned char&#41;*mapSize&#41;;
	if &#40;heightmapData == NULL&#41;
	&#123;
		fclose&#40;pFile&#41;;
		free&#40;heightmapData&#41;;
		return -1;
	&#125;
	
	fseek&#40;pFile, 0, SEEK_SET&#41;;
	fread&#40;heightmapData, 1, mapSize, pFile &#41;;
 
	int result = ferror&#40;pFile&#41;;
	if &#40;result&#41;
	&#123;
		printf&#40;"ERROR&#58; Failed to get data!"&#41;;
		fclose&#40;pFile&#41;;
		free&#40;heightmapData&#41;;
		return -1;
	&#125;
	
	fclose&#40;pFile&#41;;
	
	numPatches = &#40;int&#41;&#40;mapWidth / 3&#41; * &#40;int&#41;&#40;mapHeight / 3&#41;;
	//printf&#40;"num patches&#58; %d\n", numPatches&#41;;
	bpatchArray = malloc&#40;sizeof&#40;struct BPatch&#41; * numPatches&#41;;
	
	int errorCheck = createPatches&#40;heightmapData&#41;;
	if &#40;errorCheck != 1&#41;
	&#123;
		fclose&#40;pFile&#41;;
		free&#40;heightmapData&#41;;
		free&#40;bpatchArray&#41;;
		return -1;
	&#125;
	
	free&#40;heightmapData&#41;;
	return 1;
&#125;

static int
createPatches&#40;unsigned char * a_map&#41;
&#123;
	int u, v, x, z, patchIndex = 0, nodeIndex = 0;
	for &#40;u = 0; u < mapHeight - 3; u += 3&#41;
	&#123;
		//printf&#40;"u&#58; %d\n", u&#41;;
		for &#40;v = 0; v < mapWidth - 3; v += 3&#41;
		&#123;
			//printf&#40;"v&#58; %d\n", v&#41;;
			//patchIndex = &#40;u * mapWidth&#41; + v;
			nodeIndex = 0;
			for &#40;x = u; x < u + 4; x++&#41;
			&#123;
				//printf&#40;"z&#58; %d\n", z&#41;;
				for&#40;z = v; z < v + 4; z++&#41;
				&#123;
					//printf&#40;"x&#58; %d\n", x&#41;;
					bpatchArray&#91;patchIndex&#93;.nodes&#91;nodeIndex&#93;.x = &#40;float&#41;&#40;&#40;x - mapCenter&#41; * SCALE_X&#41;;
					bpatchArray&#91;patchIndex&#93;.nodes&#91;nodeIndex&#93;.y = &#40;float&#41;&#40;a_map&#91;&#40;x * mapWidth&#41; + z&#93; * SCALE_Y&#41;;
					bpatchArray&#91;patchIndex&#93;.nodes&#91;nodeIndex&#93;.z = &#40;float&#41;&#40;&#40;z - mapCenter&#41; * SCALE_Z&#41;;
					nodeIndex++;
				&#125;
			&#125;
			/*
			if &#40;patchIndex == 0&#41;
			&#123;
				int temp = 0;
				for &#40;temp = 0; temp < 16; temp++&#41;
				&#123;
					printf&#40;"patch&#58; %d\n", patchIndex&#41;;
					printf&#40;"node&#58; %d\n", temp&#41;;
					printf&#40;"x&#58; %f\n", bpatchArray&#91;patchIndex&#93;.nodes&#91;temp&#93;.x&#41;;
					printf&#40;"y&#58; %f\n", bpatchArray&#91;patchIndex&#93;.nodes&#91;temp&#93;.y&#41;;
					printf&#40;"y&#58; %f\n\n", bpatchArray&#91;patchIndex&#93;.nodes&#91;temp&#93;.z&#41;;
				&#125;
			&#125;
			*/
			//printf&#40;"patchIndex&#58; %d\n", patchIndex&#41;;
			//printf&#40;"nodeIndex&#58; %d\n\n", nodeIndex&#41;;
			patchIndex++;
		&#125;
	&#125;
	return 1;
&#125;

float
getHeightAt&#40;float a_x, float a_z&#41;
&#123;
	/* 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 = &#40;int&#41;&#40;&#40;floor&#40;a_z / 3&#41; * &#40;mapWidth / 3&#41;&#41; + floor&#40;a_x / 3&#41;&#41;;
	
	/* 4 control nodes per patch width & height */
	a_x /= 4;
	a_z /= 4;

	float u = a_x - floor&#40;a_x&#41;;
	float v = a_z - floor&#40;a_z&#41;;
	
	pspDebugScreenSetXY&#40;0, 4&#41;;
	pspDebugScreenPrintf&#40;"patch lookup index&#58; %d ", i&#41;;
	pspDebugScreenSetXY&#40;0, 5&#41;;
	pspDebugScreenPrintf&#40;"num patches&#58; %d ", numPatches&#41;;
	
	/* basis function */
	/* P0*t^3 + P1*3t^2&#40;1-t&#41; + P2*3t&#40;1-t&#41;^2 + P3*&#40;1-t&#41;^3 */
	float result_y = 0.0f;
	float u1 = 1.0f - u;
	float v1 = 1.0f - v;
	float bu&#91;4&#93; = &#123;u*u*u, 3*u1*u*u, 3*u1*u1*u, u1*u1*u1&#125;;
	float bv&#91;4&#93; = &#123;v*v*v, 3*v1*v*v, 3*v1*v1*v, v1*v1*v1&#125;;
	
	int s, t;
	for&#40;s = 0; s < 4; s++&#41;
	&#123;
		for&#40;t = 0; t < 4; t++&#41;
		&#123;
			result_y += bpatchArray&#91;i&#93;.nodes&#91;&#40;s * 4&#41; + t&#93;.y * bu&#91;s&#93; * bv&#91;t&#93;;
		&#125;
	&#125;
	return result_y;
&#125;

void
renderTerrain&#40;unsigned int a_renderMode&#41;
&#123;
	//sceGuPatchDivide&#40;4, 4&#41;;
	if &#40;a_renderMode == 0&#41;
		sceGuPatchPrim&#40;GU_POINTS&#41;;
	else if &#40;a_renderMode == 1&#41;
		sceGuPatchPrim&#40;GU_LINE_STRIP&#41;;
	else if &#40;a_renderMode == 2&#41;
		sceGuPatchPrim&#40;GU_TRIANGLE_STRIP&#41;;
	//sceGuColor&#40;0xffffffff&#41;;
	/* just some coloring stuff and strip stuff at different subdivide levels for testing */
	int i, colorIndex = 0, u = 4, v = 4;
	for &#40;i = 0; i < numPatches; i++&#41;
	&#123;
		if &#40;colorIndex == 0&#41;	/* red */
		&#123;
			u = 8;
			v = 8;
			sceGuColor&#40;0x000000ff&#41;;
		&#125;
		if &#40;colorIndex == 1&#41;	/* green */
		&#123;
			u = 4;
			v = 4;
			sceGuColor&#40;0x0000ff00&#41;;
		&#125;
		if &#40;colorIndex == 2&#41;	/* blue */
		&#123;
			u = 6;
			v = 6;
			sceGuColor&#40;0x00ff0000&#41;;
		&#125;
		sceGuPatchDivide&#40;u, v&#41;;
		sceGumDrawBezier&#40;GU_VERTEX_32BITF|GU_TRANSFORM_3D, 4, 4, 0, bpatchArray&#91;i&#93;.nodes&#41;;
		if &#40;colorIndex == 2&#41;
		&#123;
			colorIndex = -1;
		&#125;
		colorIndex++;
	&#125;
&#125;

int
destroyTerrain&#40;void&#41;
&#123;
	free&#40;bpatchArray&#41;;
	return 1;
&#125;
Last edited by Calv!n on Sat Aug 01, 2009 5:42 am, edited 2 times in total.
slasher2661996
Posts: 91
Joined: Sun Feb 22, 2009 8:32 am
Location: Melbourne Australia ZOMG

Post by slasher2661996 »

wicked engine, can i have the current source?
User avatar
Calv!n
Posts: 10
Joined: Mon Aug 13, 2007 2:18 am
Contact:

Post by Calv!n »

slasher2661996 wrote:wicked engine, can i have the current source?
I'm not sure why you would need that. I don't give out full source to my engine, especially for no good reason.
User avatar
Calv!n
Posts: 10
Joined: Mon Aug 13, 2007 2:18 am
Contact:

Post by Calv!n »

Well, its finally fixed now. I rewrote most of it and then a friend had a look-see to spot a last couple of fixes.

I'll post some screens later on once the dynamic LOD has been implemented and i apply some texturing.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

Cool, looking forward to it... and get out of the rain! Or at least, get an umbrella. ;)
slasher2661996
Posts: 91
Joined: Sun Feb 22, 2009 8:32 am
Location: Melbourne Australia ZOMG

Post by slasher2661996 »

I didn't mean the source, i mean't i working binary that i could use, but no biggie :D
User avatar
Calv!n
Posts: 10
Joined: Mon Aug 13, 2007 2:18 am
Contact:

Post by Calv!n »

Well you asked for the source so what else was I supposed to assume? ;)

The LOD is working.

Image
Image
Image

LOD
---------
green = 16 tris / patch (low LOD - anything further than 250 meters from viewpoint)
blue = 36 tris / patch (medium LOD - up tp 250 meters from viewpoint)
red = 48 tris / patch (high LOD - up to 25 meters from viewpoint)
imhotep
Posts: 41
Joined: Tue Dec 13, 2005 9:15 pm

Post by imhotep »

what are you doing this for? for you or for the public? would be cool to have this included into the sdk :p
a_noob
Posts: 97
Joined: Sun Sep 17, 2006 8:33 am
Location: _start: jr 0xDEADBEEF

Post by a_noob »

Calvin, of course I like your work as always, but you need to realize that the psp is usually handicapped when it comes to rendering large amounts of scenery, so a MipMap idea would be more suited, where you have 3 or so levels of detail already built out, and you just toggle a flag for which level to render. I'd like to know the FPS on this, along with how the gu is setup.

Code: Select all

.øOº'ºOø.
'ºOo.oOº'
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

You sorta use sceGuPatchDivide to set the level of details as it is GE which will subdivide your nodes into a 2 x udiv x vdiv vertex list to draw. Is it your intention for getHeightAt not to be aware of the level of details? If you really want to have a more accurate height, you need to take into account udiv and vdiv of your bpatch and if it is not accurate enough, you also need to take the intersection of your Y axis on the closest triangle computed by your function.

Or am I wrong somewhere ?
User avatar
Calv!n
Posts: 10
Joined: Mon Aug 13, 2007 2:18 am
Contact:

Post by Calv!n »

a_noob: I'm not sure what your point is. What you described is the essence of this project.

hlide: the bezier patches are the raw data. When you desire a point from the point list of nodes it doesn't matter what LOD you get it at. The function getHeightAt() will retrieve a point on the surface at any given percentage across u, v on the patch.

The render function just takes the point cloud of nodes and go through each path and subdivides accordingly on the GE.

When I work on this further I will post more results.
Post Reply