Groverburger's 3D Engine (g3d) v1.5.2 Release

Showcase your libraries, tools and other projects that help your fellow love users.
Frankom
Prole
Posts: 1
Joined: Thu Jan 26, 2023 9:37 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Frankom »

It's working with anim9?
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Jasoco »

It's amazing what you can do with shaders when you spend a little time Googling and experimenting.
Screenshot 2023-01-29 at 1.35.47 AM.png
Screenshot 2023-01-29 at 1.35.47 AM.png (1023.73 KiB) Viewed 10744 times
Screenshot 2023-01-29 at 1.33.44 AM.png
Screenshot 2023-01-29 at 1.33.44 AM.png (763.4 KiB) Viewed 10744 times
Screenshot 2023-01-29 at 1.32.39 AM.png
Screenshot 2023-01-29 at 1.32.39 AM.png (979.47 KiB) Viewed 10744 times
Screenshot 2023-01-29 at 1.33.22 AM.png
Screenshot 2023-01-29 at 1.33.22 AM.png (1.55 MiB) Viewed 10744 times
Screenshot 2023-01-29 at 1.36.58 AM.png
Screenshot 2023-01-29 at 1.36.58 AM.png (1.12 MiB) Viewed 10744 times
Screenshot 2023-01-29 at 1.36.23 AM.png
Screenshot 2023-01-29 at 1.36.23 AM.png (750.86 KiB) Viewed 10744 times
I do wish I could do true lighting though. With shadows and such. Right now it's pretty simplistic. A mixture of calculated point lights and pre-baked lighting. Also every texture can have an associated "bright pixel" atlas for textures that need to have parts that are always full brightness. (For example, the green lights on the doors, the fluorescent lights in the last screenshot and the soda machine facades are all bright pixels.)

Also, everything is pretty much coded tables instead of .obj files. I haven't been able to figure out how to use 3D modeling software and the ones that I like don't export the files with the proper axis "up" that g3d expects. So I find it easier to just construct everything from triangles manually. I feel that uses a lot of memory though. I had a lot of memory problems with some of my earlier experiments where I was trying to make too big of a world with way too many polygons. So I borrowed the method used by Leadhaul for building my maps and creating my sprites.
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Bigfoot71 »

I made a pull request on the g3d repo so that the objloader.lua script accepts models with non-triangulated faces, here is my version if you want to use it now:

Code: Select all

-- written by groverbuger for g3d
-- september 2021
-- MIT license

----------------------------------------------------------------------------------------------------
-- simple obj loader
----------------------------------------------------------------------------------------------------

-- give path of file
-- returns a lua table representation
return function (path, uFlip, vFlip)
    local positions, uvs, normals = {}, {}, {}
    local result = {}

    -- go line by line through the file
    for line in love.filesystem.lines(path) do
        local words = {}

        -- split the line into words
        for word in line:gmatch "([^%s]+)" do
            table.insert(words, word)
        end

        local firstWord = words[1]

        if firstWord == "v" then
            -- if the first word in this line is a "v", then this defines a vertex's position

            table.insert(positions, {tonumber(words[2]), tonumber(words[3]), tonumber(words[4])})
        elseif firstWord == "vt" then
            -- if the first word in this line is a "vt", then this defines a texture coordinate

            local u, v = tonumber(words[2]), tonumber(words[3])

            -- optionally flip these texture coordinates
            if uFlip then u = 1 - u end
            if vFlip then v = 1 - v end

            table.insert(uvs, {u, v})
        elseif firstWord == "vn" then
            -- if the first word in this line is a "vn", then this defines a vertex normal
            table.insert(normals, {tonumber(words[2]), tonumber(words[3]), tonumber(words[4])})
        elseif firstWord == "f" then

            -- if the first word in this line is a "f", then this is a face
            -- a face takes three point definitions
            -- the arguments a point definition takes are vertex, vertex texture, vertex normal in that order

            local vertices = {}
            for i = 2, #words do
                local v, vt, vn = words[i]:match "(%d*)/(%d*)/(%d*)"
                v, vt, vn = tonumber(v), tonumber(vt), tonumber(vn)
                table.insert(vertices, {
                    v and positions[v][1] or 0,
                    v and positions[v][2] or 0,
                    v and positions[v][3] or 0,
                    vt and uvs[vt][1] or 0,
                    vt and uvs[vt][2] or 0,
                    vn and normals[vn][1] or 0,
                    vn and normals[vn][2] or 0,
                    vn and normals[vn][3] or 0,
                })
            end

            -- triangulate the face if it's not already a triangle
            if #vertices > 3 then
                -- choose a central vertex
                local centralVertex = vertices[1]

                -- connect the central vertex to each of the other vertices to create triangles
                for i = 2, #vertices - 1 do
                    table.insert(result, centralVertex)
                    table.insert(result, vertices[i])
                    table.insert(result, vertices[i + 1])
                end
            else
                for i = 1, #vertices do
                    table.insert(result, vertices[i])
                end
            end

        end
    end

    return result
end
It works very well for all the models I have tested, tell me what you think ^^
My avatar code for the curious :D V1, V2, V3.
Rigachupe
Citizen
Posts: 83
Joined: Fri Jun 18, 2021 11:21 am

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Rigachupe »

Can you draw a 3D line in this engine? I tried it by defining a vertex array as a 3D line with two points.

This is the desired format
local vertexFormatLines={
{"VertexPosition", "float", 3},
{"VertexColor", "byte", 4},
}

local vertices={{0,0,0,1,1,1,1},{0,0,100,1,1,1,1}}
model.geometry=love.graphics.newMesh(vertexFormatLines,vertices,"lines")

Then to draw it like this:
love.graphics.draw(model.geometry)

The error blue screen shows:
Invalid mesh draw mode 'lines', expected one of the 'triangles', 'fan', 'points'.

Any idea how to do a 3D line without the need to simulate some kind of 3D triangle strip?
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Bigfoot71 »

Rigachupe wrote: Mon Jan 30, 2023 6:43 pm Can you draw a 3D line in this engine? I tried it by defining a vertex array as a 3D line with two points.

This is the desired format
local vertexFormatLines={
{"VertexPosition", "float", 3},
{"VertexColor", "byte", 4},
}

local vertices={{0,0,0,1,1,1,1},{0,0,100,1,1,1,1}}
model.geometry=love.graphics.newMesh(vertexFormatLines,vertices,"lines")

Then to draw it like this:
love.graphics.draw(model.geometry)

The error blue screen shows:
Invalid mesh draw mode 'lines', expected one of the 'triangles', 'fan', 'points'.

Any idea how to do a 3D line without the need to simulate some kind of 3D triangle strip?
By generating a cylinder? Here is a function I had some time ago:

Code: Select all

function generate_cylinder(height, radius, nb_segments)

    local vertices = {}
    local angle_step = 2 * math.pi / nb_segments
    local angle = 0

    local base_z = -height / 2
    local tip_z = height / 2

    for i = 1, nb_segments do

        local x1 = radius * math.cos(angle)
        local y1 = radius * math.sin(angle)
        local x2 = radius * math.cos(angle + angle_step)
        local y2 = radius * math.sin(angle + angle_step)

        -- Base triangle

        table.insert(vertices, {0,0,base_z})
        table.insert(vertices, {x1,y1,base_z})
        table.insert(vertices, {x2,y2,base_z})

        -- Tip triangle

        table.insert(vertices, {0,0,tip_z})
        table.insert(vertices, {x1,y1,tip_z})
        table.insert(vertices, {x2,y2,tip_z})

        -- Side triangles

        table.insert(vertices, {x1,y1,base_z})
        table.insert(vertices, {x1,y1,tip_z})
        table.insert(vertices, {x2,y2,base_z})

        table.insert(vertices, {x2,y2,tip_z})
        table.insert(vertices, {x2,y2,base_z})
        table.insert(vertices, {x1,y1,tip_z})

        angle = angle + angle_step

    end

    return vertices

end
I made a demo while also trying a plane line style.

Edit: I just added UVs and normals to the function above so I'm sharing it if it can help:

Code: Select all

function generate_cylinder(height, radius, nb_segments)

    local vertices = {}
    local angle_step = 2 * math.pi / nb_segments
    local angle = 0

    local base_z = -height / 2
    local tip_z = height / 2

    for i = 1, nb_segments do

        local x1 = radius * math.cos(angle)
        local y1 = radius * math.sin(angle)
        local x2 = radius * math.cos(angle + angle_step)
        local y2 = radius * math.sin(angle + angle_step)

        -- Base triangle

        table.insert(vertices, {0,0,base_z, 0.5,0.5, 0,0,-1})
        table.insert(vertices, {x1,y1,base_z, 0,1, 0,0,-1})
        table.insert(vertices, {x2,y2,base_z, 1,1, 0,0,-1})

        -- Tip triangle

        table.insert(vertices, {0,0,tip_z, 0.5,0.5, 0,0,1})
        table.insert(vertices, {x1,y1,tip_z, 0,0, 0,0,1})
        table.insert(vertices, {x2,y2,tip_z, 1,0, 0,0,1})

        -- Side triangles

        local edge1_x, edge1_y, edge1_z = x1 - x2, y1 - y2, base_z - tip_z
        local edge2_x, edge2_y, edge2_z = x2 - x1, y2 - y1, base_z - tip_z

        local normal_x = edge1_y * edge2_z - edge1_z * edge2_y
        local normal_y = edge1_z * edge2_x - edge1_x * edge2_z
        local normal_z = edge1_x * edge2_y - edge1_y * edge2_x

        local normal_length = math.sqrt(normal_x * normal_x + normal_y * normal_y + normal_z * normal_z)
        local nx, ny, nz = normal_x / normal_length, normal_y / normal_length, normal_z / normal_length

        table.insert(vertices, {x1,y1,base_z, 0, 0.5 - 0.5*y1/radius, nx,ny,nz})
        table.insert(vertices, {x1,y1,tip_z, 1, 0.5 - 0.5*y1/radius, nx,ny,nz})
        table.insert(vertices, {x2,y2,base_z, 0, 0.5 - 0.5*y2/radius, nx,ny,nz})

        table.insert(vertices, {x2,y2,tip_z, 1, 0.5 - 0.5*y2/radius, nx,ny,nz})
        table.insert(vertices, {x2,y2,base_z, 0, 0.5 - 0.5*y2/radius, nx,ny,nz})
        table.insert(vertices, {x1,y1,tip_z, 1, 0.5 - 0.5*y1/radius, nx,ny,nz})

        angle = angle + angle_step

    end

    return vertices

end
I also tried a slightly more original and demonstrative example.
Image
Attachments
Line3D-2.love
(14.39 KiB) Downloaded 163 times
Line3D.love
(13.88 KiB) Downloaded 171 times
Last edited by Bigfoot71 on Thu Feb 02, 2023 3:06 pm, edited 1 time in total.
My avatar code for the curious :D V1, V2, V3.
User avatar
Hydrogen Maniac
Citizen
Posts: 79
Joined: Sat Dec 19, 2015 9:59 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Hydrogen Maniac »

Jasoco wrote: Sun Jan 29, 2023 7:01 am I do wish I could do true lighting though. With shadows and such. Right now it's pretty simplistic. A mixture of calculated point lights and pre-baked lighting. Also every texture can have an associated "bright pixel" atlas for textures that need to have parts that are always full brightness. (For example, the green lights on the doors, the fluorescent lights in the last screenshot and the soda machine facades are all bright pixels.)

Also, everything is pretty much coded tables instead of .obj files. I haven't been able to figure out how to use 3D modeling software and the ones that I like don't export the files with the proper axis "up" that g3d expects. So I find it easier to just construct everything from triangles manually. I feel that uses a lot of memory though. I had a lot of memory problems with some of my earlier experiments where I was trying to make too big of a world with way too many polygons. So I borrowed the method used by Leadhaul for building my maps and creating my sprites.
This looks great :awesome: ! I really like how you handled the light coming off of lava and the sky.

If you want to give implementing shadows a shot without losing your sanity I recommend looking at the source code of Hoarder's Horrible House Of Stuff. I've had the same issue with modelling software not using the same up vector as g3d but solved it by modifying the objloader to read the y axis as the z axis and vice versa.
I'm way too sober for this...
User avatar
vico
Prole
Posts: 21
Joined: Mon Nov 13, 2017 9:43 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by vico »

Jasoco wrote: Sun Jan 29, 2023 7:01 am It's amazing what you can do with shaders when you spend a little time Googling and experimenting.

Screenshot 2023-01-29 at 1.35.47 AM.png
Screenshot 2023-01-29 at 1.33.44 AM.png
Screenshot 2023-01-29 at 1.32.39 AM.png
Screenshot 2023-01-29 at 1.33.22 AM.png
Screenshot 2023-01-29 at 1.36.58 AM.png
Screenshot 2023-01-29 at 1.36.23 AM.png

I do wish I could do true lighting though. With shadows and such. Right now it's pretty simplistic. A mixture of calculated point lights and pre-baked lighting. Also every texture can have an associated "bright pixel" atlas for textures that need to have parts that are always full brightness. (For example, the green lights on the doors, the fluorescent lights in the last screenshot and the soda machine facades are all bright pixels.)

Also, everything is pretty much coded tables instead of .obj files. I haven't been able to figure out how to use 3D modeling software and the ones that I like don't export the files with the proper axis "up" that g3d expects. So I find it easier to just construct everything from triangles manually. I feel that uses a lot of memory though. I had a lot of memory problems with some of my earlier experiments where I was trying to make too big of a world with way too many polygons. So I borrowed the method used by Leadhaul for building my maps and creating my sprites.
Much amazing! Care to share your googlings?

EDIT: sorry the double post, thought the forum had post auto-merge feature enabled.
Last edited by vico on Mon Feb 06, 2023 2:24 am, edited 1 time in total.
User avatar
vico
Prole
Posts: 21
Joined: Mon Nov 13, 2017 9:43 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by vico »

Okay. I'm trying to resume starting to make a basic voxel demo myself (to learn more about how to use g3d).

I managed to create a cube using lua tables (still dont understand much what each part of the vert table represents) and at same time put a hello world to see how 2d elements would interact with the 3d enviroment (mostly for maybe create an UI on top of that):

Code: Select all

function love.conf(t)
    t.window.width = 848
    t.window.height = 480
end

Code: Select all

-- Codado por Vico
-- Primeira edição: 12/01/2023
-- À definir a licença

-- Biblioteca g3d
local g3d = require "g3d"

-- Modelo de Cubo
local cube_verts = {
	-- top
	{0,1,1, 0,0},
	{0,0,1, 0,1},
	{1,1,1, 1,0},
	
	{0,0,1, 0,1},
	{1,0,1, 1,1},
	{1,1,1, 1,0},

	-- bottom
	{0,0,0, 1,0},
	{0,1,0, 1,1},
	{1,1,0, 0,1},

	{1,0,0, 0,0},
	{0,0,0, 1,0},
	{1,1,0, 0,1},

	-- side 1
	{0,0,1, 0,0},
	{0,0,0, 0,1},
	{1,0,1, 1,0},

	{0,0,0, 0,1},
	{1,0,0, 1,1},
	{1,0,1, 1,0},

	-- side 2
	{0,1,0, 1,1},
	{0,1,1, 1,0},
	{1,1,1, 0,0},

	{1,1,0, 0,1},
	{0,1,0, 1,1},
	{1,1,1, 0,0},

	-- side 3
	{0,0,0, 1,1},
	{0,0,1, 1,0},
	{0,1,1, 0,0},

	{0,1,0, 0,1},
	{0,0,0, 1,1},
	{0,1,1, 0,0},

	-- side 4
	{1,0,1, 0,0},
	{1,0,0, 0,1},
	{1,1,1, 1,0},

	{1,0,0, 0,1},
	{1,1,0, 1,1},
	{1,1,1, 1,0},
};

-- Modelo 3D
local dirt = g3d.newModel(cube_verts, "assets/textures/blocks/dirt.png");

local timer = 0;

-- debug
function love.draw()
    love.graphics.print("Hello World", 400, 300)
	dirt:draw();
end

function love.update(dt)
	timer = timer + dt;
	g3d.camera.firstPersonMovement(dt);
    if love.keyboard.isDown "escape" then
        love.event.push "quit"
    end
end

function love.mousemoved(x,y, dx,dy)
    g3d.camera.firstPersonLook(dx,dy);
end
But seems like the cube is being "weirdly" rendered (inside-out), like the issue i reported on g3d_voxel, and the 2d test is rendered behind the cube:
Image
User avatar
Hydrogen Maniac
Citizen
Posts: 79
Joined: Sat Dec 19, 2015 9:59 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by Hydrogen Maniac »

vico wrote: Mon Feb 06, 2023 2:23 am But seems like the cube is being "weirdly" rendered (inside-out), like the issue i reported on g3d_voxel, and the 2d test is rendered behind the cube:
Rendering the scene to a canvas seems to solve it. I still have no idea why this is the case but the below code should work as intended.

Code: Select all

-- Codado por Vico
-- Primeira edição: 12/01/2023
-- À definir a licença

-- Biblioteca g3d
local g3d = require "g3d"

-- Modelo de Cubo
local cube_verts = {
	-- top
	{0,1,1, 0,0},
	{0,0,1, 0,1},
	{1,1,1, 1,0},
	
	{0,0,1, 0,1},
	{1,0,1, 1,1},
	{1,1,1, 1,0},

	-- bottom
	{0,0,0, 1,0},
	{0,1,0, 1,1},
	{1,1,0, 0,1},

	{1,0,0, 0,0},
	{0,0,0, 1,0},
	{1,1,0, 0,1},

	-- side 1
	{0,0,1, 0,0},
	{0,0,0, 0,1},
	{1,0,1, 1,0},

	{0,0,0, 0,1},
	{1,0,0, 1,1},
	{1,0,1, 1,0},

	-- side 2
	{0,1,0, 1,1},
	{0,1,1, 1,0},
	{1,1,1, 0,0},

	{1,1,0, 0,1},
	{0,1,0, 1,1},
	{1,1,1, 0,0},

	-- side 3
	{0,0,0, 1,1},
	{0,0,1, 1,0},
	{0,1,1, 0,0},

	{0,1,0, 0,1},
	{0,0,0, 1,1},
	{0,1,1, 0,0},

	-- side 4
	{1,0,1, 0,0},
	{1,0,0, 0,1},
	{1,1,1, 1,0},

	{1,0,0, 0,1},
	{1,1,0, 1,1},
	{1,1,1, 1,0},
};

-- Modelo 3D
local dirt = g3d.newModel(cube_verts, "assets/textures/blocks/dirt.png");

local timer = 0;

-- debug
local canvas = love.graphics.newCanvas(love.graphics.getDimensions())
function love.draw()
    love.graphics.setCanvas({canvas,depth=true})
    love.graphics.clear()
    love.graphics.print("Hello World", 400, 300)
	dirt:draw();

    love.graphics.setCanvas()
    love.graphics.draw(canvas)
end

function love.update(dt)
	timer = timer + dt;
	g3d.camera.firstPersonMovement(dt);
    if love.keyboard.isDown "escape" then
        love.event.push "quit"
    end
end

function love.mousemoved(x,y, dx,dy)
    g3d.camera.firstPersonLook(dx,dy);
end
Untitled.png
Untitled.png (20.06 KiB) Viewed 10388 times

vico wrote: Mon Feb 06, 2023 2:23 am (still dont understand much what each part of the vert table represents)
I threw this together in paint, maybe it will help you understand how the models are built. :)
Verts.png
Verts.png (24.43 KiB) Viewed 10388 times
I'm way too sober for this...
User avatar
vico
Prole
Posts: 21
Joined: Mon Nov 13, 2017 9:43 pm

Re: Groverburger's 3D Engine (g3d) v1.5.2 Release

Post by vico »

Hydrogen Maniac wrote: Mon Feb 06, 2023 8:34 pm I threw this together in paint, maybe it will help you understand how the models are built. :)
Verts.png
Thank you so much for this explanation. It really helped me to visualize how it works.

I just have another question: how could i store the model in a text-based format like json? I really dont want to use .obj because i want to allow easy editing of the models (maybe for future modding purposes).
I tried to export a simple cube in JSON model in Blockbench and it spilled the following code:

Code: Select all

{
	"credit": "Made with Blockbench",
	"elements": [
		{
			"from": [1, 1, 1],
			"to": [2, 2, 2],
			"color": 5,
			"faces": {
				"north": {"uv": [0, 0, 1, 1], "texture": "#missing"},
				"east": {"uv": [0, 0, 1, 1], "texture": "#missing"},
				"south": {"uv": [0, 0, 1, 1], "texture": "#missing"},
				"west": {"uv": [0, 0, 1, 1], "texture": "#missing"},
				"up": {"uv": [0, 0, 1, 1], "texture": "#missing"},
				"down": {"uv": [0, 0, 1, 1], "texture": "#missing"}
			}
		}
	]
}
Is that information enough to render a cube in g3d? Or i need to use another standard or make one from scratch?
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 17 guests