Vu (Virtual Universe) 3D Engine
repository·main·Indexed 42 days ago
https://github.com/gazed/vuA lightweight, modular 3D engine written in Go and optimized for simple games using Vulkan 1.3. It supports Windows, MacOS, and iOS, providing specialized packages for physics, rendering, audio, device input, and asset loading. Key features include PBR materials, raycasting, and a physics system supporting spheres and convex hulls.
What's inside Vu
- Vu (Virtual Universe) is a small 3D engine written in Go, designed for simple games. It utilizes Vulkan 1.3 for graphics and supports Windows, MacOS, and iOS. The engine is modular, consisting of several specialized packages for physics, rendering, audio, and device input.
Understand the Assets loading mechanism
mainThe
Assetssystem provides a default location for application data files. These files are loaded during application startup to create runtime assets.To ensure compatibility, applications should maintain a top-level
assetsfolder that mirrors the engine's internal assets folder structure.For specific details on location conventions and how to override the default asset paths, refer to the
vu/load/load.goimplementation.Configure Bash for vu development
mainUse the following aliases and functions in your
~/.bashrcto streamline Go development, Git operations, and file editing.Key Aliases
- Go Build:
gb(standard),gd(with debug tags),gbwin(Windows GUI mode, no console),gbrel(Windows release mode, stripped). - Git:
gl(formatted log). - Vim:
vi(opens in tabs).
Custom Functions
rgv: Usesripgrepto find files matching a pattern and opens them in Vim tabs.
Environment Variables
- Ensure
PATHincludes~/binand the Windows SDK path if packaging Windows Store apps.
# needed for bash shared by all users. cd $HOME # edit files that match a ripgrep pattern function rgv { vi -p $(rg -l $1) } # open vim files in tabs. alias vi='vim -p' # golang alias gb='go build' alias gd='go build --tags debug' # build windows app without console popping up. alias gbwin='go build -ldflags "-H windowsgui"' alias gbrel='go build -ldflags "-H windowsgui -s -w"' # put the following in .inputrc #set bell-style none # git alias gl='git log --date=short --pretty=format:"%C(yellow)%h%Cblue%>(12)%ad %Cgreen%<(7)%aN %Cred%d%Creset %s"' # git aware prompt export PS1="\[\e[32m\][\w]\e[89m\$(GIT_PS1_SHOWDIRTYSTATE=1 __git_ps1)\e[00m $ " # add local binaries export PATH=$PATH:~/bin # add windows SDK for packaging windows store apps export PATH=$PATH:"/c/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x64/"- Go Build:
Configure Vim for vu development
mainTo support the project's workflow, use a
.vimrcconfigured for Go and GLSL development. Key features include:- Go Integration: Automatically runs
gofmton save and setsgo buildas themakeprgfor the quickfix window. - GLSL Support: Enables syntax highlighting for
.frag,.vert, and.glslfiles. - Tab Management: Configured for heavy tab usage with shortcuts for switching and editing.
- Formatting: Includes a command to clear trailing whitespace and a custom
GoFmtcommand.
Recommended
.vimrcconfiguration"prevent backup files set nobackup nowritebackup " want some nice colors syntax on set background=dark colorscheme solarized " turn on nice indenting with 4 spaces per tab. set tabstop=4 shiftwidth=4 expandtab set laststatus=2 " turn on the status line so line numbers are there too. set statusline=%t\ %y\ format:\ %{&ff};\ [%c,%l] " allow lots of tabs. set tabpagemax=100 " vim-tabs set tabpagemax=50 nnoremap <S-l> :tabnext<CR> noremap <S-h> :tabprevious<CR> noremap tt :tabedit<Space> noremap tn :tabm<Space> " save with Control-X noremap <C-x> :w<CR> " clear trailing spaces with \s noremap \s ::%s/\s\+$//e<CR> " highlight trailing whitespace highlight ExtraWhitespace ctermbg=red guibg=red match ExtraWhitespace /\s\+$/ " glsl syntax highlighting " from https://www.vim.org/scripts/script.php?script_id=1002 augroup glsl_syntax autocmd! autocmd BufNewFile,BufRead *.frag,*.vert,*.glsl setf glsl augroup END " gofmt based on https://stackoverflow.com/questions/72135274/run-gofmt-on-vim-without-plugin function! GoFmt() cexpr system('gofmt -e -w ' . expand('%')) edit! endfunction command! GoFmt call GoFmt() augroup go_autocmd autocmd BufWritePost *.go GoFmt augroup END " go compile autocmd Filetype go set makeprg=go\ build autocmd QuickFixCmdPost [^l]* cwindow noremap <F5> :silent make<CR><C-L><CR> " use existing tabs or switch to a new tab set switchbuf+=usetab,newtab"prevent backup files set nobackup nowritebackup " want some nice colors syntax on set background=dark colorscheme solarized " turn on nice indenting with 4 spaces per tab. set tabstop=4 shiftwidth=4 expandtab set laststatus=2 " turn on the status line so line numbers are there too. set statusline=%t\ %y\ format:\ %{&ff};\ [%c,%l] " allow lots of tabs. set tabpagemax=100 " vim-tabs set tabpagemax=50 noremap <S-l> :tabnext<CR> noremap <S-h> :tabprevious<CR> noremap tt :tabedit<Space> noremap tn :tabm<Space> " save with Control-X noremap <C-x> :w<CR> " clear trailing spaces with \s noremap \s ::%s/\s\+$//e<CR> " highlight trailing whitespace highlight ExtraWhitespace ctermbg=red guibg=red match ExtraWhitespace /\s\+$/ " glsl syntax highlighting " from https://www.vim.org/scripts/script.php?script_id=1002 augroup glsl_syntax autocmd! autocmd BufNewFile,BufRead *.frag,*.vert,*.glsl setf glsl augroup END " gofmt based on https://stackoverflow.com/questions/72135274/run-gofmt-on-vim-without-plugin function! GoFmt() cexpr system('gofmt -e -w ' . expand('%')) edit! endfunction command! GoFmt call GoFmt() augroup go_autocmd autocmd BufWritePost *.go GoFmt augroup END " go compile autocmd Filetype go set makeprg=go\ build autocmd QuickFixCmdPost [^l]* cwindow noremap <F5> :silent make<CR><C-L><CR> " use existing tabs or switch to a new tab set switchbuf+=usetab,newtab- Go Integration: Automatically runs
Set up the development environment
mainThe
vudevelopment environment varies by platform. Ensure you have the following tools installed to maintain compatibility with the project's build and runtime requirements.Essential Tools (All Platforms)
- Go: The primary programming language.
- Vulkan SDK: For graphics.
- OpenAL + OpenAL-Soft: For audio.
Platform Specifics
Windows
- Git for Windows: Provides
git,vim, andbash. - Windows Terminal: Use with a bash shell.
- RenderDoc: For graphics debugging.
- glslc: For compiling shaders.
- Note: The project prefers
syscalloverc-goon Windows.
macOS (+ iOS)
- Xcode Developer Tools: Provides
git,vim,zsh, andclang. - Note:
c-gois required (viaclang) untilpuregoor an equivalent is implemented.
Recommended Utilities
- Font:
Hackfont. - Search/Analysis:
ripgrep(search),tre(list), andtokei(code count). - Asset Creation:
Blender(3D),Krita(2D), andAudacity(audio).
Install and build Vu
mainTo use Vu, you must first clone the repository and ensure you have the necessary build dependencies installed.
Build Steps
- Clone the repository:
git clone git@github.com:gazed/vu.git - Build the shader generator:
Navigate to
vu/assets/shaders, rungo build, and then execute the resultingshadersbinary. - Run examples:
Navigate to
vu/eg, rungo build, and execute the resulting binaries.
Build Dependencies
- Go: Version 1.23 or later.
- Vulkan: Version 1.3 or later. You must include the Vulkan validation layer from the Vulkan SDK. (MacOS and iOS users require MoltenVK).
- OpenAL: The latest 64-bit version (
OpenAL32.dll+soft_oal.dll) from openal-soft.org. - slangc: The
slangcexecutable from the Vulkan SDK is required for compiling shaders.
git clone git@github.com:gazed/vu.git- Clone the repository:
Configure Zsh for vu development
mainUse the following configuration in
~/.zshrcto enable colorized terminals, Git-friendly prompts, and Go development aliases.Key Aliases
- Go Build:
gb(standard),gd(with debug tags). - Git:
gl(formatted log). - Vim:
vi(opens in tabs).
Features
- Git Integration: Uses
vcs_infoto provide a Git-aware prompt. - History Management: Ignores duplicate commands and specific commands like
ls,fg, andexit. - Terminal Colors: Enables
CLICOLORand colorizedgrepoutput.
# colour the terminal export CLICOLOR=1 export GREP_OPTIONS='--color=auto' # use tabbed vim export EDITOR=vim alias vi='vim -v -p' # add local commands path+=('/usr/local/go/bin') path+=('/Users/rust/bin') export PATH # customize the prompt : https://jonasjacek.github.io/colors/ # 0-15 are the solorized colors in terminal preferences. export PROMPT='%(?.%F{3}√.%F{1}?%?)%f %F{3}%~%f %# ' # make it git friendly. autoload -Uz vcs_info precmd_vcs_info() { vcs_info } precmd_functions+=( precmd_vcs_info ) setopt prompt_subst RPROMPT=\$vcs_info_msg_0_ zstyle ':vcs_info:git:*' formats '%F{3}(%b)%f' # Ignore duplicate commands in history export HISTORY_IGNORE='(ls|fg|bg|exit)' setopt hist_ignore_all_dups setopt hist_find_no_dups setopt hist_ignore_space # Dont add ignored commands to history. zshaddhistory() { emulate -L zsh ## uncomment if HISTORY_IGNORE ## should use EXTENDED_GLOB syntax setopt extendedglob [[ $1 != ~{$HISTORY_IGNORE} ]] } # golang alias gb='go build' alias gd='go build --tags debug' # git alias gl='git log --date=short --pretty=format:"%C(yellow)%h%Cblue%>(12)%ad %Cgreen%<(7)%aN %Cred%d%Creset %s"'- Go Build:
Understand Asset Identifiers (aid) and Asset Types
mainIn
vu, anassetis a uniquely identified resource (like a texture or mesh) used to create game objects. Assets are identified by anaid(Asset ID), which is auint64hash generated from a combination of the asset's name and itsassetType.To determine the type of an asset from its ID, use the
kind()method on anaid. This returns theassetTypeas auint32.Supported
assetTypevalues:fnt(font)shd(shader configuration)mat(material)msh(mesh)tex(texture)aud(audio)anm(animation rig and skin)
// Example of how asset types are categorized type assetType uint32 const ( fnt assetType = iota // font shd // shader configuration mat // material msh // mesh tex // texture aud // audio anm // animation rig and skin assetTypes // end of asset types ) // kind returns the type of asset data for this aid. func (a aid) kind() uint32 { return uint32(a & math.MaxUint8) }Use SceneID to define render passes
mainThe
SceneIDtype is used to group models with a specific camera and light setup, effectively defining a render pass. The two available constants are:Scene3D: Used for normal 3D world rendering. It uses a perspective projection by default.Scene2D: Used for UI overlay rendering. It uses an orthographic projection by default.
const ( Scene3D SceneID = iota // Normal world rendering Scene2D // UI overlay rendering )How physics bodies and entities relate
mainIn
vu, physics is an optional component attached to anEntity.- Creation: You create a
Body(a wrapper aroundphysics.Body) using shape constructors likeSphereorBox. - Attachment: You attach that body to an
EntityusingEntity.AddToSimulation(body). This links the entity's ID to the physics simulation. - Simulation Loop: The engine's simulation manager updates the physics state (position, rotation) and then synchronizes those changes back to the entity's POV transform. This allows the physics engine to drive the visual movement of the entity.
- Lifecycle: If you call
DisposeBody(), the link between the entity and the physics simulation is severed.
- Creation: You create a
Understand the Entity abstraction
mainIn
vu, anEntityis a handle to a resource tracked by the application. Common examples include:- Scenes: Used to group models with cameras.
- Models: Used to group assets with shaders.
Entities are designed to be used as array indices for component data, meaning their underlying ID remains stable throughout their lifetime, but the
eIDincludes an 'edition' bit to detect if an ID has been reused after disposal.// Typical entity creation pattern scene := eng.AddScene(vu.Scene3D) model := scene.AddModel("shd:pbr0", "msh:box0", "mat:box0")Implement the Loader and Updater interfaces to run the engine
mainTo run the engine, you must implement the
LoaderandUpdaterinterfaces and pass them toeng.Run().Loader: ImplementsLoad(eng *Engine) error. Called once at startup to initialize your game state.Updater: ImplementsFrameUpdateandFixedUpdate.FrameUpdate(eng *Engine, i *Input, delta time.Duration, interpolate float64): Called every render frame. Usedeltafor frame-dependent logic andinterpolatefor smooth rendering between physics steps. Processinput.Pressedandinput.Releasedhere.FixedUpdate(eng *Engine, i *Input, step time.Duration): Called at a fixed interval (60Hz) for physics. Usestepfor physics calculations. Processinput.Downhere.
type MyGame struct{} func (g *MyGame) Load(eng *vu.Engine) error { // Initialize scenes, entities, etc. return nil } func (g *MyGame) FrameUpdate(eng *vu.Engine, i *vu.Input, delta time.Duration, interpolate float64) { // Handle frame-based logic and input.Pressed/Released } func (g *MyGame) FixedUpdate(eng *vu.Engine, i *vu.Input, step time.Duration) { // Handle physics and input.Down }