diff options
Diffstat (limited to 'Game')
-rw-r--r-- | Game/Code/Classes/UGraphic.pas | 1 | ||||
-rw-r--r-- | Game/Code/Classes/UVisualizer.pas | 156 |
2 files changed, 110 insertions, 47 deletions
diff --git a/Game/Code/Classes/UGraphic.pas b/Game/Code/Classes/UGraphic.pas index fdc7fca0..2432503c 100644 --- a/Game/Code/Classes/UGraphic.pas +++ b/Game/Code/Classes/UGraphic.pas @@ -413,6 +413,7 @@ begin // Other extensions e.g. OpenGL 1.3-2.0 or Framebuffer-Object might be loaded here // ... + //Load_GL_EXT_framebuffer_object(); end; procedure Initialize3D (Title: string); diff --git a/Game/Code/Classes/UVisualizer.pas b/Game/Code/Classes/UVisualizer.pas index 6864b6b9..e2125201 100644 --- a/Game/Code/Classes/UVisualizer.pas +++ b/Game/Code/Classes/UVisualizer.pas @@ -10,6 +10,23 @@ unit UVisualizer; * - write a plugin for projectM in C/C++ (so we need no wrapper anymore) *) +{* Note: + * It would be easier to create a seperate Render-Context (RC) for projectM + * and switch to it when necessary. This can be achieved by pbuffers + * (slow and platform specific) or the OpenGL FramebufferObject (FBO) extension + * (fast and plattform-independent but not supported by older graphic-cards/drivers). + * + * See http://oss.sgi.com/projects/ogl-sample/registry/EXT/framebuffer_object.txt + * + * To support as many cards as possible we will stick to the current dirty + * solution for now even if it is a pain to save/restore projectM's state due + * to bugs etc. + * + * This also restricts us to projectM. As other plug-ins might have different + * needs and bugs concerning the OpenGL state, USDX's state would probably be + * corrupted after the plug-in finshed drawing. + *} + interface {$IFDEF FPC} @@ -38,6 +55,8 @@ uses ULog; {$IF PROJECTM_VERSION < 1000000} // < 1.0 +// Initialization data used on projectM 0.9x creation. +// Since projectM 1.0 this data is passed via the config-file. const meshX = 32; meshY = 24; @@ -95,7 +114,7 @@ type function TVideoPlayback_ProjectM.GetName: String; begin - result := 'ProjectM'; + Result := 'ProjectM'; end; function TVideoPlayback_ProjectM.Init(): boolean; @@ -133,7 +152,7 @@ end; function TVideoPlayback_ProjectM.Open(const aFileName : string): boolean; // true if succeed begin - result := false; + Result := false; end; procedure TVideoPlayback_ProjectM.Close; @@ -159,14 +178,25 @@ end; procedure TVideoPlayback_ProjectM.SetPosition(Time: real); begin if assigned(pm) then - pm.NextPreset(); + pm.RandomPreset(); end; function TVideoPlayback_ProjectM.GetPosition: real; begin - result := 0; + Result := 0; end; +{** + * Saves the current OpenGL state. + * This is necessary to prevent projectM from corrupting USDX's current + * OpenGL state. + * + * The following steps are performed: + * - All attributes are pushed to the attribute-stack + * - Projection-/Texture-matrices are saved + * - Modelview-matrix is pushed to the Modelview-stack + * - the OpenGL error-state (glGetError) is cleared + *} procedure TVideoPlayback_ProjectM.SaveOpenGLState(); begin // save all OpenGL state-machine attributes @@ -177,12 +207,12 @@ begin // already uses 2 stack-entries so overflows might be possible on older hardware. // In contrast to this the GL_MODELVIEW stack-size is at least 32, so we can // use glPushMatrix() for this stack. - + // save projection-matrix glMatrixMode(GL_PROJECTION); glGetDoublev(GL_PROJECTION_MATRIX, @projMatrix); - {$IF (PROJECTM_VERSION >= 1000000) and (PROJECTM_VERSION < 1010000)} // [1.0..1.1) - // bugfix (1.0 and 1.01): projection-matrix is popped without being pushed first + {$IF PROJECTM_VERSION = 1000000} // 1.0, 1.01 + // bugfix: projection-matrix is popped without being pushed first glPushMatrix(); {$IFEND} @@ -193,14 +223,24 @@ begin // save modelview-matrix glMatrixMode(GL_MODELVIEW); glPushMatrix(); - {$IF (PROJECTM_VERSION >= 1000000) and (PROJECTM_VERSION < 1010000)} // [1.0..1.1) - // bugfix (1.0 and 1.01): modelview-matrix is popped without being pushed first + {$IF PROJECTM_VERSION = 1000000} // 1.0, 1.01 + // bugfix: modelview-matrix is popped without being pushed first glPushMatrix(); {$IFEND} + + // reset OpenGL error-state + glGetError(); end; +{** + * Restores the OpenGL state saved by SaveOpenGLState() + * and resets the error-state. + *} procedure TVideoPlayback_ProjectM.RestoreOpenGLState(); begin + // reset OpenGL error-state + glGetError(); + // restore projection-matrix glMatrixMode(GL_PROJECTION); glLoadMatrixd(@projMatrix); @@ -218,36 +258,45 @@ begin end; procedure TVideoPlayback_ProjectM.VisualizerStart; -var - initResult: Cardinal; begin if VisualizerStarted then Exit; + // the OpenGL state must be saved before + SaveOpenGLState(); try - {$IF PROJECTM_VERSION >= 1000000} // >= 1.0 - pm := TProjectM.Create(ProjectMPath + 'config.inp'); - {$ELSE} - pm := TProjectM.Create( - meshX, meshY, fps, textureSize, ScreenW, ScreenH, - ProjectMPath + 'presets', ProjectMPath + 'fonts'); - {$IFEND} - except on E: Exception do - begin - // Create() might fail if the config-file is not found - Log.LogError('TProjectM.Create: ' + E.Message, 'TVideoPlayback_ProjectM.VisualizerStart'); - Exit; - end; - end; - VisualizerStarted := True; + try + {$IF PROJECTM_VERSION >= 1000000} // >= 1.0 + pm := TProjectM.Create(ProjectMPath + 'config.inp'); + {$ELSE} + pm := TProjectM.Create( + meshX, meshY, fps, textureSize, ScreenW, ScreenH, + ProjectMPath + 'presets', ProjectMPath + 'fonts'); + {$IFEND} + except on E: Exception do + begin + // Create() might fail if the config-file is not found + Log.LogError('TProjectM.Create: ' + E.Message, 'TVideoPlayback_ProjectM.VisualizerStart'); + Exit; + end; + end; - // skip projectM preset - pm.RandomPreset(); - // initialize OpenGL - SaveOpenGLState(); - pm.ResetGL(ScreenW, ScreenH); - RestoreOpenGLState(); + // initialize OpenGL + pm.ResetGL(ScreenW, ScreenH); + // skip projectM default-preset + pm.RandomPreset(); + // projectM >= 1.0 uses the OpenGL FramebufferObject (FBO) extension. + // Unfortunately it does NOT reset the framebuffer-context after + // TProjectM.Create. Either glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0) for + // a manual reset or TProjectM.RenderFrame() must be called. + // We use the latter so we do not need to load the FBO extension in USDX. + pm.RenderFrame(); + + VisualizerStarted := True; + finally + RestoreOpenGLState(); + end; end; procedure TVideoPlayback_ProjectM.VisualizerStop; @@ -278,32 +327,41 @@ begin // get audio data nSamples := AudioPlayback.GetPCMData(PcmData); + // generate some data if non is available if (nSamples = 0) then nSamples := GetRandomPCMData(PcmData); + // send audio-data to projectM if (nSamples > 0) then pm.AddPCM16Data(PSmallInt(@PcmData), nSamples); // store OpenGL state (might be messed up otherwise) SaveOpenGLState(); - pm.ResetGL(ScreenW, ScreenH); - - // let projectM render a frame - pm.RenderFrame(); - - {$IFDEF UseTexture} - glBindTexture(GL_TEXTURE_2D, VisualTex); - glFlush(); - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VisualWidth, VisualHeight, 0); - {$ENDIF} - - // restore USDX OpenGL state - RestoreOpenGLState(); + try + // setup projectM's OpenGL state + pm.ResetGL(ScreenW, ScreenH); + + // let projectM render a frame + pm.RenderFrame(); + + {$IFDEF UseTexture} + glBindTexture(GL_TEXTURE_2D, VisualTex); + glFlush(); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VisualWidth, VisualHeight, 0); + {$ENDIF} + finally + // restore USDX OpenGL state + RestoreOpenGLState(); + end; // discard projectM's depth buffer information (avoid overlay) glClear(GL_DEPTH_BUFFER_BIT); end; +{** + * Draws the current frame to screen. + * TODO: this is not used yet. Data is directly drawn on GetFrame(). + *} procedure TVideoPlayback_ProjectM.DrawGL(Screen: integer); begin {$IFDEF UseTexture} @@ -313,7 +371,7 @@ begin glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); end; - + // exit if there's nothing to draw if not VisualizerStarted then Exit; @@ -352,6 +410,10 @@ begin {$ENDIF} end; +{** + * Produces random "sound"-data in case no audio-data is available. + * Otherwise the visualization will look rather boring. + *} function TVideoPlayback_ProjectM.GetRandomPCMData(var data: TPCMData): Cardinal; var i: integer; @@ -370,7 +432,7 @@ begin end; end; Inc(RndPCMcount); - result := 512; + Result := 512; end; |