/// same as video2d.c but for OpenGL #include "video3d.h" #include #include #ifdef OGL #include #include "field.h" #include "debug.h" #include "network.h" #include "input.h" #include "config.h" #include "effects.h" #include "math2d.h" #include "engine.h" #include "lists.h" #include //#include #include #include #include FT_FREETYPE_H #define DISPLAY_BPP 0 //default //display constants and variables #define DISPLAY_SCREENX 256 #define DISPLAY_SCREENY 256 #define WINDOW_SIZEX 640 #define WINDOW_SIZEY 640 #define VIEW_SANGLE 20 int detaillevel = 0; float viewdist = 70; /* the displayed screen rectangle, in mass coordinates */ Rect OGLscreenRect = {0,0,DISPLAY_SCREENX,DISPLAY_SCREENY}; GLuint fonts; void ftinit(void) { int i,j, num; GLubyte data[32*32*32]; GLint advancex, bearingx, bearingy; FT_Library library; FT_Face face; if (FT_Init_FreeType( &library )) { Die("could not initialze the freetype library\n"); } if (FT_New_Face(library, FindData("Dustismo_Roman.ttf"), 0, &face )) { Die("could not load %s font file\n", "Dustismo_Roman.ttf"); } if(FT_Set_Pixel_Sizes(face, 0, 20)) { Die("could not set font size"); } fonts = glGenLists(128); //the next 3 commands _must_ be executed before calling the Lists glListBase(fonts); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //OPTIMIZE: do this while storing the data glPixelZoom(1., -1); for (num=0; num < 128; num++) { FT_Load_Char(face, num, FT_LOAD_RENDER); for (i=0; iglyph->bitmap.rows; i++) { for (j=0; jglyph->bitmap.width; j++) { data[(i*face->glyph->bitmap.width+j)*2]= 0xFF; data[(i*face->glyph->bitmap.width+j)*2+1] = (GLubyte)face->glyph->bitmap.buffer[i*face->glyph->bitmap.width+j]; } /* for infos about the glyph metrics see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-3.html (visited: 24.2.2003) */ advancex = (int)(((float)face->glyph->metrics.horiAdvance )*(float)(face->glyph->bitmap.width) /(float)(face->glyph->metrics.width+1))+1; bearingx = (int)(((float)face->glyph->metrics.horiBearingX+1)*(float)(face->glyph->bitmap.width+1)/(float)(face->glyph->metrics.width+1)); bearingy = (int)(((float)face->glyph->metrics.horiBearingY )*(float)(face->glyph->bitmap.rows) /(float)(face->glyph->metrics.height+1)); //' ' needs an extra treatment, because horiAdvance is there 256 and the width's 0 if (num == ' ') advancex = 10; glNewList(fonts+num, GL_COMPILE); if ((bearingx != 0) || (bearingy != 0)) glBitmap(0,0,0,0, bearingx,bearingy,NULL); glDrawPixels(face->glyph->bitmap.width, face->glyph->bitmap.rows, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data); glBitmap(0,0,0,0, advancex-bearingx,-bearingy,NULL); glEndList(); } } } float getFPS(int count) { static clock_t told=0, tnew; static int countold=0; int numcount; told = tnew; tnew = clock(); numcount = count-countold; countold = count; return ((float)(numcount)/((float)(tnew-told)/CLOCKS_PER_SEC)); } void showFPS(GLint numMasses, GLint dmass, int count) { static char frbuf[30], fps[15]; int len; GLfloat color[3]; //It's projection mode now (3d) glPushMatrix(); glOrtho(-0.5, WINDOW_SIZEX, -0.5, WINDOW_SIZEY, -1.0,1.0); glMatrixMode(GL_MODELVIEW); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glColor3f(1., 1., 0.); //workaround, there should be a native ogl solution glGetFloatv(GL_CURRENT_COLOR, color); glPixelTransferf(GL_RED_SCALE, color[0]); glPixelTransferf(GL_GREEN_SCALE, color[1]); glPixelTransferf(GL_BLUE_SCALE, color[2]); if (!(count % 20)) sprintf(fps,"FPS: %f", getFPS(count)); glRasterPos2i(10, WINDOW_SIZEY-20); len=(int)strlen(fps); glCallLists(len, GL_BYTE, fps); glRasterPos2i(10, WINDOW_SIZEY-40); sprintf(frbuf,"%d visible Masses", numMasses); len=(int)strlen(frbuf); glCallLists(len, GL_BYTE, frbuf); glRasterPos2i(10, WINDOW_SIZEY-60); sprintf(frbuf,"%d drawn Masses", dmass); len=(int)strlen(frbuf); glCallLists(len, GL_BYTE, frbuf); glDisable(GL_BLEND); } void OGL_Draw_2D_Mass(Mass * m) { int x, y; FieldPoint * p; glColor3ubv(m->baseColor); for (y=m->maxRect.y; ymaxRect.y+m->maxRect.h; y++) { for (x=m->maxRect.x; xmaxRect.x+m->maxRect.w; x++) { p = FieldXY(x, y); if (p->owner == m) { glVertex2f((x-OGLscreenRect.x)&FIELDWRAP, DISPLAY_SCREENX-((y-OGLscreenRect.y)&FIELDWRAP)); } } } } void OGL_Draw_2D_Lightning(E_Lightning * l) { int i; /* FIXME: the OGL specification requires only the line width 1.0 other values depend on the implementation */ glLineWidth(l->r); glBegin(GL_LINE_STRIP); glColor3ubv(l->c); for (i=0; inumPoints - 1; i++) glVertex2f((l->points[i].x-OGLscreenRect.x)&FIELDWRAP, DISPLAY_SCREENX-((l->points[i].y-OGLscreenRect.y)&FIELDWRAP)); glEnd(); } void OGL_Draw_2D (List * visibleMasses) { int i; Mass * m; /* uses the projection Matrix set up by show FPS */ //Its Modelview now glPushMatrix(); glTranslatef(WINDOW_SIZEX-DISPLAY_SCREENX,WINDOW_SIZEY-DISPLAY_SCREENY,0); glBegin(GL_POINTS); FOR_ITEM_IN_LIST(m, visibleMasses) OGL_Draw_2D_Mass(m); glEnd(); for (i=0; effects[i]; i++) { switch (effects[i]->type) { case E_LIGHTNING: OGL_Draw_2D_Lightning((E_Lightning*)effects[i]); break; default: DebugLevel(2, "OGL_Draw_2D: cannot display effect %d\n", effects[i]->type); } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } float HeadingToGLAngle(int heading) { //return displayHeading*256/360+45; return (float)heading*360/256+90; } void CalcNormal(int x, int y, float * Normal) { double V1, V2; //I used that to create the code below /* V1[0] = 1; V1[1] = FieldPixel(x+1,y)->height - FieldPixel(x,y)->height; */ /* V1[2] = 0; */ /* V2[0] = 1; V2[1] = FieldPixel(x+1,y+1)->height - FieldPixel(x,y)->height; */ /* V2[2] = 1; */ /* Normal[0] = V1[1] * V2[2] - V1[2] * V2[1]; */ /* Normal[1] = V1[2] * V2[0] - V1[0] * V2[2]; */ /* Normal[2] = V1[0] * V2[1] - V1[1] * V2[0]; */ V1 = FieldXY(x,y+1)->height - FieldXY(x,y)->height; V1 = FieldXY(x,y+1)->height - FieldXY(x,y)->height; V2 = FieldXY(x+1,y+1)->height - FieldXY(x,y)->height; Normal[0] = V1 - V2; Normal[1] = 1; Normal[2] = - V1; V1 = FieldXY(x+1,y)->height - FieldXY(x,y)->height; V2 = FieldXY(x+1,y+1)->height - FieldXY(x,y)->height; Normal[0] += -V1; Normal[1] += 1; Normal[2] += V1 - V2; V1 = FieldXY(x+1,y)->height - FieldXY(x,y)->height; V2 = FieldXY(x+1,y-1)->height - FieldXY(x,y)->height; Normal[0] += -V1; Normal[1] += 1; Normal[2] += V2 - V1; V1 = FieldXY(x,y-1)->height - FieldXY(x,y)->height; V2 = FieldXY(x+1,y-1)->height - FieldXY(x,y)->height; Normal[0] += V1 - V2; Normal[1] += 1; Normal[2] += V1; V1 = FieldXY(x,y-1)->height - FieldXY(x,y)->height; V2 = FieldXY(x-1,y-1)->height - FieldXY(x,y)->height; Normal[0] += -V1 + V2; Normal[1] += 1; Normal[2] += V1; V1 = FieldXY(x-1,y)->height - FieldXY(x,y)->height; V2 = FieldXY(x-1,y-1)->height - FieldXY(x,y)->height; Normal[0] += V1; Normal[1] += 1; Normal[2] += V2 - V1; V1 = FieldXY(x-1,y)->height - FieldXY(x,y)->height; V2 = FieldXY(x-1,y+1)->height - FieldXY(x,y)->height; Normal[0] += V1; Normal[1] += 1; Normal[2] += -V2 + V1; V1 = FieldXY(x-1,y)->height - FieldXY(x,y)->height; V2 = FieldXY(x-1,y+1)->height - FieldXY(x,y)->height; Normal[0] += V1; Normal[1] += 1; Normal[2] += -V2 + V1; V1 = FieldXY(x,y+1)->height - FieldXY(x,y)->height; V2 = FieldXY(x-1,y+1)->height - FieldXY(x,y)->height; Normal[0] += V2 - V1; Normal[1] += 1; Normal[2] += - V1; } void OGL_Draw_3D_Mass(Mass * m) { int x, y, displayx, displayy; GLfloat black[] = {0.f, 0.f, 0.f, 0.f}, Normal[3]; FieldPoint * p; Mass * prevMass=NULL; glPushMatrix(); glTranslatef((m->maxRect.x-OGLscreenRect.x+1)&(FIELDWRAP), 0., (m->maxRect.y-OGLscreenRect.y+1)&(FIELDWRAP)); glColor4fv(black); for (y=m->maxRect.y-1; ymaxRect.y+m->maxRect.h+1; y+=1+detaillevel) { glBegin(GL_TRIANGLE_STRIP); for (x=m->maxRect.x-1; xmaxRect.x+m->maxRect.w+1; x+=2+detaillevel) { displayx = x-m->maxRect.x; displayy = y-m->maxRect.y; p = FieldXY(x, y); if (p->owner) { CalcNormal(x,y,&Normal[0]); glNormal3fv(Normal); if (p->owner != prevMass) glColor3ubv(p->owner->baseColor); glVertex3f(displayx, p->height, displayy); } else { if (p->owner != prevMass) glColor4fv(black); glVertex3f(displayx, 0.f, displayy); } prevMass = p->owner; p = FieldXY(x, y+1+detaillevel); if (p->owner) { CalcNormal(x,y+1+detaillevel,&Normal[0]); glNormal3fv(Normal); if (p->owner != prevMass) glColor3ubv(p->owner->baseColor); glVertex3f(displayx, p->height, displayy+1+detaillevel); } else { if (p->owner != prevMass) glColor4fv(black); glVertex3f(displayx, 0.f, displayy+1+detaillevel); } prevMass = p->owner; p = FieldXY(x+1+detaillevel, y); if (p->owner) { CalcNormal(x+1+detaillevel,y,&Normal[0]); glNormal3fv(Normal); if (p->owner != prevMass) glColor3ubv(p->owner->baseColor); glVertex3f(displayx+1+detaillevel, p->height, displayy); } else { if (p->owner != prevMass) glColor4fv(black); glVertex3f(displayx+1+detaillevel, 0.f, displayy); } prevMass = p->owner; p = FieldXY(x+1+detaillevel, y+1+detaillevel); if (p->owner) { CalcNormal(x+1+detaillevel,y+1+detaillevel,&Normal[0]); glNormal3fv(Normal); if (p->owner != prevMass) glColor3ubv(p->owner->baseColor); glVertex3f(displayx+1+detaillevel, p->height, displayy+1+detaillevel); } else { if (p->owner != prevMass) glColor4fv(black); glVertex3f(displayx+1+detaillevel, 0.f, displayy+1+detaillevel); } prevMass = p->owner; } glEnd();/*GL_TRIANGLE_STRIP*/ } glPopMatrix(); } void OGL_Draw_3D_BBox(Mass * m, GLint name) { int displayx, displayy; Rect visible; visible = wrap_SmallestCommonRect(OGLscreenRect, m->maxRect); glLoadName(name); glBegin(GL_QUADS); displayx = (visible.x-OGLscreenRect.x+1)&(FIELDWRAP); displayy = (visible.y-OGLscreenRect.y+1)&(FIELDWRAP); glVertex3f(displayx, 0.f, displayy); glVertex3f(displayx, 0.f, displayy+visible.h); glVertex3f(displayx, m->maxHeight, displayy+visible.h); glVertex3f(displayx, m->maxHeight, displayy); glVertex3f(displayx, 0.f, displayy); glVertex3f(displayx+visible.w, 0.f, displayy); glVertex3f(displayx+visible.w, m->maxHeight, displayy); glVertex3f(displayx, m->maxHeight, displayy); glVertex3f(displayx+visible.w, 0.f, displayy); glVertex3f(displayx+visible.w, 0.f, displayy+visible.h); glVertex3f(displayx+visible.w, m->maxHeight, displayy+visible.h); glVertex3f(displayx+visible.w, m->maxHeight, displayy); glVertex3f(displayx, 0.f, displayy+visible.h); glVertex3f(displayx+visible.w, 0.f, displayy+visible.h); glVertex3f(displayx+visible.w, m->maxHeight, displayy+visible.h); glVertex3f(displayx, m->maxHeight, displayy+visible.h); glEnd(); } void OGL_Gen_3D_List(Mass * m, GLuint list) { glNewList(list, GL_COMPILE_AND_EXECUTE); OGL_Draw_3D_Mass(m); glEndList(); } void OGL_Call_3D_List(Mass * m) { OGLdisplayData * ddata; ddata = (OGLdisplayData *)m->displayData; glCallList(ddata->list); } void OGLfreeDisplayData(DummyDisplayData * data) { OGLdisplayData * ddata; ddata = (OGLdisplayData *)data; glDeleteLists(ddata->list, 1); xfree(ddata); } void OGL_Draw_3D_Lightning(E_Lightning * l) { int i; glLineWidth(l->r); glBegin(GL_LINE_STRIP); glColor3ubv(l->c); for (i=0; inumPoints - 1; i++) glVertex3f((l->points[i].x-OGLscreenRect.x)&FIELDWRAP, HEIGHT_NORM*((l->points[i].x+l->points[i].x)&31), (l->points[i].y-OGLscreenRect.y)&FIELDWRAP); glEnd(); } void OGL_Draw_3D(List * visibleMasses, GLint * dmass) { Mass * m; GLint i; GLfloat fogcol[4]; #define SELBUFLEN 512 GLuint selBuf[SELBUFLEN], hits; OGLdisplayData * ddata; //It's projection mode glMatrixMode(GL_PROJECTION); glPushMatrix(); glFrustum(-.41, .41, -.41, .41, 1, 128+viewdist); glMatrixMode(GL_MODELVIEW); glPushMatrix(); // 60 is the distance form displaycenter, 25 is the view angle glTranslatef(0., 0., -viewdist); glRotatef(VIEW_SANGLE+5.*(key.lookDown->hits - key.lookUp->hits), 1., 0., 0.); glRotatef(HeadingToGLAngle(displayHeading), 0., 1., 0.); //center the field glTranslatef(-DISPLAY_SCREENX/2, 0., -DISPLAY_SCREENY/2); //squeeze height to a resonable value glScalef(1., 1./((float)HEIGHT_NORM*2), 1.); //speed up rendering glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); //initialize Selection buffer and Names glSelectBuffer(SELBUFLEN, selBuf); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); i = 0; FOR_ITEM_IN_LIST(m, visibleMasses) OGL_Draw_3D_BBox(m, i++); //get the number of Masses to render hits = glRenderMode(GL_RENDER); //enable Fog glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_FOG); glFogf(GL_FOG_MODE, GL_LINEAR); if (fogRadius > 0) { int fogdiv = (GLfloat)fogRadius/(GLfloat)MAX_FOG_RADIUS; fogcol[0] = (GLfloat)fogColor[0]/255.; fogcol[1] = (GLfloat)fogColor[1]/255.; fogcol[2] = (GLfloat)fogColor[2]/255.; glFogf(GL_FOG_START, 1.); glFogf(GL_FOG_END, fogRadius); for (i=0; i<3; i++) fogcol[i] -= fogdiv * fogcol[i]; glClearColor(fogcol[0], fogcol[1], fogcol[2], 1.); } else { fogcol[0] = 0.f; fogcol[1] = 0.f; fogcol[2] = 0.f; glFogf(GL_FOG_START, 80+viewdist); glFogf(GL_FOG_END, 100+viewdist); glClearColor(0., 0., 0., 1.); } fogcol[3] = 1.f; glFogfv(GL_FOG_COLOR, fogcol); if (config.grid) { glDisable(GL_FOG); glDisable(GL_BLEND); glDisable(GL_LIGHTING); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); glDisable(GL_COLOR_MATERIAL); glLineWidth(1.); } else { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); } //Draw Masses for (i=0; ispeed < 1) { ddata = (OGLdisplayData *)m->displayData; if (!ddata) { m->displayData = (DummyDisplayData *)malloc(sizeof(OGLdisplayData)); ddata = (OGLdisplayData *)m->displayData; ddata->list = glGenLists(1); ddata->OGLfreeFunc = OGLfreeDisplayData; OGL_Gen_3D_List(m, ddata->list); } else if (m->mustRedraw) { OGL_Gen_3D_List(m, ddata->list); } else OGL_Call_3D_List(m); } else OGL_Draw_3D_Mass(m); } //Draw effects for (i=0; effects[i]; i++) { switch (effects[i]->type) { case E_LIGHTNING: OGL_Draw_3D_Lightning((E_Lightning*)effects[i]); break; default: DebugLevel(2, "OGL_Draw_3D: cannot display effect %d\n", effects[i]->type); } } glDisable(GL_FOG); glDisable(GL_BLEND); glDisable(GL_LIGHTING); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); glDisable(GL_COLOR_MATERIAL); *dmass = hits; glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } void OGLInit(int width, int height) { GLfloat Lpos[]={DISPLAY_SCREENX/2, 32*HEIGHT_NORM, DISPLAY_SCREENY/2,1.}; GLfloat Lcol_ambient[] = {.1, .1, .1, .1}, Lcol_diffuse[] = {.6, .6, .6, .6},Lcol_specular[] = {.3, .3, .3, .3}; glViewport(0, 0, WINDOW_SIZEX, WINDOW_SIZEY); glClearColor(0.f, 0.f, 0.f, 1.f); glClearDepth(1.f); glLineWidth(1.f); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); //Back face culling glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); //use GL_FILL for not wireframe if (config.grid) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } else { glPolygonMode(GL_FRONT, GL_FILL); } //not use Textures glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE ); glLightfv(GL_LIGHT0, GL_POSITION, Lpos); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Lcol_ambient); glLightfv(GL_LIGHT0, GL_AMBIENT, Lcol_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, Lcol_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, Lcol_specular); //set up clean Matrices glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //initalize FreeType fonts ftinit(); } int InitVideo3d() { int sdlflags; /* Initialize SDL for video output */ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); return 0; } /* Create a OpenGL screen */ sdlflags = SDL_OPENGL; /* FIXME: doesn't work :( - still not? */ if (config.fullscreen) sdlflags |= SDL_FULLSCREEN; if ( SDL_SetVideoMode(WINDOW_SIZEX, WINDOW_SIZEY, 0, sdlflags) == NULL ) { fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError()); SDL_Quit(); return 0;; } /* Set the title bar in environments that support it */ SDL_WM_SetCaption("Mass " VERSIONNAME, NULL); /* Loop, drawing and checking events */ OGLInit(WINDOW_SIZEX, WINDOW_SIZEY); return 0; } void DoneVideo3d() { } void DrawFrame3d() { List * visibleMasses; GLint dmass; static int count; //wipe out all remainders glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* top left corner of the screen in mass coordinates */ OGLscreenRect.x = displayCenter.x - (DISPLAY_SCREENX)/2; OGLscreenRect.y = displayCenter.y - (DISPLAY_SCREENY)/2; /* Wireframe for Martin */ if (key.toggleGrid->hit) { config.grid = !config.grid; if (config.grid) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } else glPolygonMode(GL_FRONT, GL_FILL); } /* some more keystrokes */ if (key.zoomIn->hit) viewdist -= 10; if (key.zoomOut->hit) viewdist += 10; if (key.moreDetails->hit) detaillevel++; if (key.lessDetails->hit && detaillevel > 0) detaillevel--; //compute visible masses for 2d and 3d visibleMasses = GetMassesInRect(OGLscreenRect); OGL_Draw_3D(visibleMasses, &dmass); showFPS(list_len(visibleMasses), dmass, count); OGL_Draw_2D(visibleMasses); //free memory list_free(visibleMasses); //swap buffers SDL_GL_SwapBuffers(); count++; } #else /* #ifdef OGL */ int InitVideo3d(void) { printf("OpenGL not available, the game was compiled without.\n"); return -1; } void DoneVideo3d() { } void DrawFrame3d() { } #endif /* #ifdef OGL */