Page 1 of 1

360° Panorama Viewer

Posted: Thu Oct 24, 2024 7:11 pm
by dige
Projection of a panoramic image onto a sphere with OpenGl. Created with Claude.

Code: Select all

; 360° Panorama Viewer
; Benötigt OpenGL und eine equirectangular Panorama-Textur

UseJPEGImageDecoder()

; Konstanten
#SM_MOUSEWHEELPIXELS = 75  ; Windows System Metrics Konstante für Mausrad

#GL_BGR = $80E0
#GL_BGRA = $80E1


Global RollX.f
Global RollY.f
Global MouseX.l
Global MouseY.l
Global LastMouseX.l
Global LastMouseY.l
Global ZoomFactor.f = -5.0
Global IsMouseDown.l = #False

#WINDOW_WIDTH = 1900
#WINDOW_HEIGHT = 1000
#SPHERE_SEGMENTS = 32

; Deklariere Arrays für die Sphere-Daten
Global Dim Vertices.f(#SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 18)
Global Dim TexCoords.f(#SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 12)

Procedure GenerateSphere(radius.f = 1.0)
  Protected.f phi, theta, x, y, z, u, v
  Protected.l vIndex = 0, tIndex = 0
  
  For lat = 0 To #SPHERE_SEGMENTS - 1
    phi1.f = lat * #PI / #SPHERE_SEGMENTS
    phi2.f = (lat + 1) * #PI / #SPHERE_SEGMENTS
    
    For lon = 0 To #SPHERE_SEGMENTS - 1
      theta1.f = lon * 2 * #PI / #SPHERE_SEGMENTS
      theta2.f = (lon + 1) * 2 * #PI / #SPHERE_SEGMENTS
      
      ; Erstes Dreieck
      ; Vertex 1
      x = radius * Sin(phi1) * Cos(theta1)
      y = radius * Cos(phi1)
      z = radius * Sin(phi1) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 2
      x = radius * Sin(phi2) * Cos(theta1)
      y = radius * Cos(phi2)
      z = radius * Sin(phi2) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 3
      x = radius * Sin(phi2) * Cos(theta2)
      y = radius * Cos(phi2)
      z = radius * Sin(phi2) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Zweites Dreieck
      ; Vertex 1
      x = radius * Sin(phi1) * Cos(theta1)
      y = radius * Cos(phi1)
      z = radius * Sin(phi1) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 2
      x = radius * Sin(phi2) * Cos(theta2)
      y = radius * Cos(phi2)
      z = radius * Sin(phi2) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 3
      x = radius * Sin(phi1) * Cos(theta2)
      y = radius * Cos(phi1)
      z = radius * Sin(phi1) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
    Next
  Next
EndProcedure

Procedure DrawPanorama(Gadget)
  SetGadgetAttribute(Gadget, #PB_OpenGL_SetContext, #True)
  
  glPushMatrix_()
  glMatrixMode_(#GL_MODELVIEW)
  
  glTranslatef_(0, 0, ZoomFactor)
  
  glRotatef_(RollX, 1.0, 0, 0)
  glRotatef_(RollY, 0, 1.0, 0)
  
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT)
  
  ; Zeichne die Sphere mit Textur
  glEnable_(#GL_TEXTURE_2D)
  glEnableClientState_(#GL_VERTEX_ARRAY)
  glEnableClientState_(#GL_TEXTURE_COORD_ARRAY)
  
  glVertexPointer_(3, #GL_FLOAT, 0, @Vertices(0))
  glTexCoordPointer_(2, #GL_FLOAT, 0, @TexCoords(0))
  
  ; Zeichne alle Dreiecke der Sphere
  glDrawArrays_(#GL_TRIANGLES, 0, #SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 6)
  
  glDisableClientState_(#GL_VERTEX_ARRAY)
  glDisableClientState_(#GL_TEXTURE_COORD_ARRAY)
  glDisable_(#GL_TEXTURE_2D)
  
  glPopMatrix_()
  glFinish_()
  
  SetGadgetAttribute(Gadget, #PB_OpenGL_FlipBuffers, #True)
EndProcedure

Procedure SetupGL()
  glMatrixMode_(#GL_PROJECTION)
  gluPerspective_(75.0, #WINDOW_WIDTH / #WINDOW_HEIGHT, 0.1, 100.0)
  
  glMatrixMode_(#GL_MODELVIEW)
  glLoadIdentity_()
  
  glEnable_(#GL_DEPTH_TEST)
  glEnable_(#GL_CULL_FACE)
  glCullFace_(#GL_BACK)
  
  glShadeModel_(#GL_SMOOTH)
EndProcedure

Procedure SetupGLTexture(ImageHandle.i)
  
  Define.i ImageW, ImageH, ImageD
  Define.i MemoryAddress
  Define.i TextureHandle
  
  If IsImage(ImageHandle) = 0
    ProcedureReturn #False
  EndIf
  
  ImageD = ImageDepth(ImageHandle, #PB_Image_InternalDepth)
  
  StartDrawing(ImageOutput(ImageHandle))
  MemoryAddress = DrawingBuffer()
  StopDrawing()
  
  If MemoryAddress = 0
    ProcedureReturn #False
  EndIf
  
  glGenTextures_(1, @TextureHandle)
  glBindTexture_(#GL_TEXTURE_2D, TextureHandle)
  
  ImageW = ImageWidth(ImageHandle)
  ImageH = ImageHeight(ImageHandle)
  
  If ImageD = 32
    glTexImage2D_(#GL_TEXTURE_2D, 0, 4, ImageW, ImageH, 0, #GL_BGRA, #GL_UNSIGNED_BYTE, MemoryAddress)
  Else
    glTexImage2D_(#GL_TEXTURE_2D, 0, 3, ImageW, ImageH, 0, #GL_BGR, #GL_UNSIGNED_BYTE, MemoryAddress)
  EndIf
  
  glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MIN_FILTER, #GL_LINEAR)
  ;   glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MAG_FILTER, #GL_LINEAR)
  
  ProcedureReturn TextureHandle
  
EndProcedure

; Hauptprogramm
If OpenWindow(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, "360° Panorama Viewer", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  If OpenGLGadget(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT)
    SetupGL()
    
    ; Sphere generieren
    GenerateSphere(5.0)
    
  ; Textur laden
If LoadImage(0, "c:\temp\EquiRectangel_Panorama.jpg")
  ; Bild in RGB-Format konvertieren
  SetupGLTexture(0)
  
  FreeImage(0)
EndIf
    
    ; Timer für Rendering
    AddWindowTimer(0, 1, 16) ; ~60 FPS
    
    Repeat
      Event = WaitWindowEvent()
      
      Select Event
        Case #PB_Event_Timer
          If EventTimer() = 1
            DrawPanorama(0)
          EndIf
          
        Case #WM_LBUTTONDOWN
          IsMouseDown = #True
          LastMouseX = WindowMouseX(0)
          LastMouseY = WindowMouseY(0)
          
        Case #WM_LBUTTONUP
          IsMouseDown = #False
          
        Case #WM_MOUSEMOVE
          If IsMouseDown
            MouseX = WindowMouseX(0)
            MouseY = WindowMouseY(0)
            
            If LastMouseX Or LastMouseY
              RollY + (MouseX - LastMouseX) * 0.5
              RollX + (MouseY - LastMouseY) * 0.5
              
              ; Vertikale Rotation begrenzen
              If RollX > 89
                RollX = 89
              ElseIf RollX < -89
                RollX = -89
              EndIf
            EndIf
            
            LastMouseX = MouseX
            LastMouseY = MouseY
          EndIf
          
        Case #WM_MOUSEWHEEL
          WheelDelta = GetSystemMetrics_(#SM_MOUSEWHEELPIXELS)
          If WheelDelta
            ZoomFactor + (WheelDelta / 120) * 0.5
            If ZoomFactor > -0.5 : ZoomFactor = -0.5 : EndIf
            If ZoomFactor < -20.0 : ZoomFactor = -20.0 : EndIf
          EndIf
          
      EndSelect
      
    Until Event = #PB_Event_CloseWindow
    
    ; Aufräumen
    glDeleteTextures_(1, @texture)
  EndIf
  
  CloseWindow(0)
EndIf

Re: 360° Panorama Viewer

Posted: Thu Oct 24, 2024 8:23 pm
by infratec
Maybe this is the better picture:
c:\Windows\Web\Wallpaper\Theme1\img13.jpg
Maybe it is always available.
Or use a file requester to load a picture.

Re: 360° Panorama Viewer

Posted: Thu Oct 24, 2024 9:19 pm
by ChrisR
Nice 360° animated panorama :)

You can also use the current transcoded Wallpaper:

Code: Select all

If LoadImage(0,  GetEnvironmentVariable("APPDATA") + "\Microsoft\Windows\Themes\TranscodedWallpaper")
It should still be there on Windows 7 and later

Re: 360° Panorama Viewer

Posted: Fri Oct 25, 2024 3:56 pm
by SPH
Super cool !! :shock: :arrow: :wink:

Re: 360° Panorama Viewer

Posted: Sun Oct 27, 2024 5:58 pm
by Andre
Impressive! :D

Re: 360° Panorama Viewer

Posted: Sun Oct 27, 2024 7:03 pm
by Mijikai
Nice, thanks for sharing :)

Re: 360° Panorama Viewer

Posted: Wed Oct 30, 2024 4:48 pm
by ebs
This is indeed impressive, but I think zooming in/out with the mouse scroll wheel is not working correctly.

This statement

Code: Select all

WheelDelta = GetSystemMetrics_(#SM_MOUSEWHEELPIXELS)
doesn't return the number of clicks the user has scrolled the mouse wheel. It just returns 1 (#True) to indicate that the mouse has a vertical scroll wheel.

I added a callback routine to intercept the #WM_MOUSEWHEEL message and save the number of clicks. I also added a file requester to let the user choose an image file, as suggested by @infratec.

Here's my modified version:

Code: Select all

; 360° Panorama Viewer
; Benötigt OpenGL und eine equirectangular Panorama-Textur

UseJPEGImageDecoder()
UsePNGImageDecoder()

; Konstanten
#SM_MOUSEWHEELPIXELS = 75  ; Windows System Metrics Konstante für Mausrad

#GL_BGR = $80E0
#GL_BGRA = $80E1


Global RollX.f
Global RollY.f
Global MouseX.l
Global MouseY.l
Global LastMouseX.l
Global LastMouseY.l
Global ZoomFactor.f = -5.0
Global IsMouseDown.l = #False

Global WheelDelta.l

#WINDOW_WIDTH = 1900
#WINDOW_HEIGHT = 1000
#SPHERE_SEGMENTS = 32

; Deklariere Arrays für die Sphere-Daten
Global Dim Vertices.f(#SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 18)
Global Dim TexCoords.f(#SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 12)

;- callback for #WM_MOUSEWHEEL message (ebs)
Procedure CallbackProc(hwnd, uMsg, wParam, lParam)
  If uMsg = #WM_MOUSEWHEEL
    WheelDelta = (wParam & $FFFF0000) >> 16
  EndIf
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure GenerateSphere(Radius.f = 1.0)
  Protected.f PHI, theta, x, y, z, u, v
  Protected.l vIndex = 0, tIndex = 0
  
  For lat = 0 To #SPHERE_SEGMENTS - 1
    phi1.f = lat * #PI / #SPHERE_SEGMENTS
    phi2.f = (lat + 1) * #PI / #SPHERE_SEGMENTS
    
    For lon = 0 To #SPHERE_SEGMENTS - 1
      theta1.f = lon * 2 * #PI / #SPHERE_SEGMENTS
      theta2.f = (lon + 1) * 2 * #PI / #SPHERE_SEGMENTS
      
      ; Erstes Dreieck
      ; Vertex 1
      x = Radius * Sin(phi1) * Cos(theta1)
      y = Radius * Cos(phi1)
      z = Radius * Sin(phi1) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 2
      x = Radius * Sin(phi2) * Cos(theta1)
      y = Radius * Cos(phi2)
      z = Radius * Sin(phi2) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 3
      x = Radius * Sin(phi2) * Cos(theta2)
      y = Radius * Cos(phi2)
      z = Radius * Sin(phi2) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Zweites Dreieck
      ; Vertex 1
      x = Radius * Sin(phi1) * Cos(theta1)
      y = Radius * Cos(phi1)
      z = Radius * Sin(phi1) * Sin(theta1)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = lon / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 2
      x = Radius * Sin(phi2) * Cos(theta2)
      y = Radius * Cos(phi2)
      z = Radius * Sin(phi2) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = (lat + 1) / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
      
      ; Vertex 3
      x = Radius * Sin(phi1) * Cos(theta2)
      y = Radius * Cos(phi1)
      z = Radius * Sin(phi1) * Sin(theta2)
      Vertices(vIndex) = x : vIndex + 1
      Vertices(vIndex) = y : vIndex + 1
      Vertices(vIndex) = z : vIndex + 1
      
      u = (lon + 1) / #SPHERE_SEGMENTS
      v = lat / #SPHERE_SEGMENTS
      TexCoords(tIndex) = u : tIndex + 1
      TexCoords(tIndex) = v : tIndex + 1
    Next
  Next
EndProcedure

Procedure DrawPanorama(gadget)
  SetGadgetAttribute(gadget, #PB_OpenGL_SetContext, #True)
  
  glPushMatrix_()
  glMatrixMode_(#GL_MODELVIEW)
  
  glTranslatef_(0, 0, ZoomFactor)
  
  glRotatef_(RollX, 1.0, 0, 0)
  glRotatef_(RollY, 0, 1.0, 0)
  
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT)
  
  ; Zeichne die Sphere mit Textur
  glEnable_(#GL_TEXTURE_2D)
  glEnableClientState_(#GL_VERTEX_ARRAY)
  glEnableClientState_(#GL_TEXTURE_COORD_ARRAY)
  
  glVertexPointer_(3, #GL_FLOAT, 0, @Vertices(0))
  glTexCoordPointer_(2, #GL_FLOAT, 0, @TexCoords(0))
  
  ; Zeichne alle Dreiecke der Sphere
  glDrawArrays_(#GL_TRIANGLES, 0, #SPHERE_SEGMENTS * #SPHERE_SEGMENTS * 6)
  
  glDisableClientState_(#GL_VERTEX_ARRAY)
  glDisableClientState_(#GL_TEXTURE_COORD_ARRAY)
  glDisable_(#GL_TEXTURE_2D)
  
  glPopMatrix_()
  glFinish_()
  
  SetGadgetAttribute(gadget, #PB_OpenGL_FlipBuffers, #True)
EndProcedure

Procedure SetupGL()
  glMatrixMode_(#GL_PROJECTION)
  gluPerspective_(75.0, #WINDOW_WIDTH / #WINDOW_HEIGHT, 0.1, 100.0)
  
  glMatrixMode_(#GL_MODELVIEW)
  glLoadIdentity_()
  
  glEnable_(#GL_DEPTH_TEST)
  glEnable_(#GL_CULL_FACE)
  glCullFace_(#GL_BACK)
  
  glShadeModel_(#GL_SMOOTH)
EndProcedure

Procedure SetupGLTexture(ImageHandle.i)
  
  Define.i ImageW, ImageH, ImageD
  Define.i MemoryAddress
  Define.i TextureHandle
  
  If IsImage(ImageHandle) = 0
    ProcedureReturn #False
  EndIf
  
  ImageD = ImageDepth(ImageHandle, #PB_Image_InternalDepth)
  
  StartDrawing(ImageOutput(ImageHandle))
    MemoryAddress = DrawingBuffer()
  StopDrawing()
  
  If MemoryAddress = 0
    ProcedureReturn #False
  EndIf
  
  glGenTextures_(1, @TextureHandle)
  glBindTexture_(#GL_TEXTURE_2D, TextureHandle)
  
  ImageW = ImageWidth(ImageHandle)
  ImageH = ImageHeight(ImageHandle)
  
  If ImageD = 32
    glTexImage2D_(#GL_TEXTURE_2D, 0, 4, ImageW, ImageH, 0, #GL_BGRA, #GL_UNSIGNED_BYTE, MemoryAddress)
  Else
    glTexImage2D_(#GL_TEXTURE_2D, 0, 3, ImageW, ImageH, 0, #GL_BGR, #GL_UNSIGNED_BYTE, MemoryAddress)
  EndIf
  
  glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MIN_FILTER, #GL_LINEAR)
  ;   glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MAG_FILTER, #GL_LINEAR)
  
  ProcedureReturn TextureHandle
  
EndProcedure

FileSpec.s = OpenFileRequester("Select Image File", "", "Image files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png|All files (*.*)|*.*", 0)
If FileSpec
  ; Hauptprogramm
  If OpenWindow(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, "360° Panorama Viewer", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    If OpenGLGadget(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT)
      SetupGL()
      
      ; Sphere generieren
      GenerateSphere(5.0)
      
      ; Textur laden
      If LoadImage(0, FileSpec)
        ; Bild in RGB-Format konvertieren
        SetupGLTexture(0)
        
        FreeImage(0)
      EndIf
      
      ; Timer für Rendering
      AddWindowTimer(0, 1, 16) ; ~60 FPS
      
      ; callback for #WM_MOUSEWHEEL message (ebs)
      SetWindowCallback(@CallbackProc(), 0)
      
      Repeat
        Event = WaitWindowEvent()
        
        Select Event
          Case #PB_Event_Timer
            If EventTimer() = 1
              DrawPanorama(0)
            EndIf
            
          Case #WM_LBUTTONDOWN
            IsMouseDown = #True
            LastMouseX = WindowMouseX(0)
            LastMouseY = WindowMouseY(0)
            
          Case #WM_LBUTTONUP
            IsMouseDown = #False
            
          Case #WM_MOUSEMOVE
            If IsMouseDown
              MouseX = WindowMouseX(0)
              MouseY = WindowMouseY(0)
              
              If LastMouseX Or LastMouseY
                RollY + (MouseX - LastMouseX) * 0.5
                RollX + (MouseY - LastMouseY) * 0.5
                
                ; Vertikale Rotation begrenzen
                If RollX > 89
                  RollX = 89
                ElseIf RollX < -89
                  RollX = -89
                EndIf
              EndIf
              
              LastMouseX = MouseX
              LastMouseY = MouseY
            EndIf
            
          ; Case #WM_MOUSEWHEEL
            ; WheelDelta = GetSystemMetrics_(#SM_MOUSEWHEELPIXELS)
            ; If WheelDelta
              ; ZoomFactor + (WheelDelta / 120) * 0.5
              ; If ZoomFactor > -0.5 : ZoomFactor = -0.5 : EndIf
              ; If ZoomFactor < -20.0 : ZoomFactor = -20.0 : EndIf
            ; EndIf
            
        EndSelect

        If WheelDelta
          ZoomFactor + (WheelDelta / 120) * 0.5
          If ZoomFactor > -0.5 : ZoomFactor = -0.5 : EndIf
          If ZoomFactor < -20.0 : ZoomFactor = -20.0 : EndIf
          WheelDelta = 0
        EndIf

      Until Event = #PB_Event_CloseWindow
      
      ; Aufräumen
      glDeleteTextures_(1, @texture)
    EndIf
    
    CloseWindow(0)
  EndIf
EndIf

Re: 360° Panorama Viewer

Posted: Sun Nov 10, 2024 7:39 pm
by Kwai chang caine
Very nice DIGE, again better with the zoom Ebs :wink:
Thanks also to Claude for sharing 8)