Vu (Virtual Universe) 3D Engine

repository·main·Indexed 42 days ago

https://github.com/gazed/vu

A 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.

Tokens
15.1K
Snippets
65
Records
76
Agent score
54%

What's inside Vu

  1. Overview of the Vu engine

    main
    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.
  2. Understand the Assets loading mechanism

    main

    The Assets system 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 assets folder 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.go implementation.

  3. Configure Bash for vu development

    main

    Use the following aliases and functions in your ~/.bashrc to 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: Uses ripgrep to find files matching a pattern and opens them in Vim tabs.

    Environment Variables

    • Ensure PATH includes ~/bin and 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/"
  4. Configure Vim for vu development

    main

    To support the project's workflow, use a .vimrc configured for Go and GLSL development. Key features include:

    • Go Integration: Automatically runs gofmt on save and sets go build as the makeprg for the quickfix window.
    • GLSL Support: Enables syntax highlighting for .frag, .vert, and .glsl files.
    • Tab Management: Configured for heavy tab usage with shortcuts for switching and editing.
    • Formatting: Includes a command to clear trailing whitespace and a custom GoFmt command.
    "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
  5. Set up the development environment

    main

    The vu development 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, and bash.
    • Windows Terminal: Use with a bash shell.
    • RenderDoc: For graphics debugging.
    • glslc: For compiling shaders.
    • Note: The project prefers syscall over c-go on Windows.

    macOS (+ iOS)

    • Xcode Developer Tools: Provides git, vim, zsh, and clang.
    • Note: c-go is required (via clang) until purego or an equivalent is implemented.
    • Font: Hack font.
    • Search/Analysis: ripgrep (search), tre (list), and tokei (code count).
    • Asset Creation: Blender (3D), Krita (2D), and Audacity (audio).
  6. Install and build Vu

    main

    To use Vu, you must first clone the repository and ensure you have the necessary build dependencies installed.

    Build Steps

    1. Clone the repository:
      git clone git@github.com:gazed/vu.git
    2. Build the shader generator: Navigate to vu/assets/shaders, run go build, and then execute the resulting shaders binary.
    3. Run examples: Navigate to vu/eg, run go 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 slangc executable from the Vulkan SDK is required for compiling shaders.
    git clone git@github.com:gazed/vu.git
  7. Configure Zsh for vu development

    main

    Use the following configuration in ~/.zshrc to 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_info to provide a Git-aware prompt.
    • History Management: Ignores duplicate commands and specific commands like ls, fg, and exit.
    • Terminal Colors: Enables CLICOLOR and colorized grep output.
    # 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"'
  8. Understand Asset Identifiers (aid) and Asset Types

    main

    In vu, an asset is a uniquely identified resource (like a texture or mesh) used to create game objects. Assets are identified by an aid (Asset ID), which is a uint64 hash generated from a combination of the asset's name and its assetType.

    To determine the type of an asset from its ID, use the kind() method on an aid. This returns the assetType as a uint32.

    Supported assetType values:

    • 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) }
  9. Use SceneID to define render passes

    main

    The SceneID type 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
    )
  10. How physics bodies and entities relate

    main

    In vu, physics is an optional component attached to an Entity.

    1. Creation: You create a Body (a wrapper around physics.Body) using shape constructors like Sphere or Box.
    2. Attachment: You attach that body to an Entity using Entity.AddToSimulation(body). This links the entity's ID to the physics simulation.
    3. 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.
    4. Lifecycle: If you call DisposeBody(), the link between the entity and the physics simulation is severed.
  11. Understand the Entity abstraction

    main

    In vu, an Entity is 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 eID includes 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")
  12. Implement the Loader and Updater interfaces to run the engine

    main

    To run the engine, you must implement the Loader and Updater interfaces and pass them to eng.Run().

    • Loader: Implements Load(eng *Engine) error. Called once at startup to initialize your game state.
    • Updater: Implements FrameUpdate and FixedUpdate.
      • FrameUpdate(eng *Engine, i *Input, delta time.Duration, interpolate float64): Called every render frame. Use delta for frame-dependent logic and interpolate for smooth rendering between physics steps. Process input.Pressed and input.Released here.
      • FixedUpdate(eng *Engine, i *Input, step time.Duration): Called at a fixed interval (60Hz) for physics. Use step for physics calculations. Process input.Down here.
    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
    }