Tetra3D is a 3D software renderer written in Go and Ebiten and made for games.



Tetra3D Logo Dark exploration

Tetra3D Docs


If you want to support development, feel free to check out my itch.io / Steam / Patreon. I also have a Discord server here. Thanks~!

What is Tetra3D?

Tetra3D is a 3D software renderer written in Go by means of Ebiten, primarily for video games. Compared to a professional 3D rendering systems like OpenGL or Vulkan, it's slow and buggy, but it's also janky, and I love it for that.

It evokes a similar feeling to primitive 3D game consoles like the PS1, N64, or DS. Being that a software renderer is not nearly fast enough for big, modern 3D titles, the best you're going to get out of Tetra is drawing some 3D elements for your primarily 2D Ebiten game, or a relatively rudimentary fully 3D game (maybe something on the level of a PS1 or N64 game would be possible). That said, limitation breeds creativity, and I am intrigued at the thought of what people could make with Tetra.

Why did I make it?

Because there's not really too much of an ability to do 3D for gamedev in Go apart from g3n, go-gl and Raylib-go. I like Go, I like janky 3D, and so, here we are.

It's also interesting to have the ability to spontaneously do things in 3D sometimes. For example, if you were making a 2D game with Ebiten but wanted to display just a few things in 3D, Tetra3D should work well for you.

Finally, while a software renderer is not by any means fast, it is relatively simple and easy to use. Any platforms that Ebiten supports should also work for Tetra3D automatically (hopefully!).

Why Tetra3D? Why is it named that?

Because it's like a tetrahedron, a relatively primitive (but visually interesting) 3D shape made of 4 triangles. Otherwise, I had other names, but I didn't really like them very much. "Jank3D" was the second-best one, haha.

How do you get it?

go get github.com/solarlune/tetra3d

Tetra depends on kvartborg's vector package, takeyourhatoff's bitset package, and Ebiten itself for rendering. Tetra3D requires Go v1.16 or above.

How do you use it?

Make a camera, load a scene, render it. A simple 3D framework means a simple 3D API.

Here's an example:

package main

import (

	_ "image/png"



const ScreenWidth = 398
const ScreenHeight = 224

type Game struct {
	Scene        *tetra3d.Scene
	Camera       *tetra3d.Camera

func NewGame() *Game {

	g := &Game{}

	// First, we load a scene from a .dae file. LoadDAEFile returns a *Scene 
	// and an error if it was unsuccessful. 
	scene, err := tetra3d.LoadDAEFile("examples.dae") 

	if err != nil {

	g.Scene = scene

	// If you need to rotate the model because the 3D modeler you're 
	// using doesn't use the same axes as Tetra (like Blender),
	// you can call Mesh.ApplyMatrix() to apply a rotation matrix 
	// (or any other kind of matrix) to the vertices, thereby rotating 
	// them and their triangles' normals. 

	// By default (if you're using Blender), this conversion is 
	// done for you; you can change this by passing a different
	// DaeLoadOptions parameter when calling tetra3d.LoadDAEFile().

	// Tetra uses OpenGL's coordinate system (+X = Right, +Y = Up, +Z = Back), 
	// in comparison to Blender's coordinate system (+X = Right, +Y = Forward, 
	// +Z = Up). 

	// Create a new Camera. We pass the size of the screen to the Camera so
	// it can create its own buffer textures (which are *ebiten.Images).
	g.Camera = tetra3d.NewCamera(ScreenWidth, ScreenHeight)

	// Place Models or Cameras using their Position properties (which is a 
	// 3D Vector from kvartborg's vector package). Cameras look forward 
	// down the -Z axis.
	g.Camera.Position[2] = 12

	return game

func (g *Game) Update() error { return nil }

func (g *Game) Draw(screen *ebiten.Image) {

	// Call Camera.Clear() to clear its internal backing texture. This
	// should be called once per frame before drawing your *Scene.

	// Now we'll render the Scene from the camera. The Camera's ColorTexture will then 
	// hold the result. 
	// We pass both the Scene and the Models because 1) the Scene influences
	// how Models draw (fog, for example), and 2) we may not want to render
	// all Models. We might want to filter out some Models, which you can do with
	// g.Scene.FilterModels().
	g.Camera.Render(g.Scene, g.Scene.Models) 

	// Before drawing the result, clear the screen first.
	screen.Fill(color.RGBA{20, 30, 40, 255})

	// Draw the resulting texture to the screen, and you're done! You can 
	// also visualize the depth texture with g.Camera.DepthTexture.
	screen.DrawImage(g.Camera.ColorTexture, nil) 


func (g *Game) Layout(w, h int) (int, int) {
	// This is the size of the window; note that a larger (or smaller) 
	// layout doesn't seem to impact performance very much.
	return ScreenWidth, ScreenHeight

func main() {

	game := NewGame()

	if err := ebiten.RunGame(game); err != nil {


That's basically it. Note that Tetra3D is, indeed, a work-in-progress and so will require time to get to a good state. But I feel like it works pretty well and feels pretty solid to work with as is. Feel free to examine the examples folder for a couple of examples showing how Tetra3D works - simply change to their directory and calling go run . should work.

What's missing?

The following is a rough to-do list (tasks with checks have been implemented):

  • 3D rendering
  • -- Perspective projection
  • -- Orthographic projection
  • -- Billboards
  • -- Some additional way to draw 2D stuff with no perspective changes (if desired) in 3D space
  • -- Basic depth sorting (sorting vertices in a model according to distance, sorting models according to distance)
  • -- A depth buffer and depth testing - This is now implemented by means of a depth texture and Kage shader, though the downside is that it requires rendering and compositing the scene into textures twice. Also, it doesn't work on triangles from the same object (as we can't render to the depth texture while reading it for existing depth).
  • -- A more advanced depth buffer - currently, the depth is written using vertex colors.
  • -- Offscreen Rendering
  • -- Model Batching / Combination (Ebiten should batch render calls together automatically, at least partially, as long as we adhere to the efficiency guidelines)
  • Culling
  • -- Backface culling
  • -- Frustum culling
  • -- Far triangle culling
  • -- Triangle clipping to view (this isn't implemented, but not having it doesn't seem to be too much of a problem for now)
  • Debug
  • -- Debug text: overall render time, FPS, render call count, vertex count, triangle count, skipped triangle count
  • -- Wireframe debug rendering
  • -- Normal debug rendering
  • Basic Single Image Texturing
  • -- Multitexturing?
  • -- Materials / Per-triangle images
  • -- Perspective-corrected texturing (currently it's affine, see Wikipedia)
  • Animations
  • -- Armature-based animations
  • -- Object transform-based animations
  • Scenes
  • -- Fog
  • -- Ambient vertex coloring
  • -- A node or scenegraph for parenting and simple visibility culling
  • DAE model loading
  • -- Vertex colors loading
  • -- UV map loading
  • -- Normal loading
  • -- Transform / full scene loading
  • Lighting?
  • Shaders
  • -- Normal rendering (useful for, say, screen-space shaders)
  • Basic Collisions
  • -- AABB collision testing / sphere collision testing?
  • -- Raycasting
  • Multithreading (particularly for vertex transformations)
  • Prefer Discrete GPU for computers with both discrete and integrated graphics cards

Again, it's incomplete and jank. However, it's also pretty cool!

Shout-out time~

Huge shout-out to the open-source community (i.e. StackOverflow, fauxgl, tinyrenderer, learnopengl.com, etc) at large for sharing the information and code to make this possible; I would definitely have never made this happen otherwise.

  • Lots of noise when displaying triangles

    Lots of noise when displaying triangles

    Hi I've tried running a few examples with the same results (see the attached).

    I'm running Go 1.17 on Debian Stable (5.10.0-10-amd64) via Xorg. The GPU on my Thinkpad is an HD 520.

    The video I've attached is of the animations example but the same thing happens on other examples. I've tried on the latest master branch and also checked out the v0.4.2 tag with the same results.

    I know there isn't much you can do remotely for such a weird issue but figured I would still notify you. I'm a go developer myself and have built toy renderers before, but I'm kinda at a loss for how to debug this. If there is something I can do to help, let me know.

    Install steps were just checkout, go mod tidy, go run ./examples/animation/main.go.


    opened by adamrt 6
  • Buggy Z-order of billboard sprites when objects are close to each other

    Buggy Z-order of billboard sprites when objects are close to each other

    Repro is in this branch: https://github.com/eliasdaler/Tetra3d/tree/zorder_repro Check animatedTextures example

    When flying around, somewhere at this point you'll notice Z-fighting: image

    In my case, I can't place the objects apart from each other - Z-fighting like this happens when a player character gets near NPC characters and Z-fighting get pretty severe (and sometimes the Z-order is just wrong)


    opened by eliasdaler 4
  • Small objects get clipped even if I set camera.Near to very low values

    Small objects get clipped even if I set camera.Near to very low values

    Hello. I have noticed that some objects in my scene are disappearing once I get close to them. I thought that I could fix that by setting Near value lower, but unfortunately this didn't help. See the modified scene from shapes example.

    Left to Suzanne, there is an object which disappears if you get close to it. You can also see it being clipped even if you don't get close but rotate camera around sometimes. image

    opened by eliasdaler 3
  • Capsule collision doesn't work with some triangle meshes

    Capsule collision doesn't work with some triangle meshes

    See repro here: https://github.com/eliasdaler/Tetra3d/tree/bounds_issues/examples/bounds

    When trying to climb a new surface I added with a capsule, collision doesn't work properly and the capsule doesn't ascend.

    opened by eliasdaler 2
  • panic: Error: MeshPart.AddTriangles() not given enough vertices to construct complete triangles (i.e. multiples of 3 vertices).

    panic: Error: MeshPart.AddTriangles() not given enough vertices to construct complete triangles (i.e. multiples of 3 vertices).

    I'm noticing a trend in this package:

    func (part *MeshPart) AddTriangles(verts ...VertexInfo) {
    	mesh := part.Mesh
    	if part.TriangleEnd > -1 && part.TriangleEnd < mesh.triIndex {
    		panic("Error: Cannot add triangles to MeshPart non-contiguously (i.e. partA.AddTriangles(), partB.AddTriangles(), partA.AddTriangles() ).")
    	if len(verts) == 0 || len(verts)%3 > 0 {
    		panic("Error: MeshPart.AddTriangles() not given enough vertices to construct complete triangles (i.e. multiples of 3 vertices).")

    Is there any reason sanity checks constitute an entire program failure? why not restructure to..

    func (part *MeshPart) AddTriangles(verts ...VertexInfo) error {
    	mesh := part.Mesh
    	if part.TriangleEnd > -1 && part.TriangleEnd < mesh.triIndex {
    		return fmt.Errorf("cannot add triangles to MeshPart non-contiguously (i.e. partA.AddTriangles(), partB.AddTriangles(), partA.AddTriangles() ).")
    	if len(verts) == 0 || len(verts)%3 > 0 {
    		return fmt.Errorf("not enough vertices are provided to construct complete triangles (i.e. multiples of 3 vertices).")

    There are 26 uses of panic in this package: https://github.com/SolarLune/Tetra3d/search?q=panic And most aren't critical failures that require the entire application to exit immediately, most are simply malformed requests that can be reported back to abort current operation

    opened by xackery 2
  • Refactored examples by extracting common code on camera stuff

    Refactored examples by extracting common code on camera stuff

    I've been playing with the examples and I've seen there's some common code which could be extracted from them. I've created an ExampleGame struct with shared fields accross all examples (scene / cam info) and some methods to process the rotation / movement / setup of the camera.

    Some thoughts:

    • I'm not sure whether examples/base_example.go is the correct path to put this new code. I can change it to whatever path you seem fit.
    • The example shapes was not refactored as I didn't understand how to make a tetra3d.INode which is the type of the camera in that example to a tetra3d.Camera which is what most of the other examples use.
    • The example orthographic was also not changed as it treated the camera differently than the rest.
    • As I refactored several examples I began to think that ExampleGame might be a good candidate to use as a starter point for any tetra3d game one starts? It has a camera, and basic movement. Maybe rename it BaseGame?

    I think there's some more code (related to the debug options, quitting, full screen) in the examples which could be refactored. I might tackle that later.

    opened by luisparravicini 2
  • Increase glTF parsing performance

    Increase glTF parsing performance

    First of all, great project!

    While testing your usage of qmuntal/gltf I noticed LoadGLTFData consumes quite a lot of memory compared to the size of the parsed glTF file. For example, ./example/logo/tetra3d.glb weights ~58KB and LoadGLTFData consumes ~3MB and produces ~27k allocations!

    My take is that most of the memory consumed by that function is accidental. A sane size to in-memory ratio would be 2 or 3x, not 58x.

    This PR contains several memory improvements, which as side effect also have some net speed gains:

    C:\Users\qmuntaldiaz\code\Tetra3d>benchstat old.txt new.txt
    name             old time/op    new time/op    delta
    LoadGLTFData-12    1.97ms ±10%    1.36ms ±15%  -30.67%  (p=0.000 n=9+9)
    name             old speed      new speed      delta
    LoadGLTFData-12  29.9MB/s ± 9%  43.1MB/s ±13%  +44.27%  (p=0.000 n=9+9)
    name             old B/op       new B/op       delta
    LoadGLTFData-12    3.17MB ± 0%    1.64MB ± 0%  -48.19%  (p=0.000 n=10+10)
    name             old allocs/op  new allocs/op  delta
    LoadGLTFData-12     27.6k ± 0%     25.5k ± 0%   -7.60%  (p=0.000 n=10+9)
    opened by qmuntal 2
  • Crashing when loading a mesh

    Crashing when loading a mesh

    Hello Tetra3D's gltf loader crashes with this message:

    panic: runtime error: index out of range [96] with length 96
    goroutine 1 [running, locked to thread]:
    github.com/solarlune/tetra3d.LoadGLTFData({0xa113e0, 0x76529, 0x76529}, 0xc000689f20)
    	<...>/Tetra3d/gltf.go:306 +0x4a45

    I've added a part of my model to shapes.blend so that you can reproduce it (hopefully) It can be found here: https://github.com/eliasdaler/Tetra3d/tree/gltf-crash/examples/shapes

    Here's the part that causes the problem:

    for triIndex, tri := range newMesh.Triangles {
    	tri.Normal = normals[triIndex] // <- here!

    Adding a Print like this:

    fmt.Println(len(normals), len(indices), len(newMesh.Triangles), mesh.Name)

    shows this:

    96 288 608 Cylinder.003

    So, the number of triangles doesn't match number of indices... Maybe I'm doing something wrong with Blender, so if there's something I need to do with my models, please let me know! :)

    opened by eliasdaler 2
  • update path to vector package

    update path to vector package

    Hi @SolarLune,

    Thanks for this cool project.

    Just creating this PR to let you know that I have changed my GitHub handle, this will impact the github.com/kvartborg/vector package, as it has changed to github.com/quartercastle/vector.

    The old package is still available and can be installed as usual, so it shouldn't effect projects dependent on your module. I can see that I have 33 depending on my vector package and most are indirect through your packages, I will strive post PR's for those to mitigate any problems that may arise when this has been merged.

    Sorry for the inconvenience.

    opened by quartercastle 1
  • recent releases aren't using recognized semantic version numbers

    recent releases aren't using recognized semantic version numbers

    ...and go mod is unhappy with them. Currently it believe v0.5.2 is the most recent release because its the most recent one with "acceptable" semantic versioning per https://go.dev/doc/modules/version-numbers

    To fix, re-release v0.8 as v0.8.0 or v0.8.1

    opened by c-robinson 1
  • Ensure colorShader always returns a color, regardless of depth

    Ensure colorShader always returns a color, regardless of depth

    @SolarLune I pretty sure this change doesn't actually fix the problem correctly, but wanted to at least show it to you. It does seem to fix the primary problem for me in all the examples I've tried. It seems like an illogical fix since the other fragment shaders aren't necessarily returning a fragment. Seems like its usually conditional.

    Even with this change there are visual depth issues. Like the z-buffer (or painters algorithm) is off. See screenshot. But I did confirm that these depth issues exist even without my changes. I'm not sure if its part of Tetra or if this is just an issue with my crappy video card. Either way the scene rendering looks good again, just something wrong with the depth buffer.

    I also notice in that same shader you are shadowing the color variable. Maybe that's intentional, but it seemed a little off to me. I don't know much past the basics of shaders and even less when it comes to Kage shaders.

    Check out the order of these boxes. image

    And clearly something with depth sorting here too


    This references #7

    opened by adamrt 1
  • panic: runtime error: index out of range [2] with length 2

    panic: runtime error: index out of range [2] with length 2

    goroutine 1 [running, locked to thread]: github.com/solarlune/tetra3d.LoadGLTFData({0xc0000cc000, 0x6c2, 0x6c3}, 0x0) /Users/xackery/Documents/code/go/pkg/mod/github.com/solarlune/[email protected]/gltf.go:402 +0x82a7

    this time I can attach the gltf it fails with: interleaved.gltf.zip

    This is based on https://github.com/qmuntal/gltf/tree/80d3922a76d3e24371f8c99e0d213ecc8dae3ffe#data-interleaving

    opened by xackery 0
  • panic: index out of range

    panic: index out of range

    goroutine 1 [running, locked to thread]:
    github.com/solarlune/tetra3d.LoadGLTFData({0xc00044c000, 0x1d81b, 0x1d81b}, 0x0)
            /Users/xackery/Documents/code/go/pkg/mod/github.com/solarlune/[email protected]/gltf.go:162 +0x8e7a
    exit status 2


    newMat.TexturePath = doc.Images[*doc.Textures[texture.Index].Source].URI

    is missing sanity checks and making assumptions, I'm using something similar to https://github.com/qmuntal/gltf/blob/80d3922a76d3e24371f8c99e0d213ecc8dae3ffe/modeler/example_test.go#L35 to generate a gltf and it is cutting corners on some steps, and causing tetra3d to panic non-gracefully

    opened by xackery 2
  • nil exception in gltf loader

    nil exception in gltf loader

    [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x4431201]
    goroutine 1 [running, locked to thread]:
    github.com/solarlune/tetra3d.LoadGLTFData({0xc000ad8000, 0x1d71f, 0x1d71f}, 0x0)
            /Users/xackery/Documents/code/go/pkg/mod/github.com/solarlune/[email protected]/gltf.go:158 +0xba1
    github.com/xackery/quail/cmd.(*viewer).load(0xc0004e4bf0, {0xc000ad8000?, 0xc0002b3c68?, 0x1?})


    if texture := gltfMat.PBRMetallicRoughness.BaseColorTexture; texture != nil {

    isn't happy with my hand crafted gltf files

    opened by xackery 2
  • v0.11.1(Dec 17, 2022)

    Here's a quick bug-fix release - just a couple of minor fixes (and one major one for orthographic rendering). Overlapping faces should also display more accurately.

    Full Changelog: https://github.com/SolarLune/tetra3d/compare/v0.11.0...v0.11.1

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(44.24 KB)
  • v0.11.0(Dec 13, 2022)

    Yo, what's up! It's been quite awhile!


    So here's Tetra3D v0.11.0! This one is kind of a massive change, as internally I've dramatically altered Tetra3D in a few ways.


    Firstly, we've moved to indexed rendering. Previously, each triangle had its own vertices; now, each mesh is comprised of vertices, and each triangle is comprised of indices. This means that, in a best-case scenario, triangles can reuse vertices, making for, hopefully, faster rendering.

    I also created my own Vector struct, as flat structs of numbers seems to be far faster (both in mathematics as well as in allocation of memory) than the previous vector package I was using, by quartercastle. I still recommend that package outside of Tetra3D, as it's a very elegant solution for multi-dimensional vectors where speed isn't absolutely integral to the process.

    All of this should mean a fairly significant boost in performance! We can see this in the case of the stress test in the examples folder. Here's the results as performed in v0.10.0 (Average 18.6 FPS):

    Screenshot from 2022-12-12 22-03-22

    And here's the same test in v0.11.0 (Average 26.3 FPS):

    Screenshot from 2022-12-12 22-04-49

    That's over a 40% improvement, which ain't shabby!

    Collision Checking

    Collision checking has been improved to be simpler and easier - now, instead of two CollisionTest() functions, there's just one, and it just takes a CollisionTestSettings struct. In this struct, you specify the objects you want to check against, as well as an optional callback to handle a collision as it happens. This is an improvement in collision checking because previously, you could only act on all of the collisions from CollisionTest() - however, this new callback is called before any collisions happen for each collision. This is useful because you can use it to resolve collisions sequentially (i.e. if object A collides with objects B and C, and on checking with a collision against B you move A out of collision with B and C, then you won't have a collision with C, naturally). It's a small difference, but an important one, and it's helpful to be able to know that each collision will be handled properly.

    Smaller stuff

    There's also a variety of other minor changes and other performance optimizations. One such minor-ish improvement is that I've added the ability to tag objects for automatic static merging or dynamic batching (either through code or in Blender). This automatic batching is done when an object is added to or removed from a scene's hierarchy, and is automatically performed when a scene is rendered through a camera (as that way, we can ensure objects should be where they're desired to be prior to batching). This should help to allow for some easy optimizing non-moving elements in a game.

    You can now add game properties to scenes themselves, which can be rather useful for global properties, like specifying background music for a level, as an example.

    In Blender, you now have a couple of buttons to easily set an object's vector game property to the object's center or the 3D cursor.

    In Blender, you now have the ability to copy game properties from a collection instance to top-level instanced nodes (so, in other words, you can override properties on a collection instance and have them be applied to the instanced objects. This is helpful for a situation where, say, you want to change the health on a specific character; now you can check off this option and create an HP property on the instanced version of that character. That property will be passed down to the instanced nodes).

    All of the examples have been overhauled to be simpler and share common elements, like the camera and debug features.

    You can now draw sprites in 3D, such that it's drawn in 2D, but still can be obscured and "occupy space in 3D". See the relevant sprite3d example. This can be very useful for situations where you want a sprite to not be resized or crimped with the camera, but have the sprite still be tied to 3D space.

    @tomlister added the ability to export game resolution from Blender itself, so that makes loading cameras more seamless.

    @eliasdaler fixed up some of the other examples, as well.

    NOTE: I've also updated the Tetra3D add-on for Blender 3.4 support - note that this add-on probably won't work any longer with a previous version of Blender, as the default GLTF exporter now exports light intensity using customizeable units (real-world accurate or otherwise).

    Anyway, lots of other stuff, so check out the commits for the full changelog.

    What's Changed

    • GLTF Export & Resolve Scene Resolution by @tomlister in https://github.com/SolarLune/tetra3d/pull/22
    • Fix examples by @eliasdaler in https://github.com/SolarLune/tetra3d/pull/23

    New Contributors

    • @tomlister made their first contribution in https://github.com/SolarLune/tetra3d/pull/22
    • @eliasdaler made their first contribution in https://github.com/SolarLune/tetra3d/pull/23

    Full Changelog: https://github.com/SolarLune/tetra3d/compare/v0.10.0...v0.11.0

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(44.24 KB)
  • v0.10.0(Sep 26, 2022)

    OK, so!

    It's been awhile since the last release. I've added a faaaaaaaair amount of new features and stuff, so I'll just go over some of the bigger things.

    Broadphase Collision Testing

    Collisions involving triangle meshes now have a broadphase part to the collision test. This means that collision testing should be a lot faster in general, as we only have to check collisions against the triangles nearby the object we're testing, rather than all triangles. The broadphase collision size is, by default, 20 Blender Units in size (with Tetra3D adding more cells to wholly wrap around the mesh, but can be altered (or turned off) at will using BoundingTriangles.Broadphase.Resize() or BoundingTriangles.DisableBroadphase().

    Recursive Collision Testing

    Now collision testing (using one of the BoundingObject.CollisionTest functions) is done on all children recursive to the tested nodes - this means that, firstly, you can now easily check to see if a bounding object collides with a model, for example, rather than its bounding objects. This is good because most times, you'll attach tags to the Model to indicate, for example, that it's solid, or that it's dangerous - this lessens the amount of code you need to type to do fairly basic things, going from this:

    // Test all bounding objects that are children of "solid" Models in the scene
    solids := scene.Root.ChildrenRecursive().ByTags("solid").Children().BoundingObjects()
    for _, col := range bounds.CollisionTest(0, 0, 0, solids...) {
        parentModel := col.Parent().(tetra3d.INode) // Get the parent to interact with it in some way

    to this:

    // Test all bounding objects that are children of "solid" Models in the scene (but do so automatically)
    solids := scene.Root.ChildrenRecursive().ByTags("solid")
    for _, col := range bounds.CollisionTest(0, 0, 0, solids...) {
        // Here, Root is the objects that we passed for testing (so, the Nodes or Models that have "solid" tags)
        parentModel := col.Root

    Secondly, you can now compose more complex shapes out of smaller BoundingObjects by simply parenting them to a common parent in the hierarchy and then testing that parent.

    Of course, if you want to test each BoundingObject individually like you did before, that's still an option as well.


    Worlds have been added, as well. A World contains things like fog color, distance, screen clear color, etc. Worlds can be easily swapped around, and each Scene has its own World reference. A World in Tetra3D directly corresponds to a World in Blender, as well.


    Cube Lights have been added - these are lights that light triangles only within an AABB. This is useful for lighting, say, an indoor space. The light bleed can be customized, allowing you to change the light level within this specific space.

    cube lighting

    Light Groups have been added, as well - this allows you to easily control which lights influence a Model.

    Baking of lighting and baking (preliminary) AO to vertex colors has also been added. Baking AO is fairly slow, so be aware, but this should be rather useful for easily optimizing games where the lighting doesn't need to be live-calculated.


    Various parts of Tetra3D have been optimized, including lighting, collision testing, etc.


    grid image

    Grids have been added. A Grid is a network of points (vertices, in Blender) and their connections (edges). Grids allow you to get a path from one point to another, passing only through those connections. This being the case, Grids can be useful to handle things like pathfinding. Grids are most easily created through Blender, by creating a mesh like normal, and then switching the Object Type from Mesh to Grid in the Tetra3D Object Properties section.

    When an object is a Grid, it no longer is visible in the game world, and becomes a Grid (rather than a Model). Each vertex becomes a GridPoint in Tetra3D, and the edges between vertices become connections between those GridPoints.


    A basic, simple particle implementation has been added to make things a bit easier on that front. While particles aren't necessarily the hardest thing to add, they're useful in enough situations where even a basic implementation should be useful.


    Anyway, that's just some of the big stuff. I have to go now, so have fun checking everything out, lol

    Feel free to read up on the Wiki for more information, as well!

    Full Changelog: https://github.com/SolarLune/tetra3d/compare/v0.9.1...v0.10.0

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(37.12 KB)
  • v0.9.1(Jul 20, 2022)


    Here's another update to Tetra3D; this one adds some QoL impprovements and bugfixes, particularly centered around everybody's favorite core part of a game, collision detection.

    Previously, collision detection / intersection testing was done on a per-object basis - this option is still available, but now you also have the option to check for collisions against multiple objects at the same time using BoundingObject.CollisionTest() or BoundingObject.CollisionTestVec(). The result of an intersection test was previously called an IntersectionResult - now, it's called a Collision. This renaming was done to be easier to conceptualize. You can also test for collisions with a suggested movement (so you can, for example, check straight down a bit to check for ground, or check up to see if a character would bump its head when jumping).

    Normals and slopes have also been added to the collision reporting process, making it far easier to do basic physics. Slopes are currently returned as an angle ranging from 0 (straight up) to pi (straight down) - you can convert this into degrees for easier human readability using tetra3d.ToDegrees().

    I've added functions to return contact points, slopes, and MTVs that are averaged across any and all intersections as well, which makes things a bit easier to deal with when it comes to working with simple baselines.

    I've also updated the Blender addon to add a World panel to easily customize fog and the screen clear color. (Note that the screen clear color doesn't do anything by default - you use Scene.ClearColor when clearing the screen.)

    Finally, I've created a simple quick start project for Tetra3D projects here.

    Anyway, feel free to check out the changelog below!

    Full Changelog: https://github.com/SolarLune/tetra3d/compare/v0.9.0...v0.9.1

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(33.07 KB)
  • v0.9.0(Jul 7, 2022)


    It's been awhile since the last Tetra3D release, and so I wanted to give it another shot~!


    Firstly, there have been massive optimizations!

    General vertex transformation is 15-30% faster! Point lighting has been optimized, as well - internally, Tetra3D can exit lighting sooner if a triangle lies outside of the range of the light, for example. Armature animation time is down around 50%! Tetra3D is just running better in general compared to a few months ago, which is good. Part of that might be ebitengine and Go becoming faster as well, haha.

    Dynamic Batching

    Screenshot from 2022-07-07 14-21-06

    I've also added some preliminary support for dynamic batching. Dynamic Batching allows you to render many objects using a single object's material information and color - this is very good, as it is far faster than rendering each object individually. This makes things like minimaps or many individual NPCs a lot more possible. Note that this only works with a single texture / draw call currently (so you can't have, for example, multiple MeshParts with different material settings or textures in a single batch at the moment; they will all render using the first MeshPart's render information).


    I've also added billboarding to Tetra3D with a couple of different billboarding modes - now you can get some sprites in your games~

    Billboarding From left to right: Off, Horizontal (X/Z) Billboarding, Full Billboarding

    I've updated the wiki a bit more as well. Thanks, and I'll catch you guys later~!

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.8.1...v0.9.0

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(28.23 KB)
  • v0.8.1(Jun 19, 2022)


    Here's a quickie minor bugfix release that just has some nice stability / usability fixes.

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.8...v0.8.1


    Source code(tar.gz)
    Source code(zip)
  • v0.8(Mar 18, 2022)

    Yo! It's been a bit since the last version of Tetra3D - here's v0.8! This one has quite a few improvements in it!

    It Breeds Fear - Construction Worker

    Material Panel :bricks:

    A new Material Panel has been added to the Tetra3D Blender add-on, making it easier to color Materials, set shading or blending parameters, and backface culling, all from one place.

    Marker Importing :triangular_flag_on_post:

    Markers placed in animations can now be imported from Blender into Tetra3D! This can be very useful in timing events like sound playback (e.g. playing a footstep sound in time with an animation) or marking traits in an animation.

    Animated Textures :runner:

    Being that old-school renderers were working with such limited poly counts, utilizing animated textures were a key part of the experience in doing more with less. This being the case, I've added both a TextureAnimation and TexturePlayer struct - these structs allow you to play back simple sprite animations on a collection of vertices on a Mesh. This can be useful for facial animations for 3D characters, or 2D animations, among other things.

    Multiple Vertex Colors :red_circle: :large_blue_circle:

    Previously, vertices only supported a single vertex color channel. I was unsure about how to go about selecting vertices (say, for animation) easily - after some different attempts, I've come to the conclusion that the simplest way to do this was just to support multiple vertex channels. This way, you can use channels to select vertices of interest (i.e. the faces of a person's face, to do facial animation).

    Paths :infinity:

    Path support has been added to Tetra3D, allowing you to both create paths through code, or import paths from Blender into Tetra3D. You can use paths to easily make NPCs walk on a route, for example.

    There are quite a few other bugfixes and optimizations, as well, including one made by the creator of the Golang GLTF parsing library, @qmuntal - thanks a lot to him for his assistance and contribution!

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.7...v0.8

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(26.21 KB)
  • v0.7(Feb 22, 2022)

    Yo, 'sup~!

    Here's another Tetra3D release - this one has a new Blender add-on I made to assist with the process of creating stuff for Tetra3D.

    There's a bit more info on what it does over on the Wiki, but in short, it offers exporting to GLTF on save, easy bounds node setting, and object property creation. I've attached the Tetra3D add-on, of course. I've attached it below.

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.6...v0.7

    Source code(tar.gz)
    Source code(zip)
    tetra3d.py(17.38 KB)
  • v0.6(Jan 31, 2022)

    Yo, 'sup!

    Here's another Tetra3D release, this time implementing support for multiple materials, an automatic transparency mode, and custom fragment shaders!

    Multiple material support

    Previously, each Model in Tetra3D could only have one singular material. Now, a Model can have multiple materials (one material per triangle). This has been implemented internally by the creation of a new utility, the MeshPart. The MeshPart represents a group of triangles and vertices, tied to a material. When exporting a mesh from your content creation program (e.g. Blender), each material slot or mesh primitive becomes its own MeshPart in Tetra3D.

    This allows you to more easily and simply construct your model, since you can use multiple tilesets or textures for different parts of a single mesh. Note, however, that when rendering, each MeshPart constitutes a separate draw call, which is slower than rendering a single draw call. Try to consolidate MeshParts if possible. On the flip side, separate MeshParts can visually intersect, meaning that there is some inter-object depth testing now, which is nice. For more information on draw calls, see the Debugging wiki page and the Materials wiki page.

    Automatic transparency

    Automatic transparency has been added! Now, by default, a material's transparency mode will be set to the automatic transparency setting, meaning that if the material was exported as opaque from Blender, it will be treated as opaque unless the material or object's alpha transparency levels fall below 1.0 - in this case, the material will be treated as transparent. This way, objects that are wholly opaque or transparent will be rendered properly automatically.

    Custom fragment shaders

    Custom fragment shaders (written in Kage) have been added, and are able to be set directly on Materials. By using a custom fragment shader, you can control how a 3D object is visualized. Note that by using a custom fragment shader, you will override the default texturing and lighting.

    Some tips have been added to the wiki, as well, to help out with using Tetra more easily!

    There's also been a variety of bugfixes added, which can be seen by looking through the changelog in more detail below.

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.5.2...v0.6

    Welp, thank you very much for reading, and I hope you enjoy checking out Tetra3D~!

    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(Jan 10, 2022)

    cap_2022-01-09 20-49-50_00:00:04_01

    Yo! This release features transparency, tweakable on an Model's Material, as well as depth buffer accuracy improvements!


    Transparency has three modes - opaque (which is default), fully transparent, and alpha clip. Note that the differences between these materials primarily influence how they write to the depth buffer, and so if they obscure other objects. Changing their color or texture to be transparent is an important part, but changing the transparency on the material is also important.

    • Opaque materials write to the depth buffer and obscure objects behind them. Simple.
    • Transparent materials don't write to the depth buffer - they do not obscure objects behind them. They are rendered in a second render pass after all non-transparent materials, and in back-to-front sorting order.
    • Alpha clip materials write their textures' alpha values to the depth buffer, but not the transparent portions - this would be useful for certain textures or materials that are wholly transparent or opaque - for example, for a 2D sprite, or a cloak with frayed edges.

    Depth Buffer accuracy improvements


    The depth buffer previously was wildly inaccurate when objects intersected. This is because the texture was written to naively, storing the depth (which is a value from 0 to 1) directly in the color of the texture (so it was grayscale). All three channels (R, G, and B) were used to indicate the same value of depth, which meant that the G and B channels were duplicates of the R channel, and so you only really had 256 possible values of depth).

    To improve this, the depth is now encoded and distributed into the R, G, and B channels. This changes the amount of possible depth values from 256 to 256 * 256 * 256, or 16777216 possible values. This means that intersecting objects should look far more natural. Hurray!

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.5.1...v0.5.2

    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Jan 7, 2022)

    Screenshot from 2022-01-06 22-32-09

    Small bugfix release and a pretty huge optimization for point lights (around 300% faster than the previous iteration!). Sun lights are similarly optimized by simply shading whole triangles rather than individual vertices, though the difference wouldn't be as noticeable because sun lights are far simpler.

    Active and total light counts are also added to the debug text printout.

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.5.0...v0.5.1

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Jan 5, 2022)

    Yo, 'sup~!

    This release mainly has vertex-based lighting, huzzah~

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.4.3...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.3(Dec 21, 2021)

    Cameras are now loaded from GLTF files, and cloned armatures now reassign their skinned meshes properly if they are children of the armature.

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.4.2...v0.4.3

    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Dec 12, 2021)

    Minor fixes and optimizations.

    The major point of this release is optimization of the vertex transformation process. Now we sort triangles directly rather than sorting vertices and then looping through their triangles afterwards. These optimizations represent a ~50% increase in speed, going from ~20FPS in the stress test to ~30 (on my machine).

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.4...v0.4.1

    Source code(tar.gz)
    Source code(zip)
  • v0.4(Dec 11, 2021)

  • v0.3(Nov 28, 2021)

    Yo, 'sup!

    Here's Tetra3D v0.3, this one including object and armature animation support, GLTF support, and orthographic projection~!

    Full Changelog: https://github.com/SolarLune/Tetra3d/compare/v0.1...v0.3

    Source code(tar.gz)
    Source code(zip)
  • v0.1(Nov 15, 2021)

Independent game developer and programmer, owner of SolarLune Games
Simple 2D-grid game made with Ebiten

Simple 2D-grid game made with Ebiten

null 0 Mar 15, 2022
Implementation of a popular graphics benchmark written on Ebiten.

Ebiten Bunny Mark This is an implementation of the popular graphics benchmark written on Ebiten. The initial benchmark was created by Ian Lobb (code)

Artem Sedykh 17 Dec 7, 2022
Helper library to transform TMX tile maps into a simpler format for Ebiten

Ebitmx Ebitmx is a super simple parser to help render TMX maps when using Ebiten for your games. Right now is super limited to XML and CSV data struct

Raúl 20 Nov 16, 2022
Arkanoid game in Go using Ebiten game engine with ECS.

Arkanoid-go Arkanoid game in Go using Ebiten game engine with ECS. You must have Git LFS installed when cloning the repository to download assets. See

null 55 Oct 9, 2022
A simple game that I created with Ebiten game library as a way to teach myself Go. Enjoy!

galactic-asteroid-belt Overview A simple game that I created with Ebiten game library as a way to teach myself Go. Enjoy! Run To run, you will need Go

null 0 Dec 2, 2021
Minimal polymorphic solitaire engine in Go, ebiten

Gilbert Oddstream's Minimal Polymorphic Solitaire 5 There's a live WASM version here. Towards a polymorphic solitaire engine in Go+Ebiten, with help f

Oddstream 17 Dec 14, 2022
Donburi is just another Entity Component System library for Ebiten inspired by legion.

Donburi Donburi is just another Entity Component System library for Ebiten inspired by legion. It aims to be a feature rich and high performance ECS L

yohamta 86 Dec 15, 2022
Spaceshooter - A port to go of the pygame Space Shooter game using the ebiten library

Space Shooter This is a port to go of the pygame Space Shooter (https://github.c

Jesús Espino 5 Sep 29, 2022
Dedicated Game Server Hosting and Scaling for Multiplayer Games on Kubernetes

Agones is a library for hosting, running and scaling dedicated game servers on Kubernetes. Agones, is derived from the Greek word agōn which roughly t

GoogleForGames 5k Jan 6, 2023
Awesome 2D Maze-based games to play with kids in family and with friends on rich console UI. developed into Go.

gomazes Have fun time with kids and family and friends at playing awesome 2D maze-based games while feeling like a programmer on the computer console/

Jerome Amon 1 Dec 23, 2021
Open source of the build infrastructure used by Stadia Games & Entertainment

SG&E Monorepo This repository contains the open sourcing of the infrastructure developed by Stadia Games & Entertainment (SG&E) to run its operations.

Google 59 Dec 18, 2022
This project is designed to be an open source implementation for streaming desktop games using WebRTC

The aim of this project is develop a WebRTC screenshare designed for streaming video games and accepting remote inputs. There will be ansible instruct

Akilan Selvacoumar 20 Oct 6, 2022
Tetris in Go, as a test in using Go for small games

Tetris in Go This is an attempt at implementing Tetris in Go in a way which has

Vlad-Ștefan Harbuz 1 May 8, 2022
Using finite projective planes to make card (maybe video) games

pairwise What it is Using finite projective plane to generate card (maybe video) games. Running Run with go run . Right now uses Go 1.17 but 1.18 just

null 0 Jan 24, 2022
Tcell-game-template - A small template repository for simple tcell based games

tcell game template This is a template repository used for making small terminal

Marcel Schramm 0 Jan 22, 2022
Minecraft Bedrock Edition server software written in Go

Dragonfly Dragonfly is a heavily asynchronous server software for Minecraft Bedrock Edition written in Go.

Dragonfly 462 Jan 3, 2023
Minecraft server made in go, faster and better!

ElytraGo Minecraft server made in go, faster and better! Project is in early stage, but I'm trying continuously update it with new lines of code :)) L

Michał Kowal 34 Dec 9, 2022
Snake game made in Go! 🐍

Snake This is a Terminal based snake game made by tristangoossens. Please star this repository to help my first big project grow! Documentation can be

Tristan Goossens 318 Nov 16, 2022
A simple chess engine for experiment, made in Golang

chess-engine A simple chess engine for experiment, made in Golang Build the engine Run the command make or make build Run the engine Run the command f

Samuel Custodio 1 Dec 26, 2021