I recently found an old script of mine, where I was playing around with GDI+ to make some simple projections of basic 3D objects.
I abandoned it quite a time ago and it still had some errors in the user input (and i'm pretty sure it still has some other errors anywhere!), so i fixed the script as far that i can share it with you.
This is what it looks like:
It basically draws 3-dimensional objects consisting of planes (triangles, not planes, i still called them planes in the script, but... triangles).
The triangles are simple objects, like this: [[-1,-1,1],[1,1,1],[1,-1,1]]
Here is the script (i know, it looks horrible)
Code: Select all
#Include gdip.ahk
#NoEnv
SetBatchLines, -1
cube := Object()
cube.Insert([[-1,-1,1],[1,1,1],[1,-1,1]])
cube.Insert([[-1,-1,1],[-1,1,1],[1,1,1]])
cube.Insert([[-1,-1,-1],[1,-1,-1],[1,1,-1]])
cube.Insert([[-1,-1,-1],[1,1,-1],[-1,1,-1]])
cube.Insert([[-1,-1,-1],[1,-1,1],[1,-1,-1]])
cube.Insert([[-1,-1,-1],[-1,-1,1],[1,-1,1]])
cube.Insert([[-1,1,-1],[-1,-1,1],[-1,-1,-1]])
cube.Insert([[-1,1,-1],[-1,1,1],[-1,-1,1]])
cube.Insert([[-1,1,-1],[1,1,-1],[1,1,1]])
cube.Insert([[-1,1,-1],[1,1,1],[-1,1,1]])
cube.Insert([[1,1,-1],[1,-1,-1],[1,-1,1]])
cube.Insert([[1,1,-1],[1,-1,1],[1,1,1]])
pyramid := Object()
pyramid.Insert([[2,-1.5,0],[5,-1.5,0],[5,1.5,0]])
pyramid.Insert([[2,-1.5,0],[5,1.5,0],[2,1.5,0]])
pyramid.Insert([[2,-1.5,0],[3.5,0,1],[5,-1.5,0]])
pyramid.Insert([[2,1.5,0],[3.5,0,1],[2,-1.5,0]])
pyramid.Insert([[5,-1.5,0],[3.5,0,1],[5,1.5,0]])
pyramid.Insert([[5,1.5,0],[3.5,0,1],[2,1.5,0]])
worldmap := Object()
worldmap.Insert("cube", cube)
worldmap.Insert("pyramid", pyramid)
; --- standard values & variable declarations ---
Width := A_ScreenWidth
Height := A_ScreenHeight
scroll_speed := 0.3
frames := 0
frametime := 0
window_x := (A_ScreenWidth - Width) / 2
window_y := (A_ScreenHeight - Height) / 2
center_x := (Width / 2)
center_y := (Height / 2)
camera := Object()
camera["vpx"] := Width / 2
camera["vpy"] := Height / 2
camera["scale"] := (Width > Height) ? (Height / 2) : (Width / 2)
camera["x"] := 0
camera["y"] := 0
camera["z"] := 6
camera["a"] := 45
camera["b"] := -45
; --- start GDI+ ---
If !pToken := Gdip_Startup()
{
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
; --- set up the LinkGUI and the "Canvas" for the main GUI ---
Gui, Link:Show, % "NA x-50 w0 h0 y" . (A_ScreenHeight + 100), Lines
WinSet, Transparent, 0, Lines
Gui, Link: +LastFound
link_hwnd := WinExist()
DllCall("RegisterShellHookWindow", "uint",link_hwnd)
Msg := DllCall("RegisterWindowMessage", "str","SHELLHOOK")
OnMessage(Msg, "LinkGuiMsg")
Gui, Canvas:-Caption +E0x80000 +LastFound +ToolWindow +OwnDialogs
Gui, Canvas:Show
hwnd1 := WinExist()
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_SetSmoothingMode(G, 4)
ActiveID := WinActive()
; --- brushes, pens & graphics tileset ---
pBrushBlack := Gdip_BrushCreateSolid(0xFF000000)
pBrushRed := Gdip_BrushCreateSolid(0xFFFF0000)
pBrushWhite := Gdip_BrushCreateSolid(0xFFFFFFFF)
pBrushGreen := Gdip_BrushCreateSolid(0xFF009900)
pBrushBlue := Gdip_BrushCreateSolid(0xFF99AAFF)
pBrushDarkGray := Gdip_BrushCreateSolid(0xFF222222)
pPenBlack := Gdip_CreatePen(0xFF000000, 1)
pPenGray := Gdip_CreatePen(0xFF888888, 1)
pPenWhite := Gdip_CreatePen(0xFFFFFFFF, 1)
pPenDarkGray := Gdip_CreatePen(0xFF444444, 1)
pPenDarkGreen := Gdip_CreatePen(0xFF009900, 1)
; --- start the main game loop & fps counter ---
SetTimer, Frame, 0
Return
;------------------------------------------------------------------------------
;------------------------------- main loop --------------------------------
Frame:
second := floor(A_TickCount / 1000)
if (frametime = 0) or (second > frametime)
{
frametime := second
fps := frames
frames := 0
}
frames ++
tickcounter++
MouseGetPos, mousex, mousey
mousey := (Height - mousey)
mousedown := GetKeyState("LButton")
ctrldown := GetKeyState("LCtrl")
; --- User mouse input ------
if (mousex < 5) {
camera["x"] -= scroll_speed * cos(camera["a"] / 57.29578)
camera["y"] += scroll_speed * sin(camera["a"] / 57.29578)
}
if (mousex > (Width - 5)) {
camera["x"] += scroll_speed * cos(camera["a"] / 57.29578)
camera["y"] -= scroll_speed * sin(camera["a"] / 57.29578)
}
if (mousey < 5) {
camera["y"] -= scroll_speed * cos(camera["a"] / 57.29578)
camera["x"] -= scroll_speed * sin(camera["a"] / 57.29578)
}
if (mousey > (Height - 5)) {
camera["y"] += scroll_speed * cos(camera["a"] / 57.29578)
camera["x"] += scroll_speed * sin(camera["a"] / 57.29578)
}
if mousedown {
if ctrldown {
current_mouse_angle := mousey
if last_mouse_angle {
camera["b"] -= (current_mouse_angle - last_mouse_angle) * 0.3
if (camera["b"] > 0)
camera["b"] := 0
if (camera["b"] < -90)
camera["b"] := -90
}
last_mouse_angle := current_mouse_angle
last_mousedown := 1
} else {
current_mouse_angle := calculateangle(mousex - center_x, mousey - center_y)
if last_mouse_angle
camera["a"] += (current_mouse_angle - last_mouse_angle)
last_mouse_angle := current_mouse_angle
last_mousedown := 1
}
}
if (!mousedown && last_mousedown) {
last_mouse_angle := 0
last_mousedown := 0
}
Gdip_FillRectangle(G, pBrushWhite, 0, 0, Width, Height)
all_planes := ""
draw_planes := Object()
for objId, object in worldmap
{
for planeId, plane in object
{
planeMidDistance := 0
rotation := "none"
draw_polygon := ""
for pointId, point in plane
{
;MsgBox % "plane (Obj: " . objId . ") " . planeId . "`nx:" . point[1] . "`ny:" point[2] . "`nz:" point[3]
draw_polygon .= (draw_polygon == "") ? polygon_firstpoint := project3dpoint(point) : ("|" . project3dpoint(point))
}
planeMidDistance /= 3
if (rotation < 0) {
draw_polygon .= "|" . polygon_firstpoint
while draw_planes[planeMidDistance]
planeMidDistance := planeMidDistance . 1
draw_planes.Insert(planeMidDistance, draw_polygon)
all_planes .= planeMidDistance . ","
}
}
}
Sort, all_planes, N R D,
Loop, Parse, all_planes, `,
{
plane_polygon := draw_planes[A_LoopField]
Gdip_FillPolygon(G, pBrushBlue, plane_polygon)
Gdip_DrawLines(G, pPenGray, plane_polygon)
}
Options = x10 y10 w400 h300 cFF000000 s11
Gdip_TextToGraphics(G, "FPS: " . fps . "`nMouse: Screen(" . mousex . "`," . mousey . "`," . mousedown . ")`nCamera: (" . camera["x"] . "," . camera["y"] . "," . camera["z"] . "|" . camera["a"] . "," . camera["b"] . ")", Options, "Courier New")
; --- update the GUI ---
UpdateLayeredWindow(hwnd1, hdc, window_x, window_y, Width, Height)
Return
^X::
ExitApp
Return
project3dpoint(point) {
global camera, planeMidDistance, rotation
static starting_x, starting_y, sum, last_x, last_y
rel_x := (point[1] - camera["x"])
rel_y := (point[2] - camera["y"])
rel_z := point[3]
angle := (calculateangle(rel_x, rel_y) + camera["a"]) / 57.29578
xy_dist := sqrt(rel_x ** 2 + rel_y ** 2)
rot_x := xy_dist * cos(angle)
rot_y := xy_dist * sin(angle)
angle := (calculateangle(rot_y, rel_z) + camera["b"]) / 57.29578
yz_dist := sqrt(rot_y ** 2 + rel_z ** 2)
rot_y := yz_dist * cos(angle)
rot_z := yz_dist * sin(angle)
rot_z := (camera["z"] - rot_z)
planeMidDistance += rot_z
planezdist += rot_z
planeydist += rot_y
projected_x := camera["vpx"] + (rot_x / rot_z) * camera["scale"]
projected_y := camera["vpy"] + -1 * ((rot_y / rot_z) * camera["scale"])
if (rotation == "none") {
starting_x := projected_x
starting_y := projected_y
rotation := "first"
sum := 0
} else if (rotation == "first") {
sum += (projected_x - starting_x) * (projected_y + starting_y)
last_x := projected_x
last_y := projected_y
rotation := "second"
} else if (rotation == "second") {
sum += (projected_x - last_x) * (projected_y + last_y)
last_x := projected_x
last_y := projected_y
sum += (starting_x - projected_x) * (starting_y + projected_y)
last_x := projected_x
last_y := projected_y
rotation := sum
}
return projected_x . "," . projected_y
}
calculateangle(a, b) {
ra := atan(abs(a)/abs(b)) * 57.29578
if (a >= 0) && (b == 0)
return 0
if (a < 0) && (b == 0)
return 180
if (a >= 0) && (b > 0)
return (90 - ra)
if (a < 0) && (b > 0)
return (90 + ra)
if (a <= 0) && (b < 0)
return (270 - ra)
return (270 + ra)
}
WheelDown::
-::
camera["z"] += 1
Return
WheelUp::
+::
camera["z"] -= 1
Return
w::
camera["y"] += cos(camera["a"] / 57.29578)
camera["x"] += sin(camera["a"] / 57.29578)
Return
s::
camera["y"] -= cos(camera["a"] / 57.29578)
camera["x"] -= sin(camera["a"] / 57.29578)
Return
d::
camera["x"] += cos(camera["a"] / 57.29578)
camera["y"] -= sin(camera["a"] / 57.29578)
Return
a::
camera["x"] -= cos(camera["a"] / 57.29578)
camera["y"] += sin(camera["a"] / 57.29578)
Return
q::
camera["a"] += 10
Return
e::
camera["a"] -= 10
Return
r::
camera["b"] += 10
Return
f::
camera["b"] -= 10
Return
http://www.autohotkey.com/board/topic/2 ... 45-by-tic/
The controls are pretty intuitive:
- move around by touching the screen border with the mouse
- turning around horizontally = click and drag mouse
- turning around vertically = Left Ctrl + click and drag mouse
- zoom in/out = mousewheel
Have fun with it and do as you like.
I'm happy over any feedback!