AHK 3D "graphics", playing around with GDI+

Post your working scripts, libraries and tools for AHK v1.1 and older
Alibaba
Posts: 480
Joined: 29 Sep 2013, 16:15
Location: Germany

AHK 3D "graphics", playing around with GDI+

07 Sep 2015, 18:15

Hey community,

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

This is what it looks like:
Image

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
Of course you need the gdip.ahk by tic:
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!
"Nothing is quieter than a loaded gun." - Heinrich Heine
User avatar
boiler
Posts: 16932
Joined: 21 Dec 2014, 02:44

Re: AHK 3D "graphics", playing around with GDI+

07 Sep 2015, 20:29

That's pretty cool. Nice demo. Thanks for sharing.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: AHK 3D "graphics", playing around with GDI+

08 Sep 2015, 01:15

should we merge it here Fun with GDIPlus (GDI+)?
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Alibaba
Posts: 480
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: AHK 3D "graphics", playing around with GDI+

08 Sep 2015, 03:55

boiler wrote:That's pretty cool. Nice demo. Thanks for sharing.
You're welcome. :)
"Nothing is quieter than a loaded gun." - Heinrich Heine
Alibaba
Posts: 480
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: AHK 3D "graphics", playing around with GDI+

08 Sep 2015, 03:59

jNizM wrote:should we merge it here Fun with GDIPlus (GDI+)?
Sure, if you think its fun enough. :)
"Nothing is quieter than a loaded gun." - Heinrich Heine
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: AHK 3D "graphics", playing around with GDI+

08 Sep 2015, 06:27

I think it would be better than 20 topics of GDI+ examples out there :D
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
F4Jonatas
Posts: 45
Joined: 22 Oct 2015, 06:35
Contact:

Re: AHK 3D "graphics", playing around with GDI+

14 Jan 2017, 09:17

Hi!
I loved your project. :bravo:
But I did not understand what this does...

Code: Select all

link_hwnd := WinExist()
DllCall("RegisterShellHookWindow", "uint",link_hwnd)
Msg := DllCall("RegisterWindowMessage", "str","SHELLHOOK")
OnMessage(Msg, "LinkGuiMsg")

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 30 guests