commit b57310b38c1e97e4cd397c1f1fd88634b85489ae Author: lhark Date: Sat Nov 25 16:18:26 2017 -0500 Initial commit OpenGL boilerplate by Benoit Ozell Packets code taken from https://github.com/jeschke/water-wave-packets diff --git a/Packets.cpp b/Packets.cpp new file mode 100644 index 0000000..0cc5ba3 --- /dev/null +++ b/Packets.cpp @@ -0,0 +1,1264 @@ +// taken from https://github.com/jeschke/water-wave-packets +// Include the OS headers +//----------------------- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Packets.h" +#pragma warning( disable: 4996 ) + + +inline float rational_tanh(float x) +{ + if (x < -3.0f) + return -1.0f; + else if (x > 3.0f) + return 1.0f; + else + return x*(27.0f + x*x) / (27.0f+9.0f*x*x); +} + + +float Packets::GetIntersectionDistance(Vector2f pos1, Vector2f dir1, Vector2f pos2, Vector2f dir2) +{ + ParametrizedLine line1(pos1, dir1); + Hyperplane line2 = Hyperplane::Through(pos2, pos2+dir2); + float intPointDist = line1.intersectionParameter(line2); + if (abs(intPointDist) > 10000.0f) + intPointDist = 10000.0f; + return intPointDist; +} + + + +inline float Packets::GetGroundVal(Vector2f &p) +{ + Vector2f pTex = Vector2f(p.x()/SCENE_EXTENT+0.5f,p.y()/SCENE_EXTENT+0.5f); // convert from world space to texture space + float val1 = m_ground[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + float val2 = m_ground[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float val3 = m_ground[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + float val4 = m_ground[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float xOffs = (pTex.x()*m_groundSizeX) - (int)(pTex.x()*m_groundSizeX); + float yOffs = (pTex.y()*m_groundSizeY) - (int)(pTex.y()*m_groundSizeY); + float valH1 = (1.0f-xOffs)*val1 + xOffs*val2; + float valH2 = (1.0f-xOffs)*val3 + xOffs*val4; + return( (1.0f-yOffs)*valH1 + yOffs*valH2 ); +} + +inline Vector2f Packets::GetGroundNormal(Vector2f &p) +{ + Vector2f pTex = Vector2f(p.x()/SCENE_EXTENT+0.5f,p.y()/SCENE_EXTENT+0.5f); // convert from world space to texture space + Vector2f val1 = m_gndDeriv[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + Vector2f val2 = m_gndDeriv[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + Vector2f val3 = m_gndDeriv[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + Vector2f val4 = m_gndDeriv[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float xOffs = (pTex.x()*m_groundSizeX) - (int)(pTex.x()*m_groundSizeX); + float yOffs = (pTex.y()*m_groundSizeY) - (int)(pTex.y()*m_groundSizeY); + Vector2f valH1 = (1.0f-xOffs)*val1 + xOffs*val2; + Vector2f valH2 = (1.0f-xOffs)*val3 + xOffs*val4; + Vector2f res = (1.0f-yOffs)*valH1 + yOffs*valH2; + Vector2f resN = Vector2f(0,1); + if (abs(res.x()) + abs(res.y()) > 0.0) + resN = res.normalized(); + return( resN ); +} + +inline float Packets::GetBoundaryDist(Vector2f &p) +{ + Vector2f pTex = Vector2f(p.x()/SCENE_EXTENT+0.5f,p.y()/SCENE_EXTENT+0.5f); // convert from world space to texture space + float val1 = m_distMap[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + float val2 = m_distMap[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float val3 = m_distMap[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + float val4 = m_distMap[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float xOffs = (pTex.x()*m_groundSizeX) - (int)(pTex.x()*m_groundSizeX); + float yOffs = (pTex.y()*m_groundSizeY) - (int)(pTex.y()*m_groundSizeY); + float valH1 = (1.0f-xOffs)*val1 + xOffs*val2; + float valH2 = (1.0f-xOffs)*val3 + xOffs*val4; + return( (1.0f-yOffs)*valH1 + yOffs*valH2 ); +} + +inline Vector2f Packets::GetBoundaryNormal(Vector2f &p) +{ + Vector2f pTex = Vector2f(p.x()/SCENE_EXTENT+0.5f,p.y()/SCENE_EXTENT+0.5f); // convert from world space to texture space + Vector2f val1 = m_bndDeriv[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + Vector2f val2 = m_bndDeriv[(int)(max(0,min(m_groundSizeY-1,pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + Vector2f val3 = m_bndDeriv[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,pTex.x()*m_groundSizeX)))]; + Vector2f val4 = m_bndDeriv[(int)(max(0,min(m_groundSizeY-1,1+pTex.y()*m_groundSizeY)))*m_groundSizeX + (int)(max(0,min(m_groundSizeX-1,1+pTex.x()*m_groundSizeX)))]; + float xOffs = (pTex.x()*m_groundSizeX) - (int)(pTex.x()*m_groundSizeX); + float yOffs = (pTex.y()*m_groundSizeY) - (int)(pTex.y()*m_groundSizeY); + Vector2f valH1 = (1.0f-xOffs)*val1 + xOffs*val2; + Vector2f valH2 = (1.0f-xOffs)*val3 + xOffs*val4; + Vector2f res = (1.0f-yOffs)*valH1 + yOffs*valH2; + Vector2f resN = Vector2f(0,1); + if (abs(res.x())+abs(res.y()) > 0.0) + resN = res.normalized(); + return( resN ); +} + + + + +#pragma warning( push ) +#pragma warning( disable : 4996) +Packets::Packets(int packetBudget) +{ + WCHAR wcInfo[512]; + + //load ground/boundary texture for CPU processing + LPCWSTR groundTexFile = WATER_TERRAIN_FILE; + tagBITMAPFILEHEADER bmpheader; + tagBITMAPINFOHEADER bmpinfo; + DWORD bytesread; + HANDLE file = CreateFile( groundTexFile , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + if (file == NULL) + { + throw std::exception("Media file not found"); + return; + } + if ( (ReadFile ( file, &bmpheader, sizeof ( BITMAPFILEHEADER ), &bytesread, NULL ) == false) + || (ReadFile(file, &bmpinfo, sizeof(BITMAPINFOHEADER), &bytesread, NULL) == false) + || (bmpheader.bfType != 'MB') + || (bmpinfo.biCompression != BI_RGB) + || (bmpinfo.biBitCount != 24) ) + { + CloseHandle ( file ); + throw std::exception("Error reading media file"); + return; + } + m_groundSizeX = abs(bmpinfo.biWidth); + m_groundSizeY = abs(bmpinfo.biHeight); + long size = bmpheader.bfSize-bmpheader.bfOffBits; + SetFilePointer ( file, bmpheader.bfOffBits, NULL, FILE_BEGIN ); + BYTE* Buffer = new BYTE[size]; + if ( ReadFile ( file, Buffer, size, &bytesread, NULL ) == false ) + { + delete[](Buffer); + CloseHandle(file); + throw std::exception("Media file not found"); + return; + } + CloseHandle(file); + // convert read buffer to our rgb datastructure + int padding = 0; + int scanlinebytes = m_groundSizeX*3; + while ( ( scanlinebytes + padding ) % 4 != 0 ) + padding++; + int psw = scanlinebytes + padding; + m_ground = new float[m_groundSizeX*m_groundSizeY]; + float *bound = new float[m_groundSizeX*m_groundSizeY]; + for (int y=0; y 11.1f/255.0f) + bound[y*m_groundSizeX+x] = 1.0f; + else + bound[y*m_groundSizeX+x] = 0.0f; + } + delete[](Buffer); + + // boundary texture distance transform + // init helper distance map (pMap) + StringCchPrintf(wcInfo, 512, L"Computing boundary distance transform.."); + OutputDebugString(wcInfo); + int *pMap = new int[m_groundSizeX*m_groundSizeY]; + #pragma omp parallel for + for (int y = 0; y < m_groundSizeY; y++) + for (int x = 0; x < m_groundSizeX; x++) + { + // if we are at the boundary, intialize the distance function with 0, otherwise with maximum value + if ((bound[y*m_groundSizeX + x] > 0.5f) && + ((bound[max(0, min(m_groundSizeY - 1, y + 1))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 0))] <= 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y + 0))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 1))] <= 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y - 1))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 0))] <= 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y + 0))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x - 1))] <= 0.5f))) + pMap[y*m_groundSizeX + x] = 0; // initialize with maximum x distance + else if ((bound[y*m_groundSizeX + x] <= 0.5f) && + ((bound[max(0, min(m_groundSizeY - 1, y + 1))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 0))] > 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y + 0))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 1))] > 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y - 1))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x + 0))] > 0.5f) + || (bound[max(0, min(m_groundSizeY - 1, y + 0))*m_groundSizeX + max(0, min(m_groundSizeX - 1, x - 1))] > 0.5f))) + pMap[y*m_groundSizeX + x] = 0; // initialize with maximum x distance + else + pMap[y*m_groundSizeX + x] = m_groundSizeX*m_groundSizeX; // initialize with maximum x distance + } + m_distMap = new float[m_groundSizeX*m_groundSizeY]; + #pragma omp parallel for + for (int y=0; y=0; x--) + { + if (pMap[y*m_groundSizeX+x] == 0) + lastBoundX = x; + pMap[y*m_groundSizeX+x] = min(pMap[y*m_groundSizeX+x], (lastBoundX-x)*(lastBoundX-x)); + } + } + #pragma omp parallel for + for (int x=0; x=0; yd--) + { + minDist = min(minDist, yd*yd+pMap[(y+yd)*m_groundSizeX+x]); + if (minDist < yd*yd) + break; + } + m_distMap[y*m_groundSizeX+x] = (float)(minDist); + } + delete[](pMap); + // m_distMap now contains the _squared_ euklidean distance to closest label boundary, so take the sqroot. And sign the distance + #pragma omp parallel for + for (int y=0; y 0.5f) + m_distMap[y*m_groundSizeX+x] = -m_distMap[y*m_groundSizeX+x]; // negative distance INSIDE a boundary regions + m_distMap[y*m_groundSizeX+x] = m_distMap[y*m_groundSizeX+x]*SCENE_EXTENT / m_groundSizeX; + } + StringCchPrintf(wcInfo, 512, L"done!\n"); + OutputDebugString(wcInfo); + + // derivative (2D normal) of the boundary texture + StringCchPrintf( wcInfo, 512, L"Computing boundary derivatives.."); + OutputDebugString( wcInfo ); + m_bndDeriv = new Vector2f[m_groundSizeX*m_groundSizeY]; + #pragma omp parallel for + for (int y=0; y m_packetNum) + ExpandWavePacketMemory(max(m_usedPackets,m_usedGhosts) + PACKET_BUFFER_DELTA); + float speedDummy, kDummy; + int firstfree = GetFreePackedID(); + m_packet[firstfree].pos1 = Vector2f(pos1x,pos1y); + m_packet[firstfree].pOld1 = m_packet[firstfree].pos1; + m_packet[firstfree].pos2 = Vector2f(pos2x,pos2y); + m_packet[firstfree].pOld2 = m_packet[firstfree].pos2; + m_packet[firstfree].dir1 = Vector2f(dir1x,dir1y); + m_packet[firstfree].dOld1 = m_packet[firstfree].dir1; + m_packet[firstfree].dir2 = Vector2f(dir2x,dir2y); + m_packet[firstfree].dOld2 = m_packet[firstfree].dir2; + m_packet[firstfree].phase = 0.0; + m_packet[firstfree].phOld = 0.0; + m_packet[firstfree].E = E; + m_packet[firstfree].use3rd = false; + m_packet[firstfree].bounced1 = false; + m_packet[firstfree].bounced2 = false; + m_packet[firstfree].bounced3 = false; + m_packet[firstfree].sliding3 = false; + // set the wavelength/freq interval + m_packet[firstfree].k_L = k_L; + float wd = GetWaterDepth(m_packet[firstfree].pos1); + m_packet[firstfree].w0_L = sqrt((GRAVITY + k_L*k_L*SIGMA/DENSITY)*k_L*rational_tanh(k_L*wd)); // this take surface tension into account + m_packet[firstfree].k_H = k_H; + m_packet[firstfree].w0_H = sqrt((GRAVITY + k_H*k_H*SIGMA/DENSITY)*k_H*rational_tanh(k_H*wd)); // this take surface tension into account + m_packet[firstfree].d_L = 0.0; + m_packet[firstfree].d_H = 0.0; + // set the representative wave as average of interval boundaries + m_packet[firstfree].k = 0.5f*(m_packet[firstfree].k_L+m_packet[firstfree].k_H); + m_packet[firstfree].w0 = sqrt((GRAVITY + m_packet[firstfree].k*m_packet[firstfree].k*SIGMA/DENSITY)*m_packet[firstfree].k*rational_tanh(m_packet[firstfree].k*wd)); // this takes surface tension into account + GetWaveParameters(GetWaterDepth(m_packet[firstfree].pos1), m_packet[firstfree].w0, m_packet[firstfree].k, kDummy, m_packet[firstfree].speed1); + m_packet[firstfree].sOld1 = m_packet[firstfree].speed1; + GetWaveParameters(GetWaterDepth(m_packet[firstfree].pos2), m_packet[firstfree].w0, m_packet[firstfree].k, kDummy, m_packet[firstfree].speed2); + m_packet[firstfree].sOld2 = m_packet[firstfree].speed2; + m_packet[firstfree].envelope = min(PACKET_ENVELOPE_MAXSIZE, max(PACKET_ENVELOPE_MINSIZE, PACKET_ENVELOPE_SIZE_FACTOR*2.0f*M_PI/m_packet[firstfree].k)); // adjust envelope size to represented wavelength + m_packet[firstfree].ampOld = 0.0; + float a1 = min(MAX_SPEEDNESS*2.0f*M_PI / m_packet[firstfree].k, GetWaveAmplitude(m_packet[firstfree].envelope*(m_packet[firstfree].pos1 - m_packet[firstfree].pos2).norm(), m_packet[firstfree].E, m_packet[firstfree].k)); + m_packet[firstfree].dAmp = 0.5f*(m_packet[firstfree].speed1+m_packet[firstfree].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[firstfree].envelope)*a1; + + // Test for wave number splitting -> if the packet interval crosses the slowest waves, divide so that each part has a monotonic speed function (assumed for travel spread/error calculation) + int i1=firstfree; + if ((m_packet[i1].w0_L>PACKET_SLOWAVE_W0) && (m_packet[i1].w0_H 45 degrees +void Packets::CreateSpreadingPacket(float xPos, float yPos, float dirx, float diry, float spreadFactor, float crestlength, float lambda_L, float lambda_H, float E) +{ + Vector2f dir = Vector2f(dirx,diry); + dir.normalize(); + Vector2f wfAlign = 0.5f*crestlength*Vector2f(dir.y(),-dir.x()); + Vector2f dirSpread1 = dir - spreadFactor*Vector2f(dir.y(),-dir.x()); + Vector2f dirSpread2 = dir + spreadFactor*Vector2f(dir.y(),-dir.x()); + CreatePacket( xPos-wfAlign.x(), yPos-wfAlign.y(), xPos+wfAlign.x(), yPos+wfAlign.y(), dirSpread1.x(), dirSpread1.y(), dirSpread2.x(), dirSpread2.y(), 2.0f*M_PI/lambda_L, 2.0f*M_PI/lambda_H, E); +} + + + +// adds a new circular wave at normalized position x,y with wavelength boundaries lambda_L and lambda_H (in meters) +void Packets::CreateCircularWavefront(float xPos, float yPos, float radius, float lambda_L, float lambda_H, float E) +{ + // adapt initial packet crestlength to impact radius and wavelength + float dAng = min(24.0f, 360.0f * ((0.5f*lambda_L + 0.5f*lambda_H)*3.0f) / (2.0f*M_PI*radius)); + for (float i = 0; i < 360.0f; i += dAng) + CreatePacket( + xPos + radius*sin(i*M_PI / 180.0f), yPos + radius*cos(i*M_PI / 180.0f), + xPos + radius*sin((i + dAng)*M_PI / 180.0f), yPos + radius*cos((i + dAng)*M_PI / 180.0f), + sin(i*M_PI / 180.0f), cos(i*M_PI / 180.0f), + sin((i + dAng)*M_PI / 180.0), cos((i + dAng)*M_PI / 180.0f), + 2.0f*M_PI / lambda_L, 2.0f*M_PI / lambda_H, E); +} + + + +// update the simulation time +void Packets::UpdateTime(float dTime) +{ + if (m_oldTime < 0) // if we are entering the first time, set the current time as time + m_oldTime = 0.0f; + else + m_oldTime = m_time; + m_time = m_oldTime + dTime; // time stepping + m_elapsedTime = abs(m_time - m_oldTime); +} + + + +// returns water depth at position p +float Packets::GetWaterDepth(Vector2f &p) +{ + float v = 1.0f-GetGroundVal(p); + return(MIN_WATER_DEPTH + (MAX_WATER_DEPTH-MIN_WATER_DEPTH)*v*v*v*v); +} + + + + +void Packets::GetWaveParameters(float waterDepth, float w_0, float kIn, float &k_out, float &speed_out) +{ + float k = kIn; + float dk = 1.0f; + float kOld; + int it = 0; + while ((dk > 1.0e-04) && (it<6)) + { + kOld = k; + k = w_0/sqrt((GRAVITY/k+k*SIGMA/DENSITY)*rational_tanh(k*waterDepth)); // this includes surface tension / capillary waves + dk = abs(k-kOld); + it++; + } + k_out = k; + float t = rational_tanh(k*waterDepth); + const float c = SIGMA/DENSITY; + speed_out = ((c*k*k + GRAVITY)*(t + waterDepth*k*(1.0f-t*t)) + 2.0f*c*k*k*t) / (2.0f*sqrt(k*(c*k*k + GRAVITY)*t)); // this is group speed as dw/dk + return; +} + + + +float Packets::GetPhaseSpeed(float w_0, float kIn) +{ + return( w_0/kIn ); +} + + + +// area = surface area of the wave packet, E = Energy, k = wavenumber +// computing the amplitude from energy flux for a wave packet +float Packets::GetWaveAmplitude(float area, float E, float k) +{ + return( sqrt(abs(E)/(abs(area)*0.5f*(DENSITY*GRAVITY+SIGMA*k*k))) ); +} + + + + + +// advects a single packet vertex with groupspeed, returns 1 if boundary reflection occured +bool Packets::AdvectPacketVertex(float elapsedTime, Vector2f &posIn, Vector2f &dirIn, float w0, float &kIn, float &speedIn, Vector2f &posOut, Vector2f &dirOut, float &speedOut) +{ + bool bounced = false; + // intialize the output with the input + posOut = posIn; + dirOut = dirIn; + speedOut = speedIn; + + // compute new direction and speed based on snells law (depending on water depth) + float speed1, k; + GetWaveParameters(GetWaterDepth( posIn ), w0, kIn, k, speed1); + speedOut = speed1; // the new speed is defined by the speed of this wave at this water depth, this is does not necessarily respect snells law! + Vector2f nDir = GetGroundNormal( posIn ); + if (abs(nDir.x())+abs(nDir.y()) > 0.1f) // if there is a change in water depth here, indicated by a non-zero ground normal + { + Vector2f pNext = posIn + elapsedTime*speed1*dirIn; + float speed2; + GetWaveParameters(GetWaterDepth(pNext), w0, kIn, k, speed2); + + float cos1 = nDir.dot(-dirIn); + float cos2 = sqrt( max(0.0f, 1.0f - (speed2*speed2)/(speed1*speed1)*(1.0f - cos1*cos1) )); + Vector2f nRefrac; + if (cos1 <= 0.0f) + nRefrac = speed2/speed1*dirIn + (speed2/speed1*cos1 + cos2)*nDir; + else + nRefrac = speed2/speed1*dirIn + (speed2/speed1*cos1 - cos2)*nDir; + if (nRefrac.norm() > 0.000001f) + dirOut = nRefrac.normalized(); + } + posOut = posIn + elapsedTime*speed1*dirOut; // advect wave vertex position + + // if we ran into a boundary -> step back and bounce off + if (GetBoundaryDist(posOut)<0.0f) + { + Vector2f nor = GetBoundaryNormal(posOut); + float a = nor.dot(dirOut); + if (a <= -0.08f) // a wave reflects if it travels with >4.5 degrees towards a surface. Otherwise, it gets diffracted + { + bounced = true; + // step back until we are outside the boundary + Vector2f pD = posIn; + Vector2f vD = elapsedTime*speedIn*dirOut; + for (int j = 0; j < 16; j++) + { + Vector2f pDD = pD + vD; + if (GetBoundaryDist(pDD) > 0.0f) + pD = pDD; + vD = 0.5f*vD; + } + Vector2f wayVec = pD - posIn; + float lGone = wayVec.norm(); + posOut = pD; + // compute the traveling direction after the bounce + dirOut = -dirOut; + Vector2f nor2 = GetBoundaryNormal(posOut); + float a2 = nor2.dot(dirOut); + Vector2f bFrac = a2*nor2 - dirOut; + Vector2f d0 = dirOut + 2.0f*bFrac; + dirOut = d0.normalized(); + posOut += (elapsedTime*speedOut - lGone)*dirOut; + } + } + + // if we got trapped in a boundary (again), just project onto the nearest surface (this approximates multiple bounces) + if (GetBoundaryDist(posOut) < 0.0) + for (int i2=0; i2<16; i2++) + posOut += -0.5f*GetBoundaryDist(posOut)*GetBoundaryNormal(posOut); + + return(bounced); +} + + + + + + +// updates the wavefield using the movin wavefronts and generated an output image from the wavefield +void Packets::AdvectWavePackets(float dTime) +{ + UpdateTime(dTime); + if (m_elapsedTime <= 0.0) // if there is no time advancement, do not update anything.. + return; + + // compute the new packet vertex positions, directions and speeds based on snells law + #pragma omp parallel for + for (int uP=0; uP find new "has to bounce" point if no other vertex bounced + { + float s = 0.0f; + float sD = 0.5f; + Vector2f posOld = m_packet[i1].pOld1; + Vector2f dirOld = m_packet[i1].dOld1; + float speedOld = m_packet[i1].sOld1; + Vector2f pos = m_packet[i1].pos1; + Vector2f dir = m_packet[i1].dir1; + float speed = m_packet[i1].speed1; + float wN = m_packet[i1].k; + float w0 = m_packet[i1].w0; + for (int j=0; j<16; j++) + { + Vector2f p = (1.0f-(s+sD))*m_packet[i1].pOld1 + (s+sD)*m_packet[i1].pOld3; + Vector2f d = (1.0f-(s+sD))*m_packet[i1].dOld1 + (s+sD)*m_packet[i1].dOld3; + float sp = (1.0f-(s+sD))*m_packet[i1].sOld1 + (s+sD)*m_packet[i1].sOld3; + Vector2f posD, dirD; + float speedD; + if (!AdvectPacketVertex(m_elapsedTime, p, d, w0, wN, sp, posD, dirD, speedD)) + { + s += sD; + posOld = p; + dirOld = d; + speedOld = sp; + pos = posD; + dir = dirD; + speed = speedD; + } + sD = 0.5f*sD; + } + m_packet[i1].pOld3 = posOld; + m_packet[i1].dOld3 = dirOld.normalized(); + m_packet[i1].sOld3 = speedOld; + m_packet[i1].pos3 = pos; + m_packet[i1].dir3 = dir; + m_packet[i1].speed3 = speed; + } + } + + + // first contact to a boundary -> sent a ghost packet, make packet invisible for now, add 3rd vertex + if (m_usedGhosts + m_usedPackets > m_packetNum) + ExpandWavePacketMemory(m_usedGhosts + m_usedPackets + PACKET_BUFFER_DELTA); + #pragma omp parallel for + for (int uP = m_usedPackets-1; uP>=0; uP--) + if ((!m_packet[m_usedPacket[uP]].use3rd) && (m_packet[m_usedPacket[uP]].bounced1 || m_packet[m_usedPacket[uP]].bounced2)) + { + int i1 = m_usedPacket[uP]; + int firstghost = GetFreeGhostID(); + m_ghostPacket[firstghost].pos = 0.5f*(m_packet[i1].pOld1+m_packet[i1].pOld2); + m_ghostPacket[firstghost].dir = (m_packet[i1].dOld1+m_packet[i1].dOld2).normalized(); // the new position is wrong after the reflection, so use the old direction instead + m_ghostPacket[firstghost].speed = 0.5f*(m_packet[i1].sOld1+m_packet[i1].sOld2); + m_ghostPacket[firstghost].envelope = m_packet[i1].envelope; + m_ghostPacket[firstghost].ampOld = m_packet[i1].ampOld; + m_ghostPacket[firstghost].dAmp = m_ghostPacket[firstghost].ampOld* m_ghostPacket[firstghost].speed*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_ghostPacket[firstghost].envelope); + m_ghostPacket[firstghost].k = m_packet[i1].k; + m_ghostPacket[firstghost].phase = m_packet[i1].phOld; + m_ghostPacket[firstghost].dPhase = m_packet[i1].phase-m_packet[i1].phOld; + m_ghostPacket[firstghost].bending = GetIntersectionDistance(m_ghostPacket[firstghost].pos, m_ghostPacket[firstghost].dir, m_packet[i1].pOld1, m_packet[i1].dOld1); + // hide this packet from display + m_packet[i1].ampOld = 0.0; + m_packet[i1].dAmp = 0.0; + // emit all (higher-)frequency waves after a bounce + if ((PACKET_BOUNCE_FREQSPLIT) && (m_packet[i1].k_L < PACKET_BOUNCE_FREQSPLIT_K)) // split the frequency range if smallest wave is > 20cm + { + m_packet[i1].k_L = PACKET_BOUNCE_FREQSPLIT_K; + m_packet[i1].w0_L = sqrt(GRAVITY/m_packet[i1].k_L)*m_packet[i1].k_L; // initial guess for angular frequency + m_packet[i1].w0 = 0.5f*(m_packet[i1].w0_L+m_packet[i1].w0_H); + // distribute the error according to current speed difference + float dummySpeed; + Vector2f pos = 0.5f*(m_packet[i1].pos1+m_packet[i1].pos2); + float wd = GetWaterDepth(pos); + GetWaveParameters(wd, m_packet[i1].w0_L, m_packet[i1].k_L, m_packet[i1].k_L, dummySpeed); + GetWaveParameters(wd, m_packet[i1].w0_H, m_packet[i1].k_H, m_packet[i1].k_H, dummySpeed); + GetWaveParameters(wd, m_packet[i1].w0, 0.5f*(m_packet[i1].k_L+m_packet[i1].k_H), m_packet[i1].k, dummySpeed); + m_packet[i1].d_L = 0.0; m_packet[i1].d_H = 0.0; // reset the internally tracked error + m_packet[i1].envelope = min(PACKET_ENVELOPE_MAXSIZE, max(PACKET_ENVELOPE_MINSIZE, PACKET_ENVELOPE_SIZE_FACTOR*2.0f*M_PI/m_packet[i1].k)); + } + //if both vertices bounced, the reflected wave needs to smoothly reappear + if (m_packet[i1].bounced1==m_packet[i1].bounced2) + { + m_packet[i1].ampOld = 0.0; + m_packet[i1].dAmp = 0.5f*(m_packet[i1].speed1+m_packet[i1].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[i1].envelope)*GetWaveAmplitude( m_packet[i1].envelope*(m_packet[i1].pos1-m_packet[i1].pos2).norm(), m_packet[i1].E, m_packet[i1].k); + } + if (m_packet[i1].bounced1 != m_packet[i1].bounced2) // only one vertex bounced -> insert 3rd "wait for bounce" vertex and reorder such that 1st is waiting for bounce + { + if (m_packet[i1].bounced1) // if the first bounced an the second did not -> exchange the two points, as we assume that the second bounced already and the 3rd will be "ahead" of the first.. + { + WAVE_PACKET *seg = &m_packet[i1]; // use the 3rd vertex as copy element + seg->pos3 = seg->pos2; seg->pos2 = seg->pos1; seg->pos1 = seg->pos3; + seg->pOld3 = seg->pOld2; seg->pOld2 = seg->pOld1; seg->pOld1 = seg->pOld3; + seg->dir3 = seg->dir2; seg->dir2 = seg->dir1; seg->dir1 = seg->dir3; + seg->dOld3 = seg->dOld2; seg->dOld2 = seg->dOld1; seg->dOld1 = seg->dOld3; + seg->speed3 = seg->speed2; seg->speed2 = seg->speed1; seg->speed1 = seg->speed3; + seg->sOld3 = seg->sOld2; seg->sOld2 = seg->sOld1; seg->sOld1 = seg->sOld3; + seg->bounced3 = seg->bounced2; seg->bounced2 = seg->bounced1; seg->bounced1 = seg->bounced3; + } + float s = 0.0; + float sD = 0.5f; + Vector2f posOld = m_packet[i1].pOld1; + Vector2f dirOld = m_packet[i1].dOld1; + float speedOld = m_packet[i1].sOld1; + Vector2f pos = m_packet[i1].pos1; + Vector2f dir = m_packet[i1].dir1; + float speed = m_packet[i1].speed1; + float wN = m_packet[i1].k; + float w0 = m_packet[i1].w0; + for (int j=0; j<16; j++) // find the last point before the boundary that does not bounce in this timestep, it becomes the 3rd point + { + Vector2f p = (1.0f-(s+sD))*m_packet[i1].pOld1 + (s+sD)*m_packet[i1].pOld2; + Vector2f d = (1.0f-(s+sD))*m_packet[i1].dOld1 + (s+sD)*m_packet[i1].dOld2; + float sp = (1.0f-(s+sD))*m_packet[i1].sOld1 + (s+sD)*m_packet[i1].sOld2; + Vector2f posD, dirD; + float speedD; + if (!AdvectPacketVertex(m_elapsedTime, p, d, w0, wN, sp, posD, dirD, speedD)) + { + s += sD; + posOld = p; + dirOld = d; + speedOld = sp; + pos = posD; + dir = dirD; + speed = speedD; + } + sD = 0.5f*sD; + } + // the new 3rd vertex has "has to bounce" state (not sliding yet) + m_packet[i1].pOld3 = posOld; + m_packet[i1].dOld3 = dirOld.normalized(); + m_packet[i1].sOld3 = speedOld; + m_packet[i1].pos3 = pos; + m_packet[i1].dir3 = dir; + m_packet[i1].speed3 = speed; + } + } + + + // define new state based on current state and bouncings + #pragma omp parallel for + for (int uP = 0; uP case "intiate new 3rd vertex" + { + m_packet[i1].use3rd = true; + m_packet[i1].bounced3 = false; + m_packet[i1].sliding3 = false; + } + } + else // 3rd vertex already present + { + if (!m_packet[i1].sliding3) // 3rd point was in "waiting for bounce" state + { + if (!m_packet[i1].bounced3) // case: 3rd "has to bounce" vertex did not bounce -> make it sliding in any case + m_packet[i1].sliding3 = true; + else if ((m_packet[i1].bounced1) || (m_packet[i1].bounced2)) // case: 3rd "has to bounce" and one other point bounced as well -> release 3rd vertex + m_packet[i1].use3rd = false; + } + else // 3rd point was already in "sliding" state + { + if (m_packet[i1].bounced3) // if sliding 3rd point bounced, release it + m_packet[i1].use3rd = false; + } + // if we released this wave from a boundary (3rd vertex released) -> blend it smoothly in again + if (!m_packet[i1].use3rd) + { + m_packet[i1].ampOld = 0.0; + m_packet[i1].dAmp = 0.5f*(m_packet[i1].speed1+m_packet[i1].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[i1].envelope)*GetWaveAmplitude( m_packet[i1].envelope*(m_packet[i1].pos1-m_packet[i1].pos2).norm(), m_packet[i1].E, m_packet[i1].k); + } + } + } + + + // wavenumber interval subdivision if travel distance between fastest and slowest wave packets differ more than PACKET_SPLIT_DISPERSION x envelope size + if ( max(m_usedGhosts + m_usedPackets, 2*m_usedPackets) > m_packetNum) + ExpandWavePacketMemory(max(m_usedGhosts + m_usedPackets, 2 * m_usedPackets) + PACKET_BUFFER_DELTA); + #pragma omp parallel for + for (int uP = m_usedPackets-1; uP>=0; uP--) + if (!m_packet[m_usedPacket[uP]].use3rd) + { + int i1 = m_usedPacket[uP]; + float speedDummy,kDummy; + Vector2f pos = 0.5f*(m_packet[i1].pos1+m_packet[i1].pos2); + float wd = GetWaterDepth(pos); + GetWaveParameters(wd, m_packet[i1].w0, m_packet[i1].k, kDummy, speedDummy); + float dist_Ref = m_elapsedTime*speedDummy; + GetWaveParameters(wd, m_packet[i1].w0_L, m_packet[i1].k_L, kDummy, speedDummy); + m_packet[i1].k_L = kDummy; + m_packet[i1].d_L += fabs(m_elapsedTime*speedDummy-dist_Ref); // taking the abs augments any errors caused by waterdepth independent slowest wave assumption.. + GetWaveParameters(wd, m_packet[i1].w0_H, m_packet[i1].k_H, kDummy, speedDummy); + m_packet[i1].k_H = kDummy; + m_packet[i1].d_H += fabs(m_elapsedTime*speedDummy-dist_Ref); + if (m_packet[i1].d_L+m_packet[i1].d_H > PACKET_SPLIT_DISPERSION*m_packet[i1].envelope) // if fastest/slowest waves in this packet diverged too much -> subdivide + { + // first create a ghost for the old packet + int firstghost = GetFreeGhostID(); + m_ghostPacket[firstghost].pos = 0.5f*(m_packet[i1].pOld1+m_packet[i1].pOld2); + m_ghostPacket[firstghost].dir = (0.5f*(m_packet[i1].pos1+m_packet[i1].pos2)-0.5f*(m_packet[i1].pOld1+m_packet[i1].pOld2)).normalized(); + m_ghostPacket[firstghost].speed = 0.5f*(m_packet[i1].sOld1+m_packet[i1].sOld2); + m_ghostPacket[firstghost].envelope = m_packet[i1].envelope; + m_ghostPacket[firstghost].ampOld = m_packet[i1].ampOld; + m_ghostPacket[firstghost].dAmp = m_ghostPacket[firstghost].ampOld* m_ghostPacket[firstghost].speed*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_ghostPacket[firstghost].envelope); + m_ghostPacket[firstghost].k = m_packet[i1].k; + m_ghostPacket[firstghost].phase = m_packet[i1].phOld; + m_ghostPacket[firstghost].dPhase = m_packet[i1].phase-m_packet[i1].phOld; + m_ghostPacket[firstghost].bending = GetIntersectionDistance(m_ghostPacket[firstghost].pos, m_ghostPacket[firstghost].dir, m_packet[i1].pOld1, m_packet[i1].dOld1); + // create new packet and copy ALL parameters + int firstfree = GetFreePackedID(); + m_packet[firstfree] = m_packet[i1]; + // split the frequency range + m_packet[firstfree].k_H = m_packet[i1].k; + m_packet[firstfree].w0_H = m_packet[i1].w0; + m_packet[firstfree].w0 = 0.5f*(m_packet[firstfree].w0_L+m_packet[firstfree].w0_H); + // distribute the error according to current speed difference + float speed_L,speed_M,speed_H; + GetWaveParameters( wd, m_packet[firstfree].w0_L, m_packet[firstfree].k_L, m_packet[firstfree].k_L, speed_L); + GetWaveParameters( wd, m_packet[firstfree].w0_H, m_packet[firstfree].k_H, m_packet[firstfree].k_H, speed_H); + GetWaveParameters( wd, m_packet[firstfree].w0, 0.5f*(m_packet[firstfree].k_L+m_packet[firstfree].k_H), m_packet[firstfree].k, speed_M); + float dSL = abs(abs(speed_L)-abs(speed_M)); + float dSH = abs(abs(speed_H)-abs(speed_M)); + float d_All = m_packet[i1].d_L; + m_packet[firstfree].d_L = dSL*d_All / (dSH + dSL); + m_packet[firstfree].d_H = d_All - m_packet[firstfree].d_L; + m_packet[firstfree].envelope = min(PACKET_ENVELOPE_MAXSIZE, max(PACKET_ENVELOPE_MINSIZE, PACKET_ENVELOPE_SIZE_FACTOR*2.0f*M_PI/m_packet[firstfree].k)); + // set the new upper freq. boundary and representative freq. + m_packet[i1].k_L = m_packet[i1].k; + m_packet[i1].w0_L = m_packet[i1].w0; + m_packet[i1].w0 = 0.5f*(m_packet[i1].w0_L+m_packet[i1].w0_H); + // distribute the error according to current speed difference + GetWaveParameters( wd, m_packet[i1].w0_L, m_packet[i1].k_L, m_packet[i1].k_L, speed_L); + GetWaveParameters( wd, m_packet[i1].w0_H, m_packet[i1].k_H, m_packet[i1].k_H, speed_H); + GetWaveParameters( wd, m_packet[i1].w0, 0.5f*(m_packet[i1].k_L+m_packet[i1].k_H), m_packet[i1].k, speed_M); + dSL = abs(abs(speed_L)-abs(speed_M)); + dSH = abs(abs(speed_H)-abs(speed_M)); + d_All = m_packet[i1].d_H; + m_packet[i1].d_L = dSL*d_All / (dSH + dSL); + m_packet[i1].d_H = d_All - m_packet[i1].d_L; + m_packet[i1].envelope = min(PACKET_ENVELOPE_MAXSIZE, max(PACKET_ENVELOPE_MINSIZE, PACKET_ENVELOPE_SIZE_FACTOR*2.0f*M_PI/m_packet[i1].k)); + // distribute the energy such that both max. wave gradients are equal -> both get the same wave shape + m_packet[firstfree].E = abs(m_packet[i1].E)/(1.0f + (m_packet[i1].envelope*m_packet[firstfree].k*m_packet[firstfree].k*(DENSITY*GRAVITY+SIGMA*m_packet[i1].k*m_packet[i1].k))/(m_packet[firstfree].envelope*m_packet[i1].k*m_packet[i1].k*(DENSITY*GRAVITY+SIGMA*m_packet[firstfree].k*m_packet[firstfree].k))); + m_packet[i1].E = abs(m_packet[i1].E)-m_packet[firstfree].E; + // smoothly ramp the new waves + m_packet[i1].phase=0; + m_packet[i1].ampOld = 0.0f; + m_packet[i1].dAmp = 0.5f*(m_packet[i1].speed1+m_packet[i1].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[i1].envelope)*GetWaveAmplitude( m_packet[i1].envelope*(m_packet[i1].pos1-m_packet[i1].pos2).norm(), m_packet[i1].E, m_packet[i1].k); + m_packet[firstfree].phase = 0.0f; + m_packet[firstfree].ampOld = 0.0f; + m_packet[firstfree].dAmp = 0.5f*(m_packet[firstfree].speed1+m_packet[firstfree].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[firstfree].envelope)*GetWaveAmplitude( m_packet[firstfree].envelope*(m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm(), m_packet[firstfree].E, m_packet[firstfree].k); + } + } + + + // crest-refinement of packets of regular packet (not at any boundary, i.e. having no 3rd vertex) + if (max(m_usedGhosts + m_usedPackets, 2 * m_usedPackets) > m_packetNum) + ExpandWavePacketMemory(max(m_usedGhosts + m_usedPackets, 2 * m_usedPackets) + PACKET_BUFFER_DELTA); + #pragma omp parallel for + for (int uP = m_usedPackets-1; uP>=0; uP--) + if (!m_packet[m_usedPacket[uP]].use3rd) + { + int i1 = m_usedPacket[uP]; + float sDiff = (m_packet[i1].pos2-m_packet[i1].pos1).norm(); + float aDiff = m_packet[i1].dir1.dot(m_packet[i1].dir2); + if ((sDiff > m_packet[i1].envelope) || (aDiff <= PACKET_SPLIT_ANGLE)) // if the two vertices move towards each other, do not subdivide + { + int firstghost = GetFreeGhostID(); + m_ghostPacket[firstghost].pos = 0.5f*(m_packet[i1].pOld1+m_packet[i1].pOld2); + m_ghostPacket[firstghost].dir = (m_packet[i1].dOld1+m_packet[i1].dOld2).normalized(); + m_ghostPacket[firstghost].speed = 0.5f*(m_packet[i1].sOld1+m_packet[i1].sOld2); + m_ghostPacket[firstghost].envelope = m_packet[i1].envelope; + m_ghostPacket[firstghost].ampOld = m_packet[i1].ampOld; + m_ghostPacket[firstghost].dAmp = m_ghostPacket[firstghost].ampOld* m_ghostPacket[firstghost].speed*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_ghostPacket[firstghost].envelope); + m_ghostPacket[firstghost].k = m_packet[i1].k; + m_ghostPacket[firstghost].phase = m_packet[i1].phOld; + m_ghostPacket[firstghost].dPhase = m_packet[i1].phase-m_packet[i1].phOld; + m_ghostPacket[firstghost].bending = GetIntersectionDistance(m_ghostPacket[firstghost].pos, m_ghostPacket[firstghost].dir, m_packet[i1].pOld1, m_packet[i1].dOld1); + // create new vertex between existing packet vertices + int firstfree = GetFreePackedID(); + m_packet[firstfree] = m_packet[i1]; // first copy all data + m_packet[firstfree].pOld1 = 0.5f*(m_packet[i1].pOld1 + m_packet[i1].pOld2); + m_packet[firstfree].dOld1 = (m_packet[i1].dOld1 + m_packet[i1].dOld2).normalized(); + m_packet[firstfree].sOld1 = 0.5f*(m_packet[i1].sOld1 + m_packet[i1].sOld2); + m_packet[firstfree].pos1 = 0.5f*(m_packet[i1].pos1 + m_packet[i1].pos2); + m_packet[firstfree].dir1 = (m_packet[i1].dir1 + m_packet[i1].dir2).normalized(); + m_packet[firstfree].speed1 = 0.5f*(m_packet[i1].speed1 + m_packet[i1].speed2); + m_packet[firstfree].E = 0.5f*m_packet[i1].E; + m_packet[firstfree].ampOld = 0.0; + m_packet[firstfree].dAmp = 0.5f*(m_packet[firstfree].speed1+m_packet[firstfree].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[firstfree].envelope)*GetWaveAmplitude( m_packet[firstfree].envelope*(m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm(), m_packet[firstfree].E, m_packet[firstfree].k); + // use the same new middle vertex here + m_packet[i1].pOld2 = m_packet[firstfree].pOld1; + m_packet[i1].dOld2 = m_packet[firstfree].dOld1; + m_packet[i1].sOld2 = m_packet[firstfree].sOld1; + m_packet[i1].pos2 = m_packet[firstfree].pos1; + m_packet[i1].dir2 = m_packet[firstfree].dir1; + m_packet[i1].speed2 = m_packet[firstfree].speed1; + m_packet[i1].E *= 0.5f; + m_packet[i1].ampOld = 0.0; + m_packet[i1].dAmp = 0.5f*(m_packet[i1].speed1+m_packet[i1].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[i1].envelope)*GetWaveAmplitude( m_packet[i1].envelope*(m_packet[i1].pos1-m_packet[i1].pos2).norm(), m_packet[i1].E, m_packet[i1].k); + } + } + + + + // crest-refinement of packets with a sliding 3rd vertex + if ( 3 * m_usedPackets > m_packetNum) + ExpandWavePacketMemory(3 * m_usedPackets + PACKET_BUFFER_DELTA); + #pragma omp parallel for + for (int uP = m_usedPackets-1; uP>=0; uP--) + if ((m_packet[m_usedPacket[uP]].use3rd) && (m_packet[m_usedPacket[uP]].sliding3)) + { + int i1 = m_usedPacket[uP]; + float sDiff1 = (m_packet[i1].pos1-m_packet[i1].pos3).norm(); + float aDiff1 = m_packet[i1].dir1.dot(m_packet[i1].dir3); + if ((sDiff1 >= m_packet[i1].envelope))// || (aDiff1 <= PACKET_SPLIT_ANGLE)) // angle criterion is removed here because this would prevent diffraction + { + int firstfree = GetFreePackedID(); + // first vertex becomes first in new wave packet, third one becomes second + m_packet[firstfree] = m_packet[i1]; // first copy all data + m_packet[firstfree].pOld2 = 0.5f*(m_packet[i1].pOld1 + m_packet[i1].pOld3); + m_packet[firstfree].dOld2 = (m_packet[i1].dOld1 + m_packet[i1].dOld3).normalized(); + m_packet[firstfree].sOld2 = 0.5f*(m_packet[i1].sOld1 + m_packet[i1].sOld3); + m_packet[firstfree].pos2 = 0.5f*(m_packet[i1].pos1 + m_packet[i1].pos3); + m_packet[firstfree].dir2 = (m_packet[i1].dir1 + m_packet[i1].dir3).normalized(); + m_packet[firstfree].speed2 = 0.5f*(m_packet[i1].speed1 + m_packet[i1].speed3); + m_packet[firstfree].ampOld = 0.0; + m_packet[firstfree].dAmp = 0.5f*(m_packet[i1].speed1+m_packet[i1].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[i1].envelope)*GetWaveAmplitude( m_packet[i1].envelope*(m_packet[i1].pos1-m_packet[i1].pos2).norm(), m_packet[i1].E, m_packet[i1].k); + m_packet[firstfree].bounced1 = false; + m_packet[firstfree].bounced2 = false; + m_packet[firstfree].bounced3 = false; + m_packet[firstfree].use3rd = false; + m_packet[firstfree].sliding3 = false; + // use the same new middle vertex here + m_packet[i1].pOld1 = m_packet[firstfree].pOld2; + m_packet[i1].dOld1 = m_packet[firstfree].dOld2; + m_packet[i1].sOld1 = m_packet[firstfree].sOld2; + m_packet[i1].pos1 = m_packet[firstfree].pos2; + m_packet[i1].dir1 = m_packet[firstfree].dir2; + m_packet[i1].speed1 = m_packet[firstfree].speed2; + // distribute the energy according to length of the two new packets + float s = (m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm()/((m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm() + (m_packet[i1].pos1-m_packet[i1].pos3).norm() + (m_packet[i1].pos2-m_packet[i1].pos3).norm()); + m_packet[firstfree].E = s*m_packet[i1].E; + m_packet[i1].E *= (1.0f-s); + } + // same procedure for the other end of sliding vertex.. + sDiff1 = (m_packet[i1].pos2-m_packet[i1].pos3).norm(); + aDiff1 = m_packet[i1].dir2.dot(m_packet[i1].dir3); + if ((sDiff1 >= m_packet[i1].envelope)/* || (aDiff1 <= PACKET_SPLIT_ANGLE)*/) // angle criterion is removed here because this would prevent diffraction + { + int firstfree = GetFreePackedID(); + // first vertex becomes first in new packet, third one becomes second + m_packet[firstfree] = m_packet[i1]; // first copy all data + m_packet[firstfree].pOld1 = 0.5f*(m_packet[i1].pOld2 + m_packet[i1].pOld3); + m_packet[firstfree].dOld1 = (m_packet[i1].dOld2 + m_packet[i1].dOld3).normalized(); + m_packet[firstfree].sOld1 = 0.5f*(m_packet[i1].sOld2 + m_packet[i1].sOld3); + m_packet[firstfree].pos1 = 0.5f*(m_packet[i1].pos2 + m_packet[i1].pos3); + m_packet[firstfree].dir1 = (m_packet[i1].dir2 + m_packet[i1].dir3).normalized(); + m_packet[firstfree].speed1 = 0.5f*(m_packet[i1].speed2 + m_packet[i1].speed3); + m_packet[firstfree].ampOld = 0.0; + m_packet[firstfree].dAmp = 0.5f*(m_packet[firstfree].speed1+m_packet[firstfree].speed2)*m_elapsedTime/(PACKET_BLEND_TRAVEL_FACTOR*m_packet[firstfree].envelope)*GetWaveAmplitude( m_packet[firstfree].envelope*(m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm(), m_packet[firstfree].E, m_packet[firstfree].k); + m_packet[firstfree].bounced1 = false; + m_packet[firstfree].bounced2 = false; + m_packet[firstfree].bounced3 = false; + m_packet[firstfree].use3rd = false; + m_packet[firstfree].sliding3 = false; + // use the same new middle vertex + m_packet[i1].pOld2 = m_packet[firstfree].pOld1; + m_packet[i1].dOld2 = m_packet[firstfree].dOld1; + m_packet[i1].sOld2 = m_packet[firstfree].sOld1; + m_packet[i1].pos2 = m_packet[firstfree].pos1; + m_packet[i1].dir2 = m_packet[firstfree].dir1; + m_packet[i1].speed2 = m_packet[firstfree].speed1; + // distribute the energy according to length of the two new packets + float s = (m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm()/((m_packet[firstfree].pos1-m_packet[firstfree].pos2).norm() + (m_packet[i1].pos1-m_packet[i1].pos3).norm() + (m_packet[i1].pos2-m_packet[i1].pos3).norm()); + m_packet[firstfree].E = s*m_packet[i1].E; + m_packet[i1].E *= (1.0f-s); + } + } + + + // delete packets traveling outside the scene + #pragma omp parallel for + for (int uP = 0; uP < m_usedPackets; uP++) + { + int i1 = m_usedPacket[uP]; + m_packet[i1].toDelete = false; + if (!m_packet[i1].use3rd) + { + Vector2f dir = m_packet[i1].pos1 - m_packet[i1].pOld1; + Vector2f dir2 = m_packet[i1].pos2 - m_packet[i1].pOld2; + if ((((m_packet[i1].pos1.x() < -0.5f*SCENE_EXTENT) && (dir.x() < 0.0)) + || ((m_packet[i1].pos1.x() > +0.5f*SCENE_EXTENT) && (dir.x() > 0.0)) + || ((m_packet[i1].pos1.y() < -0.5f*SCENE_EXTENT) && (dir.y() < 0.0)) + || ((m_packet[i1].pos1.y() > +0.5f*SCENE_EXTENT) && (dir.y() > 0.0))) + && + (((m_packet[i1].pos2.x() < -0.5f*SCENE_EXTENT) && (dir2.x() < 0.0)) + || ((m_packet[i1].pos2.x() > +0.5f*SCENE_EXTENT) && (dir2.x() > 0.0)) + || ((m_packet[i1].pos2.y() < -0.5f*SCENE_EXTENT) && (dir2.y() < 0.0)) + || ((m_packet[i1].pos2.y() > +0.5f*SCENE_EXTENT) && (dir2.y() > 0.0)))) + m_packet[i1].toDelete = true; + } + } + + // damping, insignificant packet removal (if too low amplitude), reduce energy of steep waves with too high gradient + m_softDampFactor = 1.0f + 100.0f*pow(max(0.0f, (float)(m_usedPackets)/(float)(m_packetBudget) - 1.0f), 2.0f); + #pragma omp parallel for + for (int uP = 0; uP < m_usedPackets; uP++) + if ((!m_packet[m_usedPacket[uP]].use3rd) && (!m_packet[m_usedPacket[uP]].toDelete)) + { + int i1 = m_usedPacket[uP]; + float dampViscosity = -2.0f*m_packet[i1].k*m_packet[i1].k*KINEMATIC_VISCOSITY; + float dampJunkfilm = -0.5f*m_packet[i1].k*sqrt(0.5f*KINEMATIC_VISCOSITY*m_packet[i1].w0); + m_packet[i1].E *= exp((dampViscosity + dampJunkfilm)*m_elapsedTime*m_softDampFactor); // wavelength-dependent damping + // amplitude computation: lower if too steep, delete if too low + float area = m_packet[i1].envelope*(m_packet[i1].pos2 - m_packet[i1].pos1).norm(); + float a1 = GetWaveAmplitude( area, m_packet[i1].E, m_packet[i1].k); + if (a1*m_packet[i1].k < PACKET_KILL_AMPLITUDE_DERIV) + m_packet[i1].toDelete = true; + else + { + // get the biggest wave as reference for energy reduction (conservative but important to not remove too much energy in case of large k intervals) + float a_L = GetWaveAmplitude(area, m_packet[i1].E, m_packet[i1].k_L); + float a_H = GetWaveAmplitude(area, m_packet[i1].E, m_packet[i1].k_H); + float k; + if (a_L*m_packet[i1].k_L < a_H*m_packet[i1].k_H) // take the smallest wave steepness (=amplitude derivative) + { + a1 = a_L; + k = m_packet[i1].k_L; + } + else + { + a1 = a_H; + k = m_packet[i1].k_H; + } + if (a1 > MAX_SPEEDNESS*2.0f*M_PI / k) + { + a1 = MAX_SPEEDNESS*2.0f*M_PI / k; + m_packet[i1].E = a1*a1*(area*0.5f*(DENSITY*GRAVITY + SIGMA*k*k)); + } + } + m_packet[i1].ampOld = min(a1, m_packet[i1].ampOld + m_packet[i1].dAmp); // smoothly increase amplitude from last timestep + // update variables needed for packet display + Vector2f posMidNew = 0.5f*(m_packet[i1].pos1 + m_packet[i1].pos2); + Vector2f posMidOld = 0.5f*(m_packet[i1].pOld1 + m_packet[i1].pOld2); + Vector2f dirN = (posMidNew - posMidOld).normalized(); // vector in traveling direction + m_packet[i1].midPos = posMidNew; + m_packet[i1].travelDir = dirN; + m_packet[i1].bending = GetIntersectionDistance(posMidNew, dirN, m_packet[i1].pos1, m_packet[i1].dir1); + } + // delete all packets identified to be deleted (important: NO parallelization here!) + for (int uP = 0; uP < m_usedPackets; uP++) + if (m_packet[m_usedPacket[uP]].toDelete) + DeletePacket(uP); + + // advect ghost packets + #pragma omp parallel for + for (int uG = 0; uG < m_usedGhosts; uG++) + { + int i1 = m_usedGhost[uG]; + m_ghostPacket[i1].pos += m_elapsedTime*m_ghostPacket[i1].speed*m_ghostPacket[i1].dir; + m_ghostPacket[i1].phase += m_ghostPacket[i1].dPhase; + m_ghostPacket[i1].ampOld = max(0.0f, m_ghostPacket[i1].ampOld - m_softDampFactor*m_ghostPacket[i1].dAmp); // decrease amplitude to let this wave disappear (take budget-based softdamping into account) + } + // delete all ghost packets if they traveled long enough (important: NO parallelization here!) + for (int uG = 0; uG < m_usedGhosts; uG++) + if (m_ghostPacket[m_usedGhost[uG]].ampOld <= 0.0) + DeleteGhost(uG); +} + + + diff --git a/Packets.h b/Packets.h new file mode 100644 index 0000000..92c62cf --- /dev/null +++ b/Packets.h @@ -0,0 +1,147 @@ +// Taken from https://github.com/jeschke/water-wave-packets +#pragma once + +#include "constants.h" + +#include +#include + +using namespace Eigen; +using namespace std; + + +// simulation parameters +#define PACKET_SPLIT_ANGLE 0.95105f // direction angle variation threshold: 0.95105=18 degree +#define PACKET_SPLIT_DISPERSION 0.3f // if the fastest wave in a packet traveled PACKET_SPLIT_DISPERSION*Envelopesize ahead, or the slowest by the same amount behind, subdivide this packet into two wavelength intervals +#define PACKET_KILL_AMPLITUDE_DERIV 0.0001f // waves below this maximum amplitude derivative gets killed +#define PACKET_BLEND_TRAVEL_FACTOR 1.0f // in order to be fully blended (appear or disappear), any wave must travel PACKET_BLEND_TRAVEL_FACTOR times "envelope size" in space (1.0 is standard) +#define PACKET_ENVELOPE_SIZE_FACTOR 3.0f // size of the envelope relative to wavelength (determines how many "bumps" appear) +#define PACKET_ENVELOPE_MINSIZE 0.02f // minimum envelope size in meters (smallest expected feature) +#define PACKET_ENVELOPE_MAXSIZE 10.0f // maximum envelope size in meters (largest expected feature) +#define PACKET_BOUNCE_FREQSPLIT true // (boolean) should a wave packet produce smaller waves at a bounce/reflection (->widen the wavelength interval of this packet)? +#define PACKET_BOUNCE_FREQSPLIT_K 31.4f // if k_L is smaller than this value (lambda = 20cm), the wave is (potentially) split after a bounce +#define MAX_SPEEDNESS 0.07f // all wave amplitudes a are limited to a <= MAX_SPEEDNESS*2.0*M_PI/k + +// physical parameters +#define SIGMA 0.074f // surface tension N/m at 20 grad celsius +#define GRAVITY 9.81f // GRAVITY m/s^2 +#define DENSITY 998.2071f // water density at 20 degree celsius +#define KINEMATIC_VISCOSITY 0.0000089f // kinematic viscosity +#define PACKET_SLOWAVE_K 143.1405792f // k of the slowest possible wave packet +#define PACKET_SLOWAVE_W0 40.2646141f // w_0 of the slowest possible wave packet + +// memory management +#define PACKET_BUFFER_DELTA 500000 // initial number of vertices, packet memory will be increased on demand by this stepsize + + + + +struct WAVE_PACKET +{ + // positions, directions, speed of the tracked vertices + Vector2f pos1,pos2,pos3; // 2D position + Vector2f dir1,dir2,dir3; // current movement direction + float speed1,speed2,speed3; // speed of the particle + Vector2f pOld1,pOld2,pOld3; // position in last timestep (needed to handle bouncing) + Vector2f dOld1,dOld2,dOld3; // direction in last timestep (needed to handle bouncing) + float sOld1,sOld2,sOld3; // speed in last timestep (needed to handle bouncing) + Vector2f midPos; // middle position (tracked each timestep, used for rendering) + Vector2f travelDir; // travel direction (tracked each timestep, used for rendering) + float bending; // point used for circular arc bending of the wave function inside envelope + + // bouncing and sliding + bool bounced1, bounced2, bounced3; // indicates if this vertex bounced in this timestep + bool sliding3; // indicates if the 3rd vertex is "sliding" (used for diffraction) + bool use3rd; // indicates if the third vertex is present (it marks a (potential) sliding point) + // wave function related + float phase; // phase of the representative wave inside the envelope, phase speed vs. group speed + float phOld; // old phase + float E; // wave energy flux for this packet (determines amplitude) + float envelope; // envelope size for this packet + float k,w0; // w0 = angular frequency, k = current wavenumber + float k_L,w0_L,k_H,w0_H; // w0 = angular frequency, k = current wavenumber, L/H are for lower/upper boundary + float d_L,d_H; // d = travel distance to reference wave (gets accumulated over time), L/H are for lower/upper boundary + float ampOld; // amplitude from last timestep, will be smoothly adjusted in each timestep to meet current desired amplitude + float dAmp; // amplitude change in each timestep (depends on desired waveheight so all waves (dis)appear with same speed) + // serial deletion step variable + bool toDelete; // used internally for parallel deletion criterion computation +public: +EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + + + +struct GHOST_PACKET +{ + Vector2f pos; // 2D position + Vector2f dir; // current movement direction + float speed; // speed of the packet + float envelope; // envelope size for this packet + float bending; // point used for circular arc bending of the wave function inside envelope + float k; // k = current (representative) wavenumber(s) + float phase; // phase of the representative wave inside the envelope + float dPhase; // phase speed relative to group speed inside the envelope + float ampOld; // amplitude from last timestep, will be smoothly adjusted in each timestep to meet current desired amplitude + float dAmp; // change in amplitude in each timestep (waves travel PACKET_BLEND_TRAVEL_FACTOR*envelopesize in space until they disappear) +public: +EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + + +class Packets +{ +public: + // scene + int m_groundSizeX, m_groundSizeY; // pixel size of the ground texture + float *m_ground; // texture containing the water depth and land (0.95) + float *m_distMap; // distance map of the boundary map + Vector2f *m_gndDeriv; + Vector2f *m_bndDeriv; + + // packet managing + WAVE_PACKET *m_packet; // wave packet data + GHOST_PACKET*m_ghostPacket; // ghost packet data + int m_packetBudget; // this can be changed any time (soft budget) + int m_packetNum; // current size of the buffer used for packets / ghosts + float m_softDampFactor; + int *m_usedPacket; + int m_usedPackets; + int *m_freePacket; + int m_freePackets; + int *m_usedGhost; + int m_usedGhosts; + int *m_freeGhost; + int m_freeGhosts; + + // simulation + float m_time; + float m_oldTime; + float m_elapsedTime; + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + Packets(int packetBudget); + ~Packets(void); + void Reset(); + float GetBoundaryDist(Vector2f &p); + Vector2f GetBoundaryNormal(Vector2f &p); + float GetGroundVal(Vector2f &p); + Vector2f GetGroundNormal(Vector2f &p); + float GetWaterDepth(Vector2f &p); + void UpdateTime(float dTime); + void ExpandWavePacketMemory(int targetNum); + int GetFreePackedID(); + void DeletePacket(int id); + int GetFreeGhostID(); + void DeleteGhost(int id); + void CreatePacket(float pos1x, float pos1y, float pos2x, float pos2y, float dir1x, float dir1y, float dir2x, float dir2y, float k_L, float k_H, float E); + void CreateLinearWavefront(float xPos, float yPos, float dirx, float diry, float crestlength, float lambda_L, float lambda_H, float E); + void CreateSpreadingPacket(float xPos, float yPos, float dirx, float diry, float spreadFactor, float crestlength, float lambda_L, float lambda_H, float E); + void CreateCircularWavefront(float xPos, float yPos, float radius, float lambda_L, float lambda_H, float E); + void GetWaveParameters(float waterDepth, float w0, float kIn, float &k_out, float &speed_out); + float GetPhaseSpeed(float w_0, float kIn); + float GetWaveAmplitude(float area, float E, float k); + float GetIntersectionDistance(Vector2f pos1, Vector2f dir1, Vector2f pos2, Vector2f dir2); + bool AdvectPacketVertex(float elapsedTime, Vector2f &posIn, Vector2f &dirIn, float w0, float &kIn, float &speedIn, Vector2f &posOut, Vector2f &dirOut, float &speedOut); + void AdvectWavePackets(float dTime); +}; diff --git a/constants.h b/constants.h new file mode 100644 index 0000000..1fe4a38 --- /dev/null +++ b/constants.h @@ -0,0 +1,44 @@ +// Taken from https://github.com/jeschke/water-wave-packets +// Originally GlobalDefs.h + +// Global definitions needed for packet simulation and rendering + +// scene parameters +#define SCENE_EXTENT 100.0f // extent of the entire scene (packets traveling outside are removed) +#define MIN_WATER_DEPTH 0.1f // minimum water depth (meters) +#define MAX_WATER_DEPTH 5.0f // maximum water depth (meters) +#define WATER_TERRAIN_FILE "TestIsland.bmp"// Contains water depth and land height in different channels + + +// rendering parameters +#define PACKET_GPU_BUFFER_SIZE 1000000 // maximum number of wave packets to be displayed in one draw call + + +/* +// Fast rendering setup +#define WAVETEX_WIDTH_FACTOR 0.5 // the wavemesh texture compared to screen resolution +#define WAVETEX_HEIGHT_FACTOR 1 // the wavemesh texture compared to screen resolution +#define WAVEMESH_WIDTH_FACTOR 0.1 // the fine wave mesh compared to screen resolution +#define WAVEMESH_HEIGHT_FACTOR 0.25 // the fine wave mesh compared to screen resolution +#define AA_OVERSAMPLE_FACTOR 2 // anti aliasing applied in BOTH X and Y directions {1,2,4,8} +*/ + +/* +// Balanced rendering setup +#define WAVETEX_WIDTH_FACTOR 1 // the wavemesh texture compared to screen resolution +#define WAVETEX_HEIGHT_FACTOR 2 // the wavemesh texture compared to screen resolution +#define WAVEMESH_WIDTH_FACTOR 1 // the fine wave mesh compared to screen resolution +#define WAVEMESH_HEIGHT_FACTOR 2 // the fine wave mesh compared to screen resolution +#define AA_OVERSAMPLE_FACTOR 2 // anti aliasing applied in BOTH X and Y directions {1,2,4,8} +*/ + + +// High quality rendering setup +#define WAVETEX_WIDTH_FACTOR 2 // the wavemesh texture compared to screen resolution +#define WAVETEX_HEIGHT_FACTOR 4 // the wavemesh texture compared to screen resolution +#define WAVEMESH_WIDTH_FACTOR 2 // the fine wave mesh compared to screen resolution +#define WAVEMESH_HEIGHT_FACTOR 4 // the fine wave mesh compared to screen resolution +#define AA_OVERSAMPLE_FACTOR 4 // anti aliasing applied in BOTH X and Y directions {1,2,4,8} + + + diff --git a/inf2705.h b/inf2705.h new file mode 100644 index 0000000..55d5fb6 --- /dev/null +++ b/inf2705.h @@ -0,0 +1,3070 @@ +// Version: mar nov 21 12:54:32 EST 2017 +#ifndef INF2705_MATRICE_H +#define INF2705_MATRICE_H + +//////////////////////////////////////////////////////////////////////////// +// +// Classe pour les matrices du pipeline +// (INF2705, Benoît Ozell) +//////////////////////////////////////////////////////////////////////////// + +#include +#include + +#define GLM_SWIZZLE +// (à compter de GLM 9.8, c'est plutôt la variable ci-dessous qu'il faut définir) +#define GLM_FORCE_SWIZZLE +#define GLM_FORCE_RADIANS 1 +#include +#include +#include +#include +#include + +class MatricePipeline +{ +public: + MatricePipeline() + { matr_.push( glm::mat4(1.0) ); } + + operator glm::mat4() const { return matr_.top(); } + operator const GLfloat*() const { return glm::value_ptr(matr_.top()); } + + void LoadIdentity() + { matr_.top() = glm::mat4(1.0); } + // Note: la librairie glm s’occupe de convertir les glm::vec3 en glm::vec4 pour la multiplication par glm::mat4 matr_. + void Scale( GLfloat sx, GLfloat sy, GLfloat sz ) + { matr_.top() = glm::scale( matr_.top(), glm::vec3(sx,sy,sz) ); } + void Translate( GLfloat tx, GLfloat ty, GLfloat tz ) + { matr_.top() = glm::translate( matr_.top(), glm::vec3(tx,ty,tz) ); } + void Rotate( GLfloat angle, GLfloat x, GLfloat y, GLfloat z ) + { matr_.top() = glm::rotate( matr_.top(), (GLfloat)glm::radians(angle), glm::vec3(x,y,z) ); } + + void LookAt( GLdouble obsX, GLdouble obsY, GLdouble obsZ, GLdouble versX, GLdouble versY, GLdouble versZ, GLdouble upX, GLdouble upY, GLdouble upZ ) + { matr_.top() = glm::lookAt( glm::vec3( obsX, obsY, obsZ ), glm::vec3( versX, versY, versZ ), glm::vec3( upX, upY, upZ ) ); } + void Frustum( GLdouble gauche, GLdouble droite, GLdouble bas, GLdouble haut, GLdouble planAvant, GLdouble planArriere ) + { matr_.top() = glm::frustum( gauche, droite, bas, haut, planAvant, planArriere ); } + void Perspective( GLdouble fovy, GLdouble aspect, GLdouble planAvant, GLdouble planArriere ) + { matr_.top() = glm::perspective( glm::radians(fovy), aspect, planAvant, planArriere );} + void Ortho( GLdouble gauche, GLdouble droite, GLdouble bas, GLdouble haut, GLdouble planAvant, GLdouble planArriere ) + { matr_.top() = glm::ortho( gauche, droite, bas, haut, planAvant, planArriere ); } + void Ortho2D( GLdouble gauche, GLdouble droite, GLdouble bas, GLdouble haut ) + { matr_.top() = glm::ortho( gauche, droite, bas, haut ); } + + void PushMatrix() + { matr_.push( matr_.top() ); } + void PopMatrix() + { matr_.pop(); } + + glm::mat4 getMatr() + { return matr_.top(); } + glm::mat4 setMatr( glm::mat4 m ) + { return( matr_.top() = m ); } + + friend std::ostream& operator<<( std::ostream& o, const MatricePipeline& mp ) + { + //return o << glm::to_string(mp.matr_.top()); + glm::mat4 m = mp.matr_.top(); //o.precision(3); o.width(6); + return o << std::endl + << " " << m[0][0] << " " << m[1][0] << " " << m[2][0] << " " << m[3][0] << std::endl + << " " << m[0][1] << " " << m[1][1] << " " << m[2][1] << " " << m[3][1] << std::endl + << " " << m[0][2] << " " << m[1][2] << " " << m[2][2] << " " << m[3][2] << std::endl + << " " << m[0][3] << " " << m[1][3] << " " << m[2][3] << " " << m[3][3] << std::endl; + } + +private: + std::stack matr_; +}; + +#endif + +#ifndef INF2705_NUANCEUR_H +#define INF2705_NUANCEUR_H + +//////////////////////////////////////////////////////////////////////////// +// +// Classe pour charger les nuanceurs +// (INF2705, Benoît Ozell) +//////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +class ProgNuanceur +{ +public: + ProgNuanceur( ) : prog_(0), etiquette_("vide") { } + ~ProgNuanceur( ) { /* glDeleteShader(); */ glDeleteProgram( prog_ ); } + operator GLuint() const { return prog_; } + ProgNuanceur& operator=( GLuint prog ) { prog_ = prog; return *this; } + + // Demander à OpenGL de créer un programme de nuanceur + void creer( const std::string etiquette = "" ) + { + if ( prog_ ) glDeleteProgram( prog_ ); + prog_ = glCreateProgram(); + etiquette_ = etiquette; + if ( etiquette_ == "" ) { std::ostringstream oss; oss << prog_; etiquette_ += oss.str(); } + } + + // Charger en mémoire le contenu du fichier + static const GLchar *lireNuanceur( const GLchar *fich ) + { + // Ouvrir le fichier + std::ifstream fichier( fich ); + if ( fichier.fail() ) + { + std::cerr << "!!! " << fich << ": " << strerror(errno) << std::endl; + return NULL; + } + + // Lire le fichier + std::stringstream contenuFichier; + contenuFichier << fichier.rdbuf(); + fichier.close(); + + // Obtenir le contenu du fichier + std::string contenu = contenuFichier.str(); + const int taille = contenu.size(); + + // Retourner une chaîne pour le nuanceur + char *source = new char[taille+1]; + strcpy( source, contenu.c_str() ); + return source; + } + + // Afficher le log de la compilation + static bool afficherLogCompile( GLuint nuanceurObj ) + { + // afficher le message d'erreur, le cas échéant + int infologLength = 0; + glGetShaderiv( nuanceurObj, GL_INFO_LOG_LENGTH, &infologLength ); + if ( infologLength > 1 ) + { + char* infoLog = new char[infologLength+1]; + int charsWritten = 0; + glGetShaderInfoLog( nuanceurObj, infologLength, &charsWritten, infoLog ); + std::cout << std::endl << infoLog << std::endl; + delete[] infoLog; + return( false ); + } + return( true ); + } + + // Compiler et attacher le nuanceur + bool attacher( GLuint type, GLsizei nbChaine, const GLchar **chaines, const GLint *longueur = NULL ) + { + GLuint nuanceurObj = glCreateShader( type ); + glShaderSource( nuanceurObj, nbChaine, chaines, longueur ); + glCompileShader( nuanceurObj ); + glAttachShader( prog_, nuanceurObj ); + return ( afficherLogCompile(nuanceurObj) ); + } + bool attacher( GLuint type, const GLchar *fich ) + { + bool rc = false; + const GLchar *fichChaine = lireNuanceur( fich ); + if ( fichChaine != NULL ) + { + rc = attacher( type, 1, &fichChaine, NULL ); + delete [] fichChaine; + } + return( rc ); + } + bool attacher( GLuint type, const GLchar *preambule, const GLchar *fich ) + { + bool rc = false; + if ( fich == NULL ) // le nuanceur complet est dans le préambule + rc = attacher( type, 1, &preambule, NULL ); + else + { + const GLchar *chaines[2] = { preambule, lireNuanceur( fich ) }; + if ( chaines[1] != NULL ) + { + rc = attacher( type, 2, chaines, NULL ); + delete [] chaines[1]; + } + } + return( rc ); + } + bool attacher( GLuint type, const std::string preambule, const GLchar *fich ) + { return attacher( type, preambule.c_str(), fich ); } + + // Afficher le log de l'édition des liens + static bool afficherLogLink( GLuint progObj ) + { + // afficher le message d'erreur, le cas échéant + int infologLength = 0; + glGetProgramiv( progObj, GL_INFO_LOG_LENGTH, &infologLength ); + if ( infologLength > 1 ) + { + char* infoLog = new char[infologLength+1]; + int charsWritten = 0; + glGetProgramInfoLog( progObj, infologLength, &charsWritten, infoLog ); + std::cout << "progObj" << std::endl << infoLog << std::endl; + delete[] infoLog; + return( false ); + } + return( true ); + } + + // Faire l'édition des liens du programme + bool lier( ) + { + glLinkProgram( prog_ ); + return( afficherLogLink(prog_) ); + } + + // le nuanceur de sommets minimal + static const GLchar *chainesSommetsMinimal; + // le nuanceur de fragments minimal + static const GLchar *chainesFragmentsMinimal; + +private: + GLuint prog_; // LE programme + std::string etiquette_; // une étiquette pour identifier le programme +}; + +// le nuanceur de sommets minimal +const GLchar *ProgNuanceur::chainesSommetsMinimal = +{ + "#version 410\n" + "uniform mat4 matrModel;\n" + "uniform mat4 matrVisu;\n" + "uniform mat4 matrProj;\n" + "layout(location=0) in vec4 Vertex;\n" + "layout(location=3) in vec4 Color;\n" + "out vec4 couleur;\n" + "void main( void )\n" + "{\n" + " // transformation standard du sommet\n" + " gl_Position = matrProj * matrVisu * matrModel * Vertex;\n" + " // couleur du sommet\n" + " couleur = Color;\n" + "}\n" +}; +// le nuanceur de fragments minimal +const GLchar *ProgNuanceur::chainesFragmentsMinimal = +{ + "#version 410\n" + "in vec4 couleur;\n" + "out vec4 FragColor;\n" + "void main( void )\n" + "{\n" + " // la couleur du fragment est la couleur interpolée\n" + " FragColor = couleur;\n" + "}\n" +}; + +#endif + +#ifndef INF2705_FENETRE_H +#define INF2705_FENETRE_H + +//////////////////////////////////////////////////////////////////////////// +// +// Classe pour créer une fenêtre +// (INF2705, Benoît Ozell) +//////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#if defined( __APPLE__ ) +#define FENETRE_LIB_GLFW 1 +#else +#define FENETRE_LIB_SDL 1 +#endif + +#if defined(FENETRE_LIB_GLFW) +# include +#else +# include +#endif +#include +#include + +typedef enum { +#if defined(FENETRE_LIB_GLFW) + + TP_ECHAP = GLFW_KEY_ESCAPE, + TP_BAS = GLFW_KEY_UP, + TP_HAUT = GLFW_KEY_DOWN, + TP_PAGEPREC = GLFW_KEY_PAGE_UP, + TP_PAGESUIV = GLFW_KEY_PAGE_DOWN, + TP_DEBUT = GLFW_KEY_HOME, + TP_FIN = GLFW_KEY_END, + TP_EGAL = '=', // GLFW_KEY_EQUALS, + TP_SUPERIEUR = '>', // GLFW_KEY_GREATER, + TP_INFERIEUR = '<', // GLFW_KEY_LESS, + TP_DROITE = GLFW_KEY_RIGHT, + TP_GAUCHE = GLFW_KEY_LEFT, + TP_PLUS = '+', // GLFW_KEY_PLUS, + TP_MOINS = GLFW_KEY_MINUS, + TP_CROCHETDROIT = GLFW_KEY_RIGHT_BRACKET, + TP_CROCHETGAUCHE = GLFW_KEY_LEFT_BRACKET, + TP_POINT = GLFW_KEY_PERIOD, + TP_VIRGULE = GLFW_KEY_COMMA, + TP_POINTVIRGULE = GLFW_KEY_SEMICOLON, + TP_BARREOBLIQUE = GLFW_KEY_SLASH, + TP_ESPACE = GLFW_KEY_SPACE, + TP_SOULIGNE = '_', // GLFW_KEY_UNDERSCORE, + + TP_0 = GLFW_KEY_0, + TP_1 = GLFW_KEY_1, + TP_2 = GLFW_KEY_2, + TP_3 = GLFW_KEY_3, + TP_4 = GLFW_KEY_4, + TP_5 = GLFW_KEY_5, + TP_6 = GLFW_KEY_6, + TP_7 = GLFW_KEY_7, + TP_8 = GLFW_KEY_8, + TP_9 = GLFW_KEY_9, + + TP_a = GLFW_KEY_A, + TP_b = GLFW_KEY_B, + TP_c = GLFW_KEY_C, + TP_d = GLFW_KEY_D, + TP_e = GLFW_KEY_E, + TP_f = GLFW_KEY_F, + TP_g = GLFW_KEY_G, + TP_h = GLFW_KEY_H, + TP_i = GLFW_KEY_I, + TP_j = GLFW_KEY_J, + TP_k = GLFW_KEY_K, + TP_l = GLFW_KEY_L, + TP_m = GLFW_KEY_M, + TP_n = GLFW_KEY_N, + TP_o = GLFW_KEY_O, + TP_p = GLFW_KEY_P, + TP_q = GLFW_KEY_Q, + TP_r = GLFW_KEY_R, + TP_s = GLFW_KEY_S, + TP_t = GLFW_KEY_T, + TP_u = GLFW_KEY_U, + TP_v = GLFW_KEY_V, + TP_w = GLFW_KEY_W, + TP_x = GLFW_KEY_X, + TP_y = GLFW_KEY_Y, + TP_z = GLFW_KEY_Z, + +#else + + TP_ECHAP = SDLK_ESCAPE, + TP_BAS = SDLK_UP, + TP_HAUT = SDLK_DOWN, + TP_PAGEPREC = SDLK_PAGEUP, + TP_PAGESUIV = SDLK_PAGEDOWN, + TP_DEBUT = SDLK_HOME, + TP_FIN = SDLK_END, + TP_EGAL = SDLK_EQUALS, + TP_SUPERIEUR = SDLK_GREATER, + TP_INFERIEUR = SDLK_LESS, + TP_DROITE = SDLK_RIGHT, + TP_GAUCHE = SDLK_LEFT, + TP_PLUS = SDLK_PLUS, + TP_MOINS = SDLK_MINUS, + TP_CROCHETDROIT = SDLK_RIGHTBRACKET, + TP_CROCHETGAUCHE = SDLK_LEFTBRACKET, + TP_POINT = SDLK_PERIOD, + TP_VIRGULE = SDLK_COMMA, + TP_POINTVIRGULE = SDLK_SEMICOLON, + TP_BARREOBLIQUE = SDLK_SLASH, + TP_ESPACE = SDLK_SPACE, + TP_SOULIGNE = SDLK_UNDERSCORE, + + TP_0 = SDLK_0, + TP_1 = SDLK_1, + TP_2 = SDLK_2, + TP_3 = SDLK_3, + TP_4 = SDLK_4, + TP_5 = SDLK_5, + TP_6 = SDLK_6, + TP_7 = SDLK_7, + TP_8 = SDLK_8, + TP_9 = SDLK_9, + + TP_a = SDLK_a, + TP_b = SDLK_b, + TP_c = SDLK_c, + TP_d = SDLK_d, + TP_e = SDLK_e, + TP_f = SDLK_f, + TP_g = SDLK_g, + TP_h = SDLK_h, + TP_i = SDLK_i, + TP_j = SDLK_j, + TP_k = SDLK_k, + TP_l = SDLK_l, + TP_m = SDLK_m, + TP_n = SDLK_n, + TP_o = SDLK_o, + TP_p = SDLK_p, + TP_q = SDLK_q, + TP_r = SDLK_r, + TP_s = SDLK_s, + TP_t = SDLK_t, + TP_u = SDLK_u, + TP_v = SDLK_v, + TP_w = SDLK_w, + TP_x = SDLK_x, + TP_y = SDLK_y, + TP_z = SDLK_z, + +#endif +} TP_touche; + +typedef enum { +#if defined(FENETRE_LIB_GLFW) + TP_BOUTON_GAUCHE = GLFW_MOUSE_BUTTON_1, + TP_BOUTON_MILIEU = GLFW_MOUSE_BUTTON_3, + TP_BOUTON_DROIT = GLFW_MOUSE_BUTTON_2, + TP_RELACHE = GLFW_RELEASE, + TP_PRESSE = GLFW_PRESS, +#else + TP_BOUTON_GAUCHE = SDL_BUTTON_LEFT, + TP_BOUTON_MILIEU = SDL_BUTTON_MIDDLE, + TP_BOUTON_DROIT = SDL_BUTTON_RIGHT, + TP_RELACHE = SDL_RELEASED, + TP_PRESSE = SDL_PRESSED, +#endif +} TP_bouton; + + +// la fenêtre graphique +class FenetreTP +{ +#if defined(FENETRE_LIB_GLFW) + static void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ) + { + FenetreTP *fen = (FenetreTP*) glfwGetWindowUserPointer( window ); + if ( action == GLFW_PRESS ) + fen->clavier( (TP_touche) key ); + } + static void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ) + { + FenetreTP *fen = (FenetreTP*) glfwGetWindowUserPointer( window ); + double xpos, ypos; glfwGetCursorPos( window, &xpos, &ypos ); + fen->sourisClic( button, action, xpos, ypos ); + } + static void cursor_position_callback( GLFWwindow* window, double xpos, double ypos ) + { + FenetreTP *fen = (FenetreTP*) glfwGetWindowUserPointer( window ); + fen->sourisMouvement( xpos, ypos ); + } + static void scroll_callback( GLFWwindow* window, double xoffset, double yoffset ) + { + FenetreTP *fen = (FenetreTP*) glfwGetWindowUserPointer( window ); + fen->sourisWheel( xoffset, yoffset ); + } + static void window_refresh_callback( GLFWwindow* window ) + { + FenetreTP *fen = (FenetreTP*) glfwGetWindowUserPointer( window ); + // int left, top, right, bottom; + // glfwGetWindowFrameSize( window, &left, &top, &right, &bottom ); + // fen->redimensionner( right-left, top-bottom ); + int width, height; + glfwGetWindowSize( window, &width, &height ); + fen->redimensionner( width, height ); + fen->afficherScene(); + fen->swap(); + } +#else +#endif + +public: + FenetreTP( std::string nom = "INF2705 TP", + int largeur = 900, int hauteur = 600, + int xpos = 100, int ypos = 100 ) + : fenetre_(NULL), +#if defined(FENETRE_LIB_GLFW) +#else + contexte_(NULL), +#endif + largeur_(largeur), hauteur_(hauteur) + { +#if defined(FENETRE_LIB_GLFW) + // initialiser GLFW + if ( !glfwInit() ) + { + glfwdie( "ERREUR: Incapable d'initialiser GLFW3\n"); + } + + // demander certaines caractéristiques: + glfwWindowHint( GLFW_RED_BITS, 8 ); + glfwWindowHint( GLFW_GREEN_BITS, 8 ); + glfwWindowHint( GLFW_BLUE_BITS, 8 ); + glfwWindowHint( GLFW_ALPHA_BITS, 8 ); + //glfwWindowHint( GLFW_DOUBLEBUFFER, GL_TRUE ); + glfwWindowHint( GLFW_DEPTH_BITS, 24 ); + glfwWindowHint( GLFW_STENCIL_BITS, 8 ); +#if defined( __APPLE__ ) + glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 4 ); + glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 1 ); + glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); + glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); +#endif + + // créer la fenêtre + fenetre_ = glfwCreateWindow( largeur, hauteur, nom.c_str(), NULL, NULL ); + if ( !fenetre_ ) + { + glfwdie( "ERROR: Incapable de créer la fenêtre GLFW3\n"); + } + glfwMakeContextCurrent( fenetre_ ); + glfwSwapInterval(1); + + glfwSetWindowUserPointer( fenetre_, this ); + glfwSetKeyCallback( fenetre_, key_callback ); + glfwSetMouseButtonCallback( fenetre_, mouse_button_callback ); + glfwSetCursorPosCallback( fenetre_, cursor_position_callback ); + glfwSetScrollCallback( fenetre_, scroll_callback ); + glfwSetWindowRefreshCallback( fenetre_, window_refresh_callback ); +#else + // initialiser SDL + const Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS; + if ( SDL_WasInit( flags ) == 0 ) + { + if ( SDL_Init( flags ) < 0 ) sdldie( "ERREUR: Incapable d'initialiser SDL" ); + atexit( SDL_Quit ); + } + + // demander certaines caractéristiques: https://wiki.libsdl.org/SDL_GL_SetAttribute + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); + //SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); + SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); + //SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4 ); + SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ); +#if 0 + SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE ); + //SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 ); +#endif + + // créer la fenêtre + fenetre_ = SDL_CreateWindow( nom.c_str(), xpos, ypos, largeur, hauteur, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE ); + if ( !fenetre_ ) sdldie( "ERREUR: Incapable de créer la fenêtre SDL" ); + verifierErreurSDL(__LINE__); + + // créer le contexte OpenGL + contexte_ = SDL_GL_CreateContext( fenetre_ ); + verifierErreurSDL(__LINE__); + + // s'assurer que les réaffichage seront synchronisés avec le rafraîchissement de l'écran + SDL_GL_SetSwapInterval( 1 ); + verifierErreurSDL(__LINE__); +#endif + + // initiliaser GLEW + initialiserGLEW( ); + + // imprimer un peu d'information OpenGL + imprimerInfosGL( ); + } + +#if defined(FENETRE_LIB_GLFW) + void quit( ) + { + glfwDestroyWindow( fenetre_ ); + glfwTerminate(); + exit(0); + } + ~FenetreTP( ) + { + quit(); + } +#else + void quit( ) + { + SDL_Event sdlevent; sdlevent.type = SDL_QUIT; + SDL_PushEvent( &sdlevent ); + } + ~FenetreTP( ) + { + SDL_GL_DeleteContext( contexte_ ); contexte_ = NULL; + SDL_DestroyWindow( fenetre_ ); fenetre_ = NULL; + } +#endif + + // mettre à jour la fenêtre OpenGL: le tampon arrière devient le tampon avant et vice-versa +#if defined(FENETRE_LIB_GLFW) + void swap( ) { glfwSwapBuffers( fenetre_ ); } +#else + void swap( ) { SDL_GL_SwapWindow( fenetre_ ); } +#endif + + // fonction appelée pour tracer la scène + void afficherScene( ); + // fonction appelée lors d'un événement de redimensionnement + void redimensionner( GLsizei w, GLsizei h ); + // fonction appelée lors d'un événement de clavier + void clavier( TP_touche touche ); + // fonctions appelées lors d'un événement de souris + void sourisClic( int button, int state, int x, int y ); + void sourisWheel( int x, int y ); + void sourisMouvement( int x, int y ); + + // fonction de gestion de la boucle des événements + bool gererEvenement( ) + { +#if defined(FENETRE_LIB_GLFW) + glfwPollEvents(); + return( !glfwWindowShouldClose( fenetre_ ) ); +#else + SDL_Event e; + while ( SDL_PollEvent( &e ) ) + { + switch ( e.type ) + { + case SDL_QUIT: // c'est la fin + return( false ); + break; + case SDL_WINDOWEVENT: + if ( e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) // redimensionnement + { + largeur_ = e.window.data1; + hauteur_ = e.window.data2; + redimensionner( largeur_, hauteur_ ); + } + else if ( e.window.event == SDL_WINDOWEVENT_SHOWN ) // affichage + { + SDL_GetWindowSize( fenetre_, &largeur_, &hauteur_ ); + redimensionner( largeur_, hauteur_ ); + } + //else + // std::cout << "//@FenetreTP,WINDOWEVENT;" << " e.window.event=" << e.window.event << std::endl; + break; + case SDL_KEYDOWN: // une touche est pressée + clavier( (TP_touche) e.key.keysym.sym ); + break; + case SDL_KEYUP: // une touche est relâchée + break; + case SDL_MOUSEBUTTONDOWN: // un bouton de la souris est pressé + case SDL_MOUSEBUTTONUP: // un bouton de la souris est relâché + sourisClic( e.button.button, e.button.state, e.button.x, e.button.y ); + break; + case SDL_MOUSEMOTION: // la souris est déplacée + sourisMouvement( e.motion.x, e.motion.y ); + break; + case SDL_MOUSEWHEEL: // la molette de la souris est tournée + sourisWheel( e.wheel.x, e.wheel.y ); + break; + default: + //std::cerr << "//@FenetreTP," << __LINE__ << ";" << " e.type=" << e.type << std::endl; + break; + } + } + return( true ); +#endif + } + + // + // Quelques fonctions utilitaires + // + + // Charger en mémoire le contenu du fichier + static void imprimerTouches( ) + { + // Ouvrir le fichier + std::ifstream fichier( "touches.txt" ); + if ( fichier.fail() ) return; + + // Lire le fichier + std::stringstream contenuFichier; + contenuFichier << fichier.rdbuf(); + fichier.close(); + + // Ecrire le contenu du fichier + std::cout << " touches possibles :" << std::endl + << contenuFichier.str() << std::endl; + } + + // afficher les versions des éléments du pipeline OpenGL + static void imprimerInfosGL( const int verbose = 1 ) + { +#define PBYTE(CHAINE) ( (CHAINE) != NULL ? (CHAINE) : (const GLubyte *) "????" ) +#define PCHAR(CHAINE) ( (CHAINE) != NULL ? (CHAINE) : (const char *) "????" ) + + if ( verbose >= 1 ) + { +#if defined(FENETRE_LIB_GLFW) + int major, minor, rev; glfwGetVersion( &major, &minor, &rev ); + std::cout << "// GLFW " << major << "." << minor << "." << rev << std::endl; +#else + SDL_version linked; SDL_GetVersion( &linked ); + std::cout << "// SDL " << (int) linked.major << "." << (int) linked.minor << "." << (int) linked.patch << std::endl; +#endif + + const GLubyte *glVersion = glGetString( GL_VERSION ); + const GLubyte *glVendor = glGetString( GL_VENDOR ); + const GLubyte *glRenderer = glGetString( GL_RENDERER ); + const GLubyte *glslVersion = glGetString( GL_SHADING_LANGUAGE_VERSION ); + std::cout << "// OpenGL " << PBYTE(glVersion) << PBYTE(glVendor) << std::endl; + std::cout << "// GPU " << PBYTE(glRenderer) << std::endl; + std::cout << "// GLSL " << PBYTE(glslVersion) << std::endl; + + if ( verbose >= 2 ) + { + const GLubyte *glExtensions = glGetString( GL_EXTENSIONS ); + std::cout << "// extensions = " << PBYTE(glExtensions) << std::endl; + } + } +#undef PBYTE +#undef PCHAR + return; + } + +#if defined(FENETRE_LIB_GLFW) + // donner une message et mourir... + static void glfwdie( const char *msg ) + { + //const char *sdlerror = SDL_GetError(); + //std::cout << "glfwdie " << msg << " " << sdlerror << std::endl; + std::cout << "glfwdie " << msg << " " << std::endl; + //glfwTerminate(); + exit(1); + } +#else + // donner une message et mourir... + static void sdldie( const char *msg ) + { + const char *sdlerror = SDL_GetError(); + std::cout << "sdldie " << msg << " " << sdlerror << std::endl; + SDL_Quit(); + exit(1); + } + // vérifier les erreurs + static void verifierErreurSDL( int line = -1 ) + { + const char *sdlerror = SDL_GetError(); + if ( *sdlerror != '\0' ) + { + std::cout << "SDL Error: " << sdlerror << std::endl; + if ( line != -1 ) + std::cout << "line: " << line; + std::cout << std::endl; + SDL_ClearError(); + } + } +#endif + // La fonction glGetError() permet de savoir si une erreur est survenue depuis le dernier appel à cette fonction. + static int VerifierErreurGL( const std::string message ) + { + int rc = 0; + GLenum err; + while ( ( err = glGetError() ) != GL_NO_ERROR ) + { + std::cerr << "Erreur OpenGL, " << message << " " << std::endl; + switch ( err ) + { + case GL_INVALID_ENUM: + std::cerr << "GL_INVALID_ENUM: Valeur d'une énumération hors limite."; + break; + case GL_INVALID_VALUE: + std::cerr << "GL_INVALID_VALUE: Valeur numérique hors limite."; + break; + case GL_INVALID_OPERATION: + std::cerr << "GL_INVALID_OPERATION: Opération non permise dans l'état courant."; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION: L'objet est incomplet."; + break; + case GL_OUT_OF_MEMORY: + std::cerr << "GL_OUT_OF_MEMORY: Pas assez de mémoire pour exécuter la commande."; + break; + case GL_STACK_UNDERFLOW: + std::cerr << "GL_STACK_UNDERFLOW: Une opération entraînerait un débordement de pile interne."; + break; + case GL_STACK_OVERFLOW: + std::cerr << "GL_STACK_OVERFLOW: Une opération entraînerait un débordement de pile interne."; + break; + default: + std::cerr << "err = " << err << ": Erreur inconnue!"; + break; + } + std::cerr << std::endl; + ++rc; + } + return( rc ); + } + + // La fonction afficherAxes affiche des axes qui représentent l'orientation courante du repère + // Les axes sont colorés ainsi: X = Rouge, Y = Vert, Z = Bleu + static void afficherAxes( const GLfloat longueurAxe = 1.0, const GLfloat largeurLigne = 2.0 ) + { +#if !defined( __APPLE__ ) + glPushAttrib( GL_ENABLE_BIT | GL_LINE_BIT ); + glLineWidth( largeurLigne ); + + const GLfloat coo[] = { 0., 0., 0., + longueurAxe, 0., 0., + 0., longueurAxe, 0., + 0., 0., longueurAxe }; + const GLfloat couleur[] = { 1., 1., 1., + 1., 0., 0., + 0., 1., 0., + 0., 0., 1. }; + +#if 0 + const GLuint connec[] = { 0, 1, 0, 2, 0, 3 }; + GLint locVertex = 0; + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, coo ); + glEnableVertexAttribArray(locVertex); + GLint locColor = 3; + glVertexAttribPointer( locColor, 3, GL_FLOAT, GL_FALSE, 0, couleur ); + glEnableVertexAttribArray(locColor); + glDrawElements( GL_LINES, sizeof(connec)/sizeof(GLuint), GL_UNSIGNED_INT, connec ); +#else + glBegin( GL_LINES );{ + glColor3fv( &(couleur[3]) ); glVertex3fv( &(coo[0]) ); glVertex3fv( &(coo[3]) ); + glColor3fv( &(couleur[6]) ); glVertex3fv( &(coo[0]) ); glVertex3fv( &(coo[6]) ); + glColor3fv( &(couleur[9]) ); glVertex3fv( &(coo[0]) ); glVertex3fv( &(coo[9]) ); + }glEnd( ); +#endif + + glPopAttrib( ); +#endif + return; + } + +private: + void initialiserGLEW( ) + { + //#if defined(_GLEW_H__) + glewExperimental = GL_TRUE; + GLenum rev = glewInit(); + if ( rev != GLEW_OK ) + { + std::cout << "Error: " << glewGetErrorString(rev) << std::endl; + exit( 1 ); + } + glGetError(); // Afin d'ignorer l'erreur générée par GLEW. Voir https://www.opengl.org/wiki/OpenGL_Loading_Library#GLEW_.28OpenGL_Extension_Wrangler.29 + //#endif + } + +#if defined(FENETRE_LIB_GLFW) + GLFWwindow *fenetre_; +#else + SDL_Window *fenetre_; + SDL_GLContext contexte_; +#endif + GLsizei largeur_; // la largeur de la fenêtre + GLsizei hauteur_; // la hauteur de la fenêtre +}; + +#endif + +#ifndef INF2705_TEXTURE_H +#define INF2705_TEXTURE_H + +//////////////////////////////////////////////////////////////////////////// +// +// Fonctions pour charger une texture +// (INF2705, Benoît Ozell) +//////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +/* + * Windows Bitmap File Loader + * Version 1.2.5 (20120929) + * + * Supported Formats: 1, 4, 8, 16, 24, 32 Bit Images + * Alpha Bitmaps are also supported. + * Supported compression types: RLE 8, BITFIELDS + * + * Created by: Benjamin Kalytta, 2006 - 2012 + * Thanks for bug fixes goes to: Chris Campbell + * + * Licence: Free to use, URL to my source and my name is required in your source code. + * + * Source can be found at http://www.kalytta.com/bitmap.h + * + * Warning: This code should not be used in unmodified form in a production environment. + * It should only serve as a basis for your own development. + * There is only a minimal error handling in this code. (Notice added 20111211) + */ + +#include +#include + +#ifndef __LITTLE_ENDIAN__ +# ifndef __BIG_ENDIAN__ +# define __LITTLE_ENDIAN__ +# endif +#endif + +#ifdef __LITTLE_ENDIAN__ +# define BITMAP_SIGNATURE 0x4d42 +#else +# define BITMAP_SIGNATURE 0x424d +#endif + +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int32 int32_t; +#elif defined(__GNUC__) || defined(__CYGWIN__) || defined(__MWERKS__) || defined(__WATCOMC__) || defined(__PGI) || defined(__LCC__) +# include +#else +typedef unsigned int uint32_t; +typedef unsigned short int uint16_t; +typedef unsigned char uint8_t; +typedef int int32_t; +#endif + +#pragma pack(push, 1) + +typedef struct _BITMAP_FILEHEADER { + uint16_t Signature; + uint32_t Size; + uint32_t Reserved; + uint32_t BitsOffset; +} BITMAP_FILEHEADER; + +#define BITMAP_FILEHEADER_SIZE 14 + +typedef struct _BITMAP_HEADER { + uint32_t HeaderSize; + int32_t Width; + int32_t Height; + uint16_t Planes; + uint16_t BitCount; + uint32_t Compression; + uint32_t SizeImage; + int32_t PelsPerMeterX; + int32_t PelsPerMeterY; + uint32_t ClrUsed; + uint32_t ClrImportant; + uint32_t RedMask; + uint32_t GreenMask; + uint32_t BlueMask; + uint32_t AlphaMask; + uint32_t CsType; + uint32_t Endpoints[9]; // see http://msdn2.microsoft.com/en-us/library/ms536569.aspx + uint32_t GammaRed; + uint32_t GammaGreen; + uint32_t GammaBlue; +} BITMAP_HEADER; + +typedef struct _RGBA { + uint8_t Red; + uint8_t Green; + uint8_t Blue; + uint8_t Alpha; +} RGBA; + +typedef struct _BGRA { + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Alpha; +} BGRA; + +#pragma pack(pop) + +class CBitmap { +private: + BITMAP_FILEHEADER m_BitmapFileHeader; + BITMAP_HEADER m_BitmapHeader; + RGBA *m_BitmapData; + unsigned int m_BitmapSize; + + // Masks and bit counts shouldn't exceed 32 Bits +public: + class CColor { + public: + static inline unsigned int BitCountByMask(unsigned int Mask) { + unsigned int BitCount = 0; + while (Mask) { + Mask &= Mask - 1; + BitCount++; + } + return BitCount; + } + + static inline unsigned int BitPositionByMask(unsigned int Mask) { + return BitCountByMask((Mask & (~Mask + 1)) - 1); + } + + static inline unsigned int ComponentByMask(unsigned int Color, unsigned int Mask) { + unsigned int Component = Color & Mask; + return Component >> BitPositionByMask(Mask); + } + + static inline unsigned int BitCountToMask(unsigned int BitCount) { + return (BitCount == 32) ? 0xFFFFFFFF : (1 << BitCount) - 1; + } + + static unsigned int Convert(unsigned int Color, unsigned int FromBitCount, unsigned int ToBitCount) { + if (ToBitCount < FromBitCount) { + Color >>= (FromBitCount - ToBitCount); + } else { + Color <<= (ToBitCount - FromBitCount); + if (Color > 0) { + Color |= BitCountToMask(ToBitCount - FromBitCount); + } + } + return Color; + } + }; + +public: + + CBitmap() : m_BitmapData(0), m_BitmapSize(0) { + Dispose(); + } + + CBitmap(const char* Filename) : m_BitmapData(0), m_BitmapSize(0) { + Load(Filename); + } + + ~CBitmap() { + Dispose(); + } + + void Dispose() { + if (m_BitmapData) { + delete[] m_BitmapData; + m_BitmapData = 0; + } + memset(&m_BitmapFileHeader, 0, sizeof(m_BitmapFileHeader)); + memset(&m_BitmapHeader, 0, sizeof(m_BitmapHeader)); + } + + /* Load specified Bitmap and stores it as RGBA in an internal buffer */ + + bool Load(const char *Filename) { + std::ifstream file(Filename, std::ios::binary | std::ios::in); + + if (file.bad()) { + return false; + } + + if (file.is_open() == false) { + return false; + } + + Dispose(); + + file.read((char*) &m_BitmapFileHeader, BITMAP_FILEHEADER_SIZE); + if (m_BitmapFileHeader.Signature != BITMAP_SIGNATURE) { + return false; + } + + file.read((char*) &m_BitmapHeader, sizeof(BITMAP_HEADER)); + + /* Load Color Table */ + + file.seekg(BITMAP_FILEHEADER_SIZE + m_BitmapHeader.HeaderSize, std::ios::beg); + + unsigned int ColorTableSize = 0; + + if (m_BitmapHeader.BitCount == 1) { + ColorTableSize = 2; + } else if (m_BitmapHeader.BitCount == 4) { + ColorTableSize = 16; + } else if (m_BitmapHeader.BitCount == 8) { + ColorTableSize = 256; + } + + // Always allocate full sized color table + + BGRA* ColorTable = new BGRA[ColorTableSize]; // std::bad_alloc exception should be thrown if memory is not available + + file.read((char*) ColorTable, sizeof(BGRA) * m_BitmapHeader.ClrUsed); + + /* ... Color Table for 16 bits images are not supported yet */ + + m_BitmapSize = GetWidth() * GetHeight(); + m_BitmapData = new RGBA[m_BitmapSize]; + + unsigned int LineWidth = ((GetWidth() * GetBitCount() / 8) + 3) & ~3; + uint8_t *Line = new uint8_t[LineWidth]; + + file.seekg(m_BitmapFileHeader.BitsOffset, std::ios::beg); + + int Index = 0; + bool Result = true; + + if (m_BitmapHeader.Compression == 0) { + for (unsigned int i = 0; i < GetHeight(); i++) { + file.read((char*) Line, LineWidth); + + uint8_t *LinePtr = Line; + + for (unsigned int j = 0; j < GetWidth(); j++) { + if (m_BitmapHeader.BitCount == 1) { + uint32_t Color = *((uint8_t*) LinePtr); + for (int k = 0; k < 8; k++) { + m_BitmapData[Index].Red = ColorTable[Color & 0x80 ? 1 : 0].Red; + m_BitmapData[Index].Green = ColorTable[Color & 0x80 ? 1 : 0].Green; + m_BitmapData[Index].Blue = ColorTable[Color & 0x80 ? 1 : 0].Blue; + m_BitmapData[Index].Alpha = ColorTable[Color & 0x80 ? 1 : 0].Alpha; + Index++; + Color <<= 1; + } + LinePtr++; + j += 7; + } else if (m_BitmapHeader.BitCount == 4) { + uint32_t Color = *((uint8_t*) LinePtr); + m_BitmapData[Index].Red = ColorTable[(Color >> 4) & 0x0f].Red; + m_BitmapData[Index].Green = ColorTable[(Color >> 4) & 0x0f].Green; + m_BitmapData[Index].Blue = ColorTable[(Color >> 4) & 0x0f].Blue; + m_BitmapData[Index].Alpha = ColorTable[(Color >> 4) & 0x0f].Alpha; + Index++; + m_BitmapData[Index].Red = ColorTable[Color & 0x0f].Red; + m_BitmapData[Index].Green = ColorTable[Color & 0x0f].Green; + m_BitmapData[Index].Blue = ColorTable[Color & 0x0f].Blue; + m_BitmapData[Index].Alpha = ColorTable[Color & 0x0f].Alpha; + Index++; + LinePtr++; + j++; + } else if (m_BitmapHeader.BitCount == 8) { + uint32_t Color = *((uint8_t*) LinePtr); + m_BitmapData[Index].Red = ColorTable[Color].Red; + m_BitmapData[Index].Green = ColorTable[Color].Green; + m_BitmapData[Index].Blue = ColorTable[Color].Blue; + m_BitmapData[Index].Alpha = ColorTable[Color].Alpha; + Index++; + LinePtr++; + } else if (m_BitmapHeader.BitCount == 16) { + uint32_t Color = *((uint16_t*) LinePtr); + m_BitmapData[Index].Red = ((Color >> 10) & 0x1f) << 3; + m_BitmapData[Index].Green = ((Color >> 5) & 0x1f) << 3; + m_BitmapData[Index].Blue = (Color & 0x1f) << 3; + m_BitmapData[Index].Alpha = 255; + Index++; + LinePtr += 2; + } else if (m_BitmapHeader.BitCount == 24) { + uint32_t Color = *((uint32_t*) LinePtr); + m_BitmapData[Index].Blue = Color & 0xff; + m_BitmapData[Index].Green = (Color >> 8) & 0xff; + m_BitmapData[Index].Red = (Color >> 16) & 0xff; + m_BitmapData[Index].Alpha = 255; + Index++; + LinePtr += 3; + } else if (m_BitmapHeader.BitCount == 32) { + uint32_t Color = *((uint32_t*) LinePtr); + m_BitmapData[Index].Blue = Color & 0xff; + m_BitmapData[Index].Green = (Color >> 8) & 0xff; + m_BitmapData[Index].Red = (Color >> 16) & 0xff; + m_BitmapData[Index].Alpha = Color >> 24; + Index++; + LinePtr += 4; + } + } + } + } else if (m_BitmapHeader.Compression == 1) { // RLE 8 + uint8_t Count = 0; + uint8_t ColorIndex = 0; + int x = 0, y = 0; + + while (file.eof() == false) { + file.read((char*) &Count, sizeof(uint8_t)); + file.read((char*) &ColorIndex, sizeof(uint8_t)); + + if (Count > 0) { + Index = x + y * GetWidth(); + for (int k = 0; k < Count; k++) { + m_BitmapData[Index + k].Red = ColorTable[ColorIndex].Red; + m_BitmapData[Index + k].Green = ColorTable[ColorIndex].Green; + m_BitmapData[Index + k].Blue = ColorTable[ColorIndex].Blue; + m_BitmapData[Index + k].Alpha = ColorTable[ColorIndex].Alpha; + } + x += Count; + } else if (Count == 0) { + int Flag = ColorIndex; + if (Flag == 0) { + x = 0; + y++; + } else if (Flag == 1) { + break; + } else if (Flag == 2) { + char rx = 0; + char ry = 0; + file.read((char*) &rx, sizeof(char)); + file.read((char*) &ry, sizeof(char)); + x += rx; + y += ry; + } else { + Count = Flag; + Index = x + y * GetWidth(); + for (int k = 0; k < Count; k++) { + file.read((char*) &ColorIndex, sizeof(uint8_t)); + m_BitmapData[Index + k].Red = ColorTable[ColorIndex].Red; + m_BitmapData[Index + k].Green = ColorTable[ColorIndex].Green; + m_BitmapData[Index + k].Blue = ColorTable[ColorIndex].Blue; + m_BitmapData[Index + k].Alpha = ColorTable[ColorIndex].Alpha; + } + x += Count; + // Attention: Current Microsoft STL implementation seems to be buggy, tellg() always returns 0. + if (file.tellg() & 1) { + file.seekg(1, std::ios::cur); + } + } + } + } + } else if (m_BitmapHeader.Compression == 2) { // RLE 4 + /* RLE 4 is not supported */ + Result = false; + } else if (m_BitmapHeader.Compression == 3) { // BITFIELDS + + /* We assumes that mask of each color component can be in any order */ + + uint32_t BitCountRed = CColor::BitCountByMask(m_BitmapHeader.RedMask); + uint32_t BitCountGreen = CColor::BitCountByMask(m_BitmapHeader.GreenMask); + uint32_t BitCountBlue = CColor::BitCountByMask(m_BitmapHeader.BlueMask); + uint32_t BitCountAlpha = CColor::BitCountByMask(m_BitmapHeader.AlphaMask); + + for (unsigned int i = 0; i < GetHeight(); i++) { + file.read((char*) Line, LineWidth); + + uint8_t *LinePtr = Line; + + for (unsigned int j = 0; j < GetWidth(); j++) { + + uint32_t Color = 0; + + if (m_BitmapHeader.BitCount == 16) { + Color = *((uint16_t*) LinePtr); + LinePtr += 2; + } else if (m_BitmapHeader.BitCount == 32) { + Color = *((uint32_t*) LinePtr); + LinePtr += 4; + } else { + // Other formats are not valid + } + m_BitmapData[Index].Red = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.RedMask), BitCountRed, 8); + m_BitmapData[Index].Green = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.GreenMask), BitCountGreen, 8); + m_BitmapData[Index].Blue = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.BlueMask), BitCountBlue, 8); + m_BitmapData[Index].Alpha = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.AlphaMask), BitCountAlpha, 8); + + Index++; + } + } + } + + delete [] ColorTable; + delete [] Line; + + file.close(); + return Result; + } + + bool Save(const char* Filename, unsigned int BitCount = 32) { + bool Result = true; + + std::ofstream file(Filename, std::ios::out | std::ios::binary); + + if (file.is_open() == false) { + return false; + } + + BITMAP_FILEHEADER bfh; + BITMAP_HEADER bh; + memset(&bfh, 0, sizeof(bfh)); + memset(&bh, 0, sizeof(bh)); + + bfh.Signature = BITMAP_SIGNATURE; + bfh.BitsOffset = BITMAP_FILEHEADER_SIZE + sizeof(BITMAP_HEADER); + bfh.Size = (GetWidth() * GetHeight() * BitCount) / 8 + bfh.BitsOffset; + + bh.HeaderSize = sizeof(BITMAP_HEADER); + bh.BitCount = BitCount; + + if (BitCount == 32) { + bh.Compression = 3; // BITFIELD + bh.AlphaMask = 0xff000000; + bh.BlueMask = 0x00ff0000; + bh.GreenMask = 0x0000ff00; + bh.RedMask = 0x000000ff; + } else if (BitCount == 16) { + bh.Compression = 3; // BITFIELD + bh.AlphaMask = 0x00000000; + bh.BlueMask = 0x0000001f; + bh.GreenMask = 0x000007E0; + bh.RedMask = 0x0000F800; + } else { + bh.Compression = 0; // RGB + } + + unsigned int LineWidth = (GetWidth() + 3) & ~3; + + bh.Planes = 1; + bh.Height = GetHeight(); + bh.Width = GetWidth(); + bh.SizeImage = (LineWidth * BitCount * GetHeight()) / 8; + bh.PelsPerMeterX = 3780; + bh.PelsPerMeterY = 3780; + + if (BitCount == 32) { + file.write((char*) &bfh, sizeof(BITMAP_FILEHEADER)); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) m_BitmapData, bh.SizeImage); + } else if (BitCount < 16) { + uint8_t* Bitmap = new uint8_t[bh.SizeImage]; + + BGRA *Palette = 0; + unsigned int PaletteSize = 0; + + if (GetBitsWithPalette(Bitmap, bh.SizeImage, BitCount, Palette, PaletteSize)) { + bfh.BitsOffset += PaletteSize * sizeof(BGRA); + + file.write((char*) &bfh, BITMAP_FILEHEADER_SIZE); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) Palette, PaletteSize * sizeof(BGRA)); + file.write((char*) Bitmap, bh.SizeImage); + } + delete [] Bitmap; + delete [] Palette; + } else { + uint32_t RedMask = 0; + uint32_t GreenMask = 0; + uint32_t BlueMask = 0; + uint32_t AlphaMask = 0; + + if (BitCount == 16) { + RedMask = 0x0000F800; + GreenMask = 0x000007E0; + BlueMask = 0x0000001F; + AlphaMask = 0x00000000; + } else if (BitCount == 24) { + RedMask = 0x00FF0000; + GreenMask = 0x0000FF00; + BlueMask = 0x000000FF; + } else { + // Other color formats are not valid + Result = false; + } + + if (Result) { + if (GetBits(NULL, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) { + uint8_t* Bitmap = new uint8_t[bh.SizeImage]; + if (GetBits(Bitmap, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) { + file.write((char*) &bfh, sizeof(BITMAP_FILEHEADER)); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) Bitmap, bh.SizeImage); + } + delete [] Bitmap; + } + } + } + + file.close(); + return Result; + } + + unsigned int GetWidth() { + /* Add plausibility test */ + // if (abs(m_BitmapHeader.Width) > 8192) { + // m_BitmapHeader.Width = 8192; + // } + return m_BitmapHeader.Width < 0 ? -m_BitmapHeader.Width : m_BitmapHeader.Width; + } + + unsigned int GetHeight() { + /* Add plausibility test */ + // if (abs(m_BitmapHeader.Height) > 8192) { + // m_BitmapHeader.Height = 8192; + // } + return m_BitmapHeader.Height < 0 ? -m_BitmapHeader.Height : m_BitmapHeader.Height; + } + + unsigned int GetBitCount() { + /* Add plausibility test */ + // if (m_BitmapHeader.BitCount > 32) { + // m_BitmapHeader.BitCount = 32; + // } + return m_BitmapHeader.BitCount; + } + + /* Copies internal RGBA buffer to user specified buffer */ + + bool GetBits(void* Buffer, unsigned int &Size) { + bool Result = false; + if (Size == 0 || Buffer == 0) { + Size = m_BitmapSize * sizeof(RGBA); + Result = m_BitmapSize != 0; + } else { + memcpy(Buffer, m_BitmapData, Size); + Result = true; + } + return Result; + } + + /* Returns internal RGBA buffer */ + + void* GetBits() { + return m_BitmapData; + } + + /* Copies internal RGBA buffer to user specified buffer and converts it into destination + * bit format specified by component masks. + * + * Typical Bitmap color formats (BGR/BGRA): + * + * Masks for 16 bit (5-5-5): ALPHA = 0x00000000, RED = 0x00007C00, GREEN = 0x000003E0, BLUE = 0x0000001F + * Masks for 16 bit (5-6-5): ALPHA = 0x00000000, RED = 0x0000F800, GREEN = 0x000007E0, BLUE = 0x0000001F + * Masks for 24 bit: ALPHA = 0x00000000, RED = 0x00FF0000, GREEN = 0x0000FF00, BLUE = 0x000000FF + * Masks for 32 bit: ALPHA = 0xFF000000, RED = 0x00FF0000, GREEN = 0x0000FF00, BLUE = 0x000000FF + * + * Other color formats (RGB/RGBA): + * + * Masks for 32 bit (RGBA): ALPHA = 0xFF000000, RED = 0x000000FF, GREEN = 0x0000FF00, BLUE = 0x00FF0000 + * + * Bit count will be rounded to next 8 bit boundary. If IncludePadding is true, it will be ensured + * that line width is a multiple of 4. padding bytes are included if necessary. + * + * NOTE: systems with big endian byte order may require masks in inversion order. + */ + + bool GetBits(void* Buffer, unsigned int &Size, unsigned int RedMask, unsigned int GreenMask, unsigned int BlueMask, unsigned int AlphaMask, bool IncludePadding = true) { + bool Result = false; + + uint32_t BitCountRed = CColor::BitCountByMask(RedMask); + uint32_t BitCountGreen = CColor::BitCountByMask(GreenMask); + uint32_t BitCountBlue = CColor::BitCountByMask(BlueMask); + uint32_t BitCountAlpha = CColor::BitCountByMask(AlphaMask); + + unsigned int BitCount = (BitCountRed + BitCountGreen + BitCountBlue + BitCountAlpha + 7) & ~7; + + if (BitCount > 32) { + return false; + } + + unsigned int w = GetWidth(); + //unsigned int LineWidth = (w + 3) & ~3; + unsigned int dataBytesPerLine = (w * BitCount + 7) / 8; + unsigned int LineWidth = (dataBytesPerLine + 3) & ~3; + + if (Size == 0 || Buffer == 0) { + //Size = (LineWidth * GetHeight() * BitCount) / 8 + sizeof(unsigned int); + Size = (GetWidth() * GetHeight() * BitCount) / 8 + sizeof(unsigned int); + return true; + } + + uint8_t* BufferPtr = (uint8_t*) Buffer; + + Result = true; + + uint32_t BitPosRed = CColor::BitPositionByMask(RedMask); + uint32_t BitPosGreen = CColor::BitPositionByMask(GreenMask); + uint32_t BitPosBlue = CColor::BitPositionByMask(BlueMask); + uint32_t BitPosAlpha = CColor::BitPositionByMask(AlphaMask); + + unsigned int j = 0; + + for (unsigned int i = 0; i < m_BitmapSize; i++) { + *(uint32_t*) BufferPtr = + (CColor::Convert(m_BitmapData[i].Blue, 8, BitCountBlue) << BitPosBlue) | + (CColor::Convert(m_BitmapData[i].Green, 8, BitCountGreen) << BitPosGreen) | + (CColor::Convert(m_BitmapData[i].Red, 8, BitCountRed) << BitPosRed) | + (CColor::Convert(m_BitmapData[i].Alpha, 8, BitCountAlpha) << BitPosAlpha); + + if (IncludePadding) { + j++; + if (j >= w) { + for (unsigned int k = 0; k < LineWidth - dataBytesPerLine; k++) { + BufferPtr += (BitCount >> 3); + } + j = 0; + } + } + + BufferPtr += (BitCount >> 3); + } + + Size -= sizeof(unsigned int); + + return Result; + } + + /* See GetBits(). + * It creates a corresponding color table (palette) which have to be destroyed by the user after usage. + * + * Supported Bit depths are: 4, 8 + * + * Todo: Optimize, use optimized palette, do ditehring (see my dithering class), support padding for 4 bit bitmaps + */ + + bool GetBitsWithPalette(void* Buffer, unsigned int &Size, unsigned int BitCount, BGRA* &Palette, unsigned int &PaletteSize, bool OptimalPalette = false, bool IncludePadding = true) { + bool Result = false; + + if (BitCount > 16) { + return false; + } + + unsigned int w = GetWidth(); + unsigned int dataBytesPerLine = (w * BitCount + 7) / 8; + unsigned int LineWidth = (dataBytesPerLine + 3) & ~3; + + if (Size == 0 || Buffer == 0) { + Size = (LineWidth * GetHeight() * BitCount) / 8; + return true; + } + + + if (OptimalPalette) { + PaletteSize = 0; + // Not implemented + } else { + if (BitCount == 1) { + PaletteSize = 2; + // Not implemented: Who need that? + } else if (BitCount == 4) { // 2:2:1 + PaletteSize = 16; + Palette = new BGRA[PaletteSize]; + for (int r = 0; r < 4; r++) { + for (int g = 0; g < 2; g++) { + for (int b = 0; b < 2; b++) { + Palette[r | g << 2 | b << 3].Red = r ? (r << 6) | 0x3f : 0; + Palette[r | g << 2 | b << 3].Green = g ? (g << 7) | 0x7f : 0; + Palette[r | g << 2 | b << 3].Blue = b ? (b << 7) | 0x7f : 0; + Palette[r | g << 2 | b << 3].Alpha = 0xff; + } + } + } + } else if (BitCount == 8) { // 3:3:2 + PaletteSize = 256; + Palette = new BGRA[PaletteSize]; + for (int r = 0; r < 8; r++) { + for (int g = 0; g < 8; g++) { + for (int b = 0; b < 4; b++) { + Palette[r | g << 3 | b << 6].Red = r ? (r << 5) | 0x1f : 0; + Palette[r | g << 3 | b << 6].Green = g ? (g << 5) | 0x1f : 0; + Palette[r | g << 3 | b << 6].Blue = b ? (b << 6) | 0x3f : 0; + Palette[r | g << 3 | b << 6].Alpha = 0xff; + } + } + } + } else if (BitCount == 16) { // 5:5:5 + // Not implemented + } + } + + unsigned int j = 0; + uint8_t* BufferPtr = (uint8_t*) Buffer; + + for (unsigned int i = 0; i < m_BitmapSize; i++) { + if (BitCount == 1) { + // Not implemented: Who needs that? + } else if (BitCount == 4) { + *BufferPtr = ((m_BitmapData[i].Red >> 6) | (m_BitmapData[i].Green >> 7) << 2 | (m_BitmapData[i].Blue >> 7) << 3) << 4; + i++; + *BufferPtr |= (m_BitmapData[i].Red >> 6) | (m_BitmapData[i].Green >> 7) << 2 | (m_BitmapData[i].Blue >> 7) << 3; + } else if (BitCount == 8) { + *BufferPtr = (m_BitmapData[i].Red >> 5) | (m_BitmapData[i].Green >> 5) << 3 | (m_BitmapData[i].Blue >> 5) << 6; + } else if (BitCount == 16) { + // Not implemented + } + + if (IncludePadding) { + j++; + if (j >= w) { + for (unsigned int k = 0; k < (LineWidth - dataBytesPerLine); k++) { + BufferPtr += BitCount / 8; + } + j = 0; + } + } + + BufferPtr++; + } + + Result = true; + + return Result; + } + + /* Set Bitmap Bits. Will be converted to RGBA internally */ + + bool SetBits(void* Buffer, unsigned int Width, unsigned int Height, unsigned int RedMask, unsigned int GreenMask, unsigned int BlueMask, unsigned int AlphaMask = 0) { + if (Buffer == 0) { + return false; + } + + uint8_t *BufferPtr = (uint8_t*) Buffer; + + Dispose(); + + m_BitmapHeader.Width = Width; + m_BitmapHeader.Height = Height; + m_BitmapHeader.BitCount = 32; + m_BitmapHeader.Compression = 3; + + m_BitmapSize = GetWidth() * GetHeight(); + m_BitmapData = new RGBA[m_BitmapSize]; + + /* Find bit count by masks (rounded to next 8 bit boundary) */ + + unsigned int BitCount = (CColor::BitCountByMask(RedMask | GreenMask | BlueMask | AlphaMask) + 7) & ~7; + + uint32_t BitCountRed = CColor::BitCountByMask(RedMask); + uint32_t BitCountGreen = CColor::BitCountByMask(GreenMask); + uint32_t BitCountBlue = CColor::BitCountByMask(BlueMask); + uint32_t BitCountAlpha = CColor::BitCountByMask(AlphaMask); + + for (unsigned int i = 0; i < m_BitmapSize; i++) { + unsigned int Color = 0; + if (BitCount <= 8) { + Color = *((uint8_t*) BufferPtr); + BufferPtr += 1; + } else if (BitCount <= 16) { + Color = *((uint16_t*) BufferPtr); + BufferPtr += 2; + } else if (BitCount <= 24) { + Color = *((uint32_t*) BufferPtr); + BufferPtr += 3; + } else if (BitCount <= 32) { + Color = *((uint32_t*) BufferPtr); + BufferPtr += 4; + } else { + /* unsupported */ + BufferPtr += 1; + } + m_BitmapData[i].Alpha = CColor::Convert(CColor::ComponentByMask(Color, AlphaMask), BitCountAlpha, 8); + m_BitmapData[i].Red = CColor::Convert(CColor::ComponentByMask(Color, RedMask), BitCountRed, 8); + m_BitmapData[i].Green = CColor::Convert(CColor::ComponentByMask(Color, GreenMask), BitCountGreen, 8); + m_BitmapData[i].Blue = CColor::Convert(CColor::ComponentByMask(Color, BlueMask), BitCountBlue, 8); + } + + return true; + } +}; + +//////////////////////////////////////////////////////////////////////////// +// +// Fonction pour charger une texture +// +//////////////////////////////////////////////////////////////////////////// + +#include +#include +//#include + +unsigned char *ChargerImage( std::string fichier, GLsizei &largeur, GLsizei &hauteur ) +{ + // vérifier la présence du fichier BMP en essayant de l'ouvrir + FILE *img = fopen( fichier.c_str(), "r" ); + if ( !img ) + { + std::cerr << "Fichier de texture manquant: " << fichier << std::endl; + return NULL; + } + fclose( img ); + + // lire le fichier dans un objet CBitmap + CBitmap image( fichier.c_str() ); + // obtenir la taille de l'image et les composantes RGBA + largeur = image.GetWidth(); + hauteur = image.GetHeight(); + unsigned int taille = largeur * hauteur * 4; // l * h * 4 composantes + unsigned char *pixels = new unsigned char[taille]; + image.GetBits( (void*) pixels, taille, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 ); + + //SDL_Surface* surface = SDL_LoadBMP( fichier.c_str() ); + // https://wiki.libsdl.org/SDL_LoadBMP + // largeur = surface->w; + // hauteur = surface->h; + // pixels = surface->pixels; + + return pixels; +} + +#endif + +#ifndef INF2705_FORME_H +#define INF2705_FORME_H + +//////////////////////////////////////////////////////////////////////////// +// +// Fonctions variées pour afficher des formes connues +// (INF2705, Benoît Ozell) +//////////////////////////////////////////////////////////////////////////// + +#include +#include + +#if !defined( __APPLE__ ) + +/* Copyright (c) Mark J. Kilgard, 1994, 1997. */ + +/** + (c) Copyright 1993, Silicon Graphics, Inc. + + ALL RIGHTS RESERVED + + Permission to use, copy, modify, and distribute this software + for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that + both the copyright notice and this permission notice appear in + supporting documentation, and that the name of Silicon + Graphics, Inc. not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. + + THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU + "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR + OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO + EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE + ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR + CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER, + INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, + SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR + NOT SILICON GRAPHICS, INC. HAS BEEN ADVISED OF THE POSSIBILITY + OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR + PERFORMANCE OF THIS SOFTWARE. + + US Government Users Restricted Rights + + Use, duplication, or disclosure by the Government is subject to + restrictions set forth in FAR 52.227.19(c)(2) or subparagraph + (c)(1)(ii) of the Rights in Technical Data and Computer + Software clause at DFARS 252.227-7013 and/or in similar or + successor clauses in the FAR or the DOD or NASA FAR + Supplement. Unpublished-- rights reserved under the copyright + laws of the United States. Contractor/manufacturer is Silicon + Graphics, Inc., 2011 N. Shoreline Blvd., Mountain View, CA + 94039-7311. + + OpenGL(TM) is a trademark of Silicon Graphics, Inc. +*/ + +#include +#include + +static GLfloat dodec[20][3]; + +static void initDodecahedron(void) +{ + GLfloat alpha, beta; + + alpha = sqrt(2.0 / (3.0 + sqrt(5.0))); + beta = 1.0 + sqrt(6.0 / (3.0 + sqrt(5.0)) - 2.0 + 2.0 * sqrt(2.0 / (3.0 + sqrt(5.0)))); + dodec[0][0] = -alpha; dodec[0][1] = 0; dodec[0][2] = beta; + dodec[1][0] = alpha; dodec[1][1] = 0; dodec[1][2] = beta; + dodec[2][0] = -1; dodec[2][1] = -1; dodec[2][2] = -1; + dodec[3][0] = -1; dodec[3][1] = -1; dodec[3][2] = 1; + dodec[4][0] = -1; dodec[4][1] = 1; dodec[4][2] = -1; + dodec[5][0] = -1; dodec[5][1] = 1; dodec[5][2] = 1; + dodec[6][0] = 1; dodec[6][1] = -1; dodec[6][2] = -1; + dodec[7][0] = 1; dodec[7][1] = -1; dodec[7][2] = 1; + dodec[8][0] = 1; dodec[8][1] = 1; dodec[8][2] = -1; + dodec[9][0] = 1; dodec[9][1] = 1; dodec[9][2] = 1; + dodec[10][0] = beta; dodec[10][1] = alpha; dodec[10][2] = 0; + dodec[11][0] = beta; dodec[11][1] = -alpha; dodec[11][2] = 0; + dodec[12][0] = -beta; dodec[12][1] = alpha; dodec[12][2] = 0; + dodec[13][0] = -beta; dodec[13][1] = -alpha; dodec[13][2] = 0; + dodec[14][0] = -alpha; dodec[14][1] = 0; dodec[14][2] = -beta; + dodec[15][0] = alpha; dodec[15][1] = 0; dodec[15][2] = -beta; + dodec[16][0] = 0; dodec[16][1] = beta; dodec[16][2] = alpha; + dodec[17][0] = 0; dodec[17][1] = beta; dodec[17][2] = -alpha; + dodec[18][0] = 0; dodec[18][1] = -beta; dodec[18][2] = alpha; + dodec[19][0] = 0; dodec[19][1] = -beta; dodec[19][2] = -alpha; + +} + +#define DIFF3(_a,_b,_c) { \ + (_c)[0] = (_a)[0] - (_b)[0]; \ + (_c)[1] = (_a)[1] - (_b)[1]; \ + (_c)[2] = (_a)[2] - (_b)[2]; \ + } + +static void crossprod(GLfloat v1[3], GLfloat v2[3], GLfloat prod[3]) +{ + GLfloat p[3]; /* in case prod == v1 or v2 */ + + p[0] = v1[1] * v2[2] - v2[1] * v1[2]; + p[1] = v1[2] * v2[0] - v2[2] * v1[0]; + p[2] = v1[0] * v2[1] - v2[0] * v1[1]; + prod[0] = p[0]; + prod[1] = p[1]; + prod[2] = p[2]; +} + +static void normalize(GLfloat v[3]) +{ + GLfloat d; + + d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if (d == 0.0) + { + printf("normalize: zero length vector"); + v[0] = d = 1.0; + } + d = 1 / d; + v[0] *= d; + v[1] *= d; + v[2] *= d; +} + +static void pentagon(int a, int b, int c, int d, int e, GLenum shadeType) +{ + GLfloat n0[3], d1[3], d2[3]; + + DIFF3(dodec[a], dodec[b], d1); + DIFF3(dodec[b], dodec[c], d2); + crossprod(d1, d2, n0); + normalize(n0); + + glBegin(shadeType); + glNormal3fv(n0); + glVertex3fv(&dodec[a][0]); + glVertex3fv(&dodec[b][0]); + glVertex3fv(&dodec[c][0]); + glVertex3fv(&dodec[d][0]); + glVertex3fv(&dodec[e][0]); + glEnd(); +} + +static void dodecahedron(GLenum type) +{ + static int inited = 0; + + if (inited == 0) + { + inited = 1; + initDodecahedron(); + } + pentagon(0, 1, 9, 16, 5, type); + pentagon(1, 0, 3, 18, 7, type); + pentagon(1, 7, 11, 10, 9, type); + pentagon(11, 7, 18, 19, 6, type); + pentagon(8, 17, 16, 9, 10, type); + pentagon(2, 14, 15, 6, 19, type); + pentagon(2, 13, 12, 4, 14, type); + pentagon(2, 19, 18, 3, 13, type); + pentagon(3, 0, 5, 12, 13, type); + pentagon(6, 15, 8, 10, 11, type); + pentagon(4, 17, 8, 15, 14, type); + pentagon(4, 12, 5, 16, 17, type); +} + +void shapesWireDodecahedron(void) +{ + dodecahedron(GL_LINE_LOOP); +} + +void shapesSolidDodecahedron(void) +{ + dodecahedron(GL_TRIANGLE_FAN); +} + +static void recorditem(GLfloat * n1, GLfloat * n2, GLfloat * n3, + GLenum shadeType) +{ + GLfloat q0[3], q1[3]; + + DIFF3(n1, n2, q0); + DIFF3(n2, n3, q1); + crossprod(q0, q1, q1); + normalize(q1); + + glBegin(shadeType); + glNormal3fv(q1); + glVertex3fv(n1); + glVertex3fv(n2); + glVertex3fv(n3); + glEnd(); +} + +static void subdivide(GLfloat * v0, GLfloat * v1, GLfloat * v2, + GLenum shadeType) +{ + int depth = 1; + for (int i = 0; i < depth; i++) + { + for (int j = 0; i + j < depth; j++) + { + GLfloat w0[3], w1[3], w2[3]; + int k = depth - i - j; + for (int n = 0; n < 3; n++) + { + w0[n] = (i * v0[n] + j * v1[n] + k * v2[n]) / depth; + w1[n] = ((i + 1) * v0[n] + j * v1[n] + (k - 1) * v2[n]) / depth; + w2[n] = (i * v0[n] + (j + 1) * v1[n] + (k - 1) * v2[n]) / depth; + } + GLfloat l; + l = sqrt(w0[0] * w0[0] + w0[1] * w0[1] + w0[2] * w0[2]); + w0[0] /= l; + w0[1] /= l; + w0[2] /= l; + l = sqrt(w1[0] * w1[0] + w1[1] * w1[1] + w1[2] * w1[2]); + w1[0] /= l; + w1[1] /= l; + w1[2] /= l; + l = sqrt(w2[0] * w2[0] + w2[1] * w2[1] + w2[2] * w2[2]); + w2[0] /= l; + w2[1] /= l; + w2[2] /= l; + recorditem(w1, w0, w2, shadeType); + } + } +} + +static void drawtriangle(int i, GLfloat data[][3], int ndx[][3], + GLenum shadeType) +{ + GLfloat *x0 = data[ndx[i][0]]; + GLfloat *x1 = data[ndx[i][1]]; + GLfloat *x2 = data[ndx[i][2]]; + subdivide(x0, x1, x2, shadeType); +} + +/* octahedron data: The octahedron produced is centered at the + origin and has radius 1.0 */ +static GLfloat odata[6][3] = +{ + {1.0, 0.0, 0.0}, + {-1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {0.0, -1.0, 0.0}, + {0.0, 0.0, 1.0}, + {0.0, 0.0, -1.0} +}; + +static int ondex[8][3] = +{ + {0, 4, 2}, + {1, 2, 4}, + {0, 3, 4}, + {1, 4, 3}, + {0, 2, 5}, + {1, 5, 2}, + {0, 5, 3}, + {1, 3, 5} +}; + +static void octahedron(GLenum shadeType) +{ + for (int i = 7; i >= 0; i--) + { + drawtriangle(i, odata, ondex, shadeType); + } +} + +void shapesWireOctahedron(void) +{ + octahedron(GL_LINE_LOOP); +} + +void shapesSolidOctahedron(void) +{ + octahedron(GL_TRIANGLES); +} + + +/* icosahedron data: These numbers are rigged to make an + icosahedron of radius 1.0 */ + +#define X .525731112119133606 +#define Z .850650808352039932 + +static GLfloat idata[12][3] = +{ + {-X, 0, Z}, + {X, 0, Z}, + {-X, 0, -Z}, + {X, 0, -Z}, + {0, Z, X}, + {0, Z, -X}, + {0, -Z, X}, + {0, -Z, -X}, + {Z, X, 0}, + {-Z, X, 0}, + {Z, -X, 0}, + {-Z, -X, 0} +}; + +static int connectivity[20][3] = +{ + {0, 4, 1}, + {0, 9, 4}, + {9, 5, 4}, + {4, 5, 8}, + {4, 8, 1}, + {8, 10, 1}, + {8, 3, 10}, + {5, 3, 8}, + {5, 2, 3}, + {2, 7, 3}, + {7, 10, 3}, + {7, 6, 10}, + {7, 11, 6}, + {11, 0, 6}, + {0, 1, 6}, + {6, 1, 10}, + {9, 0, 11}, + {9, 11, 2}, + {9, 2, 5}, + {7, 2, 11}, +}; + +static void icosahedron(GLenum shadeType) +{ + for (int i = 19; i >= 0; i--) + { + drawtriangle(i, idata, connectivity, shadeType); + } +} + +void shapesWireIcosahedron(void) +{ + icosahedron(GL_LINE_LOOP); +} + +void shapesSolidIcosahedron(void) +{ + icosahedron(GL_TRIANGLES); +} + + +/* tetrahedron data: */ + +#define T 1.73205080756887729 + +static GLfloat tdata[4][3] = +{ + {T, T, T}, + {T, -T, -T}, + {-T, T, -T}, + {-T, -T, T} +}; +#undef T + +static int tndex[4][3] = +{ + {0, 1, 3}, + {2, 1, 0}, + {3, 2, 0}, + {1, 2, 3} +}; + +static void tetrahedron(GLenum shadeType) +{ + for (int i = 3; i >= 0; i--) + drawtriangle(i, tdata, tndex, shadeType); +} + +void shapesWireTetrahedron(void) +{ + tetrahedron(GL_LINE_LOOP); +} + +void shapesSolidTetrahedron(void) +{ + tetrahedron(GL_TRIANGLES); +} + + +/* Rim, body, lid, and bottom data must be reflected in x and + y; handle and spout data across the y axis only. */ + +static int patchdata[][16] = +{ + /* rim */ + {102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + /* body */ + {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + {24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}, + /* lid */ + {96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,}, + {0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117}, + /* bottom */ + {118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37}, + /* handle */ + {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}, + {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67}, + /* spout */ + {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83}, + {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95} +}; + +static float cpdata[][3] = +{ + {0.2,0,2.7}, {0.2,-0.112,2.7}, {0.112,-0.2,2.7}, {0,-0.2,2.7}, + {1.3375,0,2.53125}, {1.3375,-0.749,2.53125}, {0.749,-1.3375,2.53125}, + {0,-1.3375,2.53125}, {1.4375,0,2.53125}, {1.4375,-0.805, 2.53125}, + {0.805,-1.4375,2.53125}, {0,-1.4375,2.53125}, {1.5,0,2.4}, {1.5,-0.84,2.4}, + {0.84,-1.5,2.4}, {0,-1.5,2.4}, {1.75,0, 1.875}, {1.75,-0.98,1.875}, + {0.98,-1.75,1.875}, {0,-1.75,1.875}, {2,0,1.35}, {2,-1.12,1.35}, + {1.12,-2,1.35}, {0,-2,1.35}, {2,0,0.9}, {2,-1.12,0.9}, {1.12,-2,0.9}, + {0,-2,0.9}, {-2,0,0.9}, {2, 0,0.45}, {2,-1.12,0.45}, {1.12,-2,0.45}, + {0,-2,0.45}, {1.5,0,0.225}, {1.5,-0.84,0.225}, {0.84,-1.5,0.225}, + {0,-1.5,0.225}, {1.5, 0,0.15}, {1.5,-0.84,0.15}, {0.84,-1.5,0.15}, + {0,-1.5,0.15}, {-1.6,0,2.025}, {-1.6,-0.3,2.025}, {-1.5,-0.3,2.25}, + {-1.5,0,2.25}, {-2.3,0,2.025}, {-2.3,-0.3,2.025}, {-2.5,-0.3,2.25}, + {-2.5,0,2.25}, {-2.7,0,2.025}, {-2.7,-0.3,2.025}, {-3,-0.3,2.25}, + {-3,0,2.25}, {-2.7,0,1.8}, {-2.7,-0.3,1.8}, {-3,-0.3,1.8}, {-3,0,1.8}, + {-2.7,0,1.575}, {-2.7,-0.3,1.575}, {-3,-0.3,1.35}, {-3,0,1.35}, + {-2.5,0,1.125}, {-2.5,-0.3,1.125}, {-2.65,-0.3,0.9375}, {-2.65,0, 0.9375}, + {-2,-0.3,0.9}, {-1.9,-0.3,0.6}, {-1.9,0,0.6}, {1.7,0, 1.425}, + {1.7,-0.66,1.425}, {1.7,-0.66,0.6}, {1.7,0,0.6}, {2.6,0, 1.425}, + {2.6,-0.66,1.425}, {3.1,-0.66,0.825}, {3.1,0,0.825}, {2.3, 0,2.1}, + {2.3,-0.25,2.1}, {2.4,-0.25,2.025}, {2.4,0,2.025}, {2.7, 0,2.4}, + {2.7,-0.25,2.4}, {3.3,-0.25,2.4}, {3.3,0,2.4}, {2.8,0, 2.475}, + {2.8,-0.25,2.475}, {3.525,-0.25,2.49375}, {3.525,0, 2.49375}, {2.9,0,2.475}, + {2.9,-0.15,2.475}, {3.45,-0.15,2.5125}, {3.45,0,2.5125}, {2.8,0,2.4}, + {2.8,-0.15,2.4}, {3.2,-0.15,2.4}, {3.2,0,2.4}, {0,0,3.15}, {0.8,0,3.15}, + {0.8,-0.45,3.15}, {0.45, -0.8,3.15}, {0,-0.8,3.15}, {0,0,2.85}, {1.4,0,2.4}, + {1.4,-0.784, 2.4}, {0.784,-1.4,2.4}, {0,-1.4,2.4}, {0.4,0,2.55}, + {0.4,-0.224, 2.55}, {0.224,-0.4,2.55}, {0,-0.4,2.55}, {1.3,0,2.55}, + {1.3,-0.728,2.55}, {0.728,-1.3,2.55}, {0,-1.3,2.55}, {1.3,0,2.4}, + {1.3,-0.728,2.4}, {0.728,-1.3,2.4}, {0,-1.3,2.4}, {0,0,0}, {1.425,-0.798,0}, + {1.5,0,0.075}, {1.425,0,0}, {0.798,-1.425,0}, {0,-1.5, 0.075}, {0,-1.425,0}, + {1.5,-0.84,0.075}, {0.84,-1.5,0.075} +}; + +static float tex[2][2][2] = +{ + { {0, 0}, + {1, 0}}, + { {0, 1}, + {1, 1}} +}; + + +static void teapot(GLint grid, GLenum type) +{ + float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3]; + + glPushAttrib(GL_ENABLE_BIT | GL_EVAL_BIT); + glEnable(GL_AUTO_NORMAL); + glEnable(GL_MAP2_VERTEX_3); + glEnable(GL_MAP2_TEXTURE_COORD_2); + for (int i = 0; i < 10; i++) + { + for (int j = 0; j < 4; j++) + { + for (int k = 0; k < 4; k++) + { + for (int l = 0; l < 3; l++) + { + p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l]; + q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l]; + if (l == 1) q[j][k][l] *= -1.0; + if (i < 6) + { + r[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l]; + if (l == 0) r[j][k][l] *= -1.0; + s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l]; + if (l == 0) s[j][k][l] *= -1.0; + if (l == 1) s[j][k][l] *= -1.0; + } + } + } + } + glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &tex[0][0][0]); + glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &p[0][0][0]); + glMapGrid2f(grid, 0.0, 1.0, grid, 0.0, 1.0); + glEvalMesh2(type, 0, grid, 0, grid); + glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0]); + glEvalMesh2(type, 0, grid, 0, grid); + if (i < 6) + { + glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0]); + glEvalMesh2(type, 0, grid, 0, grid); + glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0]); + glEvalMesh2(type, 0, grid, 0, grid); + } + } + glPopAttrib(); +} + +void shapesSolidTeapot() +{ + teapot(14, GL_FILL); +} + +void shapesWireTeapot() +{ + teapot(10, GL_LINE); +} +#endif + + +////////////////////////////////////////////////////////// + +class FormeBase2705 +{ +public: + FormeBase2705( bool plein = true ) + : plein_(plein), + vao(0), locVertex(-1), locNormal(-1), locTexCoord(-1) + { + glGenVertexArrays( 1, &vao ); + } + ~FormeBase2705() + { + glDeleteVertexArrays( 1, &vao ); + } +protected: + bool obtenirAttributs( ) + { + GLint prog = 0; glGetIntegerv( GL_CURRENT_PROGRAM, &prog ); + if ( prog <= 0 ) + { + std::cerr << "Pas de programme actif!" << std::endl; + return(false); + } + + locVertex = glGetAttribLocation( prog, "Vertex" ); + if ( locVertex == -1 ) + { + std::cerr << "Pas de nuanceur de sommets!" << std::endl; + return(false); + } + locNormal = glGetAttribLocation( prog, "Normal" ); + locTexCoord = glGetAttribLocation( prog, "TexCoord" ); + if ( locTexCoord < 0 ) locTexCoord = glGetAttribLocation( prog, "TexCoord0" ); + if ( locTexCoord < 0 ) locTexCoord = glGetAttribLocation( prog, "MultiTexCoord0" ); + return(true); + } + + bool plein_; + GLuint vao; + GLint locVertex, locNormal, locTexCoord; +}; + +////////// + +#define AJOUTE(X,Y,Z,NX,NY,NZ,S,T) \ +{ \ + if ( locTexCoord >= 0 ) \ + { texcoord[2*nsommets+0] = (S); texcoord[2*nsommets+1] = (T); } \ + if ( locNormal >= 0 ) \ + { normales[3*nsommets+0] = (NX); normales[3*nsommets+1] = (NY); normales[3*nsommets+2] = (NZ); } \ + { sommets[3*nsommets+0] = (X); sommets[3*nsommets+1] = (Y); sommets[3*nsommets+2] = (Z); ++nsommets; } \ +} + +class FormeCube : public FormeBase2705 +{ +public: + FormeCube( GLfloat taille = 1.0, + bool plein = true ) + : FormeBase2705( plein ) + { + /* +Y */ + /* 3+-----------+2 */ + /* |\ |\ */ + /* | \ | \ */ + /* | \ | \ */ + /* | 7+-----------+6 */ + /* | | | | */ + /* | | | | */ + /* 0+---|-------+1 | */ + /* \ | \ | +X */ + /* \ | \ | */ + /* \| \| */ + /* 4+-----------+5 */ + /* +Z */ + + static GLint faces[6][4] = + { + { 3, 2, 1, 0 }, + { 5, 4, 0, 1 }, + { 6, 5, 1, 2 }, + { 7, 6, 2, 3 }, + { 4, 7, 3, 0 }, + { 4, 5, 6, 7 } + }; + static GLfloat n[6][3] = + { + { 0.0, 0.0, -1.0 }, + { 0.0, -1.0, 0.0 }, + { 1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, + { -1.0, 0.0, 0.0 }, + { 0.0, 0.0, 1.0 } + }; + GLfloat v[8][3]; + v[4][0] = v[7][0] = v[3][0] = v[0][0] = -taille / 2.; + v[6][0] = v[5][0] = v[1][0] = v[2][0] = +taille / 2.; + v[5][1] = v[4][1] = v[0][1] = v[1][1] = -taille / 2.; + v[7][1] = v[6][1] = v[2][1] = v[3][1] = +taille / 2.; + v[3][2] = v[2][2] = v[1][2] = v[0][2] = -taille / 2.; + v[4][2] = v[5][2] = v[6][2] = v[7][2] = +taille / 2.; + GLfloat t[8][2]; + t[4][0] = t[7][0] = t[3][0] = t[0][0] = 0.; + t[6][0] = t[5][0] = t[1][0] = t[2][0] = 1.; + t[5][1] = t[4][1] = t[0][1] = t[1][1] = 0.; + t[7][1] = t[6][1] = t[2][1] = t[3][1] = 1.; + + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 6*6; + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + if ( plein_ ) + { + for ( int i = 0 ; i < 6 ; ++i ) + { + AJOUTE( v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2], n[i][0], n[i][1], n[i][2], t[faces[i][0]][0], t[faces[i][0]][1] ); + AJOUTE( v[faces[i][1]][0], v[faces[i][1]][1], v[faces[i][1]][2], n[i][0], n[i][1], n[i][2], t[faces[i][1]][0], t[faces[i][1]][1] ); + AJOUTE( v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2], n[i][0], n[i][1], n[i][2], t[faces[i][2]][0], t[faces[i][2]][1] ); + AJOUTE( v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2], n[i][0], n[i][1], n[i][2], t[faces[i][2]][0], t[faces[i][2]][1] ); + AJOUTE( v[faces[i][3]][0], v[faces[i][3]][1], v[faces[i][3]][2], n[i][0], n[i][1], n[i][2], t[faces[i][3]][0], t[faces[i][3]][1] ); + AJOUTE( v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2], n[i][0], n[i][1], n[i][2], t[faces[i][0]][0], t[faces[i][0]][1] ); + } + } + else + { + for ( int i = 0 ; i < 6 ; ++i ) + { + AJOUTE( v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2], n[i][0], n[i][1], n[i][2], t[faces[i][0]][0], t[faces[i][0]][1] ); + AJOUTE( v[faces[i][1]][0], v[faces[i][1]][1], v[faces[i][1]][2], n[i][0], n[i][1], n[i][2], t[faces[i][1]][0], t[faces[i][1]][1] ); + AJOUTE( v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2], n[i][0], n[i][1], n[i][2], t[faces[i][2]][0], t[faces[i][2]][1] ); + AJOUTE( v[faces[i][3]][0], v[faces[i][3]][1], v[faces[i][3]][2], n[i][0], n[i][1], n[i][2], t[faces[i][3]][0], t[faces[i][3]][1] ); + } + } + nsommets_ = nsommets; + + // remplir VBOs + glBindVertexArray( vao ); + glGenBuffers( 3, vbo ); + + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), sommets ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + + if ( locNormal >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), normales ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + } + + if ( locTexCoord >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, 2*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 2*nsommets*sizeof(GLfloat), texcoord ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + } + + glBindVertexArray( 0 ); + } + } + ~FormeCube() + { + glDeleteBuffers( 3, vbo ); + } + void afficher() + { + glBindVertexArray( vao ); + if ( plein_ ) + glDrawArrays( GL_TRIANGLES, 0, nsommets_ ); + else + for ( int i = 0 ; i < 6 ; ++i ) glDrawArrays( GL_LINE_LOOP, 4*i, 4 ); + glBindVertexArray( 0 ); + } +private: + GLint nsommets_; + GLuint vbo[3]; +}; + +////////// + +class FormeSphere : public FormeBase2705 +{ +public: + FormeSphere( GLdouble radius, GLint slices, GLint stacks, + bool plein = true, bool entiere = true ) + : FormeBase2705( plein ) + { + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 2*(slices+1)*(stacks+2); + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + // variables locales + const GLfloat drho = M_PI / (GLfloat) stacks; + const GLfloat dtheta = 2.0 * M_PI / (GLfloat) slices; + + GLint imin, imax; + if ( locTexCoord >= 0 ) { imin = 0; imax = stacks; } else { imin = 1; imax = stacks - 1; } + if ( !entiere ) imax = imax/2 + 1; // pour se rendre seulement à la moitié supérieure + + /* texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis */ + /* t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes) */ + /* cannot use triangle fan on texturing (s coord. at top/bottom tip varies) */ + + nsommets = 0; + { + GLfloat t = 1.0; + GLfloat ds = 1.0 / slices; + GLfloat dt = 1.0 / stacks; + for ( GLint i = imin; i < imax; i++ ) + { + GLfloat rho = i * drho; + GLfloat s = 0.0; + for ( GLint j = 0; j <= slices; j++ ) + { + GLfloat x, y, z; + GLfloat theta = (j == slices) ? 0.0 : j * dtheta; + x = -sin(theta) * sin(rho); + y = cos(theta) * sin(rho); + z = cos(rho); + AJOUTE( x * radius, y * radius, z * radius, x, y, z, s, t ); + + x = -sin(theta) * sin(rho + drho); + y = cos(theta) * sin(rho + drho); + z = cos(rho + drho); + AJOUTE( x * radius, y * radius, z * radius, x, y, z, s, t-dt ); + s += ds; + } + t -= dt; + } + } + nsommetsStrip_ = nsommets; + + if ( !(locTexCoord >= 0) ) + { + AJOUTE( 0.0, 0.0, radius, 0.0, 0.0, 1.0, 0, 0 ); + for ( GLint j = 0; j <= slices; j++ ) + { + GLfloat x, y, z; + GLfloat theta = (j == slices) ? 0.0 : j * dtheta; + x = -sin(theta) * sin(drho); + y = cos(theta) * sin(drho); + z = cos(drho); + AJOUTE( x * radius, y * radius, z * radius, x, y, z, 0, 0 ); + } + } + nsommetsFan_ = nsommets - nsommetsStrip_; + + if ( !(locTexCoord >= 0) && entiere ) + { + AJOUTE( 0.0, 0.0, -radius, 0.0, 0.0, -1.0, 0, 0 ); + GLfloat rho = M_PI - drho; + for ( GLint j = slices; j >= 0; j-- ) + { + GLfloat x, y, z; + GLfloat theta = (j == slices) ? 0.0 : j * dtheta; + x = -sin(theta) * sin(rho); + y = cos(theta) * sin(rho); + z = cos(rho); + AJOUTE( x * radius, y * radius, z * radius, x, y, z, 0, 0 ); + } + } + + // remplir VBOs + glBindVertexArray( vao ); + glGenBuffers( 3, vbo ); + + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), sommets ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + + if ( locNormal >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), normales ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + } + + if ( locTexCoord >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, 2*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 2*nsommets*sizeof(GLfloat), texcoord ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + } + + glBindVertexArray( 0 ); + } + } + ~FormeSphere() + { + glDeleteBuffers( 3, vbo ); + } + void afficher() + { + glBindVertexArray( vao ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, nsommetsStrip_ ); + if ( !(locTexCoord >= 0) ) + { + glDrawArrays( GL_TRIANGLE_FAN, nsommetsStrip_, nsommetsFan_ ); + glDrawArrays( GL_TRIANGLE_FAN, nsommetsStrip_+nsommetsFan_, nsommetsFan_ ); + } + glBindVertexArray( 0 ); + } +private: + GLint nsommetsStrip_, nsommetsFan_; + GLuint vbo[3]; +}; + +////////// + +class FormeTore : public FormeBase2705 +{ +public: + FormeTore( GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings, + bool plein = true ) + : FormeBase2705( plein ) + { + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 2*(nsides+1)*(rings); + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + // variables locales + const GLfloat ringDelta = 2.0 * M_PI / rings; + const GLfloat sideDelta = 2.0 * M_PI / nsides; + + GLfloat theta = 0.0; + GLfloat cosTheta = 1.0; + GLfloat sinTheta = 0.0; + for ( int i = rings - 1; i >= 0; i-- ) + { + GLfloat theta1 = theta + ringDelta; + GLfloat cosTheta1 = cos(theta1); + GLfloat sinTheta1 = sin(theta1); + GLfloat phi = 0.0; + for ( int j = nsides; j >= 0; j-- ) + { + phi += sideDelta; + GLfloat cosPhi = cos(phi); + GLfloat sinPhi = sin(phi); + GLfloat dist = outerRadius + innerRadius * cosPhi; + + AJOUTE( cosTheta1 * dist, -sinTheta1 * dist, innerRadius * sinPhi, + cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi, + (4.0*(i+1))/rings, (4.0*j)/nsides ); + + AJOUTE( cosTheta * dist, -sinTheta * dist, innerRadius * sinPhi, + cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi, + (4.0*i)/rings, (4.0*j)/nsides ); + } + theta = theta1; + cosTheta = cosTheta1; + sinTheta = sinTheta1; + } + nsommets_ = nsommets; + + // remplir VBOs + glBindVertexArray( vao ); + glGenBuffers( 3, vbo ); + + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), sommets ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + + if ( locNormal >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), normales ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + } + + if ( locTexCoord >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, 2*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 2*nsommets*sizeof(GLfloat), texcoord ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + } + + glBindVertexArray( 0 ); + } + } + ~FormeTore() + { + glDeleteBuffers( 3, vbo ); + } + void afficher() + { + glBindVertexArray( vao ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, nsommets_ ); + glBindVertexArray( 0 ); + } +private: + GLint nsommets_; + GLuint vbo[3]; +}; + +////////// + +class FormeCylindre : public FormeBase2705 +{ +public: + FormeCylindre( GLdouble base, GLdouble top, GLdouble height, GLint slices, GLint stacks, + bool plein = true ) + : FormeBase2705( plein ) + { + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 2*(slices+1)*(stacks) + 2*(slices+1) + 2; + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + // variables locales + const GLdouble da = 2.0 * M_PI / slices; + { + // le cylindre + const GLdouble dr = (top - base) / stacks; + const GLdouble nz = (base - top) / height; + const GLdouble dz = height / stacks; + const GLfloat ds = 1.0 / slices; + const GLfloat dt = 1.0 / stacks; + GLfloat t = 0.0; + GLfloat z = 0.0; + GLfloat r = base; + for ( int j = 0; j < stacks; j++ ) + { + GLfloat s = 0.0; + for ( int i = 0; i <= slices; i++ ) + { + GLfloat a = ( i == slices ) ? 0.0 : i * da; + GLfloat x = sin( a ); + GLfloat y = cos( a ); + AJOUTE( x * r, y * r, z, x, y, nz, s, t ); + AJOUTE( x * (r + dr), y * (r + dr), z + dz, x, y, nz, s, t + dt ); + s += ds; + } + r += dr; + t += dt; + z += dz; + } + nsommetsCyl_ = nsommets; + } + { + // les deux bouts avec des disques + /* texture of a shapesDisk is a cut out of the texture unit square + * x, y in [-outerRadius, +outerRadius]; s, t in [0, 1] (linear mapping) */ + AJOUTE( 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.5, 0.5 ); + for ( int i = slices; i >= 0; i-- ) + { + GLfloat a = ( i == slices ) ? 0.0 : i * -da; + GLfloat x = sin( a ); + GLfloat y = cos( a ); + AJOUTE( base*x, base*y, 0.0, 0.0, 0.0, -1.0, 0.5*(1.0-x), 0.5*(1.0+y) ); + } + nsommetsBout_ = nsommets - nsommetsCyl_; + AJOUTE( 0.0, 0.0, height, 0.0, 0.0, +1.0, 0.5, 0.5 ); + for ( int i = slices; i >= 0; i-- ) + { + GLfloat a = ( i == slices ) ? 0.0 : i * da; + GLfloat x = sin( a ); + GLfloat y = cos( a ); + AJOUTE( top*x, top*y, height, 0.0, 0.0, +1.0, 0.5*(1.0-x), 0.5*(1.0+y) ); + } + } + + // remplir VBOs + glBindVertexArray( vao ); + glGenBuffers( 3, vbo ); + + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), sommets ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + + if ( locNormal >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), normales ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + } + + if ( locTexCoord >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, 2*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 2*nsommets*sizeof(GLfloat), texcoord ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + } + + glBindVertexArray( 0 ); + } + } + ~FormeCylindre() + { + glDeleteBuffers( 3, vbo ); + } + void afficher() + { + glBindVertexArray( vao ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, nsommetsCyl_ ); + glDrawArrays( GL_TRIANGLE_FAN, nsommetsCyl_, nsommetsBout_ ); + glDrawArrays( GL_TRIANGLE_FAN, nsommetsCyl_+nsommetsBout_, nsommetsBout_ ); + glBindVertexArray( 0 ); + } +private: + GLint nsommetsCyl_, nsommetsBout_; + GLuint vbo[3]; +}; + +////////// + +class FormeDisque : public FormeBase2705 +{ +public: + FormeDisque( GLdouble inner, GLdouble outer, GLint slices, GLint loops, + bool plein = true ) + : FormeBase2705( plein ) + { + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 2*(slices+1)*(loops); + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + // variables locales + const GLfloat da = 2.0 * M_PI / slices; + const GLfloat dr = (outer - inner) / (GLfloat) loops; + + /* texture of a shapesDisk is a cut out of the texture unit square + * x, y in [-outer, +outer]; s, t in [0, 1] * (linear mapping) */ + GLfloat r1 = inner; + for ( int l = 0; l < loops; l++ ) + { + GLfloat r2 = r1 + dr; + for ( int i = slices; i >= 0; i-- ) + { + GLfloat a = ( i == slices ) ? 0.0 : i * da; + GLfloat x = sin( a ); + GLfloat y = cos( a ); + AJOUTE( r2*x, r2*y, 0.0, 0.0, 0.0, +1.0, 0.5*( 1.0 - x*r2/outer ), 0.5*( 1.0 + y*r2/outer ) ); + AJOUTE( r1*x, r1*y, 0.0, 0.0, 0.0, +1.0, 0.5*( 1.0 - x*r1/outer ), 0.5*( 1.0 + y*r1/outer ) ); + } + r1 = r2; + } + nsommets_ = nsommets; + + // remplir VBOs + glBindVertexArray( vao ); + glGenBuffers( 3, vbo ); + + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), sommets ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + + if ( locNormal >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, 3*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 3*nsommets*sizeof(GLfloat), normales ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + } + + if ( locTexCoord >= 0 ) + { + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, 2*nsommets*sizeof(GLfloat), NULL, GL_STATIC_DRAW ); + glBufferSubData( GL_ARRAY_BUFFER, 0, 2*nsommets*sizeof(GLfloat), texcoord ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + } + + glBindVertexArray( 0 ); + } + } + ~FormeDisque() + { + glDeleteBuffers( 3, vbo ); + } + void afficher() + { + glBindVertexArray( vao ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, nsommets_ ); + glBindVertexArray( 0 ); + } +private: + GLint nsommets_; + GLuint vbo[3]; +}; + +////////// + +class FormeIcosaedre : public FormeBase2705 +{ +public: + FormeIcosaedre( bool plein = true ) + : FormeBase2705( plein ) + { + } + ~FormeIcosaedre() + { + } + void afficher() + { +#if !defined( __APPLE__ ) + shapesSolidIcosahedron( ); +#endif + } +private: +}; + +////////// + +class FormeDodecaedre : public FormeBase2705 +{ +public: + FormeDodecaedre( bool plein = true ) + : FormeBase2705( plein ) + { + } + ~FormeDodecaedre() + { + } + void afficher() + { +#if !defined( __APPLE__ ) + shapesSolidDodecahedron( ); +#endif + } +private: +}; + +////////// + +// teapot(14, true); +// teapot(10, false); +class FormeTheiere : public FormeBase2705 +{ +public: + FormeTheiere( GLint npts = 14, + bool plein = true ) + : FormeBase2705( plein ) //, npts_(npts) + { +#if 0 + if ( obtenirAttributs( ) ) + { + // initialisation + const int TAILLEMAX = 10*(npts_+1)*(npts_); + GLfloat sommets[3*TAILLEMAX], normales[3*TAILLEMAX], texcoord[2*TAILLEMAX]; + int nsommets = 0; + + } +#endif + } + ~FormeTheiere() + { +#if 0 + glDeleteBuffers( 3, vbo ); +#endif + } + void afficher() + { +#if 1 +#if !defined( __APPLE__ ) + shapesSolidTeapot(); +#endif +#else + glBindVertexArray( vao ); + //glDrawArrays( GL_TRIANGLE_STRIP, 0, nsommets_ ); + /* Rim, body, lid, and bottom data must be reflected in x and + y; handle and spout data across the y axis only. */ + int patchdata[][16] = + { + /* rim */ + {102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + /* body */ + {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + {24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}, + /* lid */ + {96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,}, + {0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117}, + /* bottom */ + {118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37}, + /* handle */ + {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}, + {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67}, + /* spout */ + {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83}, + {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95} + }; + float cpdata[][3] = + { + {0.2,0,2.7}, {0.2,-0.112,2.7}, {0.112,-0.2,2.7}, {0,-0.2,2.7}, + {1.3375,0,2.53125}, {1.3375,-0.749,2.53125}, {0.749,-1.3375,2.53125}, + {0,-1.3375,2.53125}, {1.4375,0,2.53125}, {1.4375,-0.805, 2.53125}, + {0.805,-1.4375,2.53125}, {0,-1.4375,2.53125}, {1.5,0,2.4}, {1.5,-0.84,2.4}, + {0.84,-1.5,2.4}, {0,-1.5,2.4}, {1.75,0, 1.875}, {1.75,-0.98,1.875}, + {0.98,-1.75,1.875}, {0,-1.75,1.875}, {2,0,1.35}, {2,-1.12,1.35}, + {1.12,-2,1.35}, {0,-2,1.35}, {2,0,0.9}, {2,-1.12,0.9}, {1.12,-2,0.9}, + {0,-2,0.9}, {-2,0,0.9}, {2, 0,0.45}, {2,-1.12,0.45}, {1.12,-2,0.45}, + {0,-2,0.45}, {1.5,0,0.225}, {1.5,-0.84,0.225}, {0.84,-1.5,0.225}, + {0,-1.5,0.225}, {1.5, 0,0.15}, {1.5,-0.84,0.15}, {0.84,-1.5,0.15}, + {0,-1.5,0.15}, {-1.6,0,2.025}, {-1.6,-0.3,2.025}, {-1.5,-0.3,2.25}, + {-1.5,0,2.25}, {-2.3,0,2.025}, {-2.3,-0.3,2.025}, {-2.5,-0.3,2.25}, + {-2.5,0,2.25}, {-2.7,0,2.025}, {-2.7,-0.3,2.025}, {-3,-0.3,2.25}, + {-3,0,2.25}, {-2.7,0,1.8}, {-2.7,-0.3,1.8}, {-3,-0.3,1.8}, {-3,0,1.8}, + {-2.7,0,1.575}, {-2.7,-0.3,1.575}, {-3,-0.3,1.35}, {-3,0,1.35}, + {-2.5,0,1.125}, {-2.5,-0.3,1.125}, {-2.65,-0.3,0.9375}, {-2.65,0, 0.9375}, + {-2,-0.3,0.9}, {-1.9,-0.3,0.6}, {-1.9,0,0.6}, {1.7,0, 1.425}, + {1.7,-0.66,1.425}, {1.7,-0.66,0.6}, {1.7,0,0.6}, {2.6,0, 1.425}, + {2.6,-0.66,1.425}, {3.1,-0.66,0.825}, {3.1,0,0.825}, {2.3, 0,2.1}, + {2.3,-0.25,2.1}, {2.4,-0.25,2.025}, {2.4,0,2.025}, {2.7, 0,2.4}, + {2.7,-0.25,2.4}, {3.3,-0.25,2.4}, {3.3,0,2.4}, {2.8,0, 2.475}, + {2.8,-0.25,2.475}, {3.525,-0.25,2.49375}, {3.525,0, 2.49375}, {2.9,0,2.475}, + {2.9,-0.15,2.475}, {3.45,-0.15,2.5125}, {3.45,0,2.5125}, {2.8,0,2.4}, + {2.8,-0.15,2.4}, {3.2,-0.15,2.4}, {3.2,0,2.4}, {0,0,3.15}, {0.8,0,3.15}, + {0.8,-0.45,3.15}, {0.45, -0.8,3.15}, {0,-0.8,3.15}, {0,0,2.85}, {1.4,0,2.4}, + {1.4,-0.784, 2.4}, {0.784,-1.4,2.4}, {0,-1.4,2.4}, {0.4,0,2.55}, + {0.4,-0.224, 2.55}, {0.224,-0.4,2.55}, {0,-0.4,2.55}, {1.3,0,2.55}, + {1.3,-0.728,2.55}, {0.728,-1.3,2.55}, {0,-1.3,2.55}, {1.3,0,2.4}, + {1.3,-0.728,2.4}, {0.728,-1.3,2.4}, {0,-1.3,2.4}, {0,0,0}, {1.425,-0.798,0}, + {1.5,0,0.075}, {1.425,0,0}, {0.798,-1.425,0}, {0,-1.5, 0.075}, {0,-1.425,0}, + {1.5,-0.84,0.075}, {0.84,-1.5,0.075} + }; + float tex[2][2][2] = + { + { {0, 0}, + {1, 0}}, + { {0, 1}, + {1, 1}} + }; + float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3]; + for ( int i = 0; i < 10; i++ ) + { + for ( int j = 0; j < 4; j++ ) + { + for ( int k = 0; k < 4; k++ ) + { + for ( int l = 0; l < 3; l++ ) + { + p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l]; + q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l]; + if (l == 1) q[j][k][l] *= -1.0; + if (i < 6) + { + r[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l]; + if (l == 0) r[j][k][l] *= -1.0; + s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l]; + if (l == 0) s[j][k][l] *= -1.0; + if (l == 1) s[j][k][l] *= -1.0; + } + } + } + } + glEnable( GL_AUTO_NORMAL ); + glEnable( GL_MAP2_TEXTURE_COORD_2 ); + glEnable( GL_MAP2_VERTEX_3 ); + glMap2f( GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &tex[0][0][0] ); + glMap2f( GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &p[0][0][0] ); + glMapGrid2f( npts_, 0.0, 1.0, npts_, 0.0, 1.0 ); + glEvalMesh2( GL_FILL, 0, npts_, 0, npts_ ); + glMap2f( GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0] ); + glEvalMesh2( GL_FILL, 0, npts_, 0, npts_ ); + if ( i < 6 ) + { + glMap2f( GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0] ); + glEvalMesh2( GL_FILL, 0, npts_, 0, npts_ ); + glMap2f( GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0] ); + glEvalMesh2( GL_FILL, 0, npts_, 0, npts_ ); + } + } + glBindVertexArray( 0 ); +#endif + } +private: +#if 0 + void Map2f( GLenum target, + GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, + GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, + const GLfloat *points ) + { + } + GLint un; GLfloat u1, u2, du; + GLint vn; GLfloat v1, v2, dv; + void MapGrid2f( GLint unArg, GLfloat u1Arg, GLfloat u2Arg, + GLint vnArg, GLfloat v1Arg, GLfloat v2Arg ) + { + un = unArg; u1 = u1Arg; u2 = u2Arg; du = u2 - u1; + vn = vnArg; v1 = v1Arg; v2 = v2Arg; dv = v2 - v1; + } + void EvalMesh2( GLint i1, GLint i2, GLint j1, GLint j2 ) + { + if ( plein_ ) + { + for ( int j = j1; j < j2; j += 1 ) + { + glBegin( GL_QUAD_STRIP ); + for ( int i = i1; i <= i2; i += 1 ) + { + glEvalCoord2f( i * du + u1, j * dv + v1 ); + glEvalCoord2f( i * du + u1, j + 1 * dv + v1 ); + } glEnd(); + } + } + else + { + for ( int j = j1; j <= j2; j += 1 ) + { + glBegin( GL_LINE_STRIP ); + for ( int i = i1; i <= i2; i += 1 ) + glEvalCoord2f( i * du + u1, j * dv + v1 ); + glEnd(); + } + for ( int i = i1; i <= i2; i += 1 ) + { + glBegin( GL_LINE_STRIP ); + for ( int j = j1; j <= j1; j += 1 ) + glEvalCoord2f( i * du + u1, j * dv + v1 ); + glEnd(); + } + } + } +#endif + +#if 0 + GLint npts_; + GLint nsommets_; + GLuint vbo[3]; +#endif +}; + +////////// + +#endif + diff --git a/makefile b/makefile new file mode 100644 index 0000000..eb17631 --- /dev/null +++ b/makefile @@ -0,0 +1,33 @@ +CONTEXT=sdl2 +ifeq "$(shell uname)" "Darwin" + CONTEXT=glfw3 + LDFLAGS += -lobjc -framework Foundation -framework OpenGL -framework Cocoa +endif + +CXXFLAGS += -g -W -Wall -Wno-unused-parameter -Wno-deprecated-declarations +CXXFLAGS += $(shell pkg-config --cflags glew) +CXXFLAGS += $(shell pkg-config --cflags $(CONTEXT)) + +LDFLAGS += -g +LDFLAGS += $(shell pkg-config --libs glew) +LDFLAGS += $(shell pkg-config --libs $(CONTEXT)) + +TP="tp3" +SRC=ripple + +exe : $(SRC).exe +run : exe + optirun ./$(SRC).exe +$(SRC).exe : $(SRC).cpp *.h + $(CXX) $(CXXFLAGS) -o$@ $(SRC).cpp $(LDFLAGS) + +sol : ; make SRC=$(SRC)Solution exe +runs : ; make SRC=$(SRC)Solution run + +clean : + rm -rf *.o *.exe *.exe.dSYM + +remise zip : + make clean + rm -f remise_$(TP).zip + zip -r remise_$(TP).zip *.cpp *.h *.glsl makefile *.txt textures diff --git a/nuanceurFragmentsSolution.glsl b/nuanceurFragmentsSolution.glsl new file mode 100644 index 0000000..3a4de24 --- /dev/null +++ b/nuanceurFragmentsSolution.glsl @@ -0,0 +1,141 @@ +#version 410 + +// Définition des paramètres des sources de lumière +layout (std140) uniform LightSourceParameters +{ + vec4 ambient; + vec4 diffuse; + vec4 specular; + vec4 position; + vec3 spotDirection; + float spotExponent; + float spotCutoff; // ([0.0,90.0] ou 180.0) + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; +} LightSource[1]; + +// Définition des paramètres des matériaux +layout (std140) uniform MaterialParameters +{ + vec4 emission; + vec4 ambient; + vec4 diffuse; + vec4 specular; + float shininess; +} FrontMaterial; + +// Définition des paramètres globaux du modèle de lumière +layout (std140) uniform LightModelParameters +{ + vec4 ambient; // couleur ambiante + bool localViewer; // observateur local ou à l'infini? + bool twoSide; // éclairage sur les deux côtés ou un seul? +} LightModel; + +layout (std140) uniform varsUnif +{ + // partie 1: illumination + int typeIllumination; // 0:Lambert, 1:Gouraud, 2:Phong + bool utiliseBlinn; // indique si on veut utiliser modèle spéculaire de Blinn ou Phong + bool utiliseDirect; // indique si on utilise un spot style Direct3D ou OpenGL + bool afficheNormales; // indique si on utilise les normales comme couleurs (utile pour le débogage) + // partie 3: texture + int texnumero; // numéro de la texture appliquée + bool utiliseCouleur; // doit-on utiliser la couleur de base de l'objet en plus de celle de la texture? + int afficheTexelNoir; // un texel noir doit-il être affiché 0:noir, 1:mi-coloré, 2:transparent? +}; + +uniform sampler2D laTexture; + +///////////////////////////////////////////////////////////////// + +in Attribs { + vec3 lumiDir, spotDir; + vec3 normale, obsVec; + vec2 texCoord; + vec4 couleur; +} AttribsIn; + +out vec4 FragColor; + +float calculerSpot( in vec3 spotDir, in vec3 L ) +{ + float spotFacteur; + float spotDot = dot( L, normalize( spotDir ) ); + if ( utiliseDirect ) // modèle Direct3D + { + float cosAngleInterne = cos(radians(LightSource[0].spotCutoff)); + float exposant = 1.01 + LightSource[0].spotExponent / 2.0; + float cosAngleExterne = pow( cos(radians(LightSource[0].spotCutoff)), exposant ); + // calculer le facteur spot avec la fonction smoothstep() + spotFacteur = smoothstep( cosAngleExterne, cosAngleInterne, spotDot ); + } + else // modèle OpenGL + { + spotFacteur = ( spotDot > cos(radians(LightSource[0].spotCutoff)) ) ? pow( spotDot, LightSource[0].spotExponent ) : 0.0; + } + return( spotFacteur ); +} + +vec4 calculerReflexion( in vec3 L, in vec3 N, in vec3 O ) +{ + vec4 coul = FrontMaterial.emission + FrontMaterial.ambient * LightModel.ambient; + + // calcul de la composante ambiante + coul += FrontMaterial.ambient * LightSource[0].ambient; + + // calcul de l'éclairage seulement si le produit scalaire est positif + float NdotL = max( 0.0, dot( N, L ) ); + if ( NdotL > 0.0 ) + { + // calcul de la composante diffuse + //coul += ( utiliseCouleur ? FrontMaterial.diffuse : vec4(1.0) ) * LightSource[0].diffuse * NdotL; + coul += FrontMaterial.diffuse * LightSource[0].diffuse * NdotL; + + // calcul de la composante spéculaire (Blinn ou Phong) + float NdotHV = max( 0.0, ( utiliseBlinn ) ? dot( normalize( L + O ), N ) : dot( reflect( -L, N ), O ) ); + coul += FrontMaterial.specular * LightSource[0].specular * ( ( NdotHV == 0.0 ) ? 0.0 : pow( NdotHV, FrontMaterial.shininess ) ); + } + return( coul ); +} + +void main( void ) +{ + vec3 L = normalize( AttribsIn.lumiDir ); // vecteur vers la source lumineuse + vec3 N = normalize( AttribsIn.normale ); // vecteur normal + //vec3 N = normalize( gl_FrontFacing ? AttribsIn.normale : -AttribsIn.normale ); + vec3 O = normalize( AttribsIn.obsVec ); // position de l'observateur + + // calculer la réflexion: + // si illumination de 1:Gouraud, prendre la couleur interpolée qui a été reçue + // si illumination de 2:Phong, le faire! + // si illumination de 0:Lambert, faire comme Phong, même si les normales sont les mêmes pour tous les fragments + vec4 coul = ( typeIllumination == 1 ) ? AttribsIn.couleur : calculerReflexion( L, N, O ); + + // calculer l'influence du spot + float spotFacteur = calculerSpot( AttribsIn.spotDir, L ); + coul *= spotFacteur; + //if ( spotFacteur <= 0.0 ) discard; // pour éliminer tout ce qui n'est pas dans le cône + // calcul de la composante ambiante + //coul += FrontMaterial.ambient * LightSource[0].ambient; + + // appliquer la texture s'il y a lieu + if ( texnumero != 0 ) + { + vec4 couleurTexture = texture( laTexture, AttribsIn.texCoord ); + // comment afficher un texel noir? + if ( couleurTexture.r < 0.1 && couleurTexture.g < 0.1 && couleurTexture.b < 0.1 && + spotFacteur > 0.0 ) + if ( afficheTexelNoir == 1 ) + couleurTexture = coul / 2.0; + else if ( afficheTexelNoir == 2 ) + discard; + coul *= couleurTexture; + } + + // assigner la couleur finale + FragColor = clamp( coul, 0.0, 1.0 ); + + if ( afficheNormales ) FragColor = vec4(N,1.0); +} diff --git a/nuanceurGeometrieSolution.glsl b/nuanceurGeometrieSolution.glsl new file mode 100644 index 0000000..3341035 --- /dev/null +++ b/nuanceurGeometrieSolution.glsl @@ -0,0 +1,73 @@ +#version 410 + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +uniform mat4 matrModel; +uniform mat4 matrVisu; +uniform mat4 matrProj; +uniform mat3 matrNormale; + +layout (std140) uniform varsUnif +{ + // partie 1: illumination + int typeIllumination; // 0:Lambert, 1:Gouraud, 2:Phong + bool utiliseBlinn; // indique si on veut utiliser modèle spéculaire de Blinn ou Phong + bool utiliseDirect; // indique si on utilise un spot style Direct3D ou OpenGL + bool afficheNormales; // indique si on utilise les normales comme couleurs (utile pour le débogage) + // partie 3: texture + int texnumero; // numéro de la texture appliquée + bool utiliseCouleur; // doit-on utiliser la couleur de base de l'objet en plus de celle de la texture? + int afficheTexelNoir; // un texel noir doit-il être affiché 0:noir, 1:mi-coloré, 2:transparent? +}; + +in Attribs { + vec3 lumiDir, spotDir; + vec3 normale, obsVec; + vec2 texCoord; + vec4 couleur; +} AttribsIn[]; + +out Attribs { + vec3 lumiDir, spotDir; + vec3 normale, obsVec; + vec2 texCoord; + vec4 couleur; +} AttribsOut; + +void main() +{ + // si illumination est Lambert, calculer une nouvelle normale + vec3 n = vec3(0.0); + if ( typeIllumination == 0 ) + { + vec3 p0 = gl_in[0].gl_Position.xyz; + vec3 p1 = gl_in[1].gl_Position.xyz; + vec3 p2 = gl_in[2].gl_Position.xyz; + n = cross( p1-p0, p2-p0 ); // cette nouvelle normale est déjà dans le repère de la caméra + // il n'est pas nécessaire de la multiplier par matrNormale + } + // ou faire une moyenne, MAIS CE N'EST PAS CE QU'ON VEUT! + // if ( typeIllumination == 0 ) + // { + // // calculer le centre + // for ( int i = 0 ; i < gl_in.length() ; ++i ) + // { + // n += AttribsIn[i].normale; + // } + // n /= gl_in.length(); + // } + + // émettre les sommets + for ( int i = 0 ; i < gl_in.length() ; ++i ) + { + gl_Position = matrProj * gl_in[i].gl_Position; // on termine la transformation débutée dans le nuanceur de sommets + AttribsOut.lumiDir = AttribsIn[i].lumiDir; + AttribsOut.spotDir = AttribsIn[i].spotDir; + AttribsOut.normale = ( typeIllumination == 0 ) ? n : AttribsIn[i].normale; + AttribsOut.obsVec = AttribsIn[i].obsVec; + AttribsOut.texCoord = AttribsIn[i].texCoord; + AttribsOut.couleur = AttribsIn[i].couleur; + EmitVertex(); + } +} diff --git a/nuanceurSommetsSolution.glsl b/nuanceurSommetsSolution.glsl new file mode 100644 index 0000000..80bff7d --- /dev/null +++ b/nuanceurSommetsSolution.glsl @@ -0,0 +1,130 @@ +#version 410 + +// Définition des paramètres des sources de lumière +layout (std140) uniform LightSourceParameters +{ + vec4 ambient; + vec4 diffuse; + vec4 specular; + vec4 position; + vec3 spotDirection; + float spotExponent; + float spotCutoff; // ([0.0,90.0] ou 180.0) + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; +} LightSource[1]; + +// Définition des paramètres des matériaux +layout (std140) uniform MaterialParameters +{ + vec4 emission; + vec4 ambient; + vec4 diffuse; + vec4 specular; + float shininess; +} FrontMaterial; + +// Définition des paramètres globaux du modèle de lumière +layout (std140) uniform LightModelParameters +{ + vec4 ambient; // couleur ambiante + bool localViewer; // observateur local ou à l'infini? + bool twoSide; // éclairage sur les deux côtés ou un seul? +} LightModel; + +layout (std140) uniform varsUnif +{ + // partie 1: illumination + int typeIllumination; // 0:Lambert, 1:Gouraud, 2:Phong + bool utiliseBlinn; // indique si on veut utiliser modèle spéculaire de Blinn ou Phong + bool utiliseDirect; // indique si on utilise un spot style Direct3D ou OpenGL + bool afficheNormales; // indique si on utilise les normales comme couleurs (utile pour le débogage) + // partie 3: texture + int texnumero; // numéro de la texture appliquée + bool utiliseCouleur; // doit-on utiliser la couleur de base de l'objet en plus de celle de la texture? + int afficheTexelNoir; // un texel noir doit-il être affiché 0:noir, 1:mi-coloré, 2:transparent? +}; + +uniform mat4 matrModel; +uniform mat4 matrVisu; +uniform mat4 matrProj; +uniform mat3 matrNormale; + +///////////////////////////////////////////////////////////////// + +layout(location=0) in vec4 Vertex; +layout(location=2) in vec3 Normal; +layout(location=3) in vec4 Color; +layout(location=8) in vec4 TexCoord; + +out Attribs { + vec3 lumiDir, spotDir; + vec3 normale, obsVec; + vec2 texCoord; + vec4 couleur; +} AttribsOut; + +vec4 calculerReflexion( in vec3 L, in vec3 N, in vec3 O ) +{ + vec4 coul = FrontMaterial.emission + FrontMaterial.ambient * LightModel.ambient; + + // calcul de la composante ambiante + coul += FrontMaterial.ambient * LightSource[0].ambient; + + // calcul de l'éclairage seulement si le produit scalaire est positif + float NdotL = max( 0.0, dot( N, L ) ); + if ( NdotL > 0.0 ) + { + // calcul de la composante diffuse + //coul += ( utiliseCouleur ? FrontMaterial.diffuse : vec4(1.0) ) * LightSource[0].diffuse * NdotL; + coul += FrontMaterial.diffuse * LightSource[0].diffuse * NdotL; + + // calcul de la composante spéculaire (Blinn ou Phong) + float NdotHV = max( 0.0, ( utiliseBlinn ) ? dot( normalize( L + O ), N ) : dot( reflect( -L, N ), O ) ); + coul += FrontMaterial.specular * LightSource[0].specular * ( ( NdotHV == 0.0 ) ? 0.0 : pow( NdotHV, FrontMaterial.shininess ) ); + } + return( coul ); +} + +void main( void ) +{ + // transformation standard du sommet, ** sans la projection ** + gl_Position = matrVisu * matrModel * Vertex; + + // calculer la normale qui sera interpolée pour le nuanceur de fragment + AttribsOut.normale = matrNormale * Normal; + + // calculer la position du sommet (dans le repère de la caméra) + vec3 pos = vec3( matrVisu * matrModel * Vertex ); + + // vecteur de la direction de la lumière (dans le repère de la caméra) + AttribsOut.lumiDir = vec3( ( matrVisu * LightSource[0].position ).xyz - pos ); + + // vecteur de la direction vers l'observateur (dans le repère de la caméra) + AttribsOut.obsVec = ( LightModel.localViewer ? + normalize(-pos) : // =(0-pos) un vecteur qui pointe vers le (0,0,0), c'est-à-dire vers la caméra + vec3( 0.0, 0.0, 1.0 ) ); // on considère que l'observateur (la caméra) est à l'infini dans la direction (0,0,1) + // vecteur de la direction du spot (en tenant compte seulement des rotations de la caméra) + AttribsOut.spotDir = inverse(mat3(matrVisu)) * -LightSource[0].spotDirection; + // On accepte aussi: (si on suppose que .spotDirection est déjà dans le repère de la caméra) + //AttribsOut.spotDir = -LightSource[0].spotDirection; + // On accepte aussi: (car matrVisu a seulement une translation et pas de rotation => "mat3(matrVisu) == I" ) + //AttribsOut.spotDir = -LightSource[0].spotDirection; + // On accepte aussi: (car c'était le calcul qui était dans la solution précédente présentée dans le lab!) + //AttribsOut.spotDir = -( matrVisu * vec4(LightSource[0].spotDirection,1.0) ).xyz; + + // si illumination est 1:Gouraud, calculer la réflexion ici, sinon ne rien faire de plus + if ( typeIllumination == 1 ) + { + vec3 L = normalize( AttribsOut.lumiDir ); // calcul du vecteur de la surface vers la source lumineuse + vec3 N = normalize( AttribsOut.normale ); // vecteur normal + vec3 O = normalize( AttribsOut.obsVec ); // position de l'observateur + AttribsOut.couleur = calculerReflexion( L, N, O ); + } + //else + // couleur = vec4(0.0); // inutile + + // assigner les coordonnées de texture + AttribsOut.texCoord = TexCoord.st; +} diff --git a/ripple.cpp b/ripple.cpp new file mode 100644 index 0000000..46e1dc0 --- /dev/null +++ b/ripple.cpp @@ -0,0 +1,954 @@ +// Prénoms, noms et matricule des membres de l'équipe: +// - Prénom1 NOM1 (matricule1) +// - Prénom2 NOM2 (matricule2) + +#include +#include +#include "inf2705.h" + +#define SOL 1 + +// variables pour l'utilisation des nuanceurs +GLuint prog; // votre programme de nuanceurs +GLint locVertex = -1; +GLint locNormal = -1; +GLint locTexCoord = -1; +GLint locmatrModel = -1; +GLint locmatrVisu = -1; +GLint locmatrProj = -1; +GLint locmatrNormale = -1; +GLint loclaTexture = -1; +GLuint indLightSource; +GLuint indFrontMaterial; +GLuint indLightModel; +GLuint indvarsUnif; +GLuint progBase; // le programme de nuanceurs de base +GLint locVertexBase = -1; +GLint locColorBase = -1; +GLint locmatrModelBase = -1; +GLint locmatrVisuBase = -1; +GLint locmatrProjBase = -1; + +GLuint vao[2]; +GLuint vbo[5]; +GLuint ubo[4]; + +// matrices de du pipeline graphique +MatricePipeline matrModel; +MatricePipeline matrVisu; +MatricePipeline matrProj; + +// les formes +FormeSphere *sphere = NULL, *sphereLumi = NULL; +FormeTheiere *theiere = NULL; +FormeTore *tore = NULL; +FormeCylindre *cylindre = NULL; +FormeCylindre *cone = NULL; + +// variables pour définir le point de vue +double thetaCam = 0.0; // angle de rotation de la caméra (coord. sphériques) +double phiCam = 0.0; // angle de rotation de la caméra (coord. sphériques) +double distCam = 0.0; // distance (coord. sphériques) + +// variables d'état +bool enPerspective = false; // indique si on est en mode Perspective (true) ou Ortho (false) +bool enmouvement = false; // le modèle est en mouvement/rotation automatique ou non +bool afficheAxes = true; // indique si on affiche les axes +GLenum modePolygone = GL_FILL; // comment afficher les polygones + +//////////////////////////////////////// +// déclaration des variables globales // +//////////////////////////////////////// + +// partie 1: illumination +int modele = 1; // le modèle à afficher + +// partie 3: texture +GLuint textureDE = 0; +GLuint textureECHIQUIER = 0; + +// définition des lumières +struct LightSourceParameters +{ + glm::vec4 ambient; + glm::vec4 diffuse; + glm::vec4 specular; + glm::vec4 position; + glm::vec3 spotDirection; + float spotExposant; + float spotAngle; // ([0.0,90.0] ou 180.0) + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; +} LightSource[1] = { { glm::vec4( 1.0, 1.0, 1.0, 1.0 ), + glm::vec4( 1.0, 1.0, 1.0, 1.0 ), + glm::vec4( 1.0, 1.0, 1.0, 1.0 ), + glm::vec4( 4, 1, 15, 1.0 ), + glm::vec3( -5.0, -2.0, -10.0 ), + 1.0, // l'exposant du cône + 15.0, // l'angle du cône du spot + 1., 0., 0. } }; + +// définition du matériau +struct MaterialParameters +{ + glm::vec4 emission; + glm::vec4 ambient; + glm::vec4 diffuse; + glm::vec4 specular; + float shininess; +} FrontMaterial = { glm::vec4( 0.0, 0.0, 0.0, 1.0 ), + glm::vec4( 0.1, 0.1, 0.1, 1.0 ), + glm::vec4( 1.0, 0.1, 1.0, 1.0 ), + glm::vec4( 1.0, 1.0, 1.0, 1.0 ), + 100.0 }; + +struct LightModelParameters +{ + glm::vec4 ambient; // couleur ambiante + int localViewer; // doit-on prendre en compte la position de l'observateur? (local ou à l'infini) + int twoSide; // éclairage sur les deux côtés ou un seul? +} LightModel = { glm::vec4(0,0,0,1), false, false }; + +struct +{ + // partie 1: illumination + int typeIllumination; // 0:Lambert, 1:Gouraud, 2:Phong + int utiliseBlinn; // indique si on veut utiliser modèle spéculaire de Blinn ou Phong + int utiliseDirect; // indique si on utilise un spot style Direct3D ou OpenGL + int afficheNormales; // indique si on utilise les normales comme couleurs (utile pour le débogage) + // partie 3: texture + int texnumero; // numéro de la texture appliquée + int utiliseCouleur; // doit-on utiliser la couleur de base de l'objet en plus de celle de la texture? + int afficheTexelNoir; // un texel noir doit-il être affiché 0:noir, 1:mi-coloré, 2:transparent? +} varsUnif = { 2, false, false, false, + 0, true, 0 }; +// ( En glsl, les types 'bool' et 'int' sont de la même taille, ce qui n'est pas le cas en C++. +// Ci-dessus, on triche donc un peu en déclarant les 'bool' comme des 'int', mais ça facilite la +// copie directe vers le nuanceur où les variables seront bien de type 'bool'. ) + + +void verifierAngles() +{ + if ( thetaCam > 360.0 ) + thetaCam -= 360.0; + else if ( thetaCam < 0.0 ) + thetaCam += 360.0; + + const GLdouble MINPHI = -90.0, MAXPHI = 90.0; + if ( phiCam > MAXPHI ) + phiCam = MAXPHI; + else if ( phiCam < MINPHI ) + phiCam = MINPHI; +} + +void calculerPhysique( ) +{ + if ( enmouvement ) + { + static int sensTheta = 1; + static int sensPhi = 1; + thetaCam += 0.3 * sensTheta; + phiCam += 0.5 * sensPhi; + //if ( thetaCam <= 0. || thetaCam >= 360.0 ) sensTheta = -sensTheta; + if ( phiCam < -90.0 || phiCam > 90.0 ) sensPhi = -sensPhi; + + static int sensAngle = 1; + LightSource[0].spotAngle += sensAngle * 0.3; + if ( LightSource[0].spotAngle < 5.0 ) sensAngle = -sensAngle; + if ( LightSource[0].spotAngle > 60.0 ) sensAngle = -sensAngle; + +#if 0 + static int sensExposant = 1; + LightSource[0].spotExposant += sensExposant * 0.3; + if ( LightSource[0].spotExposant < 1.0 ) sensExposant = -sensExposant; + if ( LightSource[0].spotExposant > 10.0 ) sensExposant = -sensExposant; +#endif + + // De temps à autre, alterner entre le modèle d'illumination: Lambert, Gouraud, Phong + static float type = 0; + type += 0.005; + varsUnif.typeIllumination = fmod(type,3); + } + + verifierAngles(); +} + +void chargerTextures() +{ + unsigned char *pixels; + GLsizei largeur, hauteur; + if ( ( pixels = ChargerImage( "textures/de.bmp", largeur, hauteur ) ) != NULL ) + { + glGenTextures( 1, &textureDE ); + glBindTexture( GL_TEXTURE_2D, textureDE ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, largeur, hauteur, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glBindTexture( GL_TEXTURE_2D, 0 ); + delete[] pixels; + } + if ( ( pixels = ChargerImage( "textures/echiquier.bmp", largeur, hauteur ) ) != NULL ) + { + glGenTextures( 1, &textureECHIQUIER ); + glBindTexture( GL_TEXTURE_2D, textureECHIQUIER ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, largeur, hauteur, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glBindTexture( GL_TEXTURE_2D, 0 ); + delete[] pixels; + } + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); +} + +void chargerNuanceurs() +{ + // charger le nuanceur de base + { + // créer le programme + progBase = glCreateProgram(); + + // attacher le nuanceur de sommets + { + GLuint nuanceurObj = glCreateShader( GL_VERTEX_SHADER ); + glShaderSource( nuanceurObj, 1, &ProgNuanceur::chainesSommetsMinimal, NULL ); + glCompileShader( nuanceurObj ); + glAttachShader( progBase, nuanceurObj ); + ProgNuanceur::afficherLogCompile( nuanceurObj ); + } + // attacher le nuanceur de fragments + { + GLuint nuanceurObj = glCreateShader( GL_FRAGMENT_SHADER ); + glShaderSource( nuanceurObj, 1, &ProgNuanceur::chainesFragmentsMinimal, NULL ); + glCompileShader( nuanceurObj ); + glAttachShader( progBase, nuanceurObj ); + ProgNuanceur::afficherLogCompile( nuanceurObj ); + } + + // faire l'édition des liens du programme + glLinkProgram( progBase ); + + ProgNuanceur::afficherLogLink( progBase ); + // demander la "Location" des variables + if ( ( locVertexBase = glGetAttribLocation( progBase, "Vertex" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de Vertex" << std::endl; + if ( ( locColorBase = glGetAttribLocation( progBase, "Color" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de Color" << std::endl; + if ( ( locmatrModelBase = glGetUniformLocation( progBase, "matrModel" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrModel" << std::endl; + if ( ( locmatrVisuBase = glGetUniformLocation( progBase, "matrVisu" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrVisu" << std::endl; + if ( ( locmatrProjBase = glGetUniformLocation( progBase, "matrProj" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrProj" << std::endl; + } + + // charger le nuanceur de ce TP + { + // créer le programme + prog = glCreateProgram(); + + // attacher le nuanceur de sommets +#if !defined(SOL) + const GLchar *chainesSommets = ProgNuanceur::lireNuanceur( "nuanceurSommets.glsl" ); +#else + const GLchar *chainesSommets = ProgNuanceur::lireNuanceur( "nuanceurSommetsSolution.glsl" ); +#endif + if ( chainesSommets != NULL ) + { + GLuint nuanceurObj = glCreateShader( GL_VERTEX_SHADER ); + glShaderSource( nuanceurObj, 1, &chainesSommets, NULL ); + glCompileShader( nuanceurObj ); + glAttachShader( prog, nuanceurObj ); + ProgNuanceur::afficherLogCompile( nuanceurObj ); + delete [] chainesSommets; + } +#if !defined(SOL) + const GLchar *chainesGeometrie = ProgNuanceur::lireNuanceur( "nuanceurGeometrie.glsl" ); +#else + const GLchar *chainesGeometrie = ProgNuanceur::lireNuanceur( "nuanceurGeometrieSolution.glsl" ); +#endif + if ( chainesGeometrie != NULL ) + { + GLuint nuanceurObj = glCreateShader( GL_GEOMETRY_SHADER ); + glShaderSource( nuanceurObj, 1, &chainesGeometrie, NULL ); + glCompileShader( nuanceurObj ); + glAttachShader( prog, nuanceurObj ); + ProgNuanceur::afficherLogCompile( nuanceurObj ); + delete [] chainesGeometrie; + } + // attacher le nuanceur de fragments +#if !defined(SOL) + const GLchar *chainesFragments = ProgNuanceur::lireNuanceur( "nuanceurFragments.glsl" ); +#else + const GLchar *chainesFragments = ProgNuanceur::lireNuanceur( "nuanceurFragmentsSolution.glsl" ); +#endif + if ( chainesFragments != NULL ) + { + GLuint nuanceurObj = glCreateShader( GL_FRAGMENT_SHADER ); + glShaderSource( nuanceurObj, 1, &chainesFragments, NULL ); + glCompileShader( nuanceurObj ); + glAttachShader( prog, nuanceurObj ); + ProgNuanceur::afficherLogCompile( nuanceurObj ); + delete [] chainesFragments; + } + + // faire l'édition des liens du programme + glLinkProgram( prog ); + + ProgNuanceur::afficherLogLink( prog ); + // demander la "Location" des variables + if ( ( locVertex = glGetAttribLocation( prog, "Vertex" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de Vertex" << std::endl; + if ( ( locNormal = glGetAttribLocation( prog, "Normal" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de Normal (partie 1)" << std::endl; + if ( ( locTexCoord = glGetAttribLocation( prog, "TexCoord" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de TexCoord (partie 3)" << std::endl; + if ( ( locmatrModel = glGetUniformLocation( prog, "matrModel" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrModel" << std::endl; + if ( ( locmatrVisu = glGetUniformLocation( prog, "matrVisu" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrVisu" << std::endl; + if ( ( locmatrProj = glGetUniformLocation( prog, "matrProj" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrProj" << std::endl; + if ( ( locmatrNormale = glGetUniformLocation( prog, "matrNormale" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de matrNormale (partie 1)" << std::endl; + if ( ( loclaTexture = glGetUniformLocation( prog, "laTexture" ) ) == -1 ) std::cerr << "!!! pas trouvé la \"Location\" de laTexture (partie 3)" << std::endl; + if ( ( indLightSource = glGetUniformBlockIndex( prog, "LightSourceParameters" ) ) == GL_INVALID_INDEX ) std::cerr << "!!! pas trouvé l'\"index\" de LightSource" << std::endl; + if ( ( indFrontMaterial = glGetUniformBlockIndex( prog, "MaterialParameters" ) ) == GL_INVALID_INDEX ) std::cerr << "!!! pas trouvé l'\"index\" de FrontMaterial" << std::endl; + if ( ( indLightModel = glGetUniformBlockIndex( prog, "LightModelParameters" ) ) == GL_INVALID_INDEX ) std::cerr << "!!! pas trouvé l'\"index\" de LightModel" << std::endl; + if ( ( indvarsUnif = glGetUniformBlockIndex( prog, "varsUnif" ) ) == GL_INVALID_INDEX ) std::cerr << "!!! pas trouvé l'\"index\" de varsUnif" << std::endl; + + // charger les ubo + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[0] ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(LightSource), &LightSource, GL_DYNAMIC_COPY ); + glBindBuffer( GL_UNIFORM_BUFFER, 0 ); + const GLuint bindingIndex = 0; + glBindBufferBase( GL_UNIFORM_BUFFER, bindingIndex, ubo[0] ); + glUniformBlockBinding( prog, indLightSource, bindingIndex ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[1] ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(FrontMaterial), &FrontMaterial, GL_DYNAMIC_COPY ); + glBindBuffer( GL_UNIFORM_BUFFER, 0 ); + const GLuint bindingIndex = 1; + glBindBufferBase( GL_UNIFORM_BUFFER, bindingIndex, ubo[1] ); + glUniformBlockBinding( prog, indFrontMaterial, bindingIndex ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[2] ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(LightModel), &LightModel, GL_DYNAMIC_COPY ); + glBindBuffer( GL_UNIFORM_BUFFER, 0 ); + const GLuint bindingIndex = 2; + glBindBufferBase( GL_UNIFORM_BUFFER, bindingIndex, ubo[2] ); + glUniformBlockBinding( prog, indLightModel, bindingIndex ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[3] ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(varsUnif), &varsUnif, GL_DYNAMIC_COPY ); + glBindBuffer( GL_UNIFORM_BUFFER, 0 ); + const GLuint bindingIndex = 3; + glBindBufferBase( GL_UNIFORM_BUFFER, bindingIndex, ubo[3] ); + glUniformBlockBinding( prog, indvarsUnif, bindingIndex ); + } + } +} + +// initialisation d'openGL +void initialiser() +{ + // donner l'orientation du modèle + thetaCam = 0.0; + phiCam = 0.0; + distCam = 30.0; + + // couleur de l'arrière-plan + glClearColor( 0.4, 0.2, 0.0, 1.0 ); + + // activer les etats openGL + glEnable( GL_DEPTH_TEST ); + + // charger les textures + chargerTextures(); + + // allouer les UBO pour les variables uniformes + glGenBuffers( 4, ubo ); + + // charger les nuanceurs + chargerNuanceurs(); + glUseProgram( prog ); + + // (partie 1) créer le cube + /* +Y */ + /* 3+-----------+2 */ + /* |\ |\ */ + /* | \ | \ */ + /* | \ | \ */ + /* | 7+-----------+6 */ + /* | | | | */ + /* | | | | */ + /* 0+---|-------+1 | */ + /* \ | \ | +X */ + /* \ | \ | */ + /* \| \| */ + /* 4+-----------+5 */ + /* +Z */ + + GLfloat sommets[3*4*6] = + { + -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, // P3,P2,P0,P1 + 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, // P5,P4,P1,P0 + 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // P6,P5,P2,P1 + -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, // P7,P6,P3,P2 + -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, // P4,P7,P0,P3 + -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0 // P4,P5,P7,P6 + }; + GLfloat normales[3*4*6] = + { + 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, + 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, + 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, + -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, + }; + GLfloat texcoordsDe[2*4*6] = + { + 1.000000,0.000000, 0.666666,0.000000, 1.000000,0.333333, 0.666666,0.333333, + 0.000000,0.666666, 0.333333,0.666666, 0.000000,0.333333, 0.333333,0.333333, + 0.666666,1.000000, 0.666666,0.666666, 0.333333,1.000000, 0.333333,0.666666, + 1.000000,0.333333, 0.666666,0.333333, 1.000000,0.666666, 0.666666,0.666666, + 0.333333,0.000000, 0.333333,0.333333, 0.666666,0.000000, 0.666666,0.333333, + 0.666666,0.333333, 0.333333,0.333333, 0.666666,0.666666, 0.333333,0.666666 + }; + GLfloat texcoordsEchiquier[2*4*6] = + { + -1.0, -1.0, -1.0, 2.0, 2.0, -1.0, 2.0, 2.0, + 2.0, -1.0, -1.0, -1.0, 2.0, 2.0, -1.0, 2.0, + -1.0, -1.0, -1.0, 2.0, 2.0, -1.0, 2.0, 2.0, + -1.0, 2.0, 2.0, 2.0, -1.0, -1.0, 2.0, -1.0, + 2.0, 2.0, 2.0, -1.0, -1.0, 2.0, -1.0, -1.0, + -1.0, -1.0, -1.0, 2.0, 2.0, -1.0, 2.0, 2.0 + }; + + // allouer les objets OpenGL + glGenVertexArrays( 2, vao ); + glGenBuffers( 5, vbo ); + // initialiser le VAO + glBindVertexArray( vao[0] ); + + // charger le VBO pour les sommets + glBindBuffer( GL_ARRAY_BUFFER, vbo[0] ); + glBufferData( GL_ARRAY_BUFFER, sizeof(sommets), sommets, GL_STATIC_DRAW ); + glVertexAttribPointer( locVertex, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertex); + // (partie 1) charger le VBO pour les normales + glBindBuffer( GL_ARRAY_BUFFER, vbo[1] ); + glBufferData( GL_ARRAY_BUFFER, sizeof(normales), normales, GL_STATIC_DRAW ); + glVertexAttribPointer( locNormal, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locNormal); + // (partie 3) charger le VBO pour les coordonnées de texture du dé + glBindBuffer( GL_ARRAY_BUFFER, vbo[2] ); + glBufferData( GL_ARRAY_BUFFER, sizeof(texcoordsDe), texcoordsDe, GL_STATIC_DRAW ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + // (partie 3) charger le VBO pour les coordonnées de texture de l'échiquier + glBindBuffer( GL_ARRAY_BUFFER, vbo[3] ); + glBufferData( GL_ARRAY_BUFFER, sizeof(texcoordsEchiquier), texcoordsEchiquier, GL_STATIC_DRAW ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locTexCoord); + + glBindVertexArray(0); + + // initialiser le VAO pour une ligne (montrant la direction du spot) + glBindVertexArray( vao[1] ); + GLfloat coords[] = { 0., 0., 0., 0., 0., 1. }; + glBindBuffer( GL_ARRAY_BUFFER, vbo[4] ); + glBufferData( GL_ARRAY_BUFFER, sizeof(coords), coords, GL_STATIC_DRAW ); + glVertexAttribPointer( locVertexBase, 3, GL_FLOAT, GL_FALSE, 0, 0 ); + glEnableVertexAttribArray(locVertexBase); + glBindVertexArray(0); + + // créer quelques autres formes + sphere = new FormeSphere( 1.0, 32, 32 ); + sphereLumi = new FormeSphere( 0.5, 10, 10 ); + theiere = new FormeTheiere( ); + tore = new FormeTore( 0.4, 0.8, 32, 32 ); + cylindre = new FormeCylindre( 0.3, 0.3, 3.0, 32, 32 ); + cone = new FormeCylindre( 0.0, 0.5, 3.0, 32, 32 ); +} + +void conclure() +{ + glUseProgram( 0 ); + glDeleteVertexArrays( 2, vao ); + glDeleteBuffers( 4, vbo ); + glDeleteBuffers( 4, ubo ); + delete sphere; + delete sphereLumi; + delete theiere; + delete tore; + delete cylindre; + delete cone; +} + +void afficherModele() +{ + // partie 3: paramètres de texture + switch ( varsUnif.texnumero ) + { + default: + //std::cout << "Sans texture" << std::endl; + glBindTexture( GL_TEXTURE_2D, 0 ); + break; + case 1: + //std::cout << "Texture DE" << std::endl; + glBindTexture( GL_TEXTURE_2D, textureDE ); + break; + case 2: + //std::cout << "Texture ECHIQUIER" << std::endl; + glBindTexture( GL_TEXTURE_2D, textureECHIQUIER ); + break; + } + + // Dessiner le modèle + matrModel.PushMatrix(); { + + // appliquer les rotations + matrModel.Rotate( phiCam, -1.0, 0.0, 0.0 ); + matrModel.Rotate( thetaCam, 0.0, -1.0, 0.0 ); + + // mise à l'échelle + matrModel.Scale( 5.0, 5.0, 5.0 ); + + glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); + // (partie 1: ne pas oublier de calculer et donner une matrice pour les transformations des normales) + glUniformMatrix3fv( locmatrNormale, 1, GL_TRUE, glm::value_ptr( glm::inverse( glm::mat3( matrVisu.getMatr() * matrModel.getMatr() ) ) ) ); + + switch ( modele ) + { + default: + case 1: + // afficher le cube + glBindVertexArray( vao[0] ); + glBindBuffer( GL_ARRAY_BUFFER, varsUnif.texnumero == 1 ? vbo[2] : vbo[3] ); + glVertexAttribPointer( locTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0 ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 ); + glDrawArrays( GL_TRIANGLE_STRIP, 4, 4 ); + glDrawArrays( GL_TRIANGLE_STRIP, 8, 4 ); + glDrawArrays( GL_TRIANGLE_STRIP, 12, 4 ); + glDrawArrays( GL_TRIANGLE_STRIP, 16, 4 ); + glDrawArrays( GL_TRIANGLE_STRIP, 20, 4 ); + glBindVertexArray( 0 ); + break; + case 2: + tore->afficher(); + break; + case 3: + sphere->afficher(); + break; + case 4: + matrModel.Rotate( -90.0, 1.0, 0.0, 0.0 ); + matrModel.Translate( 0.0, 0.0, -0.5 ); + matrModel.Scale( 0.5, 0.5, 0.5 ); + glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); + glUniformMatrix3fv( locmatrNormale, 1, GL_TRUE, glm::value_ptr( glm::inverse( glm::mat3( matrVisu.getMatr() * matrModel.getMatr() ) ) ) ); + theiere->afficher( ); + break; + case 5: + matrModel.PushMatrix(); { + matrModel.Translate( 0.0, 0.0, -1.5 ); + glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); + glUniformMatrix3fv( locmatrNormale, 1, GL_TRUE, glm::value_ptr( glm::inverse( glm::mat3( matrVisu.getMatr() * matrModel.getMatr() ) ) ) ); + cylindre->afficher(); + } matrModel.PopMatrix(); + break; + case 6: + matrModel.PushMatrix(); { + matrModel.Translate( 0.0, 0.0, -1.5 ); + glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); + glUniformMatrix3fv( locmatrNormale, 1, GL_TRUE, glm::value_ptr( glm::inverse( glm::mat3( matrVisu.getMatr() * matrModel.getMatr() ) ) ) ); + cone->afficher(); + } matrModel.PopMatrix(); + break; + } + } matrModel.PopMatrix(); glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); +} + +void afficherLumiere() +{ + // Dessiner la lumiere + + // tracer une ligne vers la source lumineuse + const GLfloat fact = 5.; + GLfloat coords[] = + { + LightSource[0].position.x , LightSource[0].position.y , LightSource[0].position.z, + LightSource[0].position.x+LightSource[0].spotDirection.x/fact, LightSource[0].position.y+LightSource[0].spotDirection.y/fact, LightSource[0].position.z+LightSource[0].spotDirection.z/fact + }; + glLineWidth( 3.0 ); + glVertexAttrib3f( locColorBase, 1.0, 1.0, 0.5 ); // jaune + glBindVertexArray( vao[1] ); + matrModel.PushMatrix(); { + glBindBuffer( GL_ARRAY_BUFFER, vbo[4] ); + glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(coords), coords ); + glDrawArrays( GL_LINES, 0, 2 ); + } matrModel.PopMatrix(); glUniformMatrix4fv( locmatrModelBase, 1, GL_FALSE, matrModel ); + glBindVertexArray( 0 ); + glLineWidth( 1.0 ); + + // tracer la source lumineuse + matrModel.PushMatrix(); { + matrModel.Translate( LightSource[0].position.x, LightSource[0].position.y, LightSource[0].position.z ); + glUniformMatrix4fv( locmatrModelBase, 1, GL_FALSE, matrModel ); + sphereLumi->afficher(); + } matrModel.PopMatrix(); glUniformMatrix4fv( locmatrModelBase, 1, GL_FALSE, matrModel ); +} + +// fonction d'affichage +void FenetreTP::afficherScene() +{ + // effacer l'ecran et le tampon de profondeur + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glUseProgram( progBase ); + + // définir le pipeline graphique + if ( enPerspective ) + { + matrProj.Perspective( 35.0, (GLdouble)largeur_ / (GLdouble)hauteur_, + 0.1, 60.0 ); + } + else + { + const GLfloat d = 8.0; + if ( largeur_ <= hauteur_ ) + { + matrProj.Ortho( -d, d, + -d*(GLdouble)hauteur_ / (GLdouble)largeur_, + d*(GLdouble)hauteur_ / (GLdouble)largeur_, + 0.1, 60.0 ); + } + else + { + matrProj.Ortho( -d*(GLdouble)largeur_ / (GLdouble)hauteur_, + d*(GLdouble)largeur_ / (GLdouble)hauteur_, + -d, d, + 0.1, 60.0 ); + } + } + glUniformMatrix4fv( locmatrProjBase, 1, GL_FALSE, matrProj ); + + matrVisu.LookAt( 0.0, 0.0, distCam, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ); + glUniformMatrix4fv( locmatrVisuBase, 1, GL_FALSE, matrVisu ); + + matrModel.LoadIdentity(); + glUniformMatrix4fv( locmatrModelBase, 1, GL_FALSE, matrModel ); + + // afficher les axes + if ( afficheAxes ) FenetreTP::afficherAxes( 8.0 ); + + // dessiner la scène + afficherLumiere(); + + glUseProgram( prog ); + + // mettre à jour les blocs de variables uniformes + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[0] ); + GLvoid *p = glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY ); + memcpy( p, &LightSource, sizeof(LightSource) ); + glUnmapBuffer( GL_UNIFORM_BUFFER ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[1] ); + GLvoid *p = glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY ); + memcpy( p, &FrontMaterial, sizeof(FrontMaterial) ); + glUnmapBuffer( GL_UNIFORM_BUFFER ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[2] ); + GLvoid *p = glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY ); + memcpy( p, &LightModel, sizeof(LightModel) ); + glUnmapBuffer( GL_UNIFORM_BUFFER ); + } + { + glBindBuffer( GL_UNIFORM_BUFFER, ubo[3] ); + GLvoid *p = glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY ); + memcpy( p, &varsUnif, sizeof(varsUnif) ); + glUnmapBuffer( GL_UNIFORM_BUFFER ); + } + + // mettre à jour les matrices et autres uniformes + glUniformMatrix4fv( locmatrProj, 1, GL_FALSE, matrProj ); + glUniformMatrix4fv( locmatrVisu, 1, GL_FALSE, matrVisu ); + glUniformMatrix4fv( locmatrModel, 1, GL_FALSE, matrModel ); + //glActiveTexture( GL_TEXTURE0 ); // activer la texture '0' (valeur de défaut) + glUniform1i( loclaTexture, 0 ); // '0' => utilisation de GL_TEXTURE0 + + afficherModele(); +} + +// fonction de redimensionnement de la fenêtre graphique +void FenetreTP::redimensionner( GLsizei w, GLsizei h ) +{ + glViewport( 0, 0, w, h ); +} + +static void echoEtats( ) +{ + static std::string illuminationStr[] = { "0:Lambert", "1:Gouraud", "2:Phong" }; + static std::string reflexionStr[] = { "0:Phong", "1:Blinn" }; + static std::string spotStr[] = { "0:OpenGL", "1:Direct3D" }; + std::cout << " modèle d'illumination= " << illuminationStr[varsUnif.typeIllumination] + << ", refléxion spéculaire= " << reflexionStr[varsUnif.utiliseBlinn] + << ", spot= " << spotStr[varsUnif.utiliseDirect] + << std::endl; +} + +// fonction de gestion du clavier +void FenetreTP::clavier( TP_touche touche ) +{ + // traitement des touches q et echap + switch ( touche ) + { + case TP_ECHAP: + case TP_q: // Quitter l'application + quit(); + break; + + case TP_x: // Activer/désactiver l'affichage des axes + afficheAxes = !afficheAxes; + std::cout << "// Affichage des axes ? " << ( afficheAxes ? "OUI" : "NON" ) << std::endl; + break; + + case TP_v: // Recharger les fichiers des nuanceurs et recréer le programme + chargerNuanceurs(); + std::cout << "// Recharger nuanceurs" << std::endl; + break; + + case TP_p: // Permuter la projection: perspective ou orthogonale + enPerspective = !enPerspective; + break; + + case TP_i: // Alterner entre le modèle d'illumination: Lambert, Gouraud, Phong + if ( ++varsUnif.typeIllumination > 2 ) varsUnif.typeIllumination = 0; + echoEtats( ); + break; + + case TP_r: // Alterner entre le modèle de réflexion spéculaire: Phong, Blinn + varsUnif.utiliseBlinn = !varsUnif.utiliseBlinn; + echoEtats( ); + break; + + case TP_s: // Alterner entre le modèle de spot: OpenGL, Direct3D + varsUnif.utiliseDirect = !varsUnif.utiliseDirect; + echoEtats( ); + break; + + //case TP_l: // Alterner entre une caméra locale à la scène ou distante (localViewer) + // LightModel.localViewer = !LightModel.localViewer; + // std::cout << " localViewer=" << LightModel.localViewer << std::endl; + // break; + + case TP_a: // Incrémenter l'angle du cône du spot + case TP_EGAL: + case TP_PLUS: + LightSource[0].spotAngle += 2.0; + if ( LightSource[0].spotAngle > 90.0 ) LightSource[0].spotAngle = 90.0; + std::cout << " spotAngle=" << LightSource[0].spotAngle << std::endl; + break; + case TP_z: // Décrémenter l'angle du cône du spot + case TP_MOINS: + case TP_SOULIGNE: + LightSource[0].spotAngle -= 2.0; + if ( LightSource[0].spotAngle < 0.0 ) LightSource[0].spotAngle = 0.0; + std::cout << " spotAngle=" << LightSource[0].spotAngle << std::endl; + break; + + case TP_d: // Incrémenter l'exposant du spot + case TP_BARREOBLIQUE: + LightSource[0].spotExposant += 0.3; + if ( LightSource[0].spotExposant > 89.0 ) LightSource[0].spotExposant = 89.0; + std::cout << " spotExposant=" << LightSource[0].spotExposant << std::endl; + break; + case TP_e: // Décrémenter l'exposant du spot + case TP_POINT: + LightSource[0].spotExposant -= 0.3; + if ( LightSource[0].spotExposant < 0.0 ) LightSource[0].spotExposant = 0.0; + std::cout << " spotExposant=" << LightSource[0].spotExposant << std::endl; + break; + + case TP_j: // Incrémenter le coefficient de brillance + case TP_CROCHETDROIT: + FrontMaterial.shininess *= 1.1; + std::cout << " FrontMaterial.shininess=" << FrontMaterial.shininess << std::endl; + break; + case TP_u: // Décrémenter le coefficient de brillance + case TP_CROCHETGAUCHE: + FrontMaterial.shininess /= 1.1; if ( FrontMaterial.shininess < 0.0 ) FrontMaterial.shininess = 0.0; + std::cout << " FrontMaterial.shininess=" << FrontMaterial.shininess << std::endl; + break; + + case TP_DROITE: + LightSource[0].position.x += 0.3; + break; + case TP_GAUCHE: + LightSource[0].position.x -= 0.3; + break; + case TP_BAS: + LightSource[0].position.y += 0.3; + break; + case TP_HAUT: + LightSource[0].position.y -= 0.3; + break; + + case TP_FIN: + LightSource[0].spotDirection.x += 0.6; + break; + case TP_DEBUT: + LightSource[0].spotDirection.x -= 0.6; + break; + case TP_PAGEPREC: + LightSource[0].spotDirection.y += 0.6; + break; + case TP_PAGESUIV: + LightSource[0].spotDirection.y -= 0.6; + break; + + case TP_m: // Choisir le modèle affiché: cube, tore, sphère, théière, cylindre, cône + if ( ++modele > 6 ) modele = 1; + std::cout << " modele=" << modele << std::endl; + break; + + case TP_0: + thetaCam = 0.0; phiCam = 0.0; distCam = 30.0; // placer les choses afin d'avoir une belle vue + break; + + case TP_t: // Choisir la texture utilisée: aucune, dé, échiquier + varsUnif.texnumero++; + if ( varsUnif.texnumero > 2 ) varsUnif.texnumero = 0; + std::cout << " varsUnif.texnumero=" << varsUnif.texnumero << std::endl; + break; + + // case TP_c: // Changer l'affichage de l'objet texturé avec couleurs ou sans couleur + // varsUnif.utiliseCouleur = !varsUnif.utiliseCouleur; + // std::cout << " utiliseCouleur=" << varsUnif.utiliseCouleur << std::endl; + // break; + + case TP_o: // Changer l'affichage des texels noirs (noir, mi-coloré, transparent) + varsUnif.afficheTexelNoir++; + if ( varsUnif.afficheTexelNoir > 2 ) varsUnif.afficheTexelNoir = 0; + std::cout << " afficheTexelNoir=" << varsUnif.afficheTexelNoir << std::endl; + break; + + case TP_g: // Permuter l'affichage en fil de fer ou plein + modePolygone = ( modePolygone == GL_FILL ) ? GL_LINE : GL_FILL; + glPolygonMode( GL_FRONT_AND_BACK, modePolygone ); + break; + + case TP_n: // Utiliser ou non les normales calculées comme couleur (pour le débogage) + varsUnif.afficheNormales = !varsUnif.afficheNormales; + break; + + case TP_ESPACE: // Permuter la rotation automatique du modèle + enmouvement = !enmouvement; + break; + + default: + std::cout << " touche inconnue : " << (char) touche << std::endl; + imprimerTouches(); + break; + } + +} + +// fonction callback pour un clic de souris +int dernierX = 0; // la dernière valeur en X de position de la souris +int dernierY = 0; // la derniere valeur en Y de position de la souris +static enum { deplaceCam, deplaceSpotDirection, deplaceSpotPosition } deplace = deplaceCam; +static bool pressed = false; +void FenetreTP::sourisClic( int button, int state, int x, int y ) +{ + pressed = ( state == TP_PRESSE ); + if ( pressed ) + { + // on vient de presser la souris + dernierX = x; + dernierY = y; + switch ( button ) + { + case TP_BOUTON_GAUCHE: // Tourner l'objet + deplace = deplaceCam; + break; + case TP_BOUTON_MILIEU: // Modifier l'orientation du spot + deplace = deplaceSpotDirection; + break; + case TP_BOUTON_DROIT: // Déplacer la lumière + deplace = deplaceSpotPosition; + break; + } + } + else + { + // on vient de relâcher la souris + } +} + +void FenetreTP::sourisWheel( int x, int y ) // Changer la taille du spot +{ + const int sens = +1; + LightSource[0].spotAngle += sens*y; + if ( LightSource[0].spotAngle > 90.0 ) LightSource[0].spotAngle = 90.0; + if ( LightSource[0].spotAngle < 0.0 ) LightSource[0].spotAngle = 0.0; + std::cout << " spotAngle=" << LightSource[0].spotAngle << std::endl; +} + +// fonction de mouvement de la souris +void FenetreTP::sourisMouvement( int x, int y ) +{ + if ( pressed ) + { + int dx = x - dernierX; + int dy = y - dernierY; + switch ( deplace ) + { + case deplaceCam: + thetaCam -= dx / 3.0; + phiCam -= dy / 3.0; + break; + case deplaceSpotDirection: + LightSource[0].spotDirection.x += 0.06 * dx; + LightSource[0].spotDirection.y -= 0.06 * dy; + // std::cout << " LightSource[0].spotDirection=" << glm::to_string(LightSource[0].spotDirection) << std::endl; + break; + case deplaceSpotPosition: + LightSource[0].position.x += 0.03 * dx; + LightSource[0].position.y -= 0.03 * dy; + // std::cout << " LightSource[0].position=" << glm::to_string(LightSource[0].position) << std::endl; + //glm::vec3 ecranPos( x, hauteur_-y, ecranLumi[2] ); + //LightSource[0].position = glm::vec4(glm::unProject( ecranPos, VM, P, cloture ), 1.0); + break; + } + + dernierX = x; + dernierY = y; + + verifierAngles(); + } +} + +int main( int argc, char *argv[] ) +{ + // créer une fenêtre + FenetreTP fenetre( "INF2705 TP" ); + + // allouer des ressources et définir le contexte OpenGL + initialiser(); + + bool boucler = true; + while ( boucler ) + { + // mettre à jour la physique + calculerPhysique( ); + + // affichage + fenetre.afficherScene(); + fenetre.swap(); + + // récupérer les événements et appeler la fonction de rappel + boucler = fenetre.gererEvenement(); + } + + // détruire les ressources OpenGL allouées + conclure(); + + return 0; +} diff --git a/textures/TestIsland.bmp b/textures/TestIsland.bmp new file mode 100644 index 0000000..f824b6a Binary files /dev/null and b/textures/TestIsland.bmp differ