May 14, 2024, 10:19:46 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Cubic environment mapping

Started by Ionic Wind Support Team, July 19, 2006, 07:03:20 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

kryton9

Thanks Paul that is why I was reluctant to post but did. I am sure with Aurora we will be running circles around other stuff :)

Ionic Wind Support Team

July 22, 2006, 04:41:30 PM #26 Last Edit: July 22, 2006, 09:56:29 PM by Paul Turley
While we are on the subject of timers.  There are four types of timers you can use in your program.  Here are two more similar examples:

#1 Using QueryPerformanceCounter.  The Performance Counter may not be availble on all systems according to Microsoft.  So it is a good idea to check the return value of QueryPerformanceFrequency to be sure. And fall back on timeGetTime if it returns false.  The nice thing about using QPC is it has a resoulution of up to 3 million ticks per second.  Which means you can use double precision calculations for time adjustments.

The example uses a 10 frame average to smooth out camera movements.


//structure used in our restore callback
struct RestObj
{
C3DMesh *pSphere;
C3DMesh *pRoom;
C3DScreen *pScreen;
}
#define DegToRad 0.01745329251995
#define smoothing_frames 10
//Arrow keys to move camera.
//PgUp/PgDn to pan up and down.
//Z and X to rotate on Z axis.

import int QueryPerformanceFrequency(int64 *lpFrequency);
import int QueryPerformanceCounter(int64 *lpPerformanceCount);

global sub main()
{
C3DScreen s;
C3DCamera c;
C3DMesh *room;
C3DMesh sphere;
C3DObject scene;
CDirectInput di;
double fTarget=75.0,fAdjust = 1.0;
int64 freq;
QueryPerformanceFrequency(freq);
double dfreq = freq;

//s.CreateFullScreen(800,600,32,true);
s.CreateWindowed(0,0,800,600,AWS_CAPTION|AWS_VISIBLE|AWS_SIZE|AWS_CENTERED,0,"3D Test - ESC exits",NULL,false);

di.Init(s);
c.Create(s);
c.Position(0,0,-8);
c.Orient(0,0,1,0,1,0);
c.SetBackPlane(500);

s.Clear(RGBA(0,0,0,255));
s.BeginScene(c);
s.RenderText(0,10,"Loading objects...",RGBA(255,255,0,255));
s.RenderScene();

room = CreateRoom(s,20,20,50,null);

sphere.CreateSphere(s,30f,2f,false);
sphere.EnableLighting(false);
sphere.CreateCubeTexture(0,256,"cubemap");
//render a static cubemap
for( x = 0; x < 6; x++)
{
sphere.BeginRenderCubeTexture(0,x,0,0,0);
s.Clear(RGBA(0,0,255,255));
room->Draw();
sphere.EndRenderCubeTexture();
}

//setup the restore callback
RestObj res;
res.pScreen = s;
res.pRoom = room;
res.pSphere = sphere;
s.SetRestoreCallback(&rebuild_room,res);

scene.CreateScene(s);
scene.AddChild(room);
scene.AddChild(sphere);

int fps = 0;
int smoothing = smoothing_frames;
double accumtime = 0.0;
double startTime = 0.0;
int64 iStartTime;
do
{
QueryPerformanceCounter(iStartTime);
startTime = iStartTime;
//check for keys and move camera accordingly
IF di.KeyDown(DIK_UP)
c.Move(0.0f,.25 * fAdjust);
IF di.KeyDown(DIK_DOWN)
c.Move(0.0f,-.25* fAdjust);
IF di.KeyDown(DIK_RIGHT)
c.Rotate(1.0*DegToRad*fAdjust,0,0);
IF di.KeyDown(DIK_LEFT)
c.Rotate(-1.0*DegToRad*fAdjust,0,0);
IF di.KeyDown(DIK_Z)
c.Rotate(0,0,-1.0*DegToRad*fAdjust);
IF di.KeyDown(DIK_X)
c.Rotate(0,0,1.0*DegToRad*fAdjust);
IF di.KeyDown(DIK_PRIOR)
c.Rotate(0,-1.0*DegToRad*fAdjust,0);
IF di.KeyDown(DIK_NEXT)
c.Rotate(0,1.0*DegToRad*fAdjust,0);
IF di.KeyDown(DIK_R)
c.LookAt(0,0,0);

s.Clear(RGBA(0,0,0,255));
s.BeginScene(c);
scene.Draw();
s.RenderText(0,10,"FPS:"+NumToStr(fps),RGBA(255,255,0,255));
s.RenderText(0,30,"Use arrow keys to move around",RGBA(255,255,0,255));
fps = s.RenderScene();

QueryPerformanceCounter(iStartTime);
accumtime += ((iStartTime - startTime) / dfreq) * fTarget;
smoothing--;
if(!smoothing)
{
fAdjust = accumtime / smoothing_frames;
accumtime = 0.0;
smoothing = smoothing_frames;
}


}until di.KeyDown(DIK_ESCAPE);
Scene.Free();
}

sub CreateRoom(C3DScreen *pScreen,float width, float height,float depth,C3DMesh *restore = NULL),C3DMesh *
{
C3DMesh *pRet,*pass2;
if(!restore)
pRet = new(C3DMesh,1)
else
pRet = restore;
pass2 = new(C3DMesh,1);
int i,j,k,index;
//create a box to copy.  Make it an 'inside' cube
C3DMesh temp;
temp.CreateBox(pScreen,width,height,depth,true);
numVertices = temp.GetVertexCount();
numIndices = temp.GetIndexCount();
if(restore)
pRet->Free();
//create the mesh with a vertex format that allows position, a diffuse color and a normal vector + two textures.
pRet->CreateMesh(pScreen,numVertices,numIndices,D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 );
//create the pass2 mesh. It only needs one texture and will be temporary
pass2->CreateMesh(pScreen,numVertices,numIndices,D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1 );
pRet->AddChild(pass2);
//lock the buffers and copy the data
VERTEX2TEXTURE *pVB;
VERTEX1TEXTURE *pVBPass2,*pVBOld;
word *pIB,*pIBOld,*pIBPass2;
pVB = pRet->LockVertexBuffer();
pVBOld = temp.LockVertexBuffer();
pVBPass2 = pass2->LockVertexBuffer();
pIB = pRet->LockIndexBuffer();
pIBOld = temp.LockIndexBuffer();
pIBPass2 = pass2->LockIndexBuffer();

//copy the vertices and 1st texture coordinates.  Duplicate the 2nd
for( j=0; j < numVertices; j++)
{
*pVB[j].position = *pVBOld[j].position;
*pVB[j].normal = *pVBOld[j].normal;
*pVB[j].diffuseColor = *pVBOld[j].diffuseColor;
*pVB[j].texCoords[0] = *pVBOld[j].texCoords[0];
*pVB[j].texCoords[1] = *pVBOld[j].texCoords[0];

*pVBPass2[j].position = *pVBOld[j].position;
*pVBPass2[j].normal = *pVBOld[j].normal;
*pVBPass2[j].diffuseColor = *pVBOld[j].diffuseColor;
*pVBPass2[j].texCoords[0] = *pVBOld[j].texCoords[0];
}

//copy the indices
FOR( k=0; k < numIndices; k++)
{
*pIB[k] = *pIBOld[k];
*pIBPass2[k] = *pIBOld[k];
}

//now rewrite the diffuse colors based on the side
for( i=0; i < 6; i++)//the sides
{
for( j = 0; j < 3; j++)//num of triangles per row
{
for( k =0; k < 3;k++)// num of triangles per column
{
index = i * 3 * 3 + j * 3 + k;
SELECT i
{
CASE 0:
//ceiling
*pVB[index].diffuseColor = RGBA(255,255,255,0);
*pVBPass2[index].diffuseColor = RGBA(0,0,0,0);
CASE 1:
//floor using pass2
*pVBPass2[index].diffuseColor = RGBA(255,255,255,255);
*pVB[index].diffuseColor = 0;
default:
//walls
*pVB[index].diffuseColor = RGBA(0,0,0,255);
*pVBPass2[index].diffuseColor = RGBA(0,0,0,0);
}
}
}
}

//unlock the buffers
pRet->UnlockIndexBuffer();
pRet->UnlockVertexBuffer();
temp.UnlockIndexBuffer();
temp.UnlockVertexBuffer();
pass2->UnlockIndexBuffer();
pass2->UnlockVertexBuffer();

temp.Free();

pRet->SetCulling(CULL_CW);
pass2->SetCulling(CULL_CW);

pRet->LoadTexture(0,GetStartPath() + "media\\tile3.tga",0);
pRet->LoadTexture(1,GetStartPath() + "media\\wall11.jpg",0);
pass2->LoadTexture(0,GetStartPath() + "media\\floor11.jpg",0);
//make it visible
pRet->SetVisible(TRUE);
pass2->SetVisible(TRUE);
pRet->RecalcBoundingBox();
pass2->RecalcBoundingBox();

//since we are using diffuse and alpha for texture blending turn of lighting for the objects
pRet->EnableLighting(FALSE);
pass2->EnableLighting(FALSE);

//setup the pipeline
//PASS 1
//Stage 0 - Color - Modulate the texture with diffuse
//This results in : stage0_color_result = rgb(diffuse)*t1
pRet->SetColorArg1(0,D3DTA_DIFFUSE);
pRet->SetColorArg2(0,D3DTA_TEXTURE);
pRet->SetColorOperation(0,D3DTOP_MODULATE);
//Stage 0 - Alpha - Use diffuse alpha
//This results in : stage0_alpha_result = a
pRet->SetAlphaArg1(0, D3DTA_DIFFUSE);
pRet->SetAlphaOperation(0, D3DTOP_SELECTARG1);
//Stage 1- Color - Modulate alpha add color
//This results in : pass1_color_result = stage0_color_result + stage0_alpha_result*t2
//which is : pass1_color_result = rgb(diffuse)*t1 + a(diffuse)*t2
pRet->SetColorArg1(1,D3DTA_CURRENT);
pRet->SetColorArg2(1,D3DTA_TEXTURE);
pRet->SetColorOperation(1,D3DTOP_MODULATEALPHA_ADDCOLOR);
//This results in : pass1_alpha_result = tile2_alpha
pRet->SetAlphaArg1(1, D3DTA_TEXTURE);
pRet->SetAlphaOperation(1, D3DTOP_SELECTARG1);

//PASS 2
pass2->SetColorArg1(0,D3DTA_DIFFUSE);
pass2->SetColorArg2(0,D3DTA_TEXTURE);
pass2->SetColorOperation(0,D3DTOP_MODULATE);

pass2->SetAlphaArg1(0, D3DTA_DIFFUSE);
pass2->SetAlphaOperation(0, D3DTOP_SELECTARG1);

pass2->EnableAlpha(TRUE);
pass2->SetAlphaOp(D3DBLENDOP_MAX);
pass2->SetAlphaSource(D3DBLEND_SRCALPHA);
pass2->SetAlphaDest(D3DBLEND_SRCALPHA);

//delete our local pass2 object.  The internal mesh used by the engine will
//still be there as a child of our room mesh. It will be freed when the parent mesh is freed.
delete pass2;
return pRet;
}

/*
this function is called by the engine when the d3d device has been reset and restored
Usually during an ALT_TAB switch from fullscreen to the desktop. At this point our vertex
and index buffer were released and need to be recreated.

*/
sub rebuild_room(void *pParam)
{
IF pParam
{
CreateRoom(*(RestObj)pParam.pScreen,20,20,50,*(RestObj)pParam.pRoom);
//cubemaps are not automatically restored like normal textures.  So we need to recreate it also.
*(RestObj)pParam.pSphere->CreateCubeTexture(0,256,"cubemap");
for( x = 0;x <6;x++)
{
*(RestObj)pParam.pSphere->BeginRenderCubeTexture(0,x,0,0,0);
*(RestObj)pParam.pScreen->Clear(RGBA(0,0,255,255));
*(RestObj)pParam.pRoom->Draw();
*(RestObj)pParam.pSphere->EndRenderCubeTexture();
}
}
return;
}


#2.  Using a multimedia timer.   The multimedia timer is a more accurate timer than timeGetTime but still has the limited minimum resoultion of 10ms.  Multimedia timers call a funciton in your program (callback) when they expire.  You can have either a one shot or periodic timer.  This example uses the multimedia timer to control the rendering and input speed to a specific value.  For our purposes we lock the rendering at 100fps with vsync turned off.  This example also uses a bit more oop then the last version. The callback function gets passed a dword user defined value. We use a pointer to our screen object so we can call a method each time the timer fires.


#define DegToRad 0.01745329251995f
//structure used in our restore callback
struct RestObj
{
C3DMesh *pSphere;
C3DMesh *pRoom;
C3DScreen *pScreen;
C3DObject *pScene;
}
//structure and functions for using multimedia timers
#define TIME_ONESHOT    0x0000   /* program timer for single event */
#define TIME_PERIODIC   0x0001   /* program for continuous periodic event */
struct TIMECAPS
{
    UINT wPeriodMin;
    UINT wPeriodMax;
}
import int timeGetDevCaps(TIMECAPS *ptc, UINT cbtc);
import int timeBeginPeriod(UINT uPeriod);
import int timeEndPeriod(UINT uPeriod);
import int timeSetEvent(UINT uDelay,UINT uResolution,UINT lpTimeProc,DWORD dwUser,UINT fuEvent);
import int timeKillEvent(UINT uTimerID);



//Arrow keys to move camera.
//PgUp/PgDn to pan up and down.
//Z and X to rotate on Z axis.

class CCubeScreen:C3DScreen
{
declare OnMMTimer(),int;
declare DoInit(),int;

C3DCamera c;
C3DMesh room;
C3DMesh sphere;
C3DObject scene;
CDirectInput di;
uint m_TimerID;
uint m_TimerRes;

}
int fps = 0;
CCubeScreen::OnMMTimer(),int
{

Clear(RGBA(0,0,0,255));
BeginScene(c);
scene.Draw();
RenderText(0,10,"FPS:"+NumToStr(fps),RGBA(255,255,0,255));
RenderText(0,30,"Use arrow keys to move around",RGBA(255,255,0,255));
fps = RenderScene();
IF di.KeyDown(DIK_UP)
c.Move(0.0f,.25f);
IF di.KeyDown(DIK_DOWN)
c.Move(0.0f,-.25f);
IF di.KeyDown(DIK_RIGHT)
c.Rotate(1f*DegToRad,0,0);
IF di.KeyDown(DIK_LEFT)
c.Rotate(-1f*DegToRad,0,0);
IF di.KeyDown(DIK_Z)
c.Rotate(0,0,-1f*DegToRad);
IF di.KeyDown(DIK_X)
c.Rotate(0,0,1f*DegToRad);
IF di.KeyDown(DIK_PRIOR)
c.Rotate(0,-1f*DegToRad,0);
IF di.KeyDown(DIK_NEXT)
c.Rotate(0,1f*DegToRad,0);
}

CCubeScreen::DoInit(),int
{
di.Init(this);
c.Create(this);
c.Position(0,0,-8);
c.Orient(0,0,1,0,1,0);
c.SetBackPlane(500);

Clear(RGBA(0,0,0,255));
BeginScene(c);
RenderText(0,10,"Loading objects...",RGBA(255,255,0,255));
RenderScene();

room.CreateBox(this,20,20,50,true);
room.SetCulling(CULL_NONE);
room.LoadTexture(0,GetStartPath() + "media\\wall11.jpg",0);
room.EnableLighting(FALSE);

sphere.CreateSphere(this,30f,2f,false);
sphere.EnableLighting(false);
sphere.CreateCubeTexture(0,256,"cubemap");
//render a static cubemap
for( x = 0; x < 6; x++)
{
sphere.BeginRenderCubeTexture(0,x,0,0,0);
Clear(RGBA(0,0,0,255));
room.Draw();
sphere.EndRenderCubeTexture();
}

//setup the restore callback
RestObj res;
res.pScreen = this;
res.pRoom = room;
res.pSphere = sphere;
res.pScene = scene;
SetRestoreCallback(&rebuild_room,res);

scene.CreateScene(this);
scene.AddChild(room);
scene.AddChild(sphere);

//set up the multimedia timer for camera movements
TIMECAPS tc;
m_TimerRes = 0;
timeGetDevCaps(tc,len(tc));
//use a 0ms resolution or the minimum allowed by the system
if(tc.wPeriodMin > m_TimerRes)
m_TimerRes = tc.wPeriodMin;
timeBeginPeriod(m_TimerRes);
//Setup the callback
m_TimerID = timeSetEvent(10,m_TimerRes,&TimerCallback,this,TIME_PERIODIC);

}

sub TimerCallback(UINT wTimerID, UINT msg,DWORD dwUser, DWORD dw1, DWORD dw2)
{
*(CCubeScreen)dwUser.OnMMTimer();
}

global sub main()
{
CCubeScreen s;

//s.CreateFullScreen(800,600,32,false);
s.CreateWindowed(0,0,800,600,AWS_CAPTION|AWS_VISIBLE|AWS_SIZE|AWS_CENTERED,0,"3D Test - ESC exits",NULL,false);
s.DoInit();
int fps = 0;
do
{
wait();
}until s.di.KeyDown(DIK_ESCAPE);
//stop the multimedia timer
timeKillEvent(s.m_TimerID);
timeEndPeriod(s.m_TimerRes);
s.Scene.Free();
}

/*
this function is called by the engine when the d3d device has been reset and restored
Usually during an ALT_TAB switch from fullscreen to the desktop. At this point our vertex
and index buffer were released and need to be recreated.

*/
sub rebuild_room(void *pParam)
{
IF pParam
{
//cubemaps are not automatically restored like normal textures.  So we need to recreate it.
*(RestObj)pParam.pSphere->CreateCubeTexture(0,256,"cubemap");
for(int x = 0;x < 6;x++)
{
*(RestObj)pParam.pSphere->BeginRenderCubeTexture(0,x,0,0,0);
*(RestObj)pParam.pScreen->Clear(RGBA(0,0,0,255));
*(RestObj)pParam.pRoom->Draw();
*(RestObj)pParam.pSphere->EndRenderCubeTexture();
}
*(RestObj)pParam.pScene->AddChild(*(RestObj)pParam.pRoom);
}
return;
}


The forth type of counter I will leave for the user to explore.  It is the time stamp counter of the processor and can be accessed with the single assembly instruction rdtsc.  The time stamp counter used to be a defacto standard with game programmers, until processors with power saving variable frequency cores came into existance.  So using it in a game these days is a no-no if you expect your game to run on laptops.

Ionic Wind Support Team

kryton9

The both worked, but I liked the first one in terms of smoothness. Thanks for the examples. Lots to study in that impressive looking code!!