/******************************Module*Header*******************************\ * Module Name: genwin2.c * * The new Windows style of the 3D Flying Objects screen saver. * * Texture maps .BMP files onto a simulation of a flag waving in the breeze. * * Copyright (c) 2001 Microsoft Corporation * \**************************************************************************/ #include #include #include #include #include #include "D3DSaver.h" #include "FlyingObjects.h" #include "resource.h" #include "mesh.h" enum STATE { S_FREE, S_MOVETOORIGIN, S_FADETOCOLOR, S_PAUSE, S_FADEFROMCOLOR }; #define TIME_FREE 10.0f #define TIME_FADETOCOLOR 1.0f #define TIME_PAUSE 5.0f #define TIME_FADEFROMCOLOR 1.0f // Note: There's no TIME_MOVETOORIGIN since that takes a variable amt of time. const FLOAT winTotalwidth = (FLOAT)0.75; const FLOAT winTotalheight = (FLOAT)0.75; #define MAX_FRAMES 20 // IPREC is the number of faces in the mesh that models the flag. #define IPREC 35 static int Frames = 10; static MESH winMesh[MAX_FRAMES]; static FLOAT sinAngle = (FLOAT)0.0; static FLOAT xTrans = (FLOAT)0.0; // Material properties static RGBA matlBrightSpecular = {1.0f, 1.0f, 1.0f, 1.0f}; // Lighting properties static FLOAT light0Pos[] = {-15.0f, 0.0f, -10.0f}; /******************************Public*Routine******************************\ * iPtInList * * Add a vertex and its normal to the mesh. If the vertex already exists, * add in the normal to the existing normal (we to accumulate the average * normal at each vertex). Normalization of the normals is the * responsibility of the caller. * \**************************************************************************/ static int iPtInList(MESH *mesh, int start, POINT3D *p, POINT3D *norm, BOOL blend) { int i; POINT3D *pts = mesh->pts + start; if (blend) { for (i = start; i < mesh->numPoints; i++, pts++) { if ((pts->x == p->x) && (pts->y == p->y) && (pts->z == p->z)) { mesh->norms[i].x += norm->x; mesh->norms[i].y += norm->y; mesh->norms[i].z += norm->z; return i; } } } else { i = mesh->numPoints; } mesh->pts[i] = *p; mesh->norms[i] = *norm; mesh->numPoints++; return i; } /******************************Public*Routine******************************\ * getZpos * * Get the z-position (depth) of the "wavy" flag component at the given x. * * The function used to model the wave is: * * 1/2 * z = x * sin((2*PI*x + sinAngle) / 8) * * The shape of the wave varies from frame to frame by changing the * phase, sinAngle. * \**************************************************************************/ static FLOAT getZpos(FLOAT x) { FLOAT xAbs = x - xTrans; FLOAT angle = sinAngle + ((FLOAT) (2.0 * PI) * (xAbs / winTotalwidth)); xAbs = winTotalwidth - xAbs; return (FLOAT)(-(sin((double)angle) / 8.0) * sqrt((double)(xAbs / winTotalwidth ))); } /******************************Public*Routine******************************\ * genTex * * Generate a mesh representing a frame of the flag. The phase, sinAngle, * is a global variable. * \**************************************************************************/ static BOOL genTex(MESH *winMesh) { POINT3D pos; POINT3D pts[4]; FLOAT w, h; int i; if( !newMesh(winMesh, IPREC * IPREC, IPREC * IPREC) ) return FALSE; // Width and height of each face w = (winTotalwidth) / (FLOAT)(IPREC + 1); h = winTotalheight; // Generate the mesh data. At equally spaced intervals along the x-axis, // we compute the z-position of the flag surface. pos.y = (FLOAT) 0.0; pos.z = (FLOAT) 0.0; for (i = 0, pos.x = xTrans; i < IPREC; i++, pos.x += w) { int faceCount = winMesh->numFaces; pts[0].x = (FLOAT)pos.x; pts[0].y = (FLOAT)(pos.y); pts[0].z = getZpos(pos.x); pts[1].x = (FLOAT)pos.x; pts[1].y = (FLOAT)(pos.y + h); pts[1].z = getZpos(pos.x); pts[2].x = (FLOAT)(pos.x + w); pts[2].y = (FLOAT)(pos.y); pts[2].z = getZpos(pos.x + w); pts[3].x = (FLOAT)(pos.x + w); pts[3].y = (FLOAT)(pos.y + h); pts[3].z = getZpos(pos.x + w); // Compute the face normal. ss_calcNorm(&winMesh->faces[faceCount].norm, pts + 2, pts + 1, pts); // Add the face to the mesh. winMesh->faces[faceCount].material = 0; winMesh->faces[faceCount].p[0] = iPtInList(winMesh, 0, pts, &winMesh->faces[faceCount].norm, TRUE); winMesh->faces[faceCount].p[1] = iPtInList(winMesh, 0, pts + 1, &winMesh->faces[faceCount].norm, TRUE); winMesh->faces[faceCount].p[2] = iPtInList(winMesh, 0, pts + 2, &winMesh->faces[faceCount].norm, TRUE); winMesh->faces[faceCount].p[3] = iPtInList(winMesh, 0, pts + 3, &winMesh->faces[faceCount].norm, TRUE); winMesh->numFaces++; } // Normalize the vertex normals in the mesh. ss_normalizeNorms(winMesh->norms, winMesh->numPoints); return TRUE; } /******************************Public*Routine******************************\ * initWin2Scene * * Initialize the screen saver. * * This function is exported to the main module in ss3dfo.c. * \**************************************************************************/ BOOL initWin2Scene() { int i; FLOAT angleDelta; SetProjectionMatrixInfo( TRUE, 2.0f, 2.0f, 0.0f, 3.0f ); D3DXMATRIX matView; D3DXMatrixTranslation(&matView, -0.17f, -0.04f, 1.5f); m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); // Adjust position of light 0 D3DLIGHT8 light; m_pd3dDevice->GetLight(0, &light); light.Position.x = light0Pos[0]; light.Position.y = light0Pos[1]; light.Position.z = light0Pos[2]; m_pd3dDevice->SetLight(0, &light); m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE); m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP ); Frames = MAX_FRAMES; // Generate the geometry data (stored in the array of mesh structures), // for each frame of the animation. The shape of the flag is varied by // changing the global variable sinAngle. angleDelta = (FLOAT)(2.0 * PI) / (FLOAT)Frames; sinAngle = (FLOAT) 0.0; for (i = 0; i < Frames; i++) { if( !genTex(&winMesh[i]) ) return FALSE; sinAngle += angleDelta; } return TRUE; } /******************************Public*Routine******************************\ * delWin2Scene * * Cleanup the data associated with this screen saver. * * This function is exported to the main module in ss3dfo.c. * \**************************************************************************/ void delWin2Scene() { int i; for (i = 0; i < Frames; i++) delMesh(&winMesh[i]); } /******************************Public*Routine******************************\ * updateWin2Scene * * Generate a scene by taking one of the meshes and rendering it * * This function is exported to the main module in ss3dfo.c. * \**************************************************************************/ void updateWin2Scene(int flags, FLOAT fElapsedTime) { MESH *pMesh; static double mxrot = 40.0; static double myrot = 0; static double mzrot = -12.0; static int frameNum = 0; static FLOAT fFrameNum = (FLOAT)Frames; FLOAT s = 0.0f; FLOAT ds; static FLOAT s_fTime = 0.0f; static FLOAT s_fTimeLastChange = 0.0f; static FLOAT s_fTimeNextChange = TIME_FREE; static STATE s_state = S_FREE; FLOAT fBeta; if( fElapsedTime > 0.25f ) fElapsedTime = 0.25f; FLOAT fTimeFactor = fElapsedTime * 20.0f; HRESULT hr; s_fTime += fElapsedTime; if( s_fTimeNextChange != -1.0f && s_fTime > s_fTimeNextChange ) { // Handle state transitions s_fTimeLastChange = s_fTime; switch( s_state ) { case S_FREE: s_state = S_MOVETOORIGIN; g_bMoveToOrigin = TRUE; s_fTimeNextChange = -1.0f; break; case S_MOVETOORIGIN: s_state = S_FADETOCOLOR; s_fTimeNextChange = s_fTime + TIME_FADETOCOLOR; break; case S_FADETOCOLOR: s_state = S_PAUSE; s_fTimeNextChange = s_fTime + TIME_PAUSE; break; case S_PAUSE: s_state = S_FADEFROMCOLOR; s_fTimeNextChange = s_fTime + TIME_FADEFROMCOLOR; break; case S_FADEFROMCOLOR: s_state = S_FREE; s_fTimeNextChange = s_fTime + TIME_FREE; g_bMoveToOrigin = FALSE; break; } } fBeta = 0.0f; // Handle state processing switch( s_state ) { case S_MOVETOORIGIN: if( g_bAtOrigin && frameNum == 0) s_fTimeNextChange = s_fTime; // provoke state change next time break; case S_FADETOCOLOR: fBeta = (s_fTime - s_fTimeLastChange) / TIME_FADETOCOLOR; break; case S_PAUSE: fBeta = 1.0f; break; case S_FADEFROMCOLOR: fBeta = 1.0f - ( (s_fTime - s_fTimeLastChange) / TIME_FADEFROMCOLOR ); break; } if( fBeta != 0.0f ) { // Render background logo MYVERTEX3 v[4]; FLOAT fLeft = g_pFloatRect->xMin - g_xScreenOrigin; FLOAT fRight = fLeft + g_pFloatRect->xSize; FLOAT fBottom = g_pFloatRect->yMin - g_yScreenOrigin; FLOAT fTop = g_pFloatRect->yMin + g_pFloatRect->ySize; DWORD dwColor = D3DXCOLOR( 1.0f, 1.0f, 1.0f, fBeta ); v[0].p = D3DXVECTOR3(fLeft, fBottom, 0.9f); v[0].rhw = 0.1f; v[0].dwDiffuse = dwColor; v[0].tu = 0.0f; v[0].tv = 0.0f; v[1].p = D3DXVECTOR3(fRight, fBottom, 0.9f); v[1].rhw = 0.1f; v[1].dwDiffuse = dwColor; v[1].tu = 1.0f; v[1].tv = 0.0f; v[2].p = D3DXVECTOR3(fLeft, fTop, 0.9f); v[2].rhw = 0.1f; v[2].dwDiffuse = dwColor; v[2].tu = 0.0f; v[2].tv = 1.0f; v[3].p = D3DXVECTOR3(fRight, fTop, 0.9f); v[3].rhw = 0.1f; v[3].dwDiffuse = dwColor; v[3].tu = 1.0f; v[3].tv = 1.0f; hr = m_pd3dDevice->SetTexture( 0, g_pDeviceObjects->m_pTexture2 ); hr = m_pd3dDevice->SetVertexShader( D3DFVF_MYVERTEX3 ); hr = m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, v, sizeof(MYVERTEX3) ); } m_pd3dDevice->SetTexture( 0, g_pDeviceObjects->m_pTexture ); D3DXMATRIX mat1, mat2, mat3, mat4, matFinal; D3DXMatrixRotationX(&mat1, D3DXToRadian((FLOAT)mxrot)); D3DXMatrixRotationY(&mat2, D3DXToRadian((FLOAT)myrot)); D3DXMatrixRotationZ(&mat3, D3DXToRadian((FLOAT)mzrot)); D3DXMatrixScaling( &mat4, 0.82f, 0.92f, 0.82f ); matFinal = mat4 * mat3 * mat2 * mat1 ; m_pd3dDevice->SetTransform( D3DTS_WORLD, &matFinal ); // Divide the texture into IPREC slices. ds is the texture coordinate // delta we apply as we move along the x-axis. ds = (FLOAT)1.0 / (FLOAT)IPREC; // Setup the material property of the flag. The material property, light // properties, and polygon orientation will interact with the texture. myglMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (FLOAT *) &matlBrightSpecular); myglMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, (FLOAT) 40.0); FLOAT fColor[4]; fColor[0] = 1.0f; fColor[1] = 1.0f; fColor[2] = 1.0f; fColor[3] = 1.0f - fBeta; // Adjust flag alpha so it fades when showing logo if( fColor[3] != 0.0f ) { // Render flag myglMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fColor); pMesh = &winMesh[frameNum]; INT numPrims = 0; INT numIndices = 0; INT numVertices = 0; WORD iVertexA, iVertexB, iVertexC, iVertexD; INT a,b,c,d; MFACE *faces; WORD* i; MYVERTEX2* v; hr = m_pVB2->Lock( 0, 0, (BYTE**)&v, 0 ); hr = m_pIB->Lock( 0, MAX_INDICES, (BYTE**)&i, 0 ); faces = pMesh->faces; for( int iFace = 0; iFace < pMesh->numFaces; iFace++ ) { a = faces[iFace].p[0]; b = faces[iFace].p[1]; c = faces[iFace].p[2]; d = faces[iFace].p[3]; v[numVertices].p = pMesh->pts[a]; v[numVertices].n = bSmoothShading ? -pMesh->norms[a] : -faces[iFace].norm; v[numVertices].tu = s; v[numVertices].tv = 1.0f; iVertexA = numVertices++; v[numVertices].p = pMesh->pts[b]; v[numVertices].n = bSmoothShading ? -pMesh->norms[b] : -faces[iFace].norm; v[numVertices].tu = s; v[numVertices].tv = 0.0f; iVertexB = numVertices++; v[numVertices].p = pMesh->pts[c]; v[numVertices].n = bSmoothShading ? -pMesh->norms[c] : -faces[iFace].norm; v[numVertices].tu = s+ds; v[numVertices].tv = 1.0f; iVertexC = numVertices++; v[numVertices].p = pMesh->pts[d]; v[numVertices].n = bSmoothShading ? -pMesh->norms[d] : -faces[iFace].norm; v[numVertices].tu = s+ds; v[numVertices].tv = 0.0f; iVertexD = numVertices++; s += ds; i[numIndices++] = iVertexA; i[numIndices++] = iVertexB; i[numIndices++] = iVertexC; numPrims++; i[numIndices++] = iVertexC; i[numIndices++] = iVertexB; i[numIndices++] = iVertexD; numPrims++; } hr = m_pVB2->Unlock(); hr = m_pIB->Unlock(); hr = m_pd3dDevice->SetVertexShader( D3DFVF_MYVERTEX2 ); hr = m_pd3dDevice->SetStreamSource( 0, m_pVB2, sizeof(MYVERTEX2) ); hr = m_pd3dDevice->SetIndices( m_pIB, 0 ); hr = m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, numVertices, 0, numPrims ); } // Don't change frame number if we're in S_FADETOCOLOR, S_PAUSE, // or S_FADEFROMCOLOR, unless by some chance we're in those states // but framenum is not at zero (yet). if( frameNum != 0 || s_state != S_FADETOCOLOR && s_state != S_PAUSE && s_state != S_FADEFROMCOLOR ) { fFrameNum -= fTimeFactor; frameNum = (INT)fFrameNum; if (frameNum < 0) { fFrameNum = (FLOAT)(Frames - 1); frameNum = Frames - 1; } } }