Simple 3D Software Renderer for starters

Everything related to 3D programming
jamirokwai
Enthusiast
Enthusiast
Posts: 771
Joined: Tue May 20, 2008 2:12 am
Location: Cologne, Germany
Contact:

Re: Simple 3D for starters?

Post by jamirokwai »

#NULL wrote: Fri Nov 26, 2021 7:08 pm The NeHe OpenGL examples were ported to purebasic and just recently updated by mpz.

Open a window:
NeHe's OpenGL Framework (Lesson 1)
https://www.purebasic.fr/english/viewto ... 28#p576128

Draw some polygons:
NeHe's First Polygon Tutorial (Lesson 2)
https://www.purebasic.fr/english/viewto ... 29#p576129

Others:
https://www.purebasic.fr/english/search ... mit=Search
Thanks for the links.

I saw the OpenGL-Tutorials around the forum. But I'd like to keep it software-only and cross-platform.
On the Mac, OpenGL may stop working after some time due to Apple deprecating OpenGL and promoting their own framework :-(
Regards,
JamiroKwai
jamirokwai
Enthusiast
Enthusiast
Posts: 771
Joined: Tue May 20, 2008 2:12 am
Location: Cologne, Germany
Contact:

Re: Simple 3D for starters?

Post by jamirokwai »

This is how far I got up today... Taking a break for a few days.
I will document the code later on.

It's a simple white wireframe cube or pyramid rotating on a black background.
I already moved the Sin/Cos-functions into pre-calculated tables.

Now looking into fill-algorithms, optimisation, moving objects into a single structure and other primitives.

Thanks again for the hints and links!

Edit 21-12-09 - realised, negative values for angleX, angleY in RotateObject result in an error. Using Sin(), Cos() for now instead of the tables
Edit 21-12-02 - added Pyramid
Edit 21-11-27 - first release

Code: Select all

; ################
; # Simple 3D 0.0.3
; ################
; # by Jamirokwai
; ################
; based on http://rosettacode.org/wiki/Draw_a_rotating_cube
; ################

EnableExplicit

#fullscreen = 0
#debug = 0

Structure dot
  x.d
  y.d
  z.d
EndStructure

Structure edge
  a.l
  b.l
EndStructure

Structure info
  DotsCount.i
  EdgesCount.i
  centerX.d
  centerY.d
  centerZ.d
  scaleX.i
  scaleY.i
  scaleZ.i
EndStructure

Global Dim Object_Info.info(1)
Global Dim Object_Nodes.dot(0)
Global Dim Object_Edges.edge(0)

Global Dim SinTable.d(360)
Global Dim CosTable.d(360)

Global gradX.d, gradY.d, event.i
Global width.i = 600, height.i = 600

Procedure ScaleObject(Array targetNodes.dot(1), Array targetInfo.info(1))
  Define i.i
  
  For i = 0 To ArraySize(targetNodes()) - 1
    targetNodes(i)\x * targetInfo(0)\scaleX
    targetNodes(i)\y * targetInfo(0)\scaleY
    targetNodes(i)\z * targetInfo(0)\scaleZ
  Next
EndProcedure

Procedure RotateObject(Array targetNodes.dot(1), angleX.d, angleY.d)
  Define sinX.d = Sin(angleX)
  Define cosX.d = Cos(angleX)
  Define sinY.d = Sin(angleY)
  Define cosy.d = Cos(angleY)
  Define i.i, x.d, y.d, z.d
  
  For i = 0 To ArraySize(targetNodes()) - 1
    x.d = targetNodes(i)\x
    y.d = targetNodes(i)\y
    z.d = targetNodes(i)\z
    
    If angleX <> 0
      targetNodes(i)\x = x * cosX - z * sinX
    EndIf
    targetNodes(i)\z = z * cosX + x * sinX
    
    If angleY <> 0
      z = targetNodes(i)\z
      
      targetNodes(i)\y = y * cosY - z * sinY
      targetNodes(i)\z = z * cosY + y * sinY
    EndIf
  Next i
EndProcedure

Procedure DrawObject(Array targetNode.dot(1), Array targetEdges.edge(1), Array targetInfo.info(1))
  Dim dot1.dot(1)
  Dim dot2.dot(1)
  Define i.i
  
  For i = 0 To ArraySize(targetEdges()) - 1
    dot1(0) = targetNode(targetEdges(i)\a)
    dot2(0) = targetNode(targetEdges(i)\b)
    LineXY(dot1(0)\x + targetInfo(0)\centerX, dot1(0)\y + targetInfo(0)\centerY, dot2(0)\x + targetInfo(0)\centerX, dot2(0)\y + targetInfo(0)\centerY, #White)
  Next i
EndProcedure

Procedure DrawFrame()
  CompilerIf #fullscreen = 1
    StartDrawing(ScreenOutput())
  CompilerElse
    StartDrawing(CanvasOutput(0))
    Box(0,0,800,600,#Black)
  CompilerEndIf
  DrawObject(Object_Nodes(),Object_Edges(),Object_Info())
  StopDrawing()
EndProcedure

Procedure PrepareTables()
  Define i.i
  For i = 0 To 359
    SinTable(i) = Sin(i / 100)
    CosTable(i) = Cos(i / 100)
  Next i
EndProcedure

Procedure RestoreNodes(Array targetNodes.dot(1), NodesSize)
  Define i.i
  
  ; get all Points of the Object - a cube has 8, so cube-size should be 8
  ReDim targetNodes(NodesSize)
  For i = 0 To NodesSize - 1
    Read.i targetNodes(i)\x
    Read.i targetNodes(i)\y
    Read.i targetNodes(i)\z
  Next i
EndProcedure

Procedure RestoreEdges(Array targetEdges.edge(1), EdgesSize)
  Define i.i
  
  ; get all Points of the Object - a cube has 8, so cube-size should be 8
  ReDim targetEdges(EdgesSize)
  For i = 0 To EdgesSize - 1
    Read.i targetEdges(i)\a
    Read.i targetEdges(i)\b
  Next i
EndProcedure

Procedure RestoreObject(Array targetNodes.dot(1), Array targetEdges.edge(1), Array targetInfo.info(1), objectname.s)
  Define i.i
  
  Select objectname
    Case "cube": Restore cube_info
    Case "pyramid" : Restore pyramid_info
  EndSelect
  
  With targetInfo(0)
    Read.i \DotsCount
    Read.i \EdgesCount
    Read.d \centerX
    Read.d \centerY
    Read.d \centerZ
    Read.i \scaleX
    Read.i \scaleY
    Read.i \scaleZ
    
    \centerX * width
    \centerY * height
    ;     \centerZ * width
  EndWith
  
  Select objectname
    Case "cube": Restore cube_nodes
    Case "pyramid" : Restore pyramid_nodes
  EndSelect
  
  RestoreNodes(targetNodes(), targetInfo(0)\DotsCount)
  
  Select objectname
    Case "cube": Restore cube_edges
    Case "pyramid" : Restore pyramid_edges
  EndSelect
  
  RestoreEdges(targetEdges(), targetInfo(0)\EdgesCount)
EndProcedure

InitKeyboard()
PrepareTables()

; currently only a cube is available
RestoreObject(Object_Nodes(), Object_Edges(), Object_Info(), "cube") ; or pyramid

CompilerIf #fullscreen = 1
  OpenScreen(800,600,32,"3D-Engine",#PB_Screen_NoSynchronization)
CompilerElse
  OpenWindow(0,100,100,600,600,"3D-Engine")
  CanvasGadget(0,0,0,600,600)
CompilerEndIf 

ScaleObject(Object_Nodes(), Object_Info())

gradX.d = 2; #PI/360
gradY.d = 1

CompilerIf #debug = 1
  Define frames = 0
  Define start = 0
  Define ende
  start = ElapsedMilliseconds()
CompilerEndIf

Repeat
  CompilerIf #fullscreen = 1
    ClearScreen(#Black)
  CompilerElse
    event = WindowEvent()
    Delay(1)
    If event = #PB_Event_CloseWindow
      End
    EndIf
  CompilerEndIf
  
  DrawFrame()
  RotateObject(Object_Nodes(), gradX, gradY) 
  
  CompilerIf #debug = 1
    frames + 1
    If frames = 1000
      Break
    EndIf
  CompilerEndIf
  CompilerIf #fullscreen = 1
    FlipBuffers()
    ExamineKeyboard()
    If KeyboardReleased(#PB_Key_Escape)
      Break
    EndIf
  CompilerEndIf
ForEver

CompilerIf #debug = 1
  ende = ElapsedMilliseconds()
CompilerEndIf

CompilerIf #fullscreen = 1
  CloseScreen()
CompilerEndIf

CompilerIf #debug = 1
  MessageRequester("!",Str(ende - start))
CompilerEndIf

DataSection
  ;{ Cube
  cube_info:
  Data.i   8  ; Dots of the object
  Data.i  12  ; Edges
  Data.d  0.5, 0.5, 0.5 ; Center 
  Data.i  100, 100, 100 ; Scale
  
  cube_nodes:
  Data.i -1, -1, -1
  Data.i -1, -1,  1
  Data.i -1,  1, -1
  Data.i -1,  1,  1
  Data.i  1, -1, -1
  Data.i  1, -1,  1
  Data.i  1,  1, -1
  Data.i  1,  1,  1  
  
  cube_edges:
  Data.i  0, 1
  Data.i  1, 3
  Data.i  3, 2
  Data.i  2, 0
  Data.i  4, 5
  Data.i  5, 7
  Data.i  7, 6
  Data.i  6, 4
  Data.i  0, 4
  Data.i  1, 5
  Data.i  2, 6
  Data.i  3, 7
  ;}
  
  ;{ Pyramid
  pyramid_info:
  Data.i 5 ; Anzahl der Punkte in einer Pyramide
  Data.i 8 ; Kanten der Pyramide
  Data.d  0.5, 0.5, 0.5 ; Mittelpunkt
  Data.i  200, 200, 200 ; Skalierung des Objekts
  
  pyramid_nodes:
  Data.i -1, -1, -1
  Data.i  1, -1, -1
  Data.i -1,  1, -1
  Data.i  1,  1, -1
  Data.i  0,  0,  1
  
  pyramid_edges:
  Data.i 0, 1
  Data.i 1, 3
  Data.i 3, 2
  Data.i 2, 0
  Data.i 0, 5
  Data.i 1, 5
  Data.i 2, 5
  Data.i 3, 5
  ;}
EndDataSection
Regards,
JamiroKwai
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Simple 3D Software Renderer for starters

Post by Caronte3D »

Post Reply