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.
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
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