Blog / Importing 3D Models

July 23, 2025 • 2 minute read

I've been busy with my job, but I haven't been idle with game dev. With a little bit of doc referencing/stealing, I now present Spinning Maxwell:

Maxwell spinning like the goober he is

Suprisingly difficult considering I tried 2 model formats before .gltf finally did the trick (also took embarrasingly long to figure out how to type the path, but I got it eventually).

maxwell_model := rl.LoadModel("./src/assets/source/maxwell.gltf")

Another strange problem is that when you draw the model, you can specify a transform (effectively its location), but you can't specify rotations along multiple axes.

rl.DrawModel(
    maxwell_model,
    {maxwell_pos.x, maxwell_pos.y, maxwell_pos.z},
    1.0, // Scale
    rl.WHITE // Tint (Just set it white and the texture looks fine)
)

To solve this, you have to specify the rotation not in the draw function, but in the model's rotation. This seems weird to me, as reusing that model for multiple entities would give them all the same rotation—unless you change the model rotation right before drawing each entity. But alas, this is the solution I came to:

t := f32(rl.GetTime())
rot := [3]f32 {
	la.tan(t) * la.sin(t) * 1,
	la.tan(t) * la.sin(t) * 1,
	la.tan(t) * la.sin(t) * 1,
} // Used this rotation because it's very "visual"

translation := rl.MatrixTranslate(
    maxwell_pos.x,
    maxwell_pos.y,
    maxwell_pos.z
)
rotation := rl.MatrixRotateXYZ({rot.x, rot.y, rot.z})
scale := rl.MatrixScale(1, 1, 1)
maxwell_model.transform = translation * rotation * scale

rl.DrawModel(
    maxwell_model,
    {maxwell_pos.x, maxwell_pos.y, maxwell_pos.z},
    1.0, // Scale
    rl.WHITE // Tint (Just set it white and the texture looks fine)
)

Despite some weirdness, Odin+Raylib has been treating me well. Its simple but functional nature has been a joy to program in, and I'm mostly just chugging along, learning new skills (like 3D models), and steadily learning game dev.

As for releasing this code on Github, I'm going to wait until I begin to work on the game, as right now I'm mostly exploring different aspects of Raylib, but don't worry! Any game that I'm currently making will be open source, since this project is for fun, not money.

Anyway, thanks for reading. This blog has been a shorter one, but I hope you enjoyed it, and here is the code I used to rebirth Maxwell into Odin+Raylib:

package game

import la "core:math/linalg"
import rl "vendor:raylib"

main :: proc() {
	rl.SetConfigFlags({.WINDOW_RESIZABLE, .WINDOW_TRANSPARENT})
	rl.InitWindow(1280, 720, "My Odin + Raylib game")
	maxwell_model := rl.LoadModel("./src/assets/source/maxwell.gltf")

	maxwell_pos: [3]f32 = {0.0, 10.0, 0.0}

	for !rl.WindowShouldClose() {
		input: [3]f32

		if rl.IsKeyDown(.UP) {
			input.z += 1
		}
		if rl.IsKeyDown(.DOWN) {
			input.z -= 1
		}
		if rl.IsKeyDown(.LEFT) {
			input.x -= 1
		}
		if rl.IsKeyDown(.RIGHT) {
			input.x += 1
		}
		if rl.IsKeyPressed(.SPACE) {
			rl.ToggleFullscreen()
		}

		maxwell_pos += la.normalize0(input) * 5 * rl.GetFrameTime()

		rl.BeginDrawing()
		rl.ClearBackground({160, 200, 255, 255})
		cam := rl.Camera3D {
			position   = {50.0, 50.0, 50.0},
			target     = {0, 10, 0},
			up         = {0, 1, 0},
			fovy       = 45,
			projection = .PERSPECTIVE,
		}
		rl.BeginMode3D(cam)
		t := f32(rl.GetTime())
		rot := [3]f32 {
			la.tan(t) * la.sin(t) * 1,
			la.tan(t) * la.sin(t) * 1,
			la.tan(t) * la.sin(t) * 1,
		} // Used this rotation because it's very "visual"

		translation := rl.MatrixTranslate(maxwell_pos.x, maxwell_pos.y, maxwell_pos.z)
		rotation := rl.MatrixRotateXYZ({rot.x, rot.y, rot.z})
		scale := rl.MatrixScale(1, 1, 1)
		maxwell_model.transform = translation * rotation * scale

		rl.DrawModel(
			maxwell_model,
			{maxwell_pos.x, maxwell_pos.y, maxwell_pos.z},
			1.0, // Scale
			rl.WHITE, // Tint (Just set it white and the texture looks fine)
		)
		rl.DrawGrid(20, 10.0)
		rl.EndMode3D()
		rl.EndDrawing()
		free_all(context.temp_allocator)
	}

	rl.CloseWindow()
}

Model Link