diff -r f726e36c3e24 -r 7e40820b7ed6 hedgewars/uRender.pas --- a/hedgewars/uRender.pas Sat Jun 14 07:12:22 2014 +0200 +++ b/hedgewars/uRender.pas Sat Jun 14 15:49:44 2014 +0200 @@ -23,7 +23,10 @@ interface -uses SDLh, uTypes, GLunit, uConsts{$IFDEF GL2}, uMatrix{$ENDIF}; +uses SDLh, uTypes, GLunit; + +procedure initModule; +procedure freeModule; procedure DrawSprite (Sprite: TSprite; X, Y, Frame: LongInt); procedure DrawSprite (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt); @@ -48,11 +51,13 @@ procedure DrawLine (X0, Y0, X1, Y1, Width: Single; color: LongWord); inline; procedure DrawLine (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte); -procedure DrawFillRect (r: TSDL_Rect); +procedure DrawRect (rect: TSDL_Rect; r, g, b, a: Byte; Fill: boolean); procedure DrawHedgehog (X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real); procedure DrawScreenWidget (widget: POnScreenWidget); procedure DrawWaterBody (pVertexBuffer: Pointer); +procedure RenderClear (); +procedure RenderSetClearColor (r, g, b, a: real); procedure Tint (r, g, b, a: Byte); inline; procedure Tint (c: Longword); inline; procedure untint(); inline; @@ -67,6 +72,10 @@ procedure SetScale(f: GLfloat); procedure UpdateViewLimits(); +procedure RenderSetup(); + +// TODO everything below this should not need a public interface + procedure EnableTexture(enable:Boolean); procedure SetTexCoordPointer(p: Pointer;n: Integer); @@ -86,7 +95,8 @@ implementation -uses uVariables; +uses {$IFNDEF PAS2C} StrUtils, {$ENDIF}SysUtils, uVariables, uUtils, uConsts + {$IFDEF GL2}, uMatrix, uConsole{$ENDIF}; {$IFDEF USE_TOUCH_INTERFACE} const @@ -94,6 +104,12 @@ MOVE_ANIM_TIME = 500; {$ENDIF} +{$IFDEF GL2} +var + shaderMain: GLuint; + shaderWater: GLuint; +{$ENDIF} + var LastTint: LongWord = 0; function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline; @@ -115,6 +131,292 @@ isDyAreaOffscreen:= 0; end; +procedure RenderClear(); +begin + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); +end; + +procedure RenderSetClearColor(r, g, b, a: real); +begin + glClearColor(r, g, b, a); +end; + +{$IFDEF GL2} +function CompileShader(shaderFile: string; shaderType: GLenum): GLuint; +var + shader: GLuint; + f: Textfile; + source, line: AnsiString; + sourceA: Pchar; + lengthA: GLint; + compileResult: GLint; + logLength: GLint; + log: PChar; +begin + Assign(f, PathPrefix + cPathz[ptShaders] + '/' + shaderFile); + filemode:= 0; // readonly + Reset(f); + if IOResult <> 0 then + begin + AddFileLog('Unable to load ' + shaderFile); + halt(-1); + end; + + source:=''; + while not eof(f) do + begin + ReadLn(f, line); + source:= source + line + #10; + end; + + Close(f); + + WriteLnToConsole('Compiling shader: ' + PathPrefix + cPathz[ptShaders] + '/' + shaderFile); + + sourceA:=PChar(source); + lengthA:=Length(source); + + shader:=glCreateShader(shaderType); + glShaderSource(shader, 1, @sourceA, @lengthA); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, @compileResult); + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, @logLength); + + if logLength > 1 then + begin + log := GetMem(logLength); + glGetShaderInfoLog(shader, logLength, nil, log); + WriteLnToConsole('========== Compiler log =========='); + WriteLnToConsole(shortstring(log)); + WriteLnToConsole('==================================='); + FreeMem(log, logLength); + end; + + if compileResult <> GL_TRUE then + begin + WriteLnToConsole('Shader compilation failed, halting'); + halt(-1); + end; + + CompileShader:= shader; +end; + +function CompileProgram(shaderName: string): GLuint; +var + program_: GLuint; + vs, fs: GLuint; + linkResult: GLint; + logLength: GLint; + log: PChar; +begin + program_:= glCreateProgram(); + vs:= CompileShader(shaderName + '.vs', GL_VERTEX_SHADER); + fs:= CompileShader(shaderName + '.fs', GL_FRAGMENT_SHADER); + glAttachShader(program_, vs); + glAttachShader(program_, fs); + + glBindAttribLocation(program_, aVertex, PChar('vertex')); + glBindAttribLocation(program_, aTexCoord, PChar('texcoord')); + glBindAttribLocation(program_, aColor, PChar('color')); + + glLinkProgram(program_); + glDeleteShader(vs); + glDeleteShader(fs); + + glGetProgramiv(program_, GL_LINK_STATUS, @linkResult); + glGetProgramiv(program_, GL_INFO_LOG_LENGTH, @logLength); + + if logLength > 1 then + begin + log := GetMem(logLength); + glGetProgramInfoLog(program_, logLength, nil, log); + WriteLnToConsole('========== Compiler log =========='); + WriteLnToConsole(shortstring(log)); + WriteLnToConsole('==================================='); + FreeMem(log, logLength); + end; + + if linkResult <> GL_TRUE then + begin + WriteLnToConsole('Linking program failed, halting'); + halt(-1); + end; + + CompileProgram:= program_; +end; +{$ENDIF} + +procedure RenderSetup(); +var AuxBufNum: LongInt = 0; + tmpstr: ansistring; + tmpint: LongInt; + tmpn: LongInt; +begin + // suppress hint/warning + AuxBufNum:= AuxBufNum; + + // get the max (h and v) size for textures that the gpu can support + glGetIntegerv(GL_MAX_TEXTURE_SIZE, @MaxTextureSize); + if MaxTextureSize <= 0 then + begin + MaxTextureSize:= 1024; + AddFileLog('OpenGL Warning - driver didn''t provide any valid max texture size; assuming 1024'); + end + else if (MaxTextureSize < 1024) and (MaxTextureSize >= 512) then + begin + cReducedQuality := cReducedQuality or rqNoBackground; + AddFileLog('Texture size too small for backgrounds, disabling.'); + end; + // everyone loves debugging + // find out which gpu we are using (for extension compatibility maybe?) + AddFileLog('OpenGL-- Renderer: ' + shortstring(pchar(glGetString(GL_RENDERER)))); + AddFileLog(' |----- Vendor: ' + shortstring(pchar(glGetString(GL_VENDOR)))); + AddFileLog(' |----- Version: ' + shortstring(pchar(glGetString(GL_VERSION)))); + AddFileLog(' |----- Texture Size: ' + inttostr(MaxTextureSize)); +{$IFDEF USE_VIDEO_RECORDING} + glGetIntegerv(GL_AUX_BUFFERS, @AuxBufNum); + AddFileLog(' |----- Number of auxiliary buffers: ' + inttostr(AuxBufNum)); +{$ENDIF} +{$IFNDEF PAS2C} + AddFileLog(' \----- Extensions: '); + + // fetch extentions and store them in string + tmpstr := StrPas(PChar(glGetString(GL_EXTENSIONS))); + tmpn := WordCount(tmpstr, [' ']); + tmpint := 1; + + repeat + begin + // print up to 3 extentions per row + // ExtractWord will return empty string if index out of range + AddFileLog(TrimRight( + ExtractWord(tmpint, tmpstr, [' ']) + ' ' + + ExtractWord(tmpint+1, tmpstr, [' ']) + ' ' + + ExtractWord(tmpint+2, tmpstr, [' ']) + )); + tmpint := tmpint + 3; + end; + until (tmpint > tmpn); +{$ENDIF} + AddFileLog(''); + + defaultFrame:= 0; + +{$IFDEF USE_VIDEO_RECORDING} + if GameType = gmtRecord then + begin + if glLoadExtension('GL_EXT_framebuffer_object') then + begin + CreateFramebuffer(defaultFrame, depthv, texv); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame); + AddFileLog('Using framebuffer for video recording.'); + end + else if AuxBufNum > 0 then + begin + glDrawBuffer(GL_AUX0); + glReadBuffer(GL_AUX0); + AddFileLog('Using auxiliary buffer for video recording.'); + end + else + begin + glDrawBuffer(GL_BACK); + glReadBuffer(GL_BACK); + AddFileLog('Warning: off-screen rendering is not supported; using back buffer but it may not work.'); + end; + end; +{$ENDIF} + +{$IFDEF GL2} + +{$IFDEF PAS2C} + err := glewInit(); + if err <> GLEW_OK then + begin + WriteLnToConsole('Failed to initialize GLEW.'); + halt; + end; +{$ENDIF} + +{$IFNDEF PAS2C} + if not Load_GL_VERSION_2_0 then + halt; +{$ENDIF} + + shaderWater:= CompileProgram('water'); + glUseProgram(shaderWater); + glUniform1i(glGetUniformLocation(shaderWater, pchar('tex0')), 0); + uWaterMVPLocation:= glGetUniformLocation(shaderWater, pchar('mvp')); + + shaderMain:= CompileProgram('default'); + glUseProgram(shaderMain); + glUniform1i(glGetUniformLocation(shaderMain, pchar('tex0')), 0); + uMainMVPLocation:= glGetUniformLocation(shaderMain, pchar('mvp')); + uMainTintLocation:= glGetUniformLocation(shaderMain, pchar('tint')); + + uCurrentMVPLocation:= uMainMVPLocation; + + Tint(255, 255, 255, 255); + UpdateModelviewProjection; +{$ENDIF} + +{$IFNDEF USE_S3D_RENDERING} + if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then + begin + // prepare left and right frame buffers and associated textures + if glLoadExtension('GL_EXT_framebuffer_object') then + begin + CreateFramebuffer(framel, depthl, texl); + CreateFramebuffer(framer, depthr, texr); + + // reset + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame) + end + else + cStereoMode:= smNone; + end; +{$ENDIF} + +// set view port to whole window +glViewport(0, 0, cScreenWidth, cScreenHeight); + +{$IFDEF GL2} + uMatrix.initModule; + hglMatrixMode(MATRIX_MODELVIEW); + // prepare default translation/scaling + hglLoadIdentity(); + hglScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0); + hglTranslatef(0, -cScreenHeight / 2, 0); + + EnableTexture(True); + + glEnableVertexAttribArray(aVertex); + glEnableVertexAttribArray(aTexCoord); + glGenBuffers(1, @vBuffer); + glGenBuffers(1, @tBuffer); + glGenBuffers(1, @cBuffer); +{$ELSE} + glMatrixMode(GL_MODELVIEW); + // prepare default translation/scaling + glLoadIdentity(); + glScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0); + glTranslatef(0, -cScreenHeight / 2, 0); + + // disable/lower perspective correction (will not need it anyway) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + // disable dithering + glDisable(GL_DITHER); + // enable common states by default as they save a lot + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +{$ENDIF} + + // enable alpha blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // disable/lower perspective correction (will not need it anyway) +end; + procedure openglLoadIdentity(); inline; begin {$IFDEF GL2} @@ -285,11 +587,25 @@ tmp:= cScreenHeight / cScaleFactor; ViewBottomY:= round(tmp) + cScreenHeight div 2; // ceil could make more sense ViewTopY:= round(-tmp) + cScreenHeight div 2; // floor could make more sense + + // visual debugging fun :D + if cViewLimitsDebug then + begin + // some margin on each side + tmp:= min(cScreenWidth, cScreenHeight) div 2 / cScaleFactor; + ViewLeftX := ViewLeftX + trunc(tmp); + ViewRightX := ViewRightX - trunc(tmp); + ViewBottomY:= ViewBottomY - trunc(tmp); + ViewTopY := ViewTopY + trunc(tmp); + end; + + ViewWidth := ViewRightX - ViewLeftX + 1; + ViewHeight:= ViewBottomY - ViewTopY + 1; end; procedure SetScale(f: GLfloat); begin -// leave immediately if scale factor did not change + // leave immediately if scale factor did not change if f = cScaleFactor then exit; @@ -303,7 +619,7 @@ openglPushMatrix; // save default scaling in matrix openglLoadIdentity(); openglScalef(f / cScreenWidth, -f / cScreenHeight, 1.0); - openglTranslatef(0, -cScreenHeight / 2, 0); + openglTranslatef(0, -cScreenHeight div 2, 0); end; cScaleFactor:= f; @@ -337,11 +653,16 @@ if (SourceTexture^.h = 0) or (SourceTexture^.w = 0) then exit; +{if isDxAreaOffscreen(X, W) <> 0 then + exit; +if isDyAreaOffscreen(Y, H) <> 0 then + exit;} + // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) -if (abs(X) > W) and ((abs(X + W / 2) - W / 2) > cScreenWidth / cScaleFactor) then +{if (abs(X) > W) and ((abs(X + W / 2) - W / 2) > cScreenWidth / cScaleFactor) then exit; if (abs(Y) > H) and ((abs(Y + H / 2 - (0.5 * cScreenHeight)) - H / 2) > cScreenHeight / cScaleFactor) then - exit; + exit;} rr.x:= X; rr.y:= Y; @@ -357,13 +678,13 @@ if Dir < 0 then begin - VertexBuffer[0].X:= X + rr.w/2; + VertexBuffer[0].X:= X + rr.w div 2; VertexBuffer[0].Y:= Y; - VertexBuffer[1].X:= X - rr.w/2; + VertexBuffer[1].X:= X - rr.w div 2; VertexBuffer[1].Y:= Y; - VertexBuffer[2].X:= X - rr.w/2; + VertexBuffer[2].X:= X - rr.w div 2; VertexBuffer[2].Y:= rr.h + Y; - VertexBuffer[3].X:= X + rr.w/2; + VertexBuffer[3].X:= X + rr.w div 2; VertexBuffer[3].Y:= rr.h + Y; end else @@ -402,16 +723,16 @@ openglPushMatrix; openglTranslatef(X, Y, 0); -openglScalef(Scale, Scale, 1); + +if Scale <> 1.0 then + openglScalef(Scale, Scale, 1); glBindTexture(GL_TEXTURE_2D, Texture^.id); SetVertexPointer(@Texture^.vb, Length(Texture^.vb)); SetTexCoordPointer(@Texture^.tb, Length(Texture^.vb)); -{$IFDEF GL2} UpdateModelviewProjection; -{$ENDIF} glDrawArrays(GL_TRIANGLE_FAN, 0, Length(Texture^.vb)); openglPopMatrix; @@ -457,11 +778,17 @@ hw, hh, nx, ny: LongInt; VertexBuffer, TextureBuffer: array [0..3] of TVertex2f; begin + +if isDxAreaOffscreen(X, w) <> 0 then + exit; +if isDyAreaOffscreen(Y, h) <> 0 then + exit; + // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) -if (abs(X) > W) and ((abs(X + dir * OffsetX) - W / 2) * cScaleFactor > cScreenWidth) then +{if (abs(X) > W) and ((abs(X + dir * OffsetX) - W / 2) * cScaleFactor > cScreenWidth) then exit; if (abs(Y) > H) and ((abs(Y + OffsetY - (0.5 * cScreenHeight)) - W / 2) * cScaleFactor > cScreenHeight) then - exit; + exit;} openglPushMatrix; @@ -558,11 +885,17 @@ procedure DrawTextureRotated(Texture: PTexture; hw, hh, X, Y, Dir: LongInt; Angle: real); var VertexBuffer: array [0..3] of TVertex2f; begin + +if isDxAreaOffscreen(X, 2 * hw) <> 0 then + exit; +if isDyAreaOffscreen(Y, 2 * hh) <> 0 then + exit; + // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) -if (abs(X) > 2 * hw) and ((abs(X) - hw) > cScreenWidth / cScaleFactor) then +{if (abs(X) > 2 * hw) and ((abs(X) - hw) > cScreenWidth / cScaleFactor) then exit; if (abs(Y) > 2 * hh) and ((abs(Y - 0.5 * cScreenHeight) - hh) > cScreenHeight / cScaleFactor) then - exit; + exit;} openglPushMatrix; openglTranslatef(X, Y, 0); @@ -647,12 +980,20 @@ procedure DrawTextureCentered(X, Top: LongInt; Source: PTexture); var scale: GLfloat; + left : LongInt; begin + // scale down if larger than screen if (Source^.w + 20) > cScreenWidth then - scale:= cScreenWidth / (Source^.w + 20) + begin + scale:= cScreenWidth / (Source^.w + 20); + DrawTexture(X - round(Source^.w * scale) div 2, Top, Source, scale); + end else - scale:= 1.0; - DrawTexture(X - round(Source^.w * scale) div 2, Top, Source, scale) + begin + left:= X - Source^.w div 2; + if (not isAreaOffscreen(left, Top, Source^.w, Source^.h)) then + DrawTexture(left, Top, Source); + end; end; procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); inline; @@ -690,31 +1031,36 @@ glDisable(GL_LINE_SMOOTH); end; -procedure DrawFillRect(r: TSDL_Rect); +procedure DrawRect(rect: TSDL_Rect; r, g, b, a: Byte; Fill: boolean); var VertexBuffer: array [0..3] of TVertex2f; begin // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) - -if (abs(r.x) > r.w) and ((abs(r.x + r.w / 2) - r.w / 2) * cScaleFactor > cScreenWidth) then +{if (abs(r.x) > r.w) and ((abs(r.x + r.w / 2) - r.w / 2) * cScaleFactor > cScreenWidth) then exit; if (abs(r.y) > r.h) and ((abs(r.y + r.h / 2 - (0.5 * cScreenHeight)) - r.h / 2) * cScaleFactor > cScreenHeight) then - exit; + exit;} EnableTexture(False); -Tint($00, $00, $00, $80); +Tint(r, g, b, a); -VertexBuffer[0].X:= r.x; -VertexBuffer[0].Y:= r.y; -VertexBuffer[1].X:= r.x + r.w; -VertexBuffer[1].Y:= r.y; -VertexBuffer[2].X:= r.x + r.w; -VertexBuffer[2].Y:= r.y + r.h; -VertexBuffer[3].X:= r.x; -VertexBuffer[3].Y:= r.y + r.h; +with rect do +begin + VertexBuffer[0].X:= x; + VertexBuffer[0].Y:= y; + VertexBuffer[1].X:= x + w; + VertexBuffer[1].Y:= y; + VertexBuffer[2].X:= x + w; + VertexBuffer[2].Y:= y + h; + VertexBuffer[3].X:= x; + VertexBuffer[3].Y:= y + h; +end; SetVertexPointer(@VertexBuffer[0], Length(VertexBuffer)); -glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); +if Fill then + glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)) +else + glDrawArrays(GL_LINE_LOOP, 0, Length(VertexBuffer)); untint; @@ -953,4 +1299,18 @@ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); end; +procedure initModule; +begin +end; + +procedure freeModule; +begin +{$IFDEF GL2} + glDeleteProgram(shaderMain); + glDeleteProgram(shaderWater); + glDeleteBuffers(1, @vBuffer); + glDeleteBuffers(1, @tBuffer); + glDeleteBuffers(1, @cBuffer); +{$ENDIF} +end; end.