├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── enums.go ├── examples ├── basiccube │ └── main.go ├── connectiontest │ └── main.go ├── screenshots │ ├── example-basiccube.jpg │ └── example-voxels.jpg └── voxels │ ├── assets │ ├── textures │ │ ├── LICENSE │ │ ├── README.md │ │ ├── default_dirt.png │ │ ├── default_grass.png │ │ └── default_stone_block.png │ ├── voxel.fs │ └── voxel.vs │ ├── chunk │ ├── chunk.go │ └── manager.go │ └── main.go ├── ivrchaperone.go ├── ivrcompositor.go ├── ivrrendermodels.go ├── ivrsystem.go ├── openvr.go ├── util └── fizzlevr │ ├── devicerenderables.go │ └── rendering.go ├── utils_unix.go ├── utils_windows.go └── vendored ├── openvr ├── LICENSE ├── README.md ├── bin │ ├── linux32 │ │ ├── libopenvr_api.so │ │ └── libopenvr_api.so.dbg │ ├── linux64 │ │ ├── libopenvr_api.so │ │ └── libopenvr_api.so.dbg │ ├── osx32 │ │ ├── libopenvr_api.dylib │ │ └── libopenvr_api.dylib.dSYM │ │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ └── DWARF │ │ │ └── libopenvr_api.dylib │ ├── osx64 │ │ └── OpenVR.framework │ │ │ ├── Headers │ │ │ ├── OpenVR │ │ │ ├── Resources │ │ │ └── Versions │ │ │ ├── A │ │ │ ├── Headers │ │ │ │ ├── openvr.h │ │ │ │ ├── openvr_api.cs │ │ │ │ ├── openvr_api.json │ │ │ │ ├── openvr_capi.h │ │ │ │ └── openvr_driver.h │ │ │ ├── OpenVR │ │ │ └── Resources │ │ │ │ └── Info.plist │ │ │ └── Current │ ├── win32 │ │ ├── openvr_api.dll │ │ └── openvr_api.pdb │ └── win64 │ │ ├── openvr_api.dll │ │ └── openvr_api.pdb ├── headers │ ├── openvr.h │ ├── openvr_api.cs │ ├── openvr_api.json │ ├── openvr_capi.h │ └── openvr_driver.h └── lib │ ├── linux32 │ └── libopenvr_api.so │ ├── linux64 │ └── libopenvr_api.so │ ├── osx32 │ └── libopenvr_api.dylib │ ├── win32 │ └── openvr_api.lib │ └── win64 │ └── openvr_api.lib └── openvr_version.md /.gitattributes: -------------------------------------------------------------------------------- 1 | vendored/* linguist-vendored -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | examples/basiccube/debug 2 | examples/basiccube/basiccube 3 | examples/basiccube/basiccube.exe 4 | examples/basiccube/openvr_api.dll 5 | 6 | examples/connectiontest/debug 7 | examples/connectiontest/connectiontest 8 | examples/connectiontest/connectiontest.exe 9 | examples/connectiontest/openvr_api.dll 10 | 11 | examples/voxels/debug 12 | examples/voxels/voxels 13 | examples/voxels/voxels.exe 14 | examples/voxels/openvr_api.dll 15 | 16 | *.exe 17 | *.swp 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Version v0.4.2 2 | ============== 3 | 4 | * MISC: Changed the `vendor` directory to `vendored` to support including this library with Go's 5 | `dep` tool, which currently will drop that vendor directory when being pulled into another project. 6 | 7 | Version v0.4.1 8 | ============== 9 | 10 | * BUG: Build fixes for Linux systems. 11 | 12 | Version v0.4.0 13 | ============== 14 | 15 | * NEW: ICompositor support for GetFrameTimeRemaining() and GetFrameTiming(). 16 | 17 | Version v0.3.0 18 | ============== 19 | 20 | * APIBREAK: Changes were made to support OpenVR 1.0.5 upstream. Updated binaries. 21 | Removed linux32 from lib & bin. Reviewed enumerations and brought some sets into 22 | conformity of the naming convention. 23 | 24 | * MISC: Switched to using github.com/tbogdala/fizzle's built in shaders for samples. 25 | 26 | * MISC: Switched to Mathgl for vectors instead of github.com/tbogdala/glider's. 27 | 28 | * MISC: Switched to using fizzle's Material object in examples. 29 | 30 | Version v0.2.0 31 | ============== 32 | 33 | * APIBREAK: Library now uses github.com/go-gl/mathgl/mgl32 for Vector and 34 | Matrix types where there used to be local definitions. 35 | 36 | * NEW: IChaperone support. 37 | 38 | * NEW: More IRenderModel functions supported. 39 | 40 | * NEW: Voxel engine sample in `examples/voxels`! You start at the edge of a play 41 | area and can teleport short distances by pulling the trigger on a controller 42 | and pointing to land. 43 | 44 | This example uses several additional libraries from github.com/tbogdala including 45 | glider, cubez, and fizzle. 46 | 47 | The shaders used in this sample are based on an older 48 | ADS-type shader in github.com/tbogdala/fizzle ... and eventually should be 49 | updated. 50 | 51 | * NEW: refactored code from `examples/basiccube` to `openvr-go/util/fizzlevr` which 52 | makes it easier to start new applications using the github.com/tbogdala/fizzle 53 | graphics library. 54 | 55 | * BUG: added binaries in `vendor/openvr/bin` for win32 and win64 that were missing. 56 | 57 | * MISC: Added IChaperone play area size printing to `examples/connectiontest`. 58 | 59 | * MISC: Better screenshot. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Timothy Bogdala 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Openvr-go v0.4.2 2 | ================ 3 | 4 | Openvr-go is an [Go][golang] programming language wrapper for the [OpenVR SDK][openvr-git] 5 | published by Valve for VR hardware. 6 | 7 | This package is currently synced up to v1.0.10 of OpenVR. 8 | 9 | ![voxels_ss][voxels_ss] 10 | 11 | UNDER CONSTRUCTION 12 | ================== 13 | 14 | At present, it is very much in an alpha stage with new development happening to 15 | complete the API exposed by the OpenVR SDK. 16 | 17 | Requirements 18 | ------------ 19 | 20 | * [Mathgl][mgl] - for 3d math 21 | 22 | The wrapper library itself doesn't have any dependencies besides [Mathgl][mgl]. 23 | The `connectiontest` sample in the `examples` folder also doesn't have any 24 | additional dependencies. 25 | 26 | The other samples are graphical and use the following libraries, though they are 27 | not imported by the core openvr-go module itself: 28 | 29 | * [GLFW][glfw-go] (v3.1) - creating windows and providing the OpenGL context 30 | * [Fizzle][fizzle] (v0.2.0) - provides the graphics engine 31 | * [Go GL][go-gl] - provides the backend implementation of OpenGL for [Fizzle][fizzle]. 32 | 33 | Note: At present, some examples might required the development branch of [Fizzle][fizzle]. 34 | You'll have to manually git checkout the `development` branch to compile these. 35 | 36 | Installation 37 | ------------ 38 | 39 | The dependency Go libraries for graphical examples can be installed with the following commands. 40 | 41 | ```bash 42 | go get github.com/go-gl/glfw/v3.1/glfw 43 | go get github.com/go-gl/mathgl/mgl32 44 | go get github.com/go-gl/gl/v3.3-core/gl 45 | go get github.com/tbogdala/fizzle 46 | ``` 47 | This does assume that you have the native GLFW 3.1 library installed already 48 | accessible to Go tools. 49 | 50 | Additionally, the appropriate `openvr_api.dll` or `libopenvr_api.so` file from 51 | `vendored/openvr/bin/` will either need to be copied into each example directory 52 | being built or it will need to be accessible system wide. 53 | 54 | Each sample can be built by going to that directory in a shell and executing 55 | a `go build` command. For example: 56 | 57 | ```bash 58 | cd $GOPATH/src/github.com/tbogdala/openvr-go/examples/basiccube 59 | go build 60 | cp ../../vendored/openvr/bin/win64/openvr_api.dll . 61 | ./basiccube.exe 62 | ``` 63 | 64 | Current Features 65 | ---------------- 66 | 67 | Partial implementation of the following interfaces: 68 | 69 | * IVRSystem 70 | * IVRCompositor 71 | * IVRRenderModels 72 | 73 | 74 | Implementation Notes 75 | -------------------- 76 | 77 | Some minor patches have been applied to the vendored openvr library version to 78 | better support linux. 79 | 80 | 81 | LICENSE 82 | ======= 83 | 84 | Original source code in openvr-go is released under the BSD license. See the 85 | [LICENSE][license-link] file for more details. 86 | 87 | Projects in the `vendor` folder may have their own LICENSE file. 88 | 89 | The MTCORE32px texture pack files in `examples/voxels/assets/textures` are licensed 90 | CC BY-SA 3.0 by celeron55, Perttu Ahola. 91 | https://github.com/Napiophelios/MTCORE32px 92 | 93 | [golang]: https://golang.org/ 94 | [fizzle]: https://github.com/tbogdala/fizzle 95 | [glfw-go]: https://github.com/go-gl/glfw 96 | [mgl]: https://github.com/go-gl/mathgl 97 | [go-gl]: https://github.com/go-gl/glow 98 | [license-link]: https://raw.githubusercontent.com/tbogdala/openvr-go/master/LICENSE 99 | [openvr-git]: https://github.com/ValveSoftware/openvr 100 | [basiccube_ss]: https://raw.githubusercontent.com/tbogdala/openvr-go/master/examples/screenshots/example-basiccube.jpg 101 | [voxels_ss]: https://github.com/tbogdala/openvr-go/blob/development/examples/screenshots/example-voxels.jpg?raw=true 102 | -------------------------------------------------------------------------------- /examples/basiccube/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "runtime" 9 | 10 | vr "github.com/tbogdala/openvr-go" 11 | fizzlevr "github.com/tbogdala/openvr-go/util/fizzlevr" 12 | 13 | glfw "github.com/go-gl/glfw/v3.1/glfw" 14 | mgl "github.com/go-gl/mathgl/mgl32" 15 | 16 | fizzle "github.com/tbogdala/fizzle" 17 | graphics "github.com/tbogdala/fizzle/graphicsprovider" 18 | opengl "github.com/tbogdala/fizzle/graphicsprovider/opengl" 19 | input "github.com/tbogdala/fizzle/input/glfwinput" 20 | forward "github.com/tbogdala/fizzle/renderer/forward" 21 | ) 22 | 23 | const ( 24 | nearView = 0.1 25 | farView = 30.0 26 | ) 27 | 28 | var ( 29 | windowWidth = int(1280) 30 | windowHeight = int(720) 31 | 32 | gfx graphics.GraphicsProvider 33 | mainWindow *glfw.Window 34 | kbModel *input.KeyboardModel 35 | renderer *forward.ForwardRenderer 36 | basicShader *fizzle.RenderShader 37 | renderModelShader *fizzle.RenderShader 38 | lensShader *fizzle.RenderShader 39 | cube *fizzle.Renderable 40 | 41 | // interfaces for openvr 42 | vrSystem *vr.System 43 | vrCompositor *vr.Compositor 44 | deviceRenderables *fizzlevr.DeviceRenderables 45 | distortionLens *fizzlevr.DistortionLens 46 | 47 | // render surfaces and transforms 48 | renderWidth uint32 49 | renderHeight uint32 50 | eyeTransforms *vr.EyeTransforms 51 | eyeFramebufferLeft *fizzlevr.EyeFramebuffer 52 | eyeFramebufferRight *fizzlevr.EyeFramebuffer 53 | hmdPose mgl.Mat4 54 | hmdLoc mgl.Vec3 55 | ) 56 | 57 | func init() { 58 | runtime.LockOSThread() 59 | } 60 | 61 | func main() { 62 | //////////////////////////////////////////////////////////////////////////// 63 | // start off by initializing the GL and GLFW libraries and creating a window. 64 | mainWindow, gfx = initGraphics("OpenVR Hello Cube", windowWidth, windowHeight) 65 | 66 | // set the callback functions for key input 67 | kbModel = input.NewKeyboardModel(mainWindow) 68 | kbModel.BindTrigger(glfw.KeyEscape, setShouldClose) 69 | kbModel.SetupCallbacks() 70 | 71 | //////////////////////////////////////////////////////////////////////////// 72 | // attempt to initialize the system 73 | var err error 74 | vrSystem, err = vr.Init() 75 | if err != nil || vrSystem == nil { 76 | panic("vr.Init() returned an error: " + err.Error()) 77 | } 78 | 79 | // print out some information about the headset as a good smoke test 80 | driver, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropTrackingSystemNameString) 81 | if errInt != vr.TrackedPropSuccess { 82 | panic("error getting driver name.") 83 | } 84 | displaySerial, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropSerialNumberString) 85 | if errInt != vr.TrackedPropSuccess { 86 | panic("error getting display name.") 87 | } 88 | fmt.Printf("Connected to %s %s\n", driver, displaySerial) 89 | 90 | //////////////////////////////////////////////////////////////////////////// 91 | // setup VR specifics and the initial scene 92 | 93 | // get the size of the render targets to make 94 | renderWidth, renderHeight = vrSystem.GetRecommendedRenderTargetSize() 95 | fmt.Printf("rec size: %d, %d\n", renderWidth, renderHeight) 96 | 97 | // load up our shaders 98 | err = createShaders() 99 | if err != nil { 100 | panic(err.Error()) 101 | } 102 | 103 | // create some objects and lights 104 | createScene(renderWidth, renderHeight) 105 | 106 | // get the eye transforms necessary for the VR HMD 107 | eyeTransforms = vrSystem.GetEyeTransforms(nearView, farView) 108 | 109 | // setup the framebuffers for the eyes 110 | eyeFramebufferLeft, eyeFramebufferRight = fizzlevr.CreateStereoRenderTargets(renderWidth, renderHeight) 111 | 112 | // create the lens distortion object which will be used to render the 113 | // eye framebuffers to the GLFW window. 114 | distortionLens = fizzlevr.CreateDistortionLens(vrSystem, lensShader, eyeFramebufferLeft, eyeFramebufferRight) 115 | 116 | // debug: do a little extra work right here to print out some debugging info. 117 | // this isn't required for any functionality but exists as a test of some API calls. 118 | // we even shoot 1 over on purpose in the loops to make sure the API call doesn't crash. 119 | vrRenderModels, err := vr.GetRenderModels() 120 | if err == nil { 121 | renderModelCount := vrRenderModels.GetRenderModelCount() 122 | fmt.Printf("Render Model count: %d\n", renderModelCount) 123 | for mi := uint32(0); mi <= renderModelCount; mi++ { 124 | modelName := vrRenderModels.GetRenderModelName(mi) 125 | fmt.Printf("\trender model %d: %s\n", mi, modelName) 126 | 127 | componentCount := vrRenderModels.GetComponentCount(modelName) 128 | if componentCount <= 0 { 129 | continue 130 | } 131 | fmt.Printf("\t\tcomponent count = %d\n", componentCount) 132 | for ci := uint32(0); ci <= componentCount; ci++ { 133 | componentName := vrRenderModels.GetComponentName(modelName, ci) 134 | fmt.Printf("\t\t%d = %s ", ci, componentName) 135 | if len(componentName) > 0 { 136 | componentRenderModelName := vrRenderModels.GetComponentRenderModelName(modelName, componentName) 137 | fmt.Printf("; render model = %s\n", componentRenderModelName) 138 | // try to load this thing 139 | componentModel, err2 := vrRenderModels.RenderModelLoad(componentRenderModelName) 140 | if componentModel != nil && err2 == nil { 141 | fmt.Printf("\t\tloaded model; %d faces\n", componentModel.TriangleCount) 142 | } 143 | } else { 144 | fmt.Printf("\n") 145 | } 146 | } 147 | } 148 | } 149 | 150 | // cache renderables for the connected devices 151 | deviceRenderables, err = fizzlevr.CreateDeviceRenderables(vrSystem, renderModelShader) 152 | if err != nil { 153 | fmt.Printf("Failed to load renderables for the connected devices. " + err.Error() + "\n") 154 | } 155 | 156 | // pull an interface to the compositor 157 | vrCompositor, err = vr.GetCompositor() 158 | if err != nil { 159 | panic("Failed to get the compositor interface: " + err.Error()) 160 | } 161 | 162 | // debug: print out some information about connected controllers 163 | for i := vr.TrackedDeviceIndexHmd + 1; i < vr.MaxTrackedDeviceCount; i++ { 164 | deviceClass := vrSystem.GetTrackedDeviceClass(int(i)) 165 | if deviceClass != vr.TrackedDeviceClassController { 166 | continue 167 | } 168 | axis0Type, propErr := vrSystem.GetInt32TrackedDeviceProperty(int(i), vr.PropAxis0TypeInt32) 169 | if propErr == vr.TrackedPropSuccess { 170 | axis0TypeName := vrSystem.GetControllerAxisTypeNameFromEnum(int(axis0Type)) 171 | fmt.Printf("Controller device %d:\n\taxis0: type=%d(%s)\n", i, axis0Type, axis0TypeName) 172 | } 173 | 174 | axis1Type, propErr := vrSystem.GetInt32TrackedDeviceProperty(int(i), vr.PropAxis1TypeInt32) 175 | if propErr == vr.TrackedPropSuccess { 176 | axis1TypeName := vrSystem.GetControllerAxisTypeNameFromEnum(int(axis1Type)) 177 | fmt.Printf("Controller device %d:\n\taxis1: type=%d(%s)\n", i, axis1Type, axis1TypeName) 178 | } 179 | 180 | axis2Type, propErr := vrSystem.GetInt32TrackedDeviceProperty(int(i), vr.PropAxis2TypeInt32) 181 | if propErr == vr.TrackedPropSuccess { 182 | axis2TypeName := vrSystem.GetControllerAxisTypeNameFromEnum(int(axis2Type)) 183 | fmt.Printf("Controller device %d:\n\taxis2: type=%d(%s)\n", i, axis2Type, axis2TypeName) 184 | } 185 | 186 | axis3Type, propErr := vrSystem.GetInt32TrackedDeviceProperty(int(i), vr.PropAxis3TypeInt32) 187 | if propErr == vr.TrackedPropSuccess { 188 | axis3TypeName := vrSystem.GetControllerAxisTypeNameFromEnum(int(axis3Type)) 189 | fmt.Printf("Controller device %d:\n\taxis3: type=%d(%s)\n", i, axis3Type, axis3TypeName) 190 | } 191 | 192 | axis4Type, propErr := vrSystem.GetInt32TrackedDeviceProperty(int(i), vr.PropAxis4TypeInt32) 193 | if propErr == vr.TrackedPropSuccess { 194 | axis4TypeName := vrSystem.GetControllerAxisTypeNameFromEnum(int(axis4Type)) 195 | fmt.Printf("Controller device %d:\n\taxis4: type=%d(%s)\n", i, axis4Type, axis4TypeName) 196 | } 197 | } 198 | 199 | //////////////////////////////////////////////////////////////////////////// 200 | // the main application loop 201 | for !mainWindow.ShouldClose() { 202 | handleInput() 203 | renderFrame() 204 | } 205 | 206 | vr.Shutdown() 207 | } 208 | 209 | // initGraphics creates an OpenGL window and initializes the required graphics libraries. 210 | // It will either succeed or panic. 211 | func initGraphics(title string, w int, h int) (*glfw.Window, graphics.GraphicsProvider) { 212 | // GLFW must be initialized before it's called 213 | err := glfw.Init() 214 | if err != nil { 215 | panic("Can't init glfw! " + err.Error()) 216 | } 217 | 218 | // request a OpenGL 3.3 core context 219 | glfw.WindowHint(glfw.Samples, 4) 220 | glfw.WindowHint(glfw.ContextVersionMajor, 3) 221 | glfw.WindowHint(glfw.ContextVersionMinor, 3) 222 | glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) 223 | glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) 224 | 225 | // do the actual window creation 226 | mainWindow, err = glfw.CreateWindow(w, h, title, nil, nil) 227 | if err != nil { 228 | panic("Failed to create the main window! " + err.Error()) 229 | } 230 | mainWindow.SetSizeCallback(onWindowResize) 231 | mainWindow.MakeContextCurrent() 232 | 233 | // disable v-sync for max draw rate 234 | glfw.SwapInterval(0) 235 | 236 | // initialize OpenGL 237 | gfx, err := opengl.InitOpenGL() 238 | if err != nil { 239 | panic("Failed to initialize OpenGL! " + err.Error()) 240 | } 241 | fizzle.SetGraphics(gfx) 242 | 243 | return mainWindow, gfx 244 | } 245 | 246 | // setShouldClose should be called to close the window and kill the app. 247 | func setShouldClose() { 248 | mainWindow.SetShouldClose(true) 249 | } 250 | 251 | // onWindowResize should be called when the main window gets resized. 252 | func onWindowResize(w *glfw.Window, width int, height int) { 253 | windowWidth = width 254 | windowHeight = height 255 | } 256 | 257 | // createShaders will load the shaders necessary for the sample to run. 258 | func createShaders() error { 259 | // load the diffuse shader for the cube 260 | var err error 261 | basicShader, err = forward.CreateBasicShader() 262 | if err != nil { 263 | return fmt.Errorf("Failed to compile and link the diffuse shader program!\n%v", err) 264 | } 265 | 266 | // load the shader used to draw the connected devices 267 | renderModelShader, err = fizzle.LoadShaderProgram(vr.ShaderRenderModelV, vr.ShaderRenderModelF, nil) 268 | if err != nil { 269 | return fmt.Errorf("Failed to compile and link the render model shader program!\n%v", err) 270 | } 271 | 272 | // load the shader used to render the framebuffers to a window for viewing 273 | lensShader, err = fizzle.LoadShaderProgram(vr.ShaderLensDistortionV, vr.ShaderLensDistortionF, nil) 274 | if err != nil { 275 | return fmt.Errorf("Failed to compile and link the lens distortion shader program!\n%v", err) 276 | } 277 | 278 | return nil 279 | } 280 | 281 | // createScene creates a simple test scene to render. 282 | func createScene(renderWidth, renderHeight uint32) { 283 | // create a new renderer 284 | renderer = forward.NewForwardRenderer(gfx) 285 | renderer.ChangeResolution(int32(renderWidth), int32(renderHeight)) 286 | 287 | // put a light in there 288 | light := renderer.NewDirectionalLight(mgl.Vec3{1.0, -0.5, -1.0}) 289 | light.DiffuseIntensity = 0.70 290 | light.SpecularIntensity = 0.10 291 | light.AmbientIntensity = 0.3 292 | renderer.ActiveLights[0] = light 293 | 294 | // create a 1 ft. cube to render 295 | const cubeSize = 0.30 * 0.5 296 | cube = fizzle.CreateCube(-cubeSize, -cubeSize, -cubeSize, cubeSize, cubeSize, cubeSize) 297 | cube.Material = fizzle.NewMaterial() 298 | cube.Material.Shader = basicShader 299 | cube.Material.DiffuseColor = mgl.Vec4{0.9, 0.05, 0.05, 1.0} 300 | cube.Material.SpecularColor = mgl.Vec4{1.0, 1.0, 1.0, 1.0} 301 | cube.Material.Shininess = 10.0 302 | } 303 | 304 | func handleInput() { 305 | // advise GLFW to poll for input. without this the window appears to hang. 306 | glfw.PollEvents() 307 | 308 | // handle any keyboard input 309 | kbModel.CheckKeyPresses() 310 | 311 | var event vr.VREvent 312 | for vrSystem.PollNextEvent(&event) { 313 | proccessVREvent(&event) 314 | } 315 | 316 | // TODO: update controller states 317 | } 318 | 319 | func proccessVREvent(event *vr.VREvent) { 320 | switch event.EventType { 321 | case vr.VREventTrackedDeviceActivated: 322 | // TODO: setup render model 323 | fmt.Printf("Device %d attached.\n", event.TrackedDeviceIndex) 324 | case vr.VREventTrackedDeviceDeactivated: 325 | fmt.Printf("Device %d detached.\n", event.TrackedDeviceIndex) 326 | case vr.VREventTrackedDeviceUpdated: 327 | fmt.Printf("Device %d updated.\n", event.TrackedDeviceIndex) 328 | } 329 | } 330 | 331 | func renderFrame() { 332 | // draw the framebuffers 333 | renderStereoTargets() 334 | 335 | // draw the framebuffers to the window 336 | distortionLens.Render(int32(windowWidth), int32(windowHeight)) 337 | 338 | // send the framebuffer textures out to the compositor for rendering to the HMD 339 | vrCompositor.Submit(vr.EyeLeft, uint32(eyeFramebufferLeft.ResolveTexture)) 340 | vrCompositor.Submit(vr.EyeRight, uint32(eyeFramebufferRight.ResolveTexture)) 341 | 342 | // draw the screen 343 | mainWindow.SwapBuffers() 344 | 345 | // update the HMD pose, which causes a wait to vsync the HMD 346 | updateHMDPose() 347 | } 348 | 349 | type FixedCamera struct { 350 | View mgl.Mat4 351 | Position mgl.Vec3 352 | } 353 | 354 | func (c FixedCamera) GetViewMatrix() mgl.Mat4 { 355 | return c.View 356 | } 357 | func (c FixedCamera) GetPosition() mgl.Vec3 { 358 | return c.Position 359 | } 360 | 361 | // renderScene gets called for each eye and is responsible for 362 | // rendering the entire scene. 363 | func renderScene(eye int) { 364 | gfx.Clear(graphics.COLOR_BUFFER_BIT | graphics.DEPTH_BUFFER_BIT) 365 | gfx.Enable(graphics.DEPTH_TEST) 366 | 367 | var perspective, view mgl.Mat4 368 | var camera FixedCamera 369 | if eye == vr.EyeLeft { 370 | view = eyeTransforms.PositionLeft.Mul4(hmdPose) 371 | perspective = eyeTransforms.ProjectionLeft 372 | camera.View = view 373 | camera.Position = hmdLoc 374 | } else { 375 | view = eyeTransforms.PositionRight.Mul4(hmdPose) 376 | perspective = eyeTransforms.ProjectionRight 377 | camera.View = view 378 | camera.Position = hmdLoc 379 | } 380 | 381 | // draw our cube as the main thing 382 | renderer.DrawRenderable(cube, nil, perspective, view, camera) 383 | 384 | // now draw any devices that get rendered into the scene 385 | deviceRenderables.RenderDevices(vrCompositor, perspective, view, camera) 386 | } 387 | 388 | // renderStereoTargets renders each of the left and right eye framebuffers 389 | // calling renderScene to do the rendering for the scene. 390 | func renderStereoTargets() { 391 | gfx.Enable(graphics.CULL_FACE) 392 | gfx.ClearColor(0.15, 0.15, 0.18, 1.0) // nice background color, but not black 393 | 394 | // left eye 395 | gfx.Enable(graphics.MULTISAMPLE) 396 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFramebufferLeft.RenderFramebuffer) 397 | gfx.Viewport(0, 0, int32(renderWidth), int32(renderHeight)) 398 | renderScene(vr.EyeLeft) 399 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, 0) 400 | gfx.Disable(graphics.MULTISAMPLE) 401 | 402 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, eyeFramebufferLeft.RenderFramebuffer) 403 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, eyeFramebufferLeft.ResolveFramebuffer) 404 | gfx.BlitFramebuffer(0, 0, int32(renderWidth), int32(renderHeight), 0, 0, int32(renderWidth), int32(renderHeight), graphics.COLOR_BUFFER_BIT, graphics.LINEAR) 405 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, 0) 406 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, 0) 407 | 408 | // right eye 409 | gfx.Enable(graphics.MULTISAMPLE) 410 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFramebufferRight.RenderFramebuffer) 411 | gfx.Viewport(0, 0, int32(renderWidth), int32(renderHeight)) 412 | renderScene(vr.EyeRight) 413 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, 0) 414 | gfx.Disable(graphics.MULTISAMPLE) 415 | 416 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, eyeFramebufferRight.RenderFramebuffer) 417 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, eyeFramebufferRight.ResolveFramebuffer) 418 | gfx.BlitFramebuffer(0, 0, int32(renderWidth), int32(renderHeight), 0, 0, int32(renderWidth), int32(renderHeight), graphics.COLOR_BUFFER_BIT, graphics.LINEAR) 419 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, 0) 420 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, 0) 421 | 422 | } 423 | 424 | func updateHMDPose() { 425 | // WaitGetPoses is used as a sync point in the OpenVR API. This is on a timer to keep 90fps, so 426 | // the OpenVR gives you that much time to draw a frame. By calling WaitGetPoses() you wait the 427 | // remaining amount of time. If you only used 1ms it will wait 10ms here. If you used 5ms it will wait 6ms. 428 | // (approx.) 429 | vrCompositor.WaitGetPoses(false) 430 | if vrCompositor.IsPoseValid(vr.TrackedDeviceIndexHmd) { 431 | pose := vrCompositor.GetRenderPose(vr.TrackedDeviceIndexHmd) 432 | hmdPose = mgl.Mat4(vr.Mat34ToMat4(&pose.DeviceToAbsoluteTracking)).Inv() 433 | 434 | // FIXME: this is probably broken. 435 | hmdLoc[0] = pose.DeviceToAbsoluteTracking[9] 436 | hmdLoc[1] = pose.DeviceToAbsoluteTracking[10] 437 | hmdLoc[2] = pose.DeviceToAbsoluteTracking[11] 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /examples/connectiontest/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | vr "github.com/tbogdala/openvr-go" 9 | "runtime" 10 | ) 11 | 12 | func init() { 13 | runtime.LockOSThread() 14 | } 15 | 16 | func main() { 17 | // attempt to initialize the system 18 | vrSystem, err := vr.Init() 19 | if err != nil { 20 | fmt.Printf("vr.Init() returned an error: %v\n", err) 21 | } 22 | 23 | if vrSystem == nil { 24 | panic("vrSystem is nil") 25 | } 26 | 27 | // print out the driver and display names 28 | fmt.Printf("About to test the driver and display names ...\n") 29 | driver, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropTrackingSystemNameString) 30 | if errInt != vr.TrackedPropSuccess { 31 | panic("error getting driver name.") 32 | } 33 | display, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropSerialNumberString) 34 | if errInt != vr.TrackedPropSuccess { 35 | panic("error getting display name.") 36 | } 37 | fmt.Printf("Connection Test: %s - %s\n", driver, display) 38 | 39 | // print out the recommended render target size 40 | w, h := vrSystem.GetRecommendedRenderTargetSize() 41 | fmt.Printf("Render target size: %d x %d\n", w, h) 42 | 43 | // print out the play area dimensions 44 | vrChaperone, err := vr.GetChaperone() 45 | if err != nil { 46 | panic("error getting IVRChaperone interface.") 47 | } 48 | calibrationState := vrChaperone.GetCalibrationState() 49 | fmt.Printf("Calibration state: %d (1==OK, 100's==Warning, 200's==Error)\n", calibrationState) 50 | playX, playZ := vrChaperone.GetPlayAreaSize() 51 | fmt.Printf("Play area size: %f x %f\n", playX, playZ) 52 | playRect := vrChaperone.GetPlayAreaRect() 53 | fmt.Printf("Play area corners:\n\t%v\n\t%v\n\t%v\n\t%v\n", playRect[0], playRect[1], playRect[2], playRect[3]) 54 | } 55 | -------------------------------------------------------------------------------- /examples/screenshots/example-basiccube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/examples/screenshots/example-basiccube.jpg -------------------------------------------------------------------------------- /examples/screenshots/example-voxels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/examples/screenshots/example-voxels.jpg -------------------------------------------------------------------------------- /examples/voxels/assets/textures/LICENSE: -------------------------------------------------------------------------------- 1 | MinetestCore [W.I.P.] 2 | 32 pixels TexturePack 3 | 4 | for Minetest Game 0.4.13 5 | Copyright (C) 2010-2012 celeron55, Perttu Ahola 6 | 7 | ========================== 8 | 9 | License of media (textures) : 10 | 11 | Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) 12 | http://creativecommons.org/licenses/by-sa/3.0/ 13 | 14 | License of menu/header.png 15 | Copyright (C) 2015 paramat CC BY-SA 3.0 16 | 17 | Everything not listed in this file : 18 | Copyright (C) 2010-2012 celeron55, Perttu Ahola 19 | 20 | ----------------------- 21 | 22 | If I have missed crediting anyone's work please notify me 23 | so that I can correct the oversight. 24 | -Nappi 25 | 26 | Authors of original media files : 27 | ----------------------- 28 | 29 | Created by celeron55 (CC BY-SA 3.0) : 30 | door_glass_a.png 31 | door_glass_b.png 32 | 33 | Created by Cisoun (WTFPL) : 34 | default_aspen_tree_top.png (alternates folder) 35 | default_jungletree.png 36 | default_lava.png 37 | default_leaves.png 38 | default_sapling.png 39 | default_sign_wall.png 40 | default_stone.png 41 | default_tree.png 42 | default_tree_top.png 43 | default_water.png 44 | default_pine_needles.png 45 | wool_*.png 46 | 47 | Originating from G4JC's Almost MC Texture Pack: 48 | default_torch.png 49 | default_torch_on_ceiling.png 50 | default_torch_on_floor.png 51 | 52 | Created by RealBadAngel (WTFPL) : 53 | default_water_source_animated.png 54 | default_water_flowing_animated.png 55 | 56 | Created by VanessaE (WTFPL) : 57 | default_nc_*.png 58 | default_desert_sand.png 59 | default_desert_stone.png 60 | default_sand.png 61 | default_jungletree_top.png 62 | default_torch_animated.png 63 | default_torch_on_ceiling_animated.png 64 | default_torch_on_floor_animated.png 65 | default_torch_on_floor.png 66 | vessels_*.png 67 | vmg_glowing_fungal.png 68 | vmg_mushroom_giant_cap.png 69 | 70 | Created by VanessaE (GPL3) : 71 | cottages_homedecor_shingles_wood.png 72 | cottages_homedecor_shingles_asphalt.png 73 | cottages_homedecor_shingles_terracotta.png 74 | cottages_sleepingmat.png 75 | 76 | Created by Calinou (CC BY-SA) : 77 | default_brick.png 78 | default_papyrus.png 79 | default_mineral_copper.png 80 | default_torch_new_*.png 81 | default_glass_detail.png 82 | 83 | Created by PilzAdam (WTFPL) : 84 | default_jungleleaves.png 85 | default_junglesapling.png 86 | default_obsidian_glass.png 87 | default_obsidian_shard.png 88 | default_mineral_gold.png 89 | default_snowball.png 90 | farming_bread.png 91 | farming_soil.png 92 | farming_soil_wet.png 93 | farming_soil_wet_side.png 94 | farming_string.png 95 | 96 | Created by jojoa1997 (WTFPL) : 97 | default_obsidian.png 98 | 99 | Created by InfinityProject (WTFPL) : 100 | default_mineral_diamond.png 101 | 102 | Created by Splizard (CC BY-SA 3.0) : 103 | default_snow.png 104 | default_snow_side.png 105 | default_pine_sapling.png 106 | 107 | Created by Zeg9 (CC BY-SA 3.0) : 108 | default_coal_block.png 109 | 110 | Created by paramat (CC BY-SA 3.0) : 111 | default_pinetree_top.png 112 | default_pinewood.png 113 | default_sandstone_brick.png 114 | default_obsidian_brick.png 115 | default_river_water.png 116 | default_river_water_source_animated.png 117 | default_river_water_flowing_animated.png 118 | default_acacia_sapling.png 119 | default_acacia_tree_top.png 120 | default_acacia_wood.png 121 | default_dry_grass.png 122 | default_dry_grass_side.png 123 | default_dry_grass_*.png 124 | default_junglewood.png, derived from a texture by BlockMen (CC BY-SA 3.0) 125 | default_grass.png, derived from a texture by Philipbenr (CC BY-SA 3.0) 126 | default_grass_side.png, derived from a texture by Philipbenr (CC BY-SA 3.0) 127 | default_stone_brick.png, derived from a texture by Cisoun (WTFPL) 128 | default_desert_stone_brick.png, derived from a texture by VanessaE (WTFPL) 129 | 130 | Created by BlockMen (CC BY-SA 3.0) : 131 | beds_bed_*.png 132 | default_wood.png 133 | default_clay_brick.png 134 | default_tool_steelsword.png 135 | default_diamond.png 136 | default_book.png 137 | default_tool_*.png 138 | default_lava_source_animated.png 139 | default_lava_flowing_animated.png 140 | default_chest_*.png 141 | default_mineral_mese.png 142 | default_meselamp.png 143 | 144 | sofar (CC BY-SA 3.0): 145 | default_book_written.png, based on default_book.png 146 | default_aspen_sapling 147 | default_aspen_leaves 148 | default_aspen_tree 149 | default_aspen_tree_top, derived from default_pine_tree_top (by paramat) 150 | default_aspen_wood, derived from default_pine_wood (by paramat) 151 | default_chest_wood.png 152 | default_chest_wood_locked.png 153 | 154 | 155 | Created by Neuromancer (CC BY-SA 2.0) : 156 | default_cobble.png, based on texture by Brane praefect 157 | default_mossycobble.png, based on texture by Brane praefect 158 | 159 | Created by Neuromancer (CC BY-SA 3.0) : 160 | default_dirt.png 161 | default_furnace_*.png 162 | 163 | Created by Kilbith/jp ??? (WTFPL) : 164 | trash_icon.png 165 | 166 | Created by Gambit (WTFPL) : 167 | coral4.png 168 | default_acacia_tree.png (alternates folder) 169 | default_bronze_block.png 170 | default_bronze_ingot.png 171 | default_clay_lump.png 172 | default_coal.png 173 | default_copper_block.png 174 | default_copper_ingot.png 175 | default_copper_lump.png 176 | default_diamond_block.png 177 | default_gold_block.png 178 | default_gold_ingot.png 179 | default_gold_lump.png 180 | default_grass_*.png 181 | default_gravel.png 182 | default_iron_lump.png 183 | default_mese_block.png 184 | default_mese_crystal.png 185 | default_paper.png 186 | default_pine_tree.png (alternates folder) 187 | default_steel_block.png 188 | default_steel_ingot.png 189 | doors_steel.png 190 | doors_steel_a.png 191 | doors_steel_b.png 192 | doors_trapdoor_steel.png 193 | farming_cotton_*.png 194 | farming_cotton_seed.png 195 | farming_flour.png 196 | farming_wheat.png 197 | farming_wheat_*.png 198 | farming_wheat_seed.png 199 | fishing_*.png 200 | flowers_mushroom_*.png 201 | flowers_waterlily.png 202 | gems_amethyst_block.png 203 | gems_emerald_block.png 204 | gems_emerald_gem.png 205 | default_mese_block.png (alternates folder) 206 | default_mese_crystal.png (alternates folder) 207 | gems_pearl_block.png 208 | gems_quartz_block.png 209 | gems_ruby_block.png 210 | gems_ruby_gem.png 211 | gems_sapphire_block.png 212 | gems_shadow_block.png 213 | heart.png 214 | hunger.png 215 | moreores_mithril_block.png 216 | moreores_mithril_ingot.png 217 | moreores_silver_block.png 218 | moreores_silver_ingot.png 219 | moreores_tin_block.png 220 | moreores_tin_ingot.png 221 | oresplus_*.png 222 | screwdriver.png 223 | vessels_shelf.png 224 | vessels_glass_fragments.png 225 | vmg_banana_tree.png 226 | xpanes_bar.png 227 | xdecor_*.png 228 | 229 | Created by asl97 (WTFPL) : 230 | default_ice.png 231 | 232 | Created by MasterGollum (License: WTFPL) : 233 | darkage_*.png 234 | farming_straw.png 235 | 236 | Created by Muadtralk (License: CC BY 3.0) : 237 | fire_basic_flame_animated.png 238 | 239 | Created by PenguinDad (CC BY-SA 4.0) : 240 | door_glass.png 241 | door_obsidian_glass.png 242 | 243 | Created by Krock (License: WTFPL) : 244 | mining_autominer_*.png 245 | mining_breaknode.png 246 | mining_bridgebuilder_*.png 247 | mining_grinder_front.png 248 | mining_polished_cobble.png 249 | mining_tunnelbomb_*.png 250 | 251 | Created by Duane Robertson (License: WTFPL v2) : 252 | vmg_birch_tree.png 253 | vmg_bird_of_paradise.png 254 | vmg_dirt_clayey.png 255 | vmg_dirt_sandy.png 256 | vmg_dirt_silty.png 257 | vmg_gerbera.png 258 | vmg_hibiscus.png 259 | vmg_moon_weed.png 260 | vmg_mushroom_giant_stem.png 261 | vmg_mushroom_giant_under.png 262 | vmg_orchid.png 263 | vmg_red_clay.png 264 | vmg_sand_with_rocks.png 265 | vmg_silt.png 266 | 267 | 268 | 269 | Created by Duane Robertson (License: GPL3) : 270 | vmg_fir_leaves.png, derived from the VMG textures by vlapsley 271 | vmg_birch_leaves.png, derived from the VMG textures by vlapsley 272 | vmg_cherry_blossom_leaves.png, derived from the VMG textures by vlapsley 273 | 274 | Created by Philipbenr (License: GPL3) : 275 | castle_battleaxe.png 276 | castle_corner_stonewall*.png 277 | castle_hide.png 278 | castle_crossbow.png 279 | castle_dungeon_stone.png 280 | castle_jail_door_*.png 281 | castle_pavement_brick.png 282 | castle_rubble.png 283 | castle_shield_*.png 284 | castle_slate.png 285 | castle_steel.png 286 | castle_stonewall.png 287 | castle_workbench_*.png 288 | 289 | ----------------------- 290 | 291 | If I have missed crediting anyone's work please notify me 292 | so that I can correct the oversight. 293 | -Nappi 294 | 295 | ----------------------- -------------------------------------------------------------------------------- /examples/voxels/assets/textures/README.md: -------------------------------------------------------------------------------- 1 | # MTCORE32px (for default mods only) 2 | 3 | 32 pixel texture pack based on the default 16px Minetest Game textures 4 | 5 | Covers all default mods in Minetest Game 6 | 7 | License of media (textures) : 8 | 9 | Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) 10 | 11 | http://creativecommons.org/licenses/by-sa/3.0/ 12 | 13 | Copyright (C) 2010-2016 celeron55, Perttu Ahola celeron55@gmail.com 14 | -------------------------------------------------------------------------------- /examples/voxels/assets/textures/default_dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/examples/voxels/assets/textures/default_dirt.png -------------------------------------------------------------------------------- /examples/voxels/assets/textures/default_grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/examples/voxels/assets/textures/default_grass.png -------------------------------------------------------------------------------- /examples/voxels/assets/textures/default_stone_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/examples/voxels/assets/textures/default_stone_block.png -------------------------------------------------------------------------------- /examples/voxels/assets/voxel.fs: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | uniform vec4 MATERIAL_DIFFUSE; 5 | uniform vec4 MATERIAL_SPECULAR; 6 | uniform float MATERIAL_SHININESS; 7 | 8 | uniform vec3 LIGHT_POSITION[4]; 9 | uniform vec4 LIGHT_DIFFUSE[4]; 10 | uniform float LIGHT_DIFFUSE_INTENSITY[4]; 11 | uniform float LIGHT_AMBIENT_INTENSITY[4]; 12 | uniform float LIGHT_SPECULAR_INTENSITY[4]; 13 | uniform vec3 LIGHT_DIRECTION[4]; 14 | uniform int LIGHT_COUNT; 15 | uniform vec3 CAMERA_WORLD_POSITION; 16 | 17 | uniform sampler2DArray VOXEL_TEXTURES; 18 | 19 | flat in int vs_vert_texindex; 20 | in float vs_vert_ao; 21 | in float vs_vert_ao_corner; 22 | 23 | in vec2 vs_uvcoord; 24 | in vec4 w_position; 25 | in vec3 w_normal; 26 | 27 | out vec4 frag_color; 28 | 29 | const float Epsilon = 0.0001; 30 | const int MAX_LIGHT = 4; 31 | 32 | // Gamma correction routines 33 | const float gamma = 2.2; 34 | 35 | vec3 toLinear(vec3 c) { 36 | return pow(c, vec3(gamma)); 37 | } 38 | vec4 toLinear(vec4 c) { 39 | return pow(c, vec4(gamma)); 40 | } 41 | 42 | vec3 toGamma(vec3 c) { 43 | return pow(c, vec3(1.0 / gamma)); 44 | } 45 | vec4 toGamma(vec4 c) { 46 | return pow(c, vec4(1.0 / gamma)); 47 | } 48 | 49 | // TODO: fix all the variables captured from global namespace 50 | vec4 PhongShading(int light_i) 51 | { 52 | vec3 l_light_color = LIGHT_DIFFUSE[light_i].rgb; 53 | vec3 l_specular_color = MATERIAL_SPECULAR.xyz; 54 | vec4 texColor = texture(VOXEL_TEXTURES, vec3(vs_uvcoord, vs_vert_texindex)); 55 | vec3 l_base_color = toLinear(texColor.rgb); 56 | 57 | // world space normal 58 | vec3 N = normalize(w_normal.xyz); 59 | 60 | // calculate the direction towards the light in world space 61 | vec3 L; 62 | // if light direction is not set, calculate it from the position 63 | if (abs(LIGHT_DIRECTION[light_i].x) < Epsilon && abs(LIGHT_DIRECTION[light_i].y) < Epsilon && abs(LIGHT_DIRECTION[light_i].z) < Epsilon) { 64 | L = normalize(LIGHT_POSITION[light_i] - w_position.xyz); 65 | } else { 66 | // otherwise we just use the direction here 67 | L = normalize(-LIGHT_DIRECTION[light_i]); 68 | } 69 | 70 | // get the diffuse intensity 71 | float Id = max(0.0, dot(N, L)); 72 | 73 | // calculate the specular coefficient appropriate based on diffuse intensity 74 | float S = 0.0; 75 | if (Id > 0.0 && MATERIAL_SHININESS > 0.0) { 76 | // calculate the specular 77 | vec3 R = normalize(reflect(-L, N)); 78 | 79 | // calculate surface to camera in world space 80 | vec3 E = normalize(CAMERA_WORLD_POSITION - w_position.xyz); 81 | 82 | S = pow(max(0.0, dot(E, R)), MATERIAL_SHININESS); 83 | } 84 | 85 | vec3 final_ambient = LIGHT_AMBIENT_INTENSITY[light_i] * l_base_color * l_light_color; 86 | vec3 final_diffuse = LIGHT_DIFFUSE_INTENSITY[light_i] * l_base_color * l_light_color * Id; 87 | vec3 final_specular = LIGHT_SPECULAR_INTENSITY[light_i] * l_specular_color.xyz * l_light_color * S; 88 | clamp(final_specular, 0.0, 1.0); 89 | 90 | return vec4(final_ambient + final_diffuse + final_specular, MATERIAL_DIFFUSE.a); 91 | } 92 | 93 | 94 | void main() 95 | { 96 | vec4 final_color; 97 | 98 | 99 | for (int i=0; i= LIGHT_COUNT) { 101 | break; 102 | } 103 | 104 | final_color += PhongShading(i); 105 | } 106 | 107 | // darken based on ao 108 | float aoFactorA = max(0.0, vs_vert_ao-0.4); 109 | float aoFactorB = max(0.0, vs_vert_ao_corner-0.4); 110 | float aoFactor = max(aoFactorA, aoFactorB); 111 | final_color = final_color * (1.0 - aoFactor); 112 | 113 | frag_color = toGamma(final_color); 114 | frag_color.a = 1.0; 115 | 116 | //frag_color = vec4(1.0, 1.0, 1.0, 1.0); 117 | } 118 | -------------------------------------------------------------------------------- /examples/voxels/assets/voxel.vs: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | uniform mat4 MVP_MATRIX; 5 | uniform mat4 M_MATRIX; 6 | in vec3 VERTEX_POSITION; 7 | in vec3 VERTEX_NORMAL; 8 | in vec2 VERTEX_UV_0; 9 | in float VERTEX_TEXTURE_INDEX; 10 | in float VERTEX_VOXEL_BF; 11 | 12 | flat out int vs_vert_texindex; 13 | out float vs_vert_ao; 14 | out float vs_vert_ao_corner; 15 | out vec2 vs_uvcoord; 16 | out vec4 w_position; 17 | out vec3 w_normal; 18 | 19 | void main() 20 | { 21 | vs_uvcoord = VERTEX_UV_0; 22 | 23 | int bitfield = int(VERTEX_VOXEL_BF); 24 | vs_vert_texindex = int(VERTEX_TEXTURE_INDEX); 25 | vs_vert_ao = float(bitfield & 0x01); 26 | vs_vert_ao_corner = float((bitfield & 0x02) >> 1); 27 | 28 | vec4 vert4 = vec4(VERTEX_POSITION, 1.0); 29 | mat3 normal_mat = transpose(inverse(mat3(M_MATRIX))); 30 | w_position = M_MATRIX * vert4; 31 | w_normal = normal_mat * VERTEX_NORMAL; 32 | 33 | gl_Position = MVP_MATRIX * vert4; 34 | } 35 | -------------------------------------------------------------------------------- /examples/voxels/chunk/chunk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package chunk 5 | 6 | import ( 7 | "fmt" 8 | 9 | mgl "github.com/go-gl/mathgl/mgl32" 10 | 11 | physics "github.com/tbogdala/cubez" 12 | physmath "github.com/tbogdala/cubez/math" 13 | "github.com/tbogdala/fizzle" 14 | graphics "github.com/tbogdala/fizzle/graphicsprovider" 15 | "github.com/tbogdala/glider" 16 | ) 17 | 18 | const ( 19 | // ChunkSize controls the cubic dimensions of Chunk structures 20 | ChunkSize = 16 21 | 22 | // ChunkSizeF is the size of the Chunk in float 23 | ChunkSizeF = float32(16.0) 24 | 25 | // ChunkSize2 is chunkSize squared 26 | ChunkSize2 = ChunkSize * ChunkSize 27 | 28 | // ChunkSize3 is chunkSize cubed 29 | ChunkSize3 = ChunkSize2 * ChunkSize 30 | ) 31 | 32 | // TODO: these are temporary fixes until the chunk manager has a map of block types to descriptors. 33 | const ( 34 | BlockTypeEmpty = BlockType(0) 35 | BlockTypeGrass = BlockType(1) 36 | BlockTypeStones = BlockType(2) 37 | BlockTypeDirt = BlockType(3) 38 | ) 39 | 40 | // BlockType is a type alias for the variable that will determine 41 | // what type a landscape Block is in the Chunk. 42 | type BlockType uint32 43 | 44 | // Chunk is the data structure that groups together Blocks. 45 | type Chunk struct { 46 | // X is the location of Chunk along the X axis in multiples of ChunkSize. 47 | // (e.g. a value of Chunk.X==2 for ChunkSize==16 means a world position of 32) 48 | X int 49 | 50 | // Y is the location of Chunk along the Y axis in multiples of ChunkSize. 51 | // (e.g. a value of Chunk.Y==2 for ChunkSize==16 means a world position of 32) 52 | Y int 53 | 54 | // Z is the location of Chunk along the Z axis in multiples of ChunkSize. 55 | // (e.g. a value of Chunk.Z==2 for ChunkSize==16 means a world position of 32) 56 | Z int 57 | 58 | // Blocks is a flat array [y*x*z] Block values. You can think 59 | // of it in +Z first ordering, meaning it fills a depth of Z, then moves in +X 60 | // and fills another depth of Z. 61 | // 62 | // Viz: 63 | // Y 64 | // ^ Z 65 | // |/__> X 66 | // 67 | // Further examples for RegionDepth of 16: 68 | // (0,0,0) = [0] 69 | // (0,0,5) = [5] 70 | // (1,0,0) = [16] 71 | // (1,0,5) = [21] 72 | // (0,1,0) = [256] (16*16) 73 | Blocks [ChunkSize3]Block 74 | 75 | // Renderable is the drawable OpenGL object for the chunk. In a server-side 76 | // only server, this pointer will be nil. 77 | Renderable *fizzle.Renderable 78 | 79 | // AABBCollider is an axis aligned bounding box collider for the chunk for 80 | // use in simple collision checks with the space of the entire chunk, 81 | // regarldless of the contents (i.e. will still collide on 'empty' blocks 82 | // and contains the entierty of the chunk). 83 | // Note: coordinates used in the collider are in world space. 84 | AABBCollider *glider.AABBox 85 | 86 | // colliders is a slice of calculate physics colliders for the chunk geometry. 87 | // This is generated on calls to UpdateColliders() which should be called 88 | // when the Blocks array has value changes. 89 | Colliders []physics.Collider 90 | 91 | // BoxColliders is a slice of AABBoxes that mirrors the physics colliders. 92 | // This can be used for faster raycasts since the voxels aren't rotating. 93 | BoxColliders []*glider.AABBox 94 | 95 | // Owner is the owning landscape Manager class. This will be nil if the Chunk 96 | // has not been registered yet with a manager. 97 | Owner *Manager 98 | } 99 | 100 | // Block is the basic building block of the landscape terrain. 101 | type Block struct { 102 | // Type determines the type of block that the Block struct represents 103 | // NOTE: a Type of 0 has special significance in that it's assumed 104 | // to be an empty space. 105 | Type BlockType 106 | } 107 | 108 | // NewChunk returns a newly created chunk 109 | func NewChunk(chunkX, chunkY, chunkZ int) *Chunk { 110 | c := new(Chunk) 111 | c.X = chunkX 112 | c.Y = chunkY 113 | c.Z = chunkZ 114 | c.AABBCollider = glider.NewAABBox() 115 | c.AABBCollider.Offset = mgl.Vec3{float32(c.X) * ChunkSize, float32(c.Y) * ChunkSize, float32(c.Z) * ChunkSize} 116 | c.AABBCollider.Min = mgl.Vec3{0, 0, 0} 117 | c.AABBCollider.Max = mgl.Vec3{ChunkSizeF, ChunkSizeF, ChunkSizeF} 118 | return c 119 | } 120 | 121 | // Clone creates a new Chunk object with the core data but does 122 | // not duplicate the Renderable or physics colliders 123 | func (c *Chunk) Clone() *Chunk { 124 | newChunk := NewChunk(c.X, c.Y, c.Z) 125 | newChunk.Blocks = c.Blocks 126 | return newChunk 127 | } 128 | 129 | // Destroy tells the chunk to release any special data. 130 | func (c *Chunk) Destroy() { 131 | // destroy the renderable if there's one made for the landscape node 132 | if c.Renderable != nil { 133 | c.Renderable.Destroy() 134 | } 135 | } 136 | 137 | // GetTheRenderable returns the already crafted Renderable object or makes 138 | // one, caches it and returns a pointer to it. 139 | func (c *Chunk) GetTheRenderable(textureIndexes fizzle.TextureArrayIndexes) *fizzle.Renderable { 140 | // if we already have one made then return it 141 | if c.Renderable != nil { 142 | return c.Renderable 143 | } 144 | 145 | // build a new one 146 | c.Renderable = c.buildVoxelRenderable(textureIndexes) 147 | return c.Renderable 148 | } 149 | 150 | // BlockAt returns the Block object at a given offset within the Chunk. 151 | // NOTE: not to be confused with coordinate -- this is the offset in the Cubes array. 152 | func (c *Chunk) BlockAt(x, y, z int) *Block { 153 | return &c.Blocks[(y*ChunkSize2)+(x*ChunkSize)+z] 154 | } 155 | 156 | // SetBlock sets the attribute of a block at a given coordiante. X,Y,Z should 157 | // be within range of [0..ChunkSize-1]. 158 | func (c *Chunk) SetBlock(x, y, z int, ty BlockType) { 159 | block := c.BlockAt(x, y, z) 160 | block.Type = ty 161 | } 162 | 163 | // IsVisible returns true if the block is a type of block that can be visualized normally. 164 | // Basically: if it's not air. 165 | func (b *Block) IsVisible() bool { 166 | if b.Type > 0 { 167 | return true 168 | } 169 | return false 170 | } 171 | 172 | // IsBlockVisible tests whether or not the block is visible. x,y,z are 173 | // specified in local coordinates. 174 | // NOTE: blocks on the outside of the chunk are always considered visible. 175 | func (c *Chunk) IsBlockVisible(x, y, z int) bool { 176 | // at present, the test for block visibility is if Type > 0. 177 | 178 | // the block itself 179 | if !c.Blocks[y*ChunkSize2+(x*ChunkSize)+z].IsVisible() { 180 | return false 181 | } 182 | 183 | // blocks on the edges are always visible 184 | if x == 0 || y == 0 || z == 0 || x == ChunkSize-1 || y == ChunkSize-1 || z == ChunkSize-1 { 185 | return true 186 | } 187 | 188 | // up 189 | if !c.Blocks[(y+1)*ChunkSize2+(x*ChunkSize)+z].IsVisible() { 190 | return true 191 | } 192 | 193 | // down 194 | if !c.Blocks[(y-1)*ChunkSize2+(x*ChunkSize)+z].IsVisible() { 195 | return true 196 | } 197 | 198 | // left 199 | if !c.Blocks[y*ChunkSize2+((x+1)*ChunkSize)+z].IsVisible() { 200 | return true 201 | } 202 | 203 | // right 204 | if !c.Blocks[y*ChunkSize2+((x-1)*ChunkSize)+z].IsVisible() { 205 | return true 206 | } 207 | 208 | // front 209 | if !c.Blocks[y*ChunkSize2+(x*ChunkSize)+z+1].IsVisible() { 210 | return true 211 | } 212 | 213 | // back 214 | if !c.Blocks[y*ChunkSize2+(x*ChunkSize)+z-1].IsVisible() { 215 | return true 216 | } 217 | 218 | return false 219 | } 220 | 221 | // IsBlockMovementBlocking checks to see if a block location blocks movement 222 | // from entities. Having this separate from IsVisible makes the mechanic 223 | // being checked more explicit. 224 | func (c *Chunk) IsBlockMovementBlocking(x, y, z int) bool { 225 | // Currently, the test is whether or not the type of block 226 | // is greater than 0 ... basically if there's any block present. 227 | block := c.BlockAt(x, y, z) 228 | if block.Type > 0 { 229 | return true 230 | } 231 | return false 232 | } 233 | 234 | func (c *Chunk) buildVoxelRenderable(textureIndexes fizzle.TextureArrayIndexes) *fizzle.Renderable { 235 | var xmax, ymax, zmax float32 = 1.0, 1.0, 1.0 236 | var xmin, ymin, zmin float32 = 0.0, 0.0, 0.0 237 | 238 | /* Cube vertices are layed out like this: 239 | 240 | +--------+ 6 5 241 | / | /| 242 | +--------+ | 1 0 +Y 243 | | | | | |___ +X 244 | | +------|-+ 7 4 / 245 | |/ |/ +Z 246 | +--------+ 2 3 247 | 248 | */ 249 | 250 | lookupDirs := [...]int{ 251 | // front 252 | 0, 1, 1, 1, 0, 1, 1, 1, 1, // v0 (front+up, right+front, right+up+front) 253 | 0, 1, 1, -1, 0, 1, -1, 1, 1, // v1 (front+up, left+front, left+up+front) 254 | 0, -1, 1, -1, 0, 1, -1, -1, 1, // v2 (front+bottom, left+front, left+bottom+front) 255 | 0, -1, 1, 1, 0, 1, 1, -1, 1, // v3 (front+bottom, right+front, right+bottom+front) 256 | 257 | // right 258 | 1, 1, 0, 1, 0, -1, 1, 1, -1, // v5 (right+up, right+back, right+up+back) 259 | 1, 1, 0, 1, 0, 1, 1, 1, 1, // v0 (right+up, right+front, right+up+front) 260 | 1, -1, 0, 1, 0, 1, 1, -1, 1, // v3 (right+bottom, right+font, right+bottom+front) 261 | 1, -1, 0, 1, 0, -1, 1, -1, -1, // v4 (right+bottom, right+back, right+bottom+back) 262 | 263 | // top 264 | 0, 1, -1, 1, 1, 0, 1, 1, -1, // v5 (back+up, right+up, right+up+back) 265 | 0, 1, -1, -1, 1, 0, -1, 1, -1, // v6 (back+up, left+up, left+up+back) 266 | 0, 1, 1, -1, 1, 0, -1, 1, 1, // v1 (front+up, left+up, left+up+front) 267 | 0, 1, 1, 1, 1, 0, 1, 1, 1, // v0 (front+up, right+up, right+up+front) 268 | 269 | // left 270 | -1, 1, 0, -1, 0, 1, -1, 1, 1, // v1 (left+up, left+front, left+up+front) 271 | -1, 1, 0, -1, 0, -1, -1, 1, -1, // v6 (left+up, left+back, left+up+back) 272 | -1, -1, 0, -1, 0, -1, -1, -1, -1, // v7 (left+bottom, left+back, left+bottom+back) 273 | -1, -1, 0, -1, 0, 1, -1, -1, 1, // v2 (left+bottom, left+front, left+bottom+front) 274 | 275 | // bottom 276 | 0, -1, 1, 1, -1, 0, 1, -1, 1, // v3 (front+bottom, right+bottom, right+bottom+front) 277 | 0, -1, 1, -1, -1, 0, -1, -1, 1, // v2 (front+bottom, left+bottom, left+bottom+front) 278 | 0, -1, -1, -1, -1, 0, -1, -1, -1, // v7 (back+bottom, left+bottom, left+bottom+back) 279 | 0, -1, -1, 1, -1, 0, 1, -1, -1, // v4 (back+bottom, right+bottom, right+bottom+back) 280 | 281 | // back 282 | 0, 1, -1, -1, 0, -1, -1, 1, -1, // v6 (back+up, left+back, left+up+back) 283 | 0, 1, -1, 1, 0, -1, 1, 1, -1, // v5 (back+up, right+back, right+up+back) 284 | 0, -1, -1, 1, 0, -1, 1, -1, -1, // v4 (back+bottom, right+back, right+bottom+back) 285 | 0, -1, -1, -1, 0, -1, -1, -1, -1, // v7 (back+bottom, left+back, left+bottom+back) 286 | } 287 | 288 | verts := [...]float32{ 289 | xmax, ymax, zmax, xmin, ymax, zmax, xmin, ymin, zmax, xmax, ymin, zmax, // v0,v1,v2,v3 (front) 290 | xmax, ymax, zmin, xmax, ymax, zmax, xmax, ymin, zmax, xmax, ymin, zmin, // v5,v0,v3,v4 (right) 291 | xmax, ymax, zmin, xmin, ymax, zmin, xmin, ymax, zmax, xmax, ymax, zmax, // v5,v6,v1,v0 (top) 292 | xmin, ymax, zmax, xmin, ymax, zmin, xmin, ymin, zmin, xmin, ymin, zmax, // v1,v6,v7,v2 (left) 293 | xmax, ymin, zmax, xmin, ymin, zmax, xmin, ymin, zmin, xmax, ymin, zmin, // v3,v2,v7,v4 (bottom) 294 | xmin, ymax, zmin, xmax, ymax, zmin, xmax, ymin, zmin, xmin, ymin, zmin, // v6,v5,v4,v7 (back) 295 | } 296 | indexes := [...]uint32{ 297 | 0, 1, 2, 2, 3, 0, 298 | } 299 | uvs := [...]float32{ 300 | 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 301 | } 302 | 303 | normals := [...]float32{ 304 | 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0,v1,v2,v3 (front) 305 | 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v5,v0,v3,v4 (right) 306 | 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v5,v6,v1,v0 (top) 307 | -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1,v6,v7,v2 (left) 308 | 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, // v3,v2,v7,v4 (bottom) 309 | 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, // v6,v5,v4,v7 (back) 310 | } 311 | 312 | var faceCount uint32 313 | const comboFloatsPerGrid int = len(verts) 314 | const comboIntsPerGrid int = 2 // btype, mask 315 | sectorIndexes := make([]uint32, 0, len(indexes)*ChunkSize3) 316 | btCombo := make([]uint32, 0, comboIntsPerGrid*4*ChunkSize3) 317 | vnutBuffer := make([]float32, 0, (comboFloatsPerGrid*2+len(uvs))*ChunkSize3) 318 | 319 | // loop through each block 320 | for y := 0; y < ChunkSize; y++ { 321 | for x := 0; x < ChunkSize; x++ { 322 | for z := 0; z < ChunkSize; z++ { 323 | worldX := c.X*ChunkSize + x 324 | worldY := c.Y*ChunkSize + y 325 | worldZ := c.Z*ChunkSize + z 326 | 327 | // if the block itself is not visible, then just move on 328 | if c.IsBlockVisible(x, y, z) == false { 329 | continue 330 | } 331 | 332 | // get the block 333 | block := c.BlockAt(x, y, z) 334 | 335 | // process each face on the block separately 336 | currentFaceCount := 0 337 | for face := 0; face < 6; face++ { 338 | // do we need this face? check to see if there's an obstructing block. 339 | // NOTE: this is done with a lame implementation right now, because 340 | // going back to the manager isn't efficient. 341 | switch { 342 | case face == 0: // 0, 0, +1 343 | b, _ := c.Owner.GetBlockAt(worldX, worldY, worldZ+1) 344 | if b != nil && b.Type > 0 { 345 | continue 346 | } 347 | case face == 1: // +1, 0, 0 348 | b, _ := c.Owner.GetBlockAt(worldX+1, worldY, worldZ) 349 | if b != nil && b.Type > 0 { 350 | continue 351 | } 352 | case face == 2: // 0, +1, 0 353 | b, _ := c.Owner.GetBlockAt(worldX, worldY+1, worldZ) 354 | if b != nil && b.Type > 0 { 355 | continue 356 | } 357 | case face == 3: // -1, 0, 0 358 | b, _ := c.Owner.GetBlockAt(worldX-1, worldY, worldZ) 359 | if b != nil && b.Type > 0 { 360 | continue 361 | } 362 | case face == 4: // 0, -1, 0 363 | b, _ := c.Owner.GetBlockAt(worldX, worldY-1, worldZ) 364 | if b != nil && b.Type > 0 { 365 | continue 366 | } 367 | case face == 5: // 0, 0, -1 368 | b, _ := c.Owner.GetBlockAt(worldX, worldY, worldZ-1) 369 | if b != nil && b.Type > 0 { 370 | continue 371 | } 372 | } 373 | 374 | // time to make the vertices 375 | baseV := face * 12 376 | for iv := 0; iv < 4; iv++ { 377 | iv3 := iv * 3 378 | iv2 := iv * 2 379 | 380 | // add the vertex 381 | vnutBuffer = append(vnutBuffer, verts[baseV+iv3]+float32(x)) 382 | vnutBuffer = append(vnutBuffer, verts[baseV+iv3+1]+float32(y)) 383 | vnutBuffer = append(vnutBuffer, verts[baseV+iv3+2]+float32(z)) 384 | 385 | // add the normal 386 | vnutBuffer = append(vnutBuffer, normals[baseV+iv3]) 387 | vnutBuffer = append(vnutBuffer, normals[baseV+iv3+1]) 388 | vnutBuffer = append(vnutBuffer, normals[baseV+iv3+2]) 389 | 390 | // add the uv 391 | vnutBuffer = append(vnutBuffer, uvs[iv2]) 392 | vnutBuffer = append(vnutBuffer, uvs[iv2+1]) 393 | 394 | // setup the texture index for the face type, should be per 395 | // vertex since there's no per-face way of doing it otherwise. 396 | switch block.Type { 397 | case BlockTypeGrass: 398 | btCombo = append(btCombo, uint32(textureIndexes["Grass"])) 399 | case BlockTypeStones: 400 | btCombo = append(btCombo, uint32(textureIndexes["Stones"])) 401 | case BlockTypeDirt: 402 | btCombo = append(btCombo, uint32(textureIndexes["Dirt"])) 403 | default: 404 | fmt.Printf("ERROR: No mapping for block type (%v) to a texture index!\n", block.Type) 405 | } 406 | 407 | // do some fake AO checks based on the vertex and what face it's in 408 | vertBitFLags := 0 409 | lookupOffset := (face * 36) + (iv * 9) // 36 offset numbers per face, 9 offset numbers per vertex 410 | aoOffsetA := [3]int{lookupDirs[lookupOffset], lookupDirs[lookupOffset+1], lookupDirs[lookupOffset+2]} 411 | aoOffsetB := [3]int{lookupDirs[lookupOffset+3], lookupDirs[lookupOffset+4], lookupDirs[lookupOffset+5]} 412 | aoOffsetC := [3]int{lookupDirs[lookupOffset+6], lookupDirs[lookupOffset+7], lookupDirs[lookupOffset+8]} 413 | aoBlockA, _ := c.Owner.GetBlockAt(worldX+aoOffsetA[0], worldY+aoOffsetA[1], worldZ+aoOffsetA[2]) 414 | aoBlockB, _ := c.Owner.GetBlockAt(worldX+aoOffsetB[0], worldY+aoOffsetB[1], worldZ+aoOffsetB[2]) 415 | aoBlockC, _ := c.Owner.GetBlockAt(worldX+aoOffsetC[0], worldY+aoOffsetC[1], worldZ+aoOffsetC[2]) 416 | if (aoBlockA != nil && aoBlockA.IsVisible()) || (aoBlockB != nil && aoBlockB.IsVisible()) { 417 | vertBitFLags = vertBitFLags | 0x01 418 | } 419 | if aoBlockC != nil && aoBlockC.IsVisible() { 420 | vertBitFLags = vertBitFLags | 0x02 421 | } 422 | btCombo = append(btCombo, uint32(vertBitFLags)) 423 | 424 | } // iv 425 | 426 | // time to make the element indeces 427 | for iv := 0; iv < 6; iv++ { 428 | sectorIndexes = append(sectorIndexes, indexes[iv]+uint32(currentFaceCount*4)+uint32(faceCount*2)) 429 | } 430 | 431 | // we're not skiping the face, so lets boost the count 432 | currentFaceCount++ 433 | } 434 | 435 | faceCount += uint32(currentFaceCount) * 2 436 | } // z 437 | } // x 438 | } // y 439 | 440 | // if we didn't make any faces, just stop here and return an empty renderable 441 | gfx := fizzle.GetGraphics() 442 | 443 | r := fizzle.NewRenderable() 444 | r.Core = fizzle.NewRenderableCore() 445 | if faceCount < 1 { 446 | return r 447 | } 448 | 449 | r.FaceCount = faceCount 450 | r.BoundingRect.Top[0] = ChunkSize 451 | r.BoundingRect.Top[1] = ChunkSize 452 | r.BoundingRect.Top[2] = ChunkSize 453 | r.Location[0] = float32(c.X * ChunkSize) 454 | r.Location[1] = float32(c.Y * ChunkSize) 455 | r.Location[2] = float32(c.Z * ChunkSize) 456 | 457 | r.Material = fizzle.NewMaterial() 458 | r.Material.Shininess = 0.00 459 | 460 | // calculate the memory size of floats used to calculate total memory size of float arrays 461 | const floatSize = 4 462 | const uintSize = 4 463 | 464 | r.Core.VertVBO = gfx.GenBuffer() 465 | r.Core.UvVBO = r.Core.VertVBO 466 | r.Core.NormsVBO = r.Core.VertVBO 467 | 468 | r.Core.VertVBOOffset = 0 469 | r.Core.NormsVBOOffset = floatSize * 3 470 | r.Core.UvVBOOffset = floatSize * 6 471 | r.Core.VBOStride = floatSize * (3 + 3 + 2) // vert / normal / uv 472 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.VertVBO) 473 | gfx.BufferData(graphics.ARRAY_BUFFER, floatSize*len(vnutBuffer), gfx.Ptr(&vnutBuffer[0]), graphics.STATIC_DRAW) 474 | 475 | // create a VBO to hold the combo data 476 | r.Core.ComboVBO1 = gfx.GenBuffer() 477 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.ComboVBO1) 478 | gfx.BufferData(graphics.ARRAY_BUFFER, uintSize*len(btCombo), gfx.Ptr(&btCombo[0]), graphics.STATIC_DRAW) 479 | 480 | // create a VBO to hold the face indexes 481 | r.Core.ElementsVBO = gfx.GenBuffer() 482 | gfx.BindBuffer(graphics.ELEMENT_ARRAY_BUFFER, r.Core.ElementsVBO) 483 | gfx.BufferData(graphics.ELEMENT_ARRAY_BUFFER, uintSize*len(sectorIndexes), gfx.Ptr(§orIndexes[0]), graphics.STATIC_DRAW) 484 | 485 | return r 486 | } 487 | 488 | // UpdateColliders should be called whenever the Blocks of the Chunk 489 | // change which could result in the pathing being different. 490 | func (c *Chunk) UpdateColliders() { 491 | c.Colliders, c.BoxColliders = c.buildColliders() 492 | } 493 | 494 | // collisionBlock is a temporary data structure used to create collision cubes for landscape blocks 495 | type collisionBlock struct { 496 | X, Y, StartZ, EndZ int 497 | } 498 | 499 | // buildColliders will generate the landscape physics and AABB colliders 500 | func (c *Chunk) buildColliders() ([]physics.Collider, []*glider.AABBox) { 501 | // the value used to see if the z-tracker location is not set 502 | const unsetZ = -1 503 | 504 | // create the slice to return 505 | colliders := []collisionBlock{} 506 | 507 | for y := 0; y < ChunkSize; y++ { 508 | for x := 0; x < ChunkSize; x++ { 509 | // keep track of the colliders made along the z-axis 510 | startZ := unsetZ 511 | for z := 0; z < ChunkSize; z++ { 512 | if c.IsBlockMovementBlocking(x, y, z) == false { 513 | // if we never started a block, don't start now, just keep going. 514 | if startZ == unsetZ { 515 | continue 516 | } 517 | 518 | // we end the block of landscape and create the collider cube 519 | colliders = append(colliders, collisionBlock{x, y, startZ, z - 1}) 520 | 521 | // reset the start locator 522 | startZ = unsetZ 523 | } else { 524 | // if we get here it's movement blocking, so set the start position 525 | // if it's not set already. 526 | if startZ == unsetZ { 527 | startZ = z 528 | } 529 | 530 | // are we at the end of the z-axis? if so, create a collider 531 | if z == ChunkSize-1 { 532 | colliders = append(colliders, collisionBlock{x, y, startZ, z}) 533 | 534 | // reset the start locator 535 | startZ = unsetZ 536 | } 537 | } 538 | } // z 539 | } // x 540 | } // y 541 | 542 | results := make([]physics.Collider, 0, len(colliders)) 543 | boxResults := make([]*glider.AABBox, 0, len(colliders)) 544 | for _, cb := range colliders { 545 | cb.X += c.X * ChunkSize 546 | cb.Y += c.Y * ChunkSize 547 | cb.StartZ += c.Z * ChunkSize 548 | cb.EndZ += c.Z * ChunkSize 549 | results = append(results, buildCollider(&cb)) 550 | boxResults = append(boxResults, buildBoxCollider(&cb)) 551 | } 552 | 553 | return results, boxResults 554 | } 555 | 556 | func buildBoxCollider(cb *collisionBlock) *glider.AABBox { 557 | box := glider.NewAABBox() 558 | 559 | length := float32(cb.EndZ - cb.StartZ + 1) 560 | halfLength := float32(length) / 2.0 561 | 562 | box.SetOffset3f(float32(cb.X)+0.5, float32(cb.Y)+0.5, float32(cb.StartZ)+halfLength) 563 | box.Min = mgl.Vec3{-0.5, -0.5, -halfLength} 564 | box.Max = mgl.Vec3{0.5, 0.5, halfLength} 565 | return box 566 | } 567 | 568 | func buildCollider(cb *collisionBlock) physics.Collider { 569 | length := physmath.Real(cb.EndZ - cb.StartZ + 1) 570 | halfLength := physmath.Real(length) / 2.0 571 | 572 | // create the collision box for the the cube 573 | cubeCollider := physics.NewCollisionCube(nil, physmath.Vector3{0.5, 0.5, halfLength}) 574 | cubeCollider.Body.Position = physmath.Vector3{physmath.Real(cb.X) + 0.5, physmath.Real(cb.Y) + 0.5, physmath.Real(cb.StartZ) + halfLength} 575 | cubeCollider.Body.SetInfiniteMass() 576 | cubeCollider.Body.CanSleep = false 577 | cubeCollider.Body.CalculateDerivedData() 578 | cubeCollider.CalculateDerivedData() 579 | 580 | return cubeCollider 581 | } 582 | -------------------------------------------------------------------------------- /examples/voxels/chunk/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package chunk 5 | 6 | // Manager is the landscape manager type structure that manages access to landscape 7 | // data. 8 | type Manager struct { 9 | // Chunks is the internal splice of all of the chunks registered in the Manager. 10 | // Access to this will depend on the Size parameter. 11 | Chunks []*Chunk 12 | 13 | // Size is the number of chunks in each direction to manage. 14 | Size int 15 | 16 | // X is the location of Manager along the X axis in multiples of ChunkSize. 17 | // (e.g. a value of Manager.X==2 for ChunkSize==16 means a world position of 32) 18 | X int 19 | 20 | // Y is the location of Manager along the Y axis in multiples of ChunkSize. 21 | // (e.g. a value of Manager.Y==2 for ChunkSize==16 means a world position of 32) 22 | Y int 23 | 24 | // Z is the location of Manager along the Z axis in multiples of ChunkSize. 25 | // (e.g. a value of Manager.Z==2 for ChunkSize==16 means a world position of 32) 26 | Z int 27 | } 28 | 29 | // NewManager returns a new voxel manager object that manages a cube region 30 | // of chunks. 31 | func NewManager(length int, x, y, z int) *Manager { 32 | m := new(Manager) 33 | m.Size = length 34 | m.X = x 35 | m.Y = y 36 | m.Z = z 37 | m.Chunks = make([]*Chunk, length*length*length) 38 | 39 | return m 40 | } 41 | 42 | // RegisterChunk fits the new chunk into the chunks slice of the Manager 43 | func (m *Manager) RegisterChunk(c *Chunk) bool { 44 | // see if the chunk actually fits within the geography of the Manager. 45 | localX := c.X - m.X 46 | localY := c.Y - m.Y 47 | localZ := c.Z - m.Z 48 | if localX < 0 || localX >= m.Size || localY < 0 || localY >= m.Size || localZ < 0 || localZ >= m.Size { 49 | return false 50 | } 51 | 52 | // calculate the offset in the splice 53 | offset := localY*m.Size*m.Size + localX*m.Size + localZ 54 | m.Chunks[offset] = c 55 | 56 | // pwn it 57 | c.Owner = m 58 | 59 | return true 60 | } 61 | 62 | // RegisterChunks registers all of the chunks in the slice passed in. 63 | func (m *Manager) RegisterChunks(cs []*Chunk) { 64 | for _, chunk := range cs { 65 | m.RegisterChunk(chunk) 66 | } 67 | } 68 | 69 | // GetChunksFor returns the chunks that 'owns' the world space X,Z coordinate 70 | // passed in. If no chunks are registered for this coordinate, then 71 | // an empty slice is returned. Since the Y axis is not specified, multiple 72 | // chunks can be returned that contain X,Z. 73 | /* 74 | func (m *Manager) GetChunksFor(worldX, worldZ int) []*Chunk { 75 | chunks :=[]*Chunk{} 76 | // a lame brute force search through all the chunks 77 | for _, cY := range m.Strips { 78 | for _, cX := range cY { 79 | for _, chunk := range cX { 80 | if chunk.X*ChunkSize <= worldX && worldX < chunk.X*ChunkSize+ChunkSize && 81 | chunk.Z*ChunkSize <= worldZ && worldZ < chunk.Z*ChunkSize+ChunkSize { 82 | chunks = append(chunks, chunk) 83 | } 84 | } 85 | } 86 | } 87 | return chunks 88 | } 89 | */ 90 | 91 | // GetHeightAt returns the local height of the land at the local coordinate X,Z passed in. 92 | func (m *Manager) GetHeightAt(localX, localZ int) int { 93 | size2 := m.Size * m.Size 94 | size3 := size2 * m.Size 95 | offset := localX*m.Size + localZ 96 | answer := 0 97 | 98 | for i := 0; offset+i*size2 < size3; i++ { 99 | c := m.Chunks[offset+i*size2] 100 | if c != nil { 101 | cX := localX - c.X*ChunkSize 102 | cZ := localZ - c.Z*ChunkSize 103 | for y := 0; y < ChunkSize; y++ { 104 | b := c.BlockAt(cX, y, cZ) 105 | if b != nil && b.Type != 0 { 106 | answer = y + i*ChunkSize 107 | } 108 | } 109 | } 110 | } 111 | 112 | return answer 113 | } 114 | 115 | // GetBlockAt returns the block at a given coorindate and, as an added bonus 116 | // to faithful callers, it will also return the chunk. A two for one! 117 | // NOTE: restrictions apply! If world xyz doesn't exist in the manger, (nil, nil) 118 | // is returned. 119 | func (m *Manager) GetBlockAt(worldX, worldY, worldZ int) (*Block, *Chunk) { 120 | // see if the chunk actually fits within the geography of the Manager. 121 | totalSize := m.Size * ChunkSize 122 | localX := worldX - m.X*ChunkSize 123 | localY := worldY - m.Y*ChunkSize 124 | localZ := worldZ - m.Z*ChunkSize 125 | if localX < 0 || localX >= totalSize || localY < 0 || localY >= totalSize || localZ < 0 || localZ >= totalSize { 126 | return nil, nil 127 | } 128 | 129 | cX := localX / ChunkSize 130 | cY := localY / ChunkSize 131 | cZ := localZ / ChunkSize 132 | 133 | offset := cY*m.Size*m.Size + cX*m.Size + cZ 134 | ownerChunk := m.Chunks[offset] 135 | if ownerChunk == nil { 136 | return nil, nil 137 | } 138 | 139 | block := ownerChunk.BlockAt(localX-cX*ChunkSize, localY-cY*ChunkSize, localZ-cZ*ChunkSize) 140 | return block, ownerChunk 141 | } 142 | 143 | // SetBlockAt sets the block type and color for a given locaiton in the world. It 144 | // will create a new chunk if necessary. 145 | // It returns the chunk for the block that was set. 146 | // NOTE: Does not update colliders. This was left to the calling client so that 147 | // multiple updates at a time don't needlessly recreate colliders. 148 | func (m *Manager) SetBlockAt(worldX, worldY, worldZ int, bt BlockType) *Chunk { 149 | cX := worldX / ChunkSize 150 | cY := worldY / ChunkSize 151 | cZ := worldZ / ChunkSize 152 | block, chunk := m.GetBlockAt(worldX, worldY, worldZ) 153 | 154 | if chunk != nil { 155 | block.Type = bt 156 | } else { 157 | // the chunk doesn't exist yet, so create a new one 158 | chunk = NewChunk(cX, cY, cZ) 159 | m.RegisterChunk(chunk) 160 | 161 | bX := worldX % ChunkSize 162 | bY := worldY % ChunkSize 163 | bZ := worldZ % ChunkSize 164 | chunk.SetBlock(bX, bY, bZ, bt) 165 | } 166 | 167 | return chunk 168 | } 169 | -------------------------------------------------------------------------------- /examples/voxels/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math/rand" 9 | "os" 10 | "runtime" 11 | 12 | vr "github.com/tbogdala/openvr-go" 13 | chunk "github.com/tbogdala/openvr-go/examples/voxels/chunk" 14 | fizzlevr "github.com/tbogdala/openvr-go/util/fizzlevr" 15 | 16 | glfw "github.com/go-gl/glfw/v3.1/glfw" 17 | mgl "github.com/go-gl/mathgl/mgl32" 18 | 19 | fizzle "github.com/tbogdala/fizzle" 20 | graphics "github.com/tbogdala/fizzle/graphicsprovider" 21 | opengl "github.com/tbogdala/fizzle/graphicsprovider/opengl" 22 | input "github.com/tbogdala/fizzle/input/glfwinput" 23 | fizzlerenderer "github.com/tbogdala/fizzle/renderer" 24 | forward "github.com/tbogdala/fizzle/renderer/forward" 25 | glider "github.com/tbogdala/glider" 26 | noisey "github.com/tbogdala/noisey" 27 | ) 28 | 29 | const ( 30 | voxelShaderPath = "./assets/voxel" 31 | nearView = 0.1 32 | farView = 500.0 33 | worldChunkSize = 12 34 | worldHeightGen = 24 35 | ) 36 | 37 | var ( 38 | windowWidth = int(1280) 39 | windowHeight = int(720) 40 | 41 | gfx graphics.GraphicsProvider 42 | mainWindow *glfw.Window 43 | kbModel *input.KeyboardModel 44 | renderer *forward.ForwardRenderer 45 | renderModelShader *fizzle.RenderShader 46 | lensShader *fizzle.RenderShader 47 | voxelShader *fizzle.RenderShader 48 | colorShader *fizzle.RenderShader 49 | 50 | // interfaces for openvr 51 | vrSystem *vr.System 52 | vrCompositor *vr.Compositor 53 | deviceRenderables *fizzlevr.DeviceRenderables 54 | distortionLens *fizzlevr.DistortionLens 55 | 56 | // render surfaces and transforms 57 | renderWidth uint32 58 | renderHeight uint32 59 | eyeTransforms *vr.EyeTransforms 60 | eyeFramebufferLeft *fizzlevr.EyeFramebuffer 61 | eyeFramebufferRight *fizzlevr.EyeFramebuffer 62 | hmdPose mgl.Mat4 63 | hmdLoc mgl.Vec3 64 | 65 | teleportLine *fizzle.Renderable 66 | chunkMan *chunk.Manager 67 | voxelTextures *fizzle.TextureArray 68 | playerPosition = mgl.Vec3{ 69 | float32(float32(worldChunkSize) / 2.0 * chunk.ChunkSize), 70 | float32(worldHeightGen + 2), 71 | 0.0} //float32(float32(worldChunkSize) / 2.0 * chunk.ChunkSize)} 72 | ) 73 | 74 | func init() { 75 | runtime.LockOSThread() 76 | } 77 | 78 | func main() { 79 | //////////////////////////////////////////////////////////////////////////// 80 | // start off by initializing the GL and GLFW libraries and creating a window. 81 | mainWindow, gfx = initGraphics("Voxels", windowWidth, windowHeight) 82 | 83 | // set the callback functions for key input 84 | kbModel = input.NewKeyboardModel(mainWindow) 85 | kbModel.BindTrigger(glfw.KeyEscape, setShouldClose) 86 | kbModel.SetupCallbacks() 87 | 88 | //////////////////////////////////////////////////////////////////////////// 89 | // attempt to initialize the system 90 | var err error 91 | vrSystem, err = vr.Init() 92 | if err != nil || vrSystem == nil { 93 | fmt.Printf("vr.Init() returned an error: %v\n", err) 94 | os.Exit(1) 95 | } 96 | 97 | // print out some information about the headset as a good smoke test 98 | driver, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropTrackingSystemNameString) 99 | if errInt != vr.TrackedPropSuccess { 100 | fmt.Printf("error getting driver name: %v\n", err) 101 | os.Exit(1) 102 | } 103 | displaySerial, errInt := vrSystem.GetStringTrackedDeviceProperty(int(vr.TrackedDeviceIndexHmd), vr.PropSerialNumberString) 104 | if errInt != vr.TrackedPropSuccess { 105 | fmt.Printf("error getting display name: %v\n", err) 106 | os.Exit(1) 107 | } 108 | fmt.Printf("Connected to %s %s\n", driver, displaySerial) 109 | 110 | //////////////////////////////////////////////////////////////////////////// 111 | // setup VR specifics and the initial scene 112 | 113 | // get the size of the render targets to make 114 | renderWidth, renderHeight = vrSystem.GetRecommendedRenderTargetSize() 115 | fmt.Printf("rec size: %d, %d\n", renderWidth, renderHeight) 116 | 117 | // load up our shaders 118 | err = createShaders() 119 | if err != nil { 120 | fmt.Printf("Error loading shaders: %v\n", err) 121 | os.Exit(1) 122 | } 123 | 124 | // create some objects and lights 125 | createScene(renderWidth, renderHeight) 126 | 127 | // get the eye transforms necessary for the VR HMD 128 | eyeTransforms = vrSystem.GetEyeTransforms(nearView, farView) 129 | 130 | // setup the framebuffers for the eyes 131 | eyeFramebufferLeft, eyeFramebufferRight = fizzlevr.CreateStereoRenderTargets(renderWidth, renderHeight) 132 | 133 | // create the lens distortion object which will be used to render the 134 | // eye framebuffers to the GLFW window. 135 | distortionLens = fizzlevr.CreateDistortionLens(vrSystem, lensShader, eyeFramebufferLeft, eyeFramebufferRight) 136 | 137 | // cache renderables for the connected devices 138 | deviceRenderables, err = fizzlevr.CreateDeviceRenderables(vrSystem, renderModelShader) 139 | if err != nil { 140 | fmt.Printf("Failed to load renderables for the connected devices. " + err.Error() + "\n") 141 | } 142 | 143 | // pull an interface to the compositor 144 | vrCompositor, err = vr.GetCompositor() 145 | if err != nil { 146 | fmt.Printf("Failed to get the compositor interface: %v\n", err) 147 | os.Exit(1) 148 | } 149 | 150 | //////////////////////////////////////////////////////////////////////////// 151 | // the main application loop 152 | for !mainWindow.ShouldClose() { 153 | handleInput() 154 | renderFrame() 155 | } 156 | 157 | vr.Shutdown() 158 | } 159 | 160 | // initGraphics creates an OpenGL window and initializes the required graphics libraries. 161 | // It will either succeed or panic. 162 | func initGraphics(title string, w int, h int) (*glfw.Window, graphics.GraphicsProvider) { 163 | // GLFW must be initialized before it's called 164 | err := glfw.Init() 165 | if err != nil { 166 | panic("Can't init glfw! " + err.Error()) 167 | } 168 | 169 | // request a OpenGL 3.3 core context 170 | glfw.WindowHint(glfw.Samples, 4) 171 | glfw.WindowHint(glfw.ContextVersionMajor, 3) 172 | glfw.WindowHint(glfw.ContextVersionMinor, 3) 173 | glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) 174 | glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) 175 | 176 | // do the actual window creation 177 | mainWindow, err = glfw.CreateWindow(w, h, title, nil, nil) 178 | if err != nil { 179 | panic("Failed to create the main window! " + err.Error()) 180 | } 181 | mainWindow.SetSizeCallback(onWindowResize) 182 | mainWindow.MakeContextCurrent() 183 | 184 | // disable v-sync for max draw rate 185 | glfw.SwapInterval(0) 186 | 187 | // initialize OpenGL 188 | gfx, err := opengl.InitOpenGL() 189 | if err != nil { 190 | panic("Failed to initialize OpenGL! " + err.Error()) 191 | } 192 | fizzle.SetGraphics(gfx) 193 | 194 | return mainWindow, gfx 195 | } 196 | 197 | // setShouldClose should be called to close the window and kill the app. 198 | func setShouldClose() { 199 | mainWindow.SetShouldClose(true) 200 | } 201 | 202 | // onWindowResize should be called when the main window gets resized. 203 | func onWindowResize(w *glfw.Window, width int, height int) { 204 | windowWidth = width 205 | windowHeight = height 206 | } 207 | 208 | // createShaders will load the shaders necessary for the sample to run. 209 | func createShaders() error { 210 | var err error 211 | 212 | // load the shader used to draw the connected devices 213 | renderModelShader, err = fizzle.LoadShaderProgram(vr.ShaderRenderModelV, vr.ShaderRenderModelF, nil) 214 | if err != nil { 215 | return fmt.Errorf("Failed to compile and link the render model shader program!\n%v", err) 216 | } 217 | 218 | // load the shader used to render the framebuffers to a window for viewing 219 | lensShader, err = fizzle.LoadShaderProgram(vr.ShaderLensDistortionV, vr.ShaderLensDistortionF, nil) 220 | if err != nil { 221 | return fmt.Errorf("Failed to compile and link the lens distortion shader program!\n%v", err) 222 | } 223 | 224 | voxelShader, err = fizzle.LoadShaderProgramFromFiles(voxelShaderPath, nil) 225 | if err != nil { 226 | return fmt.Errorf("Failed to compile and link the voxel shader program!\n%v", err) 227 | } 228 | 229 | colorShader, err = forward.CreateColorShader() 230 | if err != nil { 231 | return fmt.Errorf("Failed to compile and link the color shader program!\n%v", err) 232 | } 233 | 234 | return nil 235 | } 236 | 237 | // createScene creates a simple test scene to render. 238 | func createScene(renderWidth, renderHeight uint32) { 239 | // create a new renderer 240 | renderer = forward.NewForwardRenderer(gfx) 241 | renderer.ChangeResolution(int32(renderWidth), int32(renderHeight)) 242 | 243 | // put a light in there 244 | light := renderer.NewDirectionalLight(mgl.Vec3{1.0, -0.5, -1.0}) 245 | light.DiffuseIntensity = 0.70 246 | light.SpecularIntensity = 0.10 247 | light.AmbientIntensity = 0.3 248 | renderer.ActiveLights[0] = light 249 | 250 | const TextureSize = 32 251 | voxelTextureFiles := make(map[string]string) 252 | voxelTextureFiles["Grass"] = "./assets/textures/default_grass.png" 253 | voxelTextureFiles["Dirt"] = "./assets/textures/default_dirt.png" 254 | voxelTextureFiles["Stones"] = "./assets/textures/default_stone_block.png" 255 | // create the texture array object 256 | voxelTextures = fizzle.NewTextureArray(TextureSize, int32(len(voxelTextureFiles))) 257 | err := voxelTextures.LoadImagesFromFiles(voxelTextureFiles, TextureSize, 0) 258 | if err != nil { 259 | fmt.Printf("Failed to load the voxel textures!\n%v", err) 260 | os.Exit(1) 261 | } 262 | 263 | createVoxels(worldChunkSize, worldHeightGen) 264 | } 265 | 266 | func handleInput() { 267 | var controllerState vr.ControllerState 268 | 269 | // advise GLFW to poll for input. without this the window appears to hang. 270 | glfw.PollEvents() 271 | 272 | // handle any keyboard input 273 | kbModel.CheckKeyPresses() 274 | 275 | var event vr.VREvent 276 | for vrSystem.PollNextEvent(&event) { 277 | proccessVREvent(&event) 278 | } 279 | 280 | // destroy any existing teleport lines 281 | wasTeleporting := false 282 | if teleportLine != nil { 283 | teleportLine.Destroy() 284 | teleportLine = nil 285 | wasTeleporting = true 286 | } 287 | 288 | // check controller states 289 | for i := vr.TrackedDeviceIndexHmd + 1; i < vr.MaxTrackedDeviceCount; i++ { 290 | deviceClass := vrSystem.GetTrackedDeviceClass(int(i)) 291 | if deviceClass != vr.TrackedDeviceClassController { 292 | continue 293 | } 294 | 295 | // get the axis state 296 | vrSystem.GetControllerState(int(i), &controllerState) 297 | 298 | // axis 1 should be trigger 299 | const MaxTeleDist = float32(32.0) 300 | triggerVal := controllerState.Axis[1].X 301 | if triggerVal >= 0.99 { 302 | tdp := vrCompositor.GetRenderPose(i) 303 | forward := mgl.Vec4{0.0, 0.0, -1.0, 0.0} 304 | orientation := tdp.DeviceToAbsoluteTracking.Mul4x1(forward) //vec3 return 305 | controllerPosition := tdp.DeviceToAbsoluteTracking.Col(3) 306 | rod := orientation.Mul(100.0) 307 | endPosition := controllerPosition.Add(rod) 308 | teleportLine = fizzle.CreateLineV(controllerPosition, endPosition) 309 | teleportLine.Material = fizzle.NewMaterial() 310 | } else if wasTeleporting && triggerVal > 0.0 { 311 | tdp := vrCompositor.GetRenderPose(i) 312 | forward := mgl.Vec4{0.0, 0.0, -1.0, 0.0} 313 | playerTranslation := mgl.Mat3x4{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, playerPosition[0], playerPosition[1], playerPosition[2]} 314 | orientation := tdp.DeviceToAbsoluteTracking.Mul4x1(forward) //vec3 return 315 | controller3 := tdp.DeviceToAbsoluteTracking.Col(3) 316 | controller4 := mgl.Vec4{controller3[0], controller3[1], controller3[2], 1.0} 317 | controllerWorldPosition := playerTranslation.Mul4x1(controller4) 318 | //rod := orientation.Mul(MaxTeleDist) 319 | //endWorldPosition := controllerWorldPosition.Add(rod) 320 | 321 | // figure out where we hit 322 | var teleRay glider.CollisionRay 323 | teleRay.Origin = mgl.Vec3(controllerWorldPosition) 324 | teleRay.SetDirection(mgl.Vec3(orientation)) 325 | 326 | closestCollision := float32(9999.0) 327 | for _, c := range chunkMan.Chunks { 328 | if c == nil { 329 | continue 330 | } 331 | result, distance := c.AABBCollider.CollideVsRay(&teleRay) 332 | if result == glider.Intersect && distance < MaxTeleDist { 333 | // check chunk's colliders 334 | for _, aabb := range c.BoxColliders { 335 | fineCollide, fineDistance := aabb.CollideVsRay(&teleRay) 336 | if fineCollide == glider.Intersect && fineDistance < closestCollision { 337 | closestCollision = fineDistance 338 | } 339 | } 340 | } 341 | } 342 | 343 | // if we got a distance shorter than max tele, then we found a collision, so teleport 344 | if closestCollision < MaxTeleDist { 345 | // for this voxel game, we don't want to teleport room-style, 346 | // a la The Lab where you can see your play area outline. 347 | // 348 | // here we want to teleport the person, which means we have to 349 | // add the reflection of the absolute controller offset. 350 | teleVector := orientation.Mul(closestCollision) 351 | teleDestination := controllerWorldPosition.Add(teleVector).Sub(controller3) 352 | teleDestination[1] = teleDestination[1] + 2.0 353 | 354 | // update player! 355 | playerPosition = teleDestination 356 | } 357 | } 358 | } 359 | } 360 | 361 | func proccessVREvent(event *vr.VREvent) { 362 | switch event.EventType { 363 | case vr.VREventTrackedDeviceActivated: 364 | // TODO: setup render model 365 | fmt.Printf("Device %d attached.\n", event.TrackedDeviceIndex) 366 | case vr.VREventTrackedDeviceDeactivated: 367 | fmt.Printf("Device %d detached.\n", event.TrackedDeviceIndex) 368 | case vr.VREventTrackedDeviceUpdated: 369 | fmt.Printf("Device %d updated.\n", event.TrackedDeviceIndex) 370 | } 371 | } 372 | 373 | func renderFrame() { 374 | // draw the framebuffers 375 | renderStereoTargets() 376 | 377 | // draw the framebuffers to the window 378 | distortionLens.Render(int32(windowWidth), int32(windowHeight)) 379 | 380 | // send the framebuffer textures out to the compositor for rendering to the HMD 381 | vrCompositor.Submit(vr.EyeLeft, uint32(eyeFramebufferLeft.ResolveTexture)) 382 | vrCompositor.Submit(vr.EyeRight, uint32(eyeFramebufferRight.ResolveTexture)) 383 | 384 | // draw the screen 385 | mainWindow.SwapBuffers() 386 | 387 | // update the HMD pose, which causes a wait to vsync the HMD 388 | updateHMDPose() 389 | } 390 | 391 | type FixedCamera struct { 392 | View mgl.Mat4 393 | Position mgl.Vec3 394 | } 395 | 396 | func (c FixedCamera) GetViewMatrix() mgl.Mat4 { 397 | return c.View 398 | } 399 | func (c FixedCamera) GetPosition() mgl.Vec3 { 400 | return c.Position 401 | } 402 | 403 | // renderScene gets called for each eye and is responsible for 404 | // rendering the entire scene. 405 | func renderScene(eye int) { 406 | gfx.Clear(graphics.COLOR_BUFFER_BIT | graphics.DEPTH_BUFFER_BIT) 407 | gfx.Enable(graphics.DEPTH_TEST) 408 | 409 | var perspective, view, playerView mgl.Mat4 410 | var camera FixedCamera 411 | playerPosition := mgl.Translate3D(-playerPosition[0], -playerPosition[1], -playerPosition[2]) 412 | worldHmdPose := hmdPose.Mul4(playerPosition) 413 | if eye == vr.EyeLeft { 414 | playerView = eyeTransforms.PositionLeft.Mul4(worldHmdPose) 415 | view = eyeTransforms.PositionLeft.Mul4(hmdPose) 416 | perspective = eyeTransforms.ProjectionLeft 417 | camera.View = view 418 | camera.Position = hmdLoc 419 | } else { 420 | playerView = eyeTransforms.PositionRight.Mul4(worldHmdPose) 421 | view = eyeTransforms.PositionRight.Mul4(hmdPose) 422 | perspective = eyeTransforms.ProjectionRight 423 | camera.View = view 424 | camera.Position = hmdLoc 425 | } 426 | 427 | // draw the voxels 428 | for _, c := range chunkMan.Chunks { 429 | if c == nil { 430 | continue 431 | } 432 | r := c.GetTheRenderable(voxelTextures.TextureIndexes) 433 | renderer.DrawRenderableWithShader(r, voxelShader, customVoxelBinder, perspective, playerView, camera) 434 | } 435 | 436 | // draw the teleport line if one is visible 437 | if teleportLine != nil { 438 | renderer.DrawLines(teleportLine, colorShader, nil, perspective, view, camera) 439 | } 440 | 441 | // now draw any devices that get rendered into the scene 442 | deviceRenderables.RenderDevices(vrCompositor, perspective, view, camera) 443 | } 444 | 445 | // renderStereoTargets renders each of the left and right eye framebuffers 446 | // calling renderScene to do the rendering for the scene. 447 | func renderStereoTargets() { 448 | gfx.Enable(graphics.CULL_FACE) 449 | gfx.ClearColor(0.15, 0.15, 0.18, 1.0) // nice background color, but not black 450 | 451 | // left eye 452 | gfx.Enable(graphics.MULTISAMPLE) 453 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFramebufferLeft.RenderFramebuffer) 454 | gfx.Viewport(0, 0, int32(renderWidth), int32(renderHeight)) 455 | renderScene(vr.EyeLeft) 456 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, 0) 457 | gfx.Disable(graphics.MULTISAMPLE) 458 | 459 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, eyeFramebufferLeft.RenderFramebuffer) 460 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, eyeFramebufferLeft.ResolveFramebuffer) 461 | gfx.BlitFramebuffer(0, 0, int32(renderWidth), int32(renderHeight), 0, 0, int32(renderWidth), int32(renderHeight), graphics.COLOR_BUFFER_BIT, graphics.LINEAR) 462 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, 0) 463 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, 0) 464 | 465 | // right eye 466 | gfx.Enable(graphics.MULTISAMPLE) 467 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFramebufferRight.RenderFramebuffer) 468 | gfx.Viewport(0, 0, int32(renderWidth), int32(renderHeight)) 469 | renderScene(vr.EyeRight) 470 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, 0) 471 | gfx.Disable(graphics.MULTISAMPLE) 472 | 473 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, eyeFramebufferRight.RenderFramebuffer) 474 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, eyeFramebufferRight.ResolveFramebuffer) 475 | gfx.BlitFramebuffer(0, 0, int32(renderWidth), int32(renderHeight), 0, 0, int32(renderWidth), int32(renderHeight), graphics.COLOR_BUFFER_BIT, graphics.LINEAR) 476 | gfx.BindFramebuffer(graphics.READ_FRAMEBUFFER, 0) 477 | gfx.BindFramebuffer(graphics.DRAW_FRAMEBUFFER, 0) 478 | 479 | } 480 | 481 | func updateHMDPose() { 482 | // WaitGetPoses is used as a sync point in the OpenVR API. This is on a timer to keep 90fps, so 483 | // the OpenVR gives you that much time to draw a frame. By calling WaitGetPoses() you wait the 484 | // remaining amount of time. If you only used 1ms it will wait 10ms here. If you used 5ms it will wait 6ms. 485 | // (approx.) 486 | vrCompositor.WaitGetPoses(false) 487 | if vrCompositor.IsPoseValid(vr.TrackedDeviceIndexHmd) { 488 | pose := vrCompositor.GetRenderPose(vr.TrackedDeviceIndexHmd) 489 | hmdPose = mgl.Mat4(vr.Mat34ToMat4(&pose.DeviceToAbsoluteTracking)).Inv() 490 | 491 | // FIXME: this is probably broken. 492 | hmdLoc[0] = pose.DeviceToAbsoluteTracking[9] 493 | hmdLoc[1] = pose.DeviceToAbsoluteTracking[10] 494 | hmdLoc[2] = pose.DeviceToAbsoluteTracking[11] 495 | } 496 | } 497 | 498 | // ========================================================================== 499 | 500 | // createVoxels creates the chunk landscape data. 501 | func createVoxels(chunkLength, landScale int) { 502 | const seed1 = 1 503 | const seed2 = 2 504 | voxelGen := NewVoxelGenerator(seed1, seed2) 505 | chunkMan = chunk.NewManager(chunkLength, 0, 0, 0) 506 | 507 | // generate the basic land mass 508 | for y := 0; y <= landScale/chunk.ChunkSize; y++ { 509 | yOffset := y * chunk.ChunkSize 510 | for x := 0; x < chunkLength; x++ { 511 | xOffset := x * chunk.ChunkSize 512 | for z := 0; z < chunkLength; z++ { 513 | zOffset := z * chunk.ChunkSize 514 | // create the chunk at the offset 515 | newChunk := chunk.NewChunk(x, y, z) 516 | 517 | // now loop through the Blocks of the chunk 518 | for cX := 0; cX < chunk.ChunkSize; cX++ { 519 | for cZ := 0; cZ < chunk.ChunkSize; cZ++ { 520 | noise := voxelGen.GetFBM(float32(cX+xOffset), float32(cZ+zOffset)) 521 | heightF := (noise*0.5 + 0.5) * float32(landScale) 522 | heightI := int(heightF) 523 | for cY := 0; cY < chunk.ChunkSize; cY++ { 524 | // index the chunk's Blocks array 525 | chunkI := (cY * chunk.ChunkSize2) + (cX * chunk.ChunkSize) + cZ 526 | 527 | if cY+yOffset == 0 { 528 | // always have a stone floor 529 | newChunk.Blocks[chunkI].Type = chunk.BlockTypeStones 530 | } else if heightI > cY+yOffset { 531 | // if we're under the noise height, default to grass 532 | newChunk.Blocks[chunkI].Type = chunk.BlockTypeGrass 533 | 534 | if heightI-1 > cY+yOffset { 535 | // if we're under the top layer make dirt 536 | newChunk.Blocks[chunkI].Type = chunk.BlockTypeDirt 537 | } else if heightI-3 > cY+yOffset { 538 | // if we're sufficiently deep from the top layer, make stones 539 | newChunk.Blocks[chunkI].Type = chunk.BlockTypeStones 540 | } 541 | } 542 | } // cY 543 | } // cZ 544 | } // cX 545 | 546 | newChunk.UpdateColliders() 547 | chunkMan.RegisterChunk(newChunk) 548 | //fmt.Printf("Chunk registered @ %d, %d, %d\n", xOffset, yOffset, zOffset) 549 | } // z 550 | } // x 551 | } // y 552 | } 553 | 554 | // VoxelGenerator is the structure that contains the random generators for the voxels. 555 | type VoxelGenerator struct { 556 | r1 noisey.RandomSource 557 | r2 noisey.RandomSource 558 | 559 | simplex1 noisey.OpenSimplexGenerator 560 | simplex2 noisey.OpenSimplexGenerator 561 | 562 | hifreq noisey.FBMGenerator2D 563 | lofreq noisey.FBMGenerator2D 564 | flatter noisey.Scale2D 565 | control noisey.FBMGenerator2D 566 | mixer noisey.Select2D 567 | } 568 | 569 | // NewVoxelGenerator returns a new VoxelGenerator structure 570 | func NewVoxelGenerator(seed1, seed2 int64) *VoxelGenerator { 571 | lg := new(VoxelGenerator) 572 | 573 | // setup the random sources 574 | lg.r1 = rand.New(rand.NewSource(seed1)) 575 | lg.r2 = rand.New(rand.NewSource(seed2)) 576 | 577 | // setup the noise sources 578 | lg.simplex1 = noisey.NewOpenSimplexGenerator(lg.r1) 579 | lg.simplex2 = noisey.NewOpenSimplexGenerator(lg.r2) 580 | 581 | // now setup the fBm noise generators and the land selector 582 | lg.lofreq = noisey.NewFBMGenerator2D(&lg.simplex2, 3, 0.25, 1.8, 1.1) 583 | lg.hifreq = noisey.NewFBMGenerator2D(&lg.simplex1, 5, 0.75, 2.1, 1.33) 584 | lg.control = noisey.NewFBMGenerator2D(&lg.simplex2, 2, 0.5, 2.0, 1.0) 585 | 586 | lg.flatter = noisey.NewScale2D(&lg.lofreq, 0.2, 0.1, -1, 1) 587 | lg.mixer = noisey.NewSelect2D(&lg.flatter, &lg.lofreq, &lg.control, 0.3, 100, 0.2) 588 | return lg 589 | } 590 | 591 | // Get3D returns the density of a particaular coordinate 592 | func (gen *VoxelGenerator) Get3D(x, y, z float32) float32 { 593 | v := gen.simplex1.Get3D(float64(x)*0.1, float64(y)*0.1, float64(z)*0.1) 594 | return float32(v) 595 | } 596 | 597 | // GetFBM returns a fract brownian motion noise level for the coordinate 598 | func (gen *VoxelGenerator) GetFBM(x, z float32) float32 { 599 | v := gen.mixer.Get2D(float64(x)*0.1, float64(z)*0.1) 600 | return float32(v) 601 | } 602 | 603 | func customVoxelBinder(renderer fizzlerenderer.Renderer, r *fizzle.Renderable, shader *fizzle.RenderShader, texturesBound *int32) { 604 | const uintSize = 4 605 | gfx := fizzle.GetGraphics() 606 | 607 | shaderTexArray := shader.GetUniformLocation("VOXEL_TEXTURES") 608 | if shaderTexArray >= 0 { 609 | gfx.ActiveTexture(graphics.Texture(graphics.TEXTURE0 + uint32(*texturesBound))) 610 | gfx.BindTexture(graphics.TEXTURE_2D_ARRAY, voxelTextures.Texture) 611 | gfx.Uniform1i(shaderTexArray, *texturesBound) 612 | *texturesBound = *texturesBound + 1 613 | } 614 | 615 | // Note: The following will get turned to floats in the shaders because OpenGL ES 2.0 is 616 | // miserable to work with an can't have int attributes coming from VBO since 617 | // VertexAttribIPointer isn't implemented in v2.0. 618 | 619 | shaderCombo1 := shader.GetAttribLocation("VERTEX_TEXTURE_INDEX") 620 | if shaderCombo1 >= 0 { 621 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.ComboVBO1) 622 | gfx.EnableVertexAttribArray(uint32(shaderCombo1)) 623 | gfx.VertexAttribPointer(uint32(shaderCombo1), 1, graphics.UNSIGNED_INT, false, 2*uintSize, gfx.PtrOffset(0)) 624 | } 625 | 626 | shaderFakeAO := shader.GetAttribLocation("VERTEX_VOXEL_BF") 627 | if shaderFakeAO >= 0 { 628 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.ComboVBO1) 629 | gfx.EnableVertexAttribArray(uint32(shaderFakeAO)) 630 | gfx.VertexAttribPointer(uint32(shaderFakeAO), 1, graphics.UNSIGNED_INT, false, 2*uintSize, gfx.PtrOffset(uintSize)) 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /ivrchaperone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package openvr 5 | 6 | /* 7 | #include 8 | #include 9 | #include "openvr_capi.h" 10 | 11 | extern struct VR_IVRSystem_FnTable* _iSystem; 12 | 13 | 14 | 15 | // _____ _ _ ______ _____ _ 16 | // |_ _|| | | || ___ \/ __ \| | 17 | // | | | | | || |_/ /| / \/| |__ __ _ _ __ ___ _ __ ___ _ __ ___ 18 | // | | | | | || / | | | '_ \ / _` || '_ \ / _ \| '__| / _ \ | '_ \ / _ \ 19 | // _| |_ \ \_/ /| |\ \ | \__/\| | | || (_| || |_) || __/| | | (_) || | | || __/ 20 | // \___/ \___/ \_| \_| \____/|_| |_| \__,_|| .__/ \___||_| \___/ |_| |_| \___| 21 | // | | 22 | // |_| 23 | 24 | int chaperone_GetCalibrationState(struct VR_IVRChaperone_FnTable* iChaperone) { 25 | return (int)iChaperone->GetCalibrationState(); 26 | } 27 | 28 | void chaperone_GetPlayAreaSize(struct VR_IVRChaperone_FnTable* iChaperone, float* x, float* y) { 29 | iChaperone->GetPlayAreaSize(x, y); 30 | } 31 | 32 | void chaperone_GetPlayAreaRect(struct VR_IVRChaperone_FnTable* iChaperone, struct HmdQuad_t* rect) { 33 | iChaperone->GetPlayAreaRect(rect); 34 | } 35 | 36 | 37 | */ 38 | import "C" 39 | 40 | import ( 41 | mgl "github.com/go-gl/mathgl/mgl32" 42 | ) 43 | 44 | // Chaperone is an interface wrapper to IVRChaperone. 45 | type Chaperone struct { 46 | ptr *C.struct_VR_IVRChaperone_FnTable 47 | } 48 | 49 | // GetCalibrationState returns a ChaperoneCalibrationState enumeration 50 | // value indicating the current calibration state. 51 | // Note: Tis can change at any time during a session. 52 | func (chap *Chaperone) GetCalibrationState() int { 53 | result := int(C.chaperone_GetCalibrationState(chap.ptr)) 54 | return result 55 | } 56 | 57 | // GetPlayAreaSize returns the width and depth of the play area. 58 | func (chap *Chaperone) GetPlayAreaSize() (float32, float32) { 59 | var cx, cz C.float 60 | C.chaperone_GetPlayAreaSize(chap.ptr, &cx, &cz) 61 | return float32(cx), float32(cz) 62 | } 63 | 64 | // GetPlayAreaRect returns vectors for the 4 corners of the play area 65 | // in a counter-clockwise order. (0,0,0) is the center of the play area. 66 | // The height of every corner should be 0Y. 67 | func (chap *Chaperone) GetPlayAreaRect() [4]mgl.Vec3 { 68 | var crekt C.struct_HmdQuad_t 69 | C.chaperone_GetPlayAreaRect(chap.ptr, &crekt) 70 | 71 | var result [4]mgl.Vec3 72 | result[0][0] = float32(crekt.vCorners[0].v[0]) 73 | result[0][1] = float32(crekt.vCorners[0].v[1]) 74 | result[0][2] = float32(crekt.vCorners[0].v[2]) 75 | 76 | result[1][0] = float32(crekt.vCorners[1].v[0]) 77 | result[1][1] = float32(crekt.vCorners[1].v[1]) 78 | result[1][2] = float32(crekt.vCorners[1].v[2]) 79 | 80 | result[2][0] = float32(crekt.vCorners[2].v[0]) 81 | result[2][1] = float32(crekt.vCorners[2].v[1]) 82 | result[2][2] = float32(crekt.vCorners[2].v[2]) 83 | 84 | result[3][0] = float32(crekt.vCorners[3].v[0]) 85 | result[3][1] = float32(crekt.vCorners[3].v[1]) 86 | result[3][2] = float32(crekt.vCorners[3].v[2]) 87 | return result 88 | } 89 | 90 | /* 91 | TODO: 92 | 93 | void (OPENVR_FNTABLE_CALLTYPE *ReloadInfo)(); 94 | void (OPENVR_FNTABLE_CALLTYPE *SetSceneColor)(struct HmdColor_t color); 95 | void (OPENVR_FNTABLE_CALLTYPE *GetBoundsColor)(struct HmdColor_t * pOutputColorArray, int nNumOutputColors, float flCollisionBoundsFadeDistance, struct HmdColor_t * pOutputCameraColor); 96 | bool (OPENVR_FNTABLE_CALLTYPE *AreBoundsVisible)(); 97 | void (OPENVR_FNTABLE_CALLTYPE *ForceBoundsVisible)(bool bForce); 98 | */ 99 | -------------------------------------------------------------------------------- /ivrcompositor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package openvr 5 | 6 | /* 7 | #include 8 | #include 9 | #include "openvr_capi.h" 10 | 11 | extern struct VR_IVRSystem_FnTable* _iSystem; 12 | 13 | // .___ ____ ______________ _________ .__ __ 14 | // | |\ \ / /\______ \\_ ___ \ ____ _____ ______ ____ ______|__|_/ |_ ____ _______ 15 | // | | \ Y / | _// \ \/ / _ \ / \ \____ \ / _ \ / ___/| |\ __\ / _ \ \_ __ \ 16 | // | | \ / | | \\ \____( <_> )| Y Y \| |_> >( <_> ) \___ \ | | | | ( <_> ) | | \/ 17 | // |___| \___/ |____|_ / \______ / \____/ |__|_| /| __/ \____/ /____ >|__| |__| \____/ |__| 18 | // \/ \/ \/ |__| \/ 19 | 20 | 21 | 22 | EVRCompositorError compositor_WaitGetPoses(struct VR_IVRCompositor_FnTable* iCompositor, struct TrackedDevicePose_t * pRenderPoseArray, uint32_t unRenderPoseArrayCount, struct TrackedDevicePose_t * pGamePoseArray, uint32_t unGamePoseArrayCount) { 23 | return iCompositor->WaitGetPoses(pRenderPoseArray, unRenderPoseArrayCount, pGamePoseArray, unGamePoseArrayCount); 24 | } 25 | 26 | 27 | 28 | EVRCompositorError compositor_Submit(struct VR_IVRCompositor_FnTable* iCompositor, EVREye eEye, struct Texture_t * pTexture, struct VRTextureBounds_t * pBounds, EVRSubmitFlags nSubmitFlags) { 29 | return iCompositor->Submit(eEye, pTexture, pBounds, nSubmitFlags); 30 | } 31 | 32 | EVRCompositorError compositor_SubmitSimple(struct VR_IVRCompositor_FnTable* iCompositor, EVREye eEye, intptr_t texture) { 33 | struct Texture_t tex; 34 | tex.handle = (void*) texture; 35 | tex.eType = ETextureType_TextureType_OpenGL; 36 | tex.eColorSpace = EColorSpace_ColorSpace_Gamma; 37 | return iCompositor->Submit(eEye, &tex, 0, EVRSubmitFlags_Submit_Default); 38 | } 39 | 40 | float compositor_GetFrameTimeRemaining(struct VR_IVRCompositor_FnTable* iCompositor) { 41 | return iCompositor->GetFrameTimeRemaining(); 42 | } 43 | 44 | bool compositor_GetFrameTiming(struct VR_IVRCompositor_FnTable* iCompositor, struct Compositor_FrameTiming * pTiming, uint32_t unFramesAgo) { 45 | pTiming->m_nSize = sizeof(struct Compositor_FrameTiming); 46 | return iCompositor->GetFrameTiming(pTiming, unFramesAgo); 47 | } 48 | 49 | 50 | */ 51 | import "C" 52 | 53 | import ( 54 | "bytes" 55 | "fmt" 56 | 57 | mgl "github.com/go-gl/mathgl/mgl32" 58 | ) 59 | 60 | // TrackedDevicePose mirrors the OpenVR TrackedDevicePose_t structure. 61 | type TrackedDevicePose struct { 62 | DeviceToAbsoluteTracking mgl.Mat3x4 63 | Velocity mgl.Vec3 // velocity in tracker space in m/s 64 | AngularVelocity mgl.Vec3 // in radians/s 65 | TrackingResult int // ETrackingResult enum value 66 | PoseIsValid bool 67 | 68 | // This indicates that there is a device connected for this spot in the pose array. 69 | // It could go from true to false if the user unplugs the device. 70 | DeviceIsConnected bool 71 | } 72 | 73 | // Compositor is an interface wrapper to IVRCompositor. 74 | type Compositor struct { 75 | ptr *C.struct_VR_IVRCompositor_FnTable 76 | 77 | renderPoseArray [MaxTrackedDeviceCount]C.struct_TrackedDevicePose_t 78 | gamePoseArray [MaxTrackedDeviceCount]C.struct_TrackedDevicePose_t 79 | } 80 | 81 | // WaitGetPoses updates the internal copy of pose(s) to use to render scene (and optionally poses predicted two frames out for gameplay). 82 | func (comp *Compositor) WaitGetPoses(getPredictions bool) { 83 | if getPredictions { 84 | C.compositor_WaitGetPoses(comp.ptr, &comp.renderPoseArray[0], C.uint32_t(MaxTrackedDeviceCount), &comp.gamePoseArray[0], C.uint32_t(MaxTrackedDeviceCount)) 85 | } else { 86 | C.compositor_WaitGetPoses(comp.ptr, &comp.renderPoseArray[0], C.uint32_t(MaxTrackedDeviceCount), nil, 0) 87 | } 88 | } 89 | 90 | // Submit updates scene texture to display. 91 | func (comp *Compositor) Submit(eye int, texture uint32) { 92 | C.compositor_SubmitSimple(comp.ptr, C.EVREye(eye), C.intptr_t(texture)) 93 | } 94 | 95 | // IsPoseValid returns true if a render pose array at the given index has a valid pose. 96 | func (comp *Compositor) IsPoseValid(i uint) bool { 97 | if convertCBool2Int(comp.renderPoseArray[i].bPoseIsValid) != 0 { 98 | return true 99 | } 100 | return false 101 | } 102 | 103 | // GetFrameTimeRemaining returns the time in seconds left in the current (as identified by 104 | // FrameTiming's frameIndex) frame. Due to "running start", this value may roll over 105 | // to the next frame before ever reaching 0.0. 106 | func (comp *Compositor) GetFrameTimeRemaining() float32 { 107 | return float32(C.compositor_GetFrameTimeRemaining(comp.ptr)) 108 | } 109 | 110 | // GetRenderPose gets the render pose for a device at the given index. 111 | func (comp *Compositor) GetRenderPose(i uint) (tdp TrackedDevicePose) { 112 | cTDP := comp.renderPoseArray[i] 113 | fillTrackedDevicePose(&tdp, &cTDP) 114 | return tdp 115 | } 116 | 117 | func fillTrackedDevicePose(tdp *TrackedDevicePose, cTDP *C.struct_TrackedDevicePose_t) { 118 | tdp.DeviceToAbsoluteTracking[0] = float32(cTDP.mDeviceToAbsoluteTracking.m[0][0]) 119 | tdp.DeviceToAbsoluteTracking[3] = float32(cTDP.mDeviceToAbsoluteTracking.m[0][1]) 120 | tdp.DeviceToAbsoluteTracking[6] = float32(cTDP.mDeviceToAbsoluteTracking.m[0][2]) 121 | tdp.DeviceToAbsoluteTracking[9] = float32(cTDP.mDeviceToAbsoluteTracking.m[0][3]) 122 | 123 | tdp.DeviceToAbsoluteTracking[1] = float32(cTDP.mDeviceToAbsoluteTracking.m[1][0]) 124 | tdp.DeviceToAbsoluteTracking[4] = float32(cTDP.mDeviceToAbsoluteTracking.m[1][1]) 125 | tdp.DeviceToAbsoluteTracking[7] = float32(cTDP.mDeviceToAbsoluteTracking.m[1][2]) 126 | tdp.DeviceToAbsoluteTracking[10] = float32(cTDP.mDeviceToAbsoluteTracking.m[1][3]) 127 | 128 | tdp.DeviceToAbsoluteTracking[2] = float32(cTDP.mDeviceToAbsoluteTracking.m[2][0]) 129 | tdp.DeviceToAbsoluteTracking[5] = float32(cTDP.mDeviceToAbsoluteTracking.m[2][1]) 130 | tdp.DeviceToAbsoluteTracking[8] = float32(cTDP.mDeviceToAbsoluteTracking.m[2][2]) 131 | tdp.DeviceToAbsoluteTracking[11] = float32(cTDP.mDeviceToAbsoluteTracking.m[2][3]) 132 | 133 | tdp.Velocity[0] = float32(cTDP.vVelocity.v[0]) 134 | tdp.Velocity[1] = float32(cTDP.vVelocity.v[1]) 135 | tdp.Velocity[2] = float32(cTDP.vVelocity.v[2]) 136 | 137 | tdp.AngularVelocity[0] = float32(cTDP.vAngularVelocity.v[0]) 138 | tdp.AngularVelocity[1] = float32(cTDP.vAngularVelocity.v[1]) 139 | tdp.AngularVelocity[2] = float32(cTDP.vAngularVelocity.v[2]) 140 | 141 | tdp.TrackingResult = int(cTDP.eTrackingResult) 142 | 143 | if convertCBool2Int(cTDP.bPoseIsValid) != 0 { 144 | tdp.PoseIsValid = true 145 | } 146 | 147 | if convertCBool2Int(cTDP.bDeviceIsConnected) != 0 { 148 | tdp.DeviceIsConnected = true 149 | } 150 | } 151 | 152 | // FrameTiming provides a single frame's timing information to the app. 153 | type FrameTiming struct { 154 | FrameIndex uint32 155 | NumFramePresents uint32 156 | NumMisPresented uint32 157 | NumDroppedFrames uint32 158 | ReprojectionFlags uint32 159 | SystemTimeInSeconds float64 160 | PreSubmitGpuMs float32 161 | PostSubmitGpuMs float32 162 | TotalRenderGpuMs float32 163 | CompositorRenderGpuMs float32 164 | CompositorRenderCpuMs float32 165 | CompositorIdleCpuMs float32 166 | ClientFrameIntervalMs float32 167 | PresentCallCpuMs float32 168 | WaitForPresentCpuMs float32 169 | SubmitFrameMs float32 170 | WaitGetPosesCalledMs float32 171 | NewPosesReadyMs float32 172 | NewFrameReadyMs float32 173 | CompositorUpdateStartMs float32 174 | CompositorUpdateEndMs float32 175 | CompositorRenderStartMs float32 176 | HmdPose TrackedDevicePose 177 | } 178 | 179 | // GetFrameTiming teturns true if timing data is filled it. Sets oldest timing info if framesAgo 180 | // is larger than the stored history. 181 | func (comp *Compositor) GetFrameTiming(timing *FrameTiming, framesAgo uint32) bool { 182 | var cTimingData C.struct_Compositor_FrameTiming 183 | cRet := C.compositor_GetFrameTiming(comp.ptr, &cTimingData, C.uint32_t(framesAgo)) 184 | 185 | timing.FrameIndex = uint32(cTimingData.m_nFrameIndex) 186 | timing.NumFramePresents = uint32(cTimingData.m_nNumFramePresents) 187 | timing.NumMisPresented = uint32(cTimingData.m_nNumMisPresented) 188 | timing.NumDroppedFrames = uint32(cTimingData.m_nNumDroppedFrames) 189 | timing.ReprojectionFlags = uint32(cTimingData.m_nReprojectionFlags) 190 | timing.SystemTimeInSeconds = float64(cTimingData.m_flSystemTimeInSeconds) 191 | timing.PreSubmitGpuMs = float32(cTimingData.m_flPreSubmitGpuMs) 192 | timing.PostSubmitGpuMs = float32(cTimingData.m_flPostSubmitGpuMs) 193 | timing.TotalRenderGpuMs = float32(cTimingData.m_flTotalRenderGpuMs) 194 | timing.CompositorRenderGpuMs = float32(cTimingData.m_flCompositorRenderGpuMs) 195 | timing.CompositorRenderCpuMs = float32(cTimingData.m_flCompositorRenderCpuMs) 196 | timing.CompositorIdleCpuMs = float32(cTimingData.m_flCompositorIdleCpuMs) 197 | timing.ClientFrameIntervalMs = float32(cTimingData.m_flClientFrameIntervalMs) 198 | timing.PresentCallCpuMs = float32(cTimingData.m_flPresentCallCpuMs) 199 | timing.WaitForPresentCpuMs = float32(cTimingData.m_flWaitForPresentCpuMs) 200 | timing.SubmitFrameMs = float32(cTimingData.m_flSubmitFrameMs) 201 | timing.WaitGetPosesCalledMs = float32(cTimingData.m_flWaitGetPosesCalledMs) 202 | timing.NewPosesReadyMs = float32(cTimingData.m_flNewPosesReadyMs) 203 | timing.NewFrameReadyMs = float32(cTimingData.m_flNewFrameReadyMs) 204 | timing.CompositorUpdateStartMs = float32(cTimingData.m_flCompositorUpdateStartMs) 205 | timing.CompositorUpdateEndMs = float32(cTimingData.m_flCompositorUpdateEndMs) 206 | timing.CompositorRenderStartMs = float32(cTimingData.m_flCompositorRenderStartMs) 207 | 208 | fillTrackedDevicePose(&timing.HmdPose, &cTimingData.m_HmdPose) 209 | 210 | if convertCBool2Int(cRet) == 0 { 211 | return false 212 | } 213 | 214 | return true 215 | } 216 | 217 | // Show returns a formatted string with the timing information. If newlines 218 | // is true, then each field will be written on its own line in the string. 219 | func (ft *FrameTiming) Show(newlines bool) string { 220 | var b bytes.Buffer 221 | nl := " " 222 | if newlines { 223 | nl = "\n" 224 | } 225 | 226 | b.WriteString(fmt.Sprintf("FrameIndex: %v%s", ft.FrameIndex, nl)) 227 | b.WriteString(fmt.Sprintf("NumFramePresent: %v%s", ft.NumFramePresents, nl)) 228 | b.WriteString(fmt.Sprintf("NumMisPresented: %v%s", ft.NumMisPresented, nl)) 229 | b.WriteString(fmt.Sprintf("NumDroppedFrames: %v%s", ft.NumDroppedFrames, nl)) 230 | b.WriteString(fmt.Sprintf("ReprojectionFlags: %v%s", ft.ReprojectionFlags, nl)) 231 | b.WriteString(fmt.Sprintf("SystemTimeInSeconds: %v%s", ft.SystemTimeInSeconds, nl)) 232 | b.WriteString(fmt.Sprintf("PreSubmitGpuMs: %v%s", ft.PreSubmitGpuMs, nl)) 233 | b.WriteString(fmt.Sprintf("PostSubmitGpuMs: %v%s", ft.PostSubmitGpuMs, nl)) 234 | b.WriteString(fmt.Sprintf("TotalRenderGpuMs: %v%s", ft.TotalRenderGpuMs, nl)) 235 | b.WriteString(fmt.Sprintf("CompositorRenderGpuMs: %v%s", ft.CompositorRenderGpuMs, nl)) 236 | b.WriteString(fmt.Sprintf("CompositorRenderCpuMs: %v%s", ft.CompositorRenderCpuMs, nl)) 237 | b.WriteString(fmt.Sprintf("CompositorIdleCpuMs: %v%s", ft.CompositorIdleCpuMs, nl)) 238 | b.WriteString(fmt.Sprintf("ClientFrameIntervalMs: %v%s", ft.ClientFrameIntervalMs, nl)) 239 | b.WriteString(fmt.Sprintf("PresentCallCpuMs: %v%s", ft.PresentCallCpuMs, nl)) 240 | b.WriteString(fmt.Sprintf("WaitForPresentCpuMs: %v%s", ft.WaitForPresentCpuMs, nl)) 241 | b.WriteString(fmt.Sprintf("SubmitFrameMs: %v%s", ft.SubmitFrameMs, nl)) 242 | b.WriteString(fmt.Sprintf("WaitGetPosesCalledMs: %v%s", ft.WaitGetPosesCalledMs, nl)) 243 | b.WriteString(fmt.Sprintf("NewPosesReadyMs: %v%s", ft.NewPosesReadyMs, nl)) 244 | b.WriteString(fmt.Sprintf("NewFrameReadyMs: %v%s", ft.NewFrameReadyMs, nl)) 245 | b.WriteString(fmt.Sprintf("CompositorUpdateStartMs: %v%s", ft.CompositorUpdateStartMs, nl)) 246 | b.WriteString(fmt.Sprintf("CompositorUpdateEndMs: %v%s", ft.CompositorUpdateEndMs, nl)) 247 | b.WriteString(fmt.Sprintf("CompositorRenderStartMs: %v%s", ft.CompositorRenderStartMs, nl)) 248 | 249 | return b.String() 250 | } 251 | 252 | /* TODO: 253 | 254 | struct VR_IVRCompositor_FnTable 255 | { 256 | void (OPENVR_FNTABLE_CALLTYPE *SetTrackingSpace)(ETrackingUniverseOrigin eOrigin); 257 | ETrackingUniverseOrigin (OPENVR_FNTABLE_CALLTYPE *GetTrackingSpace)(); 258 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *GetLastPoses)(struct TrackedDevicePose_t * pRenderPoseArray, uint32_t unRenderPoseArrayCount, struct TrackedDevicePose_t * pGamePoseArray, uint32_t unGamePoseArrayCount); 259 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *GetLastPoseForTrackedDeviceIndex)(TrackedDeviceIndex_t unDeviceIndex, struct TrackedDevicePose_t * pOutputPose, struct TrackedDevicePose_t * pOutputGamePose); 260 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *Submit)(EVREye eEye, struct Texture_t * pTexture, struct VRTextureBounds_t * pBounds, EVRSubmitFlags nSubmitFlags); 261 | void (OPENVR_FNTABLE_CALLTYPE *ClearLastSubmittedFrame)(); 262 | void (OPENVR_FNTABLE_CALLTYPE *PostPresentHandoff)(); 263 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetFrameTimings)(struct Compositor_FrameTiming * pTiming, uint32_t nFrames); 264 | void (OPENVR_FNTABLE_CALLTYPE *GetCumulativeStats)(struct Compositor_CumulativeStats * pStats, uint32_t nStatsSizeInBytes); 265 | void (OPENVR_FNTABLE_CALLTYPE *FadeToColor)(float fSeconds, float fRed, float fGreen, float fBlue, float fAlpha, bool bBackground); 266 | struct HmdColor_t (OPENVR_FNTABLE_CALLTYPE *GetCurrentFadeColor)(bool bBackground); 267 | void (OPENVR_FNTABLE_CALLTYPE *FadeGrid)(float fSeconds, bool bFadeIn); 268 | float (OPENVR_FNTABLE_CALLTYPE *GetCurrentGridAlpha)(); 269 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *SetSkyboxOverride)(struct Texture_t * pTextures, uint32_t unTextureCount); 270 | void (OPENVR_FNTABLE_CALLTYPE *ClearSkyboxOverride)(); 271 | void (OPENVR_FNTABLE_CALLTYPE *CompositorBringToFront)(); 272 | void (OPENVR_FNTABLE_CALLTYPE *CompositorGoToBack)(); 273 | void (OPENVR_FNTABLE_CALLTYPE *CompositorQuit)(); 274 | bool (OPENVR_FNTABLE_CALLTYPE *IsFullscreen)(); 275 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetCurrentSceneFocusProcess)(); 276 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetLastFrameRenderer)(); 277 | bool (OPENVR_FNTABLE_CALLTYPE *CanRenderScene)(); 278 | void (OPENVR_FNTABLE_CALLTYPE *ShowMirrorWindow)(); 279 | void (OPENVR_FNTABLE_CALLTYPE *HideMirrorWindow)(); 280 | bool (OPENVR_FNTABLE_CALLTYPE *IsMirrorWindowVisible)(); 281 | void (OPENVR_FNTABLE_CALLTYPE *CompositorDumpImages)(); 282 | bool (OPENVR_FNTABLE_CALLTYPE *ShouldAppRenderWithLowResources)(); 283 | void (OPENVR_FNTABLE_CALLTYPE *ForceInterleavedReprojectionOn)(bool bOverride); 284 | void (OPENVR_FNTABLE_CALLTYPE *ForceReconnectProcess)(); 285 | void (OPENVR_FNTABLE_CALLTYPE *SuspendRendering)(bool bSuspend); 286 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *RequestScreenshot)(EVRScreenshotType type, char * pchDestinationFileName, char * pchVRDestinationFileName); 287 | EVRScreenshotType (OPENVR_FNTABLE_CALLTYPE *GetCurrentScreenshotType)(); 288 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *GetMirrorTextureD3D11)(EVREye eEye, void * pD3D11DeviceOrResource, void ** ppD3D11ShaderResourceView); 289 | EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *GetMirrorTextureGL)(EVREye eEye, glUInt_t * pglTextureId, glSharedTextureHandle_t * pglSharedTextureHandle); 290 | bool (OPENVR_FNTABLE_CALLTYPE *ReleaseSharedGLTexture)(glUInt_t glTextureId, glSharedTextureHandle_t glSharedTextureHandle); 291 | void (OPENVR_FNTABLE_CALLTYPE *LockGLSharedTextureForAccess)(glSharedTextureHandle_t glSharedTextureHandle); 292 | void (OPENVR_FNTABLE_CALLTYPE *UnlockGLSharedTextureForAccess)(glSharedTextureHandle_t glSharedTextureHandle); 293 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetVulkanInstanceExtensionsRequired)(char * pchValue, uint32_t unBufferSize); 294 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetVulkanDeviceExtensionsRequired)(struct VkPhysicalDevice_T * pPhysicalDevice, char * pchValue, uint32_t unBufferSize); 295 | void (OPENVR_FNTABLE_CALLTYPE *SetExplicitTimingMode)(bool bExplicitTimingMode); 296 | + EVRCompositorError (OPENVR_FNTABLE_CALLTYPE *SubmitExplicitTimingData)(); 297 | }; 298 | */ 299 | -------------------------------------------------------------------------------- /ivrrendermodels.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package openvr 5 | 6 | /* 7 | #include 8 | #include 9 | #include "openvr_capi.h" 10 | 11 | extern struct VR_IVRSystem_FnTable* _iSystem; 12 | 13 | // _____ _ _ ______ ______ _ ______ _ _ 14 | // (_____)| | | |(_____ \ (_____ \ | | | ___ \ | | | | 15 | // _ | | | | _____) ) _____) ) ____ ____ _ | | ____ ____ | | _ | | ___ _ | | ____ | | ___ 16 | // | | \ \/ / (_____ ( (_____ ( / _ )| _ \ / || | / _ ) / ___)| || || | / _ \ / || | / _ )| | /___) 17 | // _| |_ \ / | | | |( (/ / | | | |( (_| |( (/ / | | | || || || |_| |( (_| |( (/ / | ||___ | 18 | // (_____) \/ |_| |_| \____)|_| |_| \____| \____)|_| |_||_||_| \___/ \____| \____)|_|(___/ 19 | 20 | EVRRenderModelError rendermodels_LoadRenderModel_Async(struct VR_IVRRenderModels_FnTable* iRenderModels, char * pchRenderModelName, struct RenderModel_t ** ppRenderModel) { 21 | return iRenderModels->LoadRenderModel_Async(pchRenderModelName, ppRenderModel); 22 | } 23 | 24 | EVRRenderModelError rendermodels_LoadTexture_Async(struct VR_IVRRenderModels_FnTable* iRenderModels, TextureID_t textureId, struct RenderModel_TextureMap_t ** ppTexture) { 25 | return iRenderModels->LoadTexture_Async(textureId, ppTexture); 26 | } 27 | 28 | void rendermodels_FreeRenderModel(struct VR_IVRRenderModels_FnTable* iRenderModels, struct RenderModel_t * pRenderModel) { 29 | iRenderModels->FreeRenderModel(pRenderModel); 30 | } 31 | 32 | void rendermodels_FreeTexture(struct VR_IVRRenderModels_FnTable* iRenderModels, struct RenderModel_TextureMap_t * pTexture) { 33 | iRenderModels->FreeTexture(pTexture); 34 | } 35 | 36 | uint32_t rendermodels_GetRenderModelCount(struct VR_IVRRenderModels_FnTable* iRenderModels) { 37 | return iRenderModels->GetRenderModelCount(); 38 | } 39 | 40 | char* rendermodels_GetRenderModelName(struct VR_IVRRenderModels_FnTable* iRenderModels, uint32_t renderModelIndex) { 41 | uint32_t lenRequired = iRenderModels->GetRenderModelName(renderModelIndex, NULL, 0); 42 | if (lenRequired == 0) { 43 | return ""; 44 | } 45 | 46 | char* result = malloc(lenRequired + 1); 47 | iRenderModels->GetRenderModelName(renderModelIndex, result, lenRequired + 1); 48 | return result; 49 | } 50 | 51 | uint32_t rendermodels_GetComponentCount(struct VR_IVRRenderModels_FnTable* iRenderModels, char* renderModelName) { 52 | return iRenderModels->GetComponentCount(renderModelName); 53 | } 54 | 55 | char* rendermodels_GetComponentName(struct VR_IVRRenderModels_FnTable* iRenderModels, char* renderModelName, uint32_t componentIndex) { 56 | uint32_t lenRequired = iRenderModels->GetComponentName(renderModelName, componentIndex, NULL, 0); 57 | if (lenRequired == 0) { 58 | return ""; 59 | } 60 | 61 | char* result = malloc(lenRequired + 1); 62 | iRenderModels->GetComponentName(renderModelName, componentIndex, result, lenRequired + 1); 63 | return result; 64 | } 65 | 66 | char* rendermodels_GetComponentRenderModelName(struct VR_IVRRenderModels_FnTable* iRenderModels, char* renderModelName, char* componentName) { 67 | uint32_t lenRequired = iRenderModels->GetComponentRenderModelName(renderModelName, componentName, NULL, 0); 68 | if (lenRequired == 0) { 69 | return ""; 70 | } 71 | 72 | char* result = malloc(lenRequired + 1); 73 | iRenderModels->GetComponentRenderModelName(renderModelName, componentName, result, lenRequired + 1); 74 | return result; 75 | } 76 | 77 | void flatenRenderModelsTextureData(const struct RenderModel_TextureMap_t* textureData, uint8_t* dest) { 78 | int max = textureData->unWidth * textureData->unHeight * 4; 79 | for (int i=0; irubTextureMapData[i]; 81 | } 82 | } 83 | 84 | // loop through the renderModel's vertex data and copy it to a flat float array for Go to use. 85 | void flatenRenderModelsVertexData(const struct RenderModel_t * renderModel, float* dest, unsigned int* destIndexes) { 86 | const int fsize = 8; 87 | for (int i=0; iunVertexCount; i++) { 88 | const struct RenderModel_Vertex_t *vert = &renderModel->rVertexData[i]; 89 | dest[i*fsize] = vert->vPosition.v[0]; 90 | dest[i*fsize+1] = vert->vPosition.v[1]; 91 | dest[i*fsize+2] = vert->vPosition.v[2]; 92 | 93 | dest[i*fsize+3] = vert->vNormal.v[0]; 94 | dest[i*fsize+4] = vert->vNormal.v[1]; 95 | dest[i*fsize+5] = vert->vNormal.v[2]; 96 | 97 | dest[i*fsize+6] = vert->rfTextureCoord[0]; 98 | dest[i*fsize+7] = vert->rfTextureCoord[1]; 99 | } 100 | 101 | for (int i=0; iunTriangleCount * 3; i++) { 102 | destIndexes[i] = renderModel->rIndexData[i]; 103 | } 104 | } 105 | 106 | */ 107 | import "C" 108 | 109 | import ( 110 | "fmt" 111 | "runtime" 112 | "unsafe" 113 | ) 114 | 115 | // RenderModel contains all the vertex, face index and texture data required 116 | // to render an object. 117 | type RenderModel struct { 118 | VertexData []float32 119 | Indexes []uint32 120 | TriangleCount uint32 121 | 122 | TextureWidth uint32 123 | TextureHeight uint32 124 | TextureBytes []byte 125 | } 126 | 127 | func newRenderModel() *RenderModel { 128 | model := new(RenderModel) 129 | return model 130 | } 131 | 132 | // RenderModels is an interface wrapper to IVRRenderModels. 133 | type RenderModels struct { 134 | ptr *C.struct_VR_IVRRenderModels_FnTable 135 | } 136 | 137 | // RenderModelLoad syncrhonously loads the model. 138 | func (rm *RenderModels) RenderModelLoad(name string) (*RenderModel, error) { 139 | var cModel *C.struct_RenderModel_t 140 | var result C.EVRRenderModelError 141 | csName := C.CString(name) 142 | defer C.free(unsafe.Pointer(csName)) 143 | for { 144 | result = C.rendermodels_LoadRenderModel_Async(rm.ptr, csName, &cModel) 145 | if result != VRRenderModelErrorLoading { 146 | break 147 | } 148 | runtime.Gosched() 149 | } 150 | 151 | // we now have the model, right? 152 | if result != VRRenderModelErrorNone || cModel == nil { 153 | return nil, fmt.Errorf("Failed to load render model for %s: %s", name, GetErrorAsEnglish(int(result))) 154 | } 155 | 156 | var cTexture *C.struct_RenderModel_TextureMap_t 157 | for { 158 | result = C.rendermodels_LoadTexture_Async(rm.ptr, cModel.diffuseTextureId, &cTexture) 159 | if result != VRRenderModelErrorLoading { 160 | break 161 | } 162 | runtime.Gosched() 163 | } 164 | 165 | // we now have the texture, right? 166 | if result != VRRenderModelErrorNone { 167 | return nil, fmt.Errorf("Failed to load render model texture for %s: %s (%d)", name, GetErrorAsEnglish(int(result)), result) 168 | } 169 | 170 | // create the render model with the data from the C structures 171 | model := newRenderModel() 172 | model.VertexData = make([]float32, int(cModel.unVertexCount*8)) 173 | model.Indexes = make([]uint32, int(cModel.unTriangleCount)*3) 174 | model.TriangleCount = uint32(cModel.unTriangleCount) 175 | C.flatenRenderModelsVertexData(cModel, (*C.float)(unsafe.Pointer(&model.VertexData[0])), (*C.uint)(unsafe.Pointer(&model.Indexes[0]))) 176 | 177 | model.TextureWidth = uint32(cTexture.unWidth) 178 | model.TextureHeight = uint32(cTexture.unHeight) 179 | model.TextureBytes = make([]byte, model.TextureWidth*model.TextureHeight*4) 180 | C.flatenRenderModelsTextureData(cTexture, (*C.uint8_t)(unsafe.Pointer(&model.TextureBytes[0]))) 181 | 182 | C.rendermodels_FreeRenderModel(rm.ptr, cModel) 183 | C.rendermodels_FreeTexture(rm.ptr, cTexture) 184 | 185 | return model, nil 186 | } 187 | 188 | // GetRenderModelCount returns the number of available render models. 189 | func (rm *RenderModels) GetRenderModelCount() uint32 { 190 | cint := C.rendermodels_GetRenderModelCount(rm.ptr) 191 | return uint32(cint) 192 | } 193 | 194 | // GetRenderModelName is used to get the names of available render models. This 195 | // will return an empty string if the index isn't valid. 196 | func (rm *RenderModels) GetRenderModelName(renderModelIndex uint32) string { 197 | cmodelName := C.rendermodels_GetRenderModelName(rm.ptr, C.uint32_t(renderModelIndex)) 198 | result := C.GoString(cmodelName) 199 | if len(result) <= 0 { 200 | return "" 201 | } 202 | C.free(unsafe.Pointer(cmodelName)) 203 | return result 204 | } 205 | 206 | // GetComponentCount returns the number of components of the specified render model. 207 | func (rm *RenderModels) GetComponentCount(renderModelName string) uint32 { 208 | cmodelName := C.CString(renderModelName) 209 | defer C.free(unsafe.Pointer(cmodelName)) 210 | 211 | cint := C.rendermodels_GetComponentCount(rm.ptr, cmodelName) 212 | return uint32(cint) 213 | } 214 | 215 | // GetComponentName is used to get the names of available components of a given render model 216 | func (rm *RenderModels) GetComponentName(renderModelName string, componentIndex uint32) string { 217 | cmodelName := C.CString(renderModelName) 218 | defer C.free(unsafe.Pointer(cmodelName)) 219 | 220 | ccompName := C.rendermodels_GetComponentName(rm.ptr, cmodelName, C.uint32_t(componentIndex)) 221 | result := C.GoString(ccompName) 222 | if len(result) <= 0 { 223 | return "" 224 | } 225 | C.free(unsafe.Pointer(ccompName)) 226 | return result 227 | } 228 | 229 | // GetComponentRenderModelName is used to get the render model name for the 230 | // specified rendermode/component combination, to be passed to LoadRenderModel. 231 | func (rm *RenderModels) GetComponentRenderModelName(renderModelName, componentName string) string { 232 | cmodelName := C.CString(renderModelName) 233 | defer C.free(unsafe.Pointer(cmodelName)) 234 | 235 | ccomponentName := C.CString(componentName) 236 | defer C.free(unsafe.Pointer(ccomponentName)) 237 | 238 | ccompRenderModelName := C.rendermodels_GetComponentRenderModelName(rm.ptr, cmodelName, ccomponentName) 239 | result := C.GoString(ccompRenderModelName) 240 | if len(result) <= 0 { 241 | return "" 242 | } 243 | C.free(unsafe.Pointer(ccompRenderModelName)) 244 | return result 245 | } 246 | 247 | /* 248 | TODO: 249 | 250 | EVRRenderModelError (OPENVR_FNTABLE_CALLTYPE *LoadTextureD3D11_Async)(TextureID_t textureId, void * pD3D11Device, void ** ppD3D11Texture2D); 251 | EVRRenderModelError (OPENVR_FNTABLE_CALLTYPE *LoadIntoTextureD3D11_Async)(TextureID_t textureId, void * pDstTexture); 252 | void (OPENVR_FNTABLE_CALLTYPE *FreeTextureD3D11)(void * pD3D11Texture2D); 253 | uint64_t (OPENVR_FNTABLE_CALLTYPE *GetComponentButtonMask)(char * pchRenderModelName, char * pchComponentName); 254 | bool (OPENVR_FNTABLE_CALLTYPE *GetComponentState)(char * pchRenderModelName, char * pchComponentName, VRControllerState_t * pControllerState, struct RenderModel_ControllerMode_State_t * pState, struct RenderModel_ComponentState_t * pComponentState); 255 | bool (OPENVR_FNTABLE_CALLTYPE *RenderModelHasComponent)(char * pchRenderModelName, char * pchComponentName); 256 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetRenderModelThumbnailURL)(char * pchRenderModelName, char * pchThumbnailURL, uint32_t unThumbnailURLLen, EVRRenderModelError * peError); 257 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetRenderModelOriginalPath)(char * pchRenderModelName, char * pchOriginalPath, uint32_t unOriginalPathLen, EVRRenderModelError * peError); 258 | char * (OPENVR_FNTABLE_CALLTYPE *GetRenderModelErrorNameFromEnum)(EVRRenderModelError error); 259 | 260 | */ 261 | -------------------------------------------------------------------------------- /ivrsystem.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package openvr 5 | 6 | /* 7 | #include 8 | #include 9 | #include "openvr_capi.h" 10 | 11 | extern struct VR_IVRSystem_FnTable* _iSystem; 12 | 13 | // .___ ____ ______________ _________ __ 14 | // | |\ \ / /\______ \ / _____/ ___.__. _______/ |_ ____ _____ 15 | // | | \ Y / | _/ \_____ \ < | | / ___/\ __\_/ __ \ / \ 16 | // | | \ / | | \ / \ \___ | \___ \ | | \ ___/ | Y Y \ 17 | // |___| \___/ |____|_ //_______ / / ____|/____ > |__| \___ >|__|_| / 18 | // \/ \/ \/ \/ \/ \/ 19 | 20 | void system_GetRecommendedRenderTargetSize(struct VR_IVRSystem_FnTable* iSystem, uint32_t* width, uint32_t* height) { 21 | iSystem->GetRecommendedRenderTargetSize(width, height); 22 | } 23 | 24 | struct HmdMatrix44_t system_GetProjectionMatrix(struct VR_IVRSystem_FnTable* iSystem, EVREye eEye, float fNearZ, float fFarZ) { 25 | return iSystem->GetProjectionMatrix(eEye, fNearZ, fFarZ); 26 | } 27 | 28 | struct HmdMatrix34_t system_GetEyeToHeadTransform(struct VR_IVRSystem_FnTable* iSystem, EVREye eEye) { 29 | return iSystem->GetEyeToHeadTransform(eEye); 30 | } 31 | 32 | bool system_ComputeDistortion(struct VR_IVRSystem_FnTable* iSystem, EVREye eEye, float fU, float fV, struct DistortionCoordinates_t* dest) { 33 | return iSystem->ComputeDistortion(eEye, fU, fV, dest) != 0; 34 | } 35 | 36 | bool system_IsTrackedDeviceConnected(struct VR_IVRSystem_FnTable* iSystem, TrackedDeviceIndex_t unDeviceIndex) { 37 | return iSystem->IsTrackedDeviceConnected(unDeviceIndex); 38 | } 39 | 40 | ETrackedDeviceClass system_GetTrackedDeviceClass(struct VR_IVRSystem_FnTable* iSystem, TrackedDeviceIndex_t unDeviceIndex) { 41 | return iSystem->GetTrackedDeviceClass(unDeviceIndex); 42 | } 43 | 44 | bool system_IsInputFocusCapturedByAnotherProcess(struct VR_IVRSystem_FnTable* iSystem) { 45 | return iSystem->IsInputFocusCapturedByAnotherProcess(); 46 | } 47 | 48 | uint32_t system_GetStringTrackedDeviceProperty(struct VR_IVRSystem_FnTable* iSystem, TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, char * pchValue, uint32_t unBufferSize, ETrackedPropertyError * pError) { 49 | return _iSystem->GetStringTrackedDeviceProperty(unDeviceIndex, prop, pchValue, unBufferSize, pError); 50 | } 51 | 52 | bool system_PollNextEvent(struct VR_IVRSystem_FnTable* iSystem, struct VREvent_t * pEvent, uint32_t uncbVREvent) { 53 | return iSystem->PollNextEvent(pEvent, uncbVREvent); 54 | } 55 | 56 | bool system_GetControllerState(struct VR_IVRSystem_FnTable* iSystem, TrackedDeviceIndex_t unControllerDeviceIndex, VRControllerState_t * pControllerState) { 57 | return iSystem->GetControllerState(unControllerDeviceIndex, pControllerState, sizeof(VRControllerState_t)); 58 | } 59 | 60 | char* system_GetControllerAxisTypeNameFromEnum(struct VR_IVRSystem_FnTable* iSystem, EVRControllerAxisType eAxisType) { 61 | return iSystem->GetControllerAxisTypeNameFromEnum(eAxisType); 62 | } 63 | 64 | uint32_t system_GetInt32TrackedDeviceProperty(struct VR_IVRSystem_FnTable* iSystem, TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop,ETrackedPropertyError * pError) { 65 | return _iSystem->GetInt32TrackedDeviceProperty(unDeviceIndex, prop, pError); 66 | } 67 | 68 | */ 69 | import "C" 70 | import ( 71 | "unsafe" 72 | 73 | mgl "github.com/go-gl/mathgl/mgl32" 74 | ) 75 | 76 | // DistortionCoordinates is used to return the post-distortion UVs for each color channel. 77 | // UVs range from 0 to 1 with 0,0 in the upper left corner of the 78 | // source render target. The 0,0 to 1,1 range covers a single eye. 79 | type DistortionCoordinates struct { 80 | Red mgl.Vec2 81 | Green mgl.Vec2 82 | Blue mgl.Vec2 83 | } 84 | 85 | // System is an interface wrapper to IVRSystem. 86 | type System struct { 87 | ptr *C.struct_VR_IVRSystem_FnTable 88 | } 89 | 90 | // GetRecommendedRenderTargetSize returns the suggested size for the intermediate render 91 | // target that the distortion pulls from. 92 | func (sys *System) GetRecommendedRenderTargetSize() (uint32, uint32) { 93 | var w, h C.uint32_t 94 | C.system_GetRecommendedRenderTargetSize(sys.ptr, &w, &h) 95 | return uint32(w), uint32(h) 96 | } 97 | 98 | // GetProjectionMatrix returns the projection matrix for the specified eye 99 | func (sys *System) GetProjectionMatrix(eye int, near, far float32, dest *mgl.Mat4) { 100 | m44 := C.system_GetProjectionMatrix(sys.ptr, C.EVREye(eye), C.float(near), C.float(far)) 101 | 102 | dest[0] = float32(m44.m[0][0]) 103 | dest[4] = float32(m44.m[0][1]) 104 | dest[8] = float32(m44.m[0][2]) 105 | dest[12] = float32(m44.m[0][3]) 106 | 107 | dest[1] = float32(m44.m[1][0]) 108 | dest[5] = float32(m44.m[1][1]) 109 | dest[9] = float32(m44.m[1][2]) 110 | dest[13] = float32(m44.m[1][3]) 111 | 112 | dest[2] = float32(m44.m[2][0]) 113 | dest[6] = float32(m44.m[2][1]) 114 | dest[10] = float32(m44.m[2][2]) 115 | dest[14] = float32(m44.m[2][3]) 116 | 117 | dest[3] = float32(m44.m[3][0]) 118 | dest[7] = float32(m44.m[3][1]) 119 | dest[11] = float32(m44.m[3][2]) 120 | dest[15] = float32(m44.m[3][3]) 121 | } 122 | 123 | // GetEyeToHeadTransform returns the transform from eye space to the head space. Eye space is the per-eye flavor of head 124 | // space that provides stereo disparity. Instead of Model * View * Projection the sequence is Model * View * Eye^-1 * Projection. 125 | // Normally View and Eye^-1 will be multiplied together and treated as View in your application. 126 | func (sys *System) GetEyeToHeadTransform(eye int, dest *mgl.Mat3x4) { 127 | m34 := C.system_GetEyeToHeadTransform(sys.ptr, C.EVREye(eye)) 128 | 129 | dest[0] = float32(m34.m[0][0]) 130 | dest[3] = float32(m34.m[0][1]) 131 | dest[6] = float32(m34.m[0][2]) 132 | dest[9] = float32(m34.m[0][3]) 133 | 134 | dest[1] = float32(m34.m[1][0]) 135 | dest[4] = float32(m34.m[1][1]) 136 | dest[7] = float32(m34.m[1][2]) 137 | dest[10] = float32(m34.m[1][3]) 138 | 139 | dest[2] = float32(m34.m[2][0]) 140 | dest[5] = float32(m34.m[2][1]) 141 | dest[8] = float32(m34.m[2][2]) 142 | dest[11] = float32(m34.m[2][3]) 143 | } 144 | 145 | // ComputeDistortion gets the result of the distortion function for the specified eye and input UVs. UVs go from 0,0 in 146 | // the upper left of that eye's viewport and 1,1 in the lower right of that eye's viewport. 147 | // Returns true for success. Otherwise, returns false, and distortion coordinates are not suitable. 148 | func (sys *System) ComputeDistortion(eye int, u, v float32, dest *DistortionCoordinates) bool { 149 | var cDest C.struct_DistortionCoordinates_t 150 | if convertCBool2Int(C.system_ComputeDistortion(sys.ptr, C.EVREye(eye), C.float(u), C.float(v), &cDest)) == 0 { 151 | return false 152 | } 153 | 154 | dest.Red[0] = float32(cDest.rfRed[0]) 155 | dest.Red[1] = float32(cDest.rfRed[1]) 156 | dest.Green[0] = float32(cDest.rfGreen[0]) 157 | dest.Green[1] = float32(cDest.rfGreen[1]) 158 | dest.Blue[0] = float32(cDest.rfBlue[0]) 159 | dest.Blue[1] = float32(cDest.rfBlue[1]) 160 | return true 161 | } 162 | 163 | // IsTrackedDeviceConnected returns true if there is a device connected in this slot. 164 | func (sys *System) IsTrackedDeviceConnected(deviceIndex uint32) bool { 165 | if convertCBool2Int(C.system_IsTrackedDeviceConnected(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex))) != 0 { 166 | return true 167 | } 168 | return false 169 | } 170 | 171 | // GetTrackedDeviceClass returns the device class of a tracked device. If there has not been a device connected in this slot 172 | // since the application started this function will return TrackedDevice_Invalid. For previous detected 173 | // devices the function will return the previously observed device class. 174 | // 175 | // To determine which devices exist on the system, just loop from 0 to k_unMaxTrackedDeviceCount and check 176 | // the device class. Every device with something other than TrackedDevice_Invalid is associated with an 177 | // actual tracked device. 178 | func (sys *System) GetTrackedDeviceClass(deviceIndex int) int { 179 | result := C.system_GetTrackedDeviceClass(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex)) 180 | return int(result) 181 | } 182 | 183 | // IsInputFocusCapturedByAnotherProcess returns true if input focus is captured by another process. 184 | func (sys *System) IsInputFocusCapturedByAnotherProcess() bool { 185 | if convertCBool2Int(C.system_IsInputFocusCapturedByAnotherProcess(sys.ptr)) != 0 { 186 | return true 187 | } 188 | return false 189 | } 190 | 191 | // GetStringTrackedDeviceProperty returns a string property. If the device index is not valid or the property is 192 | // not a string type this function will an empty string. The int returned correspnds to the ETrackedPropertyError enumeration. 193 | func (sys *System) GetStringTrackedDeviceProperty(deviceIndex int, property int) (string, int) { 194 | // attempt to get the size of the property first 195 | var cErrorVal C.ETrackedPropertyError 196 | size := C.system_GetStringTrackedDeviceProperty(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex), C.ETrackedDeviceProperty(property), nil, 0, &cErrorVal) 197 | if size == 0 { 198 | return "", int(cErrorVal) 199 | } 200 | 201 | buffer := make([]byte, size) 202 | C.system_GetStringTrackedDeviceProperty(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex), C.ETrackedDeviceProperty(property), (*C.char)(unsafe.Pointer(&buffer[0])), size, &cErrorVal) 203 | return string(buffer[:size]), int(cErrorVal) 204 | } 205 | 206 | // VREvent is an event posted by the server to all running applications 207 | type VREvent struct { 208 | EventType uint32 // EVREventType enum 209 | TrackedDeviceIndex uint32 210 | EventAgeSeconds float32 211 | data C.VREvent_Data_t 212 | } 213 | 214 | var ( 215 | // eventBuffer is used as a temporary event item buffer 216 | eventBuffer C.struct_VREvent_t 217 | ) 218 | 219 | // PollNextEvent returns true and fills the event with the next event on the queue if there is one. 220 | // If there are no events this method returns false. 221 | func (sys *System) PollNextEvent(event *VREvent) bool { 222 | result := C.system_PollNextEvent(sys.ptr, &eventBuffer, C.sizeof_struct_VREvent_t) 223 | 224 | if convertCBool2Int(result) != 0 { 225 | // update the event structure with a copy of the event 226 | event.EventType = uint32(eventBuffer.eventType) 227 | event.TrackedDeviceIndex = uint32(eventBuffer.trackedDeviceIndex) 228 | event.EventAgeSeconds = float32(eventBuffer.eventAgeSeconds) 229 | event.data = eventBuffer.data 230 | return true 231 | } 232 | return false 233 | } 234 | 235 | // ControllerAxis represents the state of joystick and track pads. 236 | type ControllerAxis struct { 237 | X float32 238 | Y float32 239 | } 240 | 241 | // ControllerState is the current state of a controller 242 | type ControllerState struct { 243 | PacketNum uint32 244 | ButtonPressed uint64 245 | ButtonTouched uint64 246 | Axis [ControllerStateAxisCount]ControllerAxis 247 | } 248 | 249 | var ( 250 | // controllerStateBuffer is used as a temporary event item buffer 251 | controllerStateBuffer C.struct_VRControllerState_t 252 | ) 253 | 254 | // GetControllerState fills the supplied struct with the current state of the controller. 255 | // Returns false if the controller index is invalid. 256 | func (sys *System) GetControllerState(deviceIndex int, state *ControllerState) bool { 257 | result := C.system_GetControllerState(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex), &controllerStateBuffer) 258 | 259 | if convertCBool2Int(result) != 0 { 260 | state.PacketNum = uint32(controllerStateBuffer.unPacketNum) 261 | state.ButtonPressed = uint64(controllerStateBuffer.ulButtonPressed) 262 | state.ButtonTouched = uint64(controllerStateBuffer.ulButtonTouched) 263 | for i := uint(0); i < ControllerStateAxisCount; i++ { 264 | state.Axis[i].X = float32(controllerStateBuffer.rAxis[i].x) 265 | state.Axis[i].Y = float32(controllerStateBuffer.rAxis[i].y) 266 | } 267 | return true 268 | } 269 | return false 270 | } 271 | 272 | // EyeTransforms is a struct that contains the projection and translation 273 | // matrix transforms for each eye in the HMD. 274 | type EyeTransforms struct { 275 | ProjectionLeft mgl.Mat4 // left eye projection 276 | ProjectionRight mgl.Mat4 // right eye projection 277 | PositionLeft mgl.Mat4 // left eye offset 278 | PositionRight mgl.Mat4 // right eye offset 279 | } 280 | 281 | // GetEyeTransforms returns a structure containing the projection and translation 282 | // matrixes for both eyes given the near/far settings passed in. 283 | func (sys *System) GetEyeTransforms(near, far float32) *EyeTransforms { 284 | transforms := new(EyeTransforms) 285 | var m mgl.Mat4 286 | var m34 mgl.Mat3x4 287 | 288 | sys.GetProjectionMatrix(EyeLeft, near, far, &m) 289 | transforms.ProjectionLeft = mgl.Mat4(m) 290 | 291 | sys.GetProjectionMatrix(EyeRight, near, far, &m) 292 | transforms.ProjectionRight = mgl.Mat4(m) 293 | 294 | sys.GetEyeToHeadTransform(EyeLeft, &m34) 295 | transforms.PositionLeft = mgl.Mat4(Mat34ToMat4(&m34)) 296 | transforms.PositionLeft = transforms.PositionLeft.Inv() 297 | 298 | sys.GetEyeToHeadTransform(EyeRight, &m34) 299 | transforms.PositionRight = mgl.Mat4(Mat34ToMat4(&m34)) 300 | transforms.PositionRight = transforms.PositionRight.Inv() 301 | 302 | return transforms 303 | } 304 | 305 | // GetControllerAxisTypeNameFromEnum returns the name of an EVRControllerAxisType enum value 306 | func (sys *System) GetControllerAxisTypeNameFromEnum(axisType int) string { 307 | cAxisName := C.system_GetControllerAxisTypeNameFromEnum(sys.ptr, C.EVRControllerAxisType(axisType)) 308 | axisName := C.GoString(cAxisName) 309 | return axisName 310 | } 311 | 312 | // GetInt32TrackedDeviceProperty returns a int32 property. If the device index is not valid or the property is 313 | // not valid it will return 0. The second int returned correspnds to the ETrackedPropertyError enumeration. 314 | func (sys *System) GetInt32TrackedDeviceProperty(deviceIndex int, property int) (int32, int) { 315 | var cErrorVal C.ETrackedPropertyError 316 | cInt32Prop := C.system_GetInt32TrackedDeviceProperty(sys.ptr, C.TrackedDeviceIndex_t(deviceIndex), C.ETrackedDeviceProperty(property), &cErrorVal) 317 | return int32(cInt32Prop), int(cErrorVal) 318 | } 319 | 320 | /* TODO List: 321 | 322 | void (OPENVR_FNTABLE_CALLTYPE *GetProjectionRaw)(EVREye eEye, float * pfLeft, float * pfRight, float * pfTop, float * pfBottom); 323 | bool (OPENVR_FNTABLE_CALLTYPE *GetTimeSinceLastVsync)(float * pfSecondsSinceLastVsync, uint64_t * pulFrameCounter); 324 | int32_t (OPENVR_FNTABLE_CALLTYPE *GetD3D9AdapterIndex)(); 325 | void (OPENVR_FNTABLE_CALLTYPE *GetDXGIOutputInfo)(int32_t * pnAdapterIndex); 326 | void (OPENVR_FNTABLE_CALLTYPE *GetOutputDevice)(uint64_t * pnDevice, ETextureType textureType, struct VkInstance_T * pInstance); 327 | bool (OPENVR_FNTABLE_CALLTYPE *IsDisplayOnDesktop)(); 328 | bool (OPENVR_FNTABLE_CALLTYPE *SetDisplayVisibility)(bool bIsVisibleOnDesktop); 329 | void (OPENVR_FNTABLE_CALLTYPE *GetDeviceToAbsoluteTrackingPose)(ETrackingUniverseOrigin eOrigin, float fPredictedSecondsToPhotonsFromNow, struct TrackedDevicePose_t * pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount); 330 | void (OPENVR_FNTABLE_CALLTYPE *ResetSeatedZeroPose)(); 331 | struct HmdMatrix34_t (OPENVR_FNTABLE_CALLTYPE *GetSeatedZeroPoseToStandingAbsoluteTrackingPose)(); 332 | struct HmdMatrix34_t (OPENVR_FNTABLE_CALLTYPE *GetRawZeroPoseToStandingAbsoluteTrackingPose)(); 333 | uint32_t (OPENVR_FNTABLE_CALLTYPE *GetSortedTrackedDeviceIndicesOfClass)(ETrackedDeviceClass eTrackedDeviceClass, TrackedDeviceIndex_t * punTrackedDeviceIndexArray, uint32_t unTrackedDeviceIndexArrayCount, TrackedDeviceIndex_t unRelativeToTrackedDeviceIndex); 334 | EDeviceActivityLevel (OPENVR_FNTABLE_CALLTYPE *GetTrackedDeviceActivityLevel)(TrackedDeviceIndex_t unDeviceId); 335 | void (OPENVR_FNTABLE_CALLTYPE *ApplyTransform)(struct TrackedDevicePose_t * pOutputPose, struct TrackedDevicePose_t * pTrackedDevicePose, struct HmdMatrix34_t * pTransform); 336 | TrackedDeviceIndex_t (OPENVR_FNTABLE_CALLTYPE *GetTrackedDeviceIndexForControllerRole)(ETrackedControllerRole unDeviceType); 337 | ETrackedControllerRole (OPENVR_FNTABLE_CALLTYPE *GetControllerRoleForTrackedDeviceIndex)(TrackedDeviceIndex_t unDeviceIndex); 338 | bool (OPENVR_FNTABLE_CALLTYPE *GetBoolTrackedDeviceProperty)(TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError * pError); 339 | float (OPENVR_FNTABLE_CALLTYPE *GetFloatTrackedDeviceProperty)(TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError * pError); 340 | uint64_t (OPENVR_FNTABLE_CALLTYPE *GetUint64TrackedDeviceProperty)(TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError * pError); 341 | struct HmdMatrix34_t (OPENVR_FNTABLE_CALLTYPE *GetMatrix34TrackedDeviceProperty)(TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError * pError); 342 | char * (OPENVR_FNTABLE_CALLTYPE *GetPropErrorNameFromEnum)(ETrackedPropertyError error); 343 | bool (OPENVR_FNTABLE_CALLTYPE *PollNextEventWithPose)(ETrackingUniverseOrigin eOrigin, struct VREvent_t * pEvent, uint32_t uncbVREvent, TrackedDevicePose_t * pTrackedDevicePose); 344 | char * (OPENVR_FNTABLE_CALLTYPE *GetEventTypeNameFromEnum)(EVREventType eType); 345 | struct HiddenAreaMesh_t (OPENVR_FNTABLE_CALLTYPE *GetHiddenAreaMesh)(EVREye eEye, EHiddenAreaMeshType type); 346 | bool (OPENVR_FNTABLE_CALLTYPE *GetControllerStateWithPose)(ETrackingUniverseOrigin eOrigin, TrackedDeviceIndex_t unControllerDeviceIndex, VRControllerState_t * pControllerState, uint32_t unControllerStateSize, struct TrackedDevicePose_t * pTrackedDevicePose); 347 | void (OPENVR_FNTABLE_CALLTYPE *TriggerHapticPulse)(TrackedDeviceIndex_t unControllerDeviceIndex, uint32_t unAxisId, unsigned short usDurationMicroSec); 348 | char * (OPENVR_FNTABLE_CALLTYPE *GetButtonIdNameFromEnum)(EVRButtonId eButtonId); 349 | bool (OPENVR_FNTABLE_CALLTYPE *CaptureInputFocus)(); 350 | void (OPENVR_FNTABLE_CALLTYPE *ReleaseInputFocus)(); 351 | uint32_t (OPENVR_FNTABLE_CALLTYPE *DriverDebugRequest)(TrackedDeviceIndex_t unDeviceIndex, char * pchRequest, char * pchResponseBuffer, uint32_t unResponseBufferSize); 352 | EVRFirmwareError (OPENVR_FNTABLE_CALLTYPE *PerformFirmwareUpdate)(TrackedDeviceIndex_t unDeviceIndex); 353 | void (OPENVR_FNTABLE_CALLTYPE *AcknowledgeQuit_Exiting)(); 354 | void (OPENVR_FNTABLE_CALLTYPE *AcknowledgeQuit_UserPrompt)(); 355 | */ 356 | -------------------------------------------------------------------------------- /openvr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package openvr 5 | 6 | /* 7 | #cgo CFLAGS: -I${SRCDIR}/vendored/openvr/headers -std=c99 8 | #cgo windows,386 LDFLAGS: -L${SRCDIR}/vendored/openvr/bin/win32 -lopenvr_api 9 | #cgo windows,amd64 LDFLAGS: -L${SRCDIR}/vendored/openvr/bin/win64 -lopenvr_api 10 | #cgo linux,amd64 LDFLAGS: -L${SRCDIR}/vendored/openvr/bin/linux64 -lopenvr_api 11 | #cgo linux,386 LDFLAGS: -L${SRCDIR}/vendored/openvr/bin/linux32 -lopenvr_api 12 | 13 | #include 14 | #include 15 | #include "openvr_capi.h" 16 | 17 | #if defined(_WIN32) 18 | #define IMPORT __declspec(dllimport) 19 | #else 20 | #define IMPORT 21 | #endif 22 | 23 | // lets declare some externals from the openvr_api.dll 24 | IMPORT intptr_t VR_InitInternal( EVRInitError *peError, EVRApplicationType eType ); 25 | IMPORT const char * VR_GetVRInitErrorAsEnglishDescription( EVRInitError error ); 26 | IMPORT bool VR_IsInterfaceVersionValid(const char *interface); 27 | IMPORT void VR_ShutdownInternal(); 28 | IMPORT intptr_t VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError ); 29 | 30 | // api tokens; set in initInternal() 31 | intptr_t _iToken; 32 | struct VR_IVRSystem_FnTable* _iSystem; 33 | struct VR_IVRCompositor_FnTable* _iCompositor; 34 | struct VR_IVRRenderModels_FnTable* _iRenderModels; 35 | struct VR_IVRChaperone_FnTable* _iChaperone; 36 | 37 | 38 | // gets the api token and makes sure the interface is valid 39 | int initInternal(int appTypeEnum) { 40 | // get the api token 41 | EVRInitError error = EVRInitError_VRInitError_None; 42 | _iToken = VR_InitInternal(&error, appTypeEnum); 43 | if (error != EVRInitError_VRInitError_None) { 44 | const char* msg = VR_GetVRInitErrorAsEnglishDescription(error); 45 | printf("VR_InitInternal failed: %s\n", msg); 46 | return error; 47 | } 48 | 49 | bool icheck = VR_IsInterfaceVersionValid(IVRSystem_Version); 50 | if (!icheck) { 51 | printf("INVALID interface\n"); 52 | VR_ShutdownInternal(); 53 | return EVRInitError_VRInitError_Unknown; 54 | } 55 | 56 | char interfaceFnTable[256]; 57 | sprintf(interfaceFnTable, "FnTable:%s", IVRSystem_Version); 58 | _iSystem = (struct VR_IVRSystem_FnTable*) VR_GetGenericInterface(interfaceFnTable, &error); 59 | if (error != EVRInitError_VRInitError_None) { 60 | const char* msg = VR_GetVRInitErrorAsEnglishDescription(error); 61 | printf("Error on getting IVRSystem: %s\n", msg); 62 | return error; 63 | } 64 | 65 | return EVRInitError_VRInitError_None; 66 | } 67 | 68 | 69 | int compositor_SetInternalInterface() { 70 | EVRInitError error = EVRInitError_VRInitError_None; 71 | if (_iCompositor == NULL) { 72 | char interfaceFnTable[256]; 73 | sprintf(interfaceFnTable, "FnTable:%s", IVRCompositor_Version); 74 | _iCompositor = (struct VR_IVRCompositor_FnTable*) VR_GetGenericInterface(interfaceFnTable, &error); 75 | if (error != EVRInitError_VRInitError_None) { 76 | const char* msg = VR_GetVRInitErrorAsEnglishDescription(error); 77 | printf("Error on getting IVRCompositor: %s\n", msg); 78 | return error; 79 | } 80 | } 81 | return error; 82 | } 83 | 84 | int rendermodels_SetInternalInterface() { 85 | EVRInitError error = EVRInitError_VRInitError_None; 86 | if (_iRenderModels == NULL) { 87 | char interfaceFnTable[256]; 88 | sprintf(interfaceFnTable, "FnTable:%s", IVRRenderModels_Version); 89 | _iRenderModels = (struct VR_IVRRenderModels_FnTable*) VR_GetGenericInterface(interfaceFnTable, &error); 90 | if (error != EVRInitError_VRInitError_None) { 91 | const char* msg = VR_GetVRInitErrorAsEnglishDescription(error); 92 | printf("Error on getting IVRRenderModels: %s\n", msg); 93 | return error; 94 | } 95 | } 96 | return error; 97 | } 98 | 99 | int chaperone_SetInternalInterface() { 100 | EVRInitError error = EVRInitError_VRInitError_None; 101 | if (_iChaperone == NULL) { 102 | char interfaceFnTable[256]; 103 | sprintf(interfaceFnTable, "FnTable:%s", IVRChaperone_Version); 104 | _iChaperone = (struct VR_IVRChaperone_FnTable*) VR_GetGenericInterface(interfaceFnTable, &error); 105 | if (error != EVRInitError_VRInitError_None) { 106 | const char* msg = VR_GetVRInitErrorAsEnglishDescription(error); 107 | printf("Error on getting IVRChaperone: %s\n", msg); 108 | return error; 109 | } 110 | } 111 | return error; 112 | } 113 | 114 | */ 115 | import "C" 116 | import ( 117 | "fmt" 118 | 119 | mgl "github.com/go-gl/mathgl/mgl32" 120 | ) 121 | 122 | // Init initializes the internal VR api structers and on success will 123 | // return a System object with a valid IVRSystem interface ptr. 124 | func Init() (*System, error) { 125 | // initialize the module _iToken value from the openvr api 126 | e := C.initInternal(C.EVRApplicationType_VRApplication_Scene) 127 | if e == C.EVRInitError_VRInitError_None { 128 | sys := new(System) 129 | sys.ptr = C._iSystem 130 | return sys, nil 131 | } 132 | 133 | errStr := GetErrorAsEnglish(int(e)) 134 | return nil, fmt.Errorf("%s", errStr) 135 | } 136 | 137 | // Shutdown calls the ShutdownInternal function on the VR library. 138 | func Shutdown() { 139 | C.VR_ShutdownInternal() 140 | } 141 | 142 | // GetErrorAsEnglish takes an EVRInitError enumeration value and returns a string. 143 | func GetErrorAsEnglish(e int) string { 144 | cs := C.VR_GetVRInitErrorAsEnglishDescription(C.EVRInitError(e)) 145 | // NOTE: does cs need to be freed somehow? 146 | return C.GoString(cs) 147 | } 148 | 149 | // GetCompositor returns a new IVRCompositor interface. 150 | func GetCompositor() (*Compositor, error) { 151 | e := C.compositor_SetInternalInterface() 152 | if e == C.EVRInitError_VRInitError_None { 153 | comp := new(Compositor) 154 | comp.ptr = C._iCompositor 155 | return comp, nil 156 | } 157 | cs := C.VR_GetVRInitErrorAsEnglishDescription(C.EVRInitError(e)) 158 | return nil, fmt.Errorf("%s", C.GoString(cs)) 159 | } 160 | 161 | // GetRenderModels returns a new IVRRenderModels interface. 162 | func GetRenderModels() (*RenderModels, error) { 163 | e := C.rendermodels_SetInternalInterface() 164 | if e == C.EVRInitError_VRInitError_None { 165 | rm := new(RenderModels) 166 | rm.ptr = C._iRenderModels 167 | return rm, nil 168 | } 169 | cs := C.VR_GetVRInitErrorAsEnglishDescription(C.EVRInitError(e)) 170 | return nil, fmt.Errorf("%s", C.GoString(cs)) 171 | } 172 | 173 | // GetChaperone returns a new IVRChaperone interface. 174 | func GetChaperone() (*Chaperone, error) { 175 | e := C.chaperone_SetInternalInterface() 176 | if e == C.EVRInitError_VRInitError_None { 177 | chap := new(Chaperone) 178 | chap.ptr = C._iChaperone 179 | return chap, nil 180 | } 181 | cs := C.VR_GetVRInitErrorAsEnglishDescription(C.EVRInitError(e)) 182 | return nil, fmt.Errorf("%s", C.GoString(cs)) 183 | } 184 | 185 | // Mat34ToMat4 is a utility conversion function that takes a 3x4 matrix and outputs 186 | // a 4x4 matrix with an identity fourth row of {0,0,0,1}. 187 | func Mat34ToMat4(vrM34 *mgl.Mat3x4) (m4 mgl.Mat4) { 188 | m4[0] = vrM34[0] 189 | m4[1] = vrM34[1] 190 | m4[2] = vrM34[2] 191 | m4[3] = 0.0 192 | 193 | m4[4] = vrM34[3] 194 | m4[5] = vrM34[4] 195 | m4[6] = vrM34[5] 196 | m4[7] = 0.0 197 | 198 | m4[8] = vrM34[6] 199 | m4[9] = vrM34[7] 200 | m4[10] = vrM34[8] 201 | m4[11] = 0.0 202 | 203 | m4[12] = vrM34[9] 204 | m4[13] = vrM34[10] 205 | m4[14] = vrM34[11] 206 | m4[15] = 1.0 207 | return m4 208 | } 209 | 210 | var ( 211 | // ShaderRenderModelV is the render model vertex shader 212 | ShaderRenderModelV = `#version 330 213 | uniform mat4 mvp; 214 | in vec4 position; 215 | in vec3 normal; 216 | in vec2 texCoord; 217 | 218 | out vec2 v2TexCoord; 219 | void main() 220 | { 221 | v2TexCoord = texCoord; 222 | gl_Position = mvp * vec4(position.xyz, 1); 223 | }` 224 | 225 | // ShaderRenderModelF is the render model fragment shader 226 | ShaderRenderModelF = `#version 330 227 | uniform sampler2D diffuse; 228 | in vec2 v2TexCoord; 229 | out vec4 outputColor; 230 | void main() 231 | { 232 | outputColor = texture(diffuse, v2TexCoord); 233 | }` 234 | 235 | // ShaderLensDistortionV is the lens distortion vertex shader 236 | ShaderLensDistortionV = `#version 330 237 | in vec4 position; 238 | in vec2 v2UVredIn; 239 | in vec2 v2UVGreenIn; 240 | in vec2 v2UVblueIn; 241 | noperspective out vec2 v2UVred; 242 | noperspective out vec2 v2UVgreen; 243 | noperspective out vec2 v2UVblue; 244 | void main() 245 | { 246 | v2UVred = v2UVredIn; 247 | v2UVgreen = v2UVGreenIn; 248 | v2UVblue = v2UVblueIn; 249 | gl_Position = position; 250 | }` 251 | 252 | // ShaderLensDistortionF is the lens distortion fragment shader 253 | ShaderLensDistortionF = `#version 330 254 | uniform sampler2D mytexture; 255 | 256 | noperspective in vec2 v2UVred; 257 | noperspective in vec2 v2UVgreen; 258 | noperspective in vec2 v2UVblue; 259 | 260 | out vec4 outputColor; 261 | 262 | void main() 263 | { 264 | float fBoundsCheck = ( (dot( vec2( lessThan( v2UVgreen.xy, vec2(0.05, 0.05)) ), vec2(1.0, 1.0))+dot( vec2( greaterThan( v2UVgreen.xy, vec2( 0.95, 0.95)) ), vec2(1.0, 1.0))) ); 265 | if( fBoundsCheck > 1.0 ) { 266 | outputColor = vec4( 0, 0, 1, 1.0 ); 267 | } else { 268 | float red = texture(mytexture, v2UVred).x; 269 | float green = texture(mytexture, v2UVgreen).y; 270 | float blue = texture(mytexture, v2UVblue).z; 271 | outputColor = vec4( red, green, blue, 1.0 ); 272 | } 273 | 274 | }` 275 | ) 276 | -------------------------------------------------------------------------------- /util/fizzlevr/devicerenderables.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package fizzlevr 5 | 6 | import ( 7 | "fmt" 8 | 9 | mgl "github.com/go-gl/mathgl/mgl32" 10 | 11 | fizzle "github.com/tbogdala/fizzle" 12 | graphics "github.com/tbogdala/fizzle/graphicsprovider" 13 | vr "github.com/tbogdala/openvr-go" 14 | ) 15 | 16 | // DeviceRenderables creates Renderable objects for connected devices. 17 | type DeviceRenderables struct { 18 | // vrSystem is the cached reference to the ISystem interface 19 | vrSystem *vr.System 20 | 21 | // vrRenderModels is the cached reference to the IRenderModels interface 22 | vrRenderModels *vr.RenderModels 23 | 24 | // Shader is the render model shader to use 25 | Shader *fizzle.RenderShader 26 | 27 | // renderables are the loaded renderables for devices 28 | renderables map[string]*fizzle.Renderable 29 | } 30 | 31 | // CreateDeviceRenderables creates a new DeviceRenderables object which creates 32 | // Renderable objects for each connected device. 33 | func CreateDeviceRenderables(vrSystem *vr.System, shader *fizzle.RenderShader) (*DeviceRenderables, error) { 34 | deviceRenderables := new(DeviceRenderables) 35 | deviceRenderables.Shader = shader 36 | deviceRenderables.vrSystem = vrSystem 37 | 38 | // get the render models interface 39 | var err error 40 | deviceRenderables.vrRenderModels, err = vr.GetRenderModels() 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | // create the map to cache the renderables 46 | deviceRenderables.renderables = make(map[string]*fizzle.Renderable) 47 | 48 | // loop through all possible devices besides the first, which is the HMD, 49 | // and try to load the model. 50 | for i := vr.TrackedDeviceIndexHmd + 1; i < vr.MaxTrackedDeviceCount; i++ { 51 | if vrSystem.IsTrackedDeviceConnected(uint32(i)) { 52 | _, err := deviceRenderables.GetRenderableForTrackedDevice(int(i)) 53 | if err != nil { 54 | return nil, fmt.Errorf("Failed to load renderable for device index %d; %v\n", i, err) 55 | } 56 | } 57 | } 58 | 59 | return deviceRenderables, nil 60 | } 61 | 62 | // GetRenderableForTrackedDevice will look up the tracked device and create a 63 | // renderable if one hasn't been cached already. 64 | func (dr *DeviceRenderables) GetRenderableForTrackedDevice(deviceIndex int) (*fizzle.Renderable, error) { 65 | // sanity check 66 | if uint(deviceIndex) >= vr.MaxTrackedDeviceCount { 67 | return nil, fmt.Errorf("Device index out of range.") 68 | } 69 | 70 | // get the name of the device 71 | rendermodelName, errInt := dr.vrSystem.GetStringTrackedDeviceProperty(deviceIndex, vr.PropRenderModelNameString) 72 | if errInt != vr.TrackedPropSuccess { 73 | return nil, fmt.Errorf("%s", vr.GetErrorAsEnglish(errInt)) 74 | } 75 | 76 | // return a cached copy if there is one 77 | existingRenderable, okay := dr.renderables[rendermodelName] 78 | if okay { 79 | return existingRenderable, nil 80 | } 81 | 82 | // no cached copy, so load a new one 83 | renderModel, err := dr.vrRenderModels.RenderModelLoad(rendermodelName) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | // as a test, make a renderable with the data 89 | const floatSize = 4 90 | const uintSize = 4 91 | r := fizzle.NewRenderable() 92 | r.Core = fizzle.NewRenderableCore() 93 | r.FaceCount = renderModel.TriangleCount 94 | r.Material = fizzle.NewMaterial() 95 | r.Material.Shader = dr.Shader 96 | 97 | gfx := fizzle.GetGraphics() 98 | gfx.BindVertexArray(r.Core.Vao) 99 | 100 | // create a VBO to hold the vertex data 101 | r.Core.VertVBO = gfx.GenBuffer() 102 | r.Core.UvVBO = r.Core.VertVBO 103 | r.Core.NormsVBO = r.Core.VertVBO 104 | r.Core.VertVBOOffset = 0 105 | r.Core.NormsVBOOffset = floatSize * 3 106 | r.Core.UvVBOOffset = floatSize * 6 107 | r.Core.VBOStride = floatSize * (3 + 3 + 2) // vert / normal / uv 108 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.VertVBO) 109 | gfx.BufferData(graphics.ARRAY_BUFFER, floatSize*len(renderModel.VertexData), gfx.Ptr(&renderModel.VertexData[0]), graphics.STATIC_DRAW) 110 | 111 | // create a VBO to hold the face indexes 112 | r.Core.ElementsVBO = gfx.GenBuffer() 113 | gfx.BindBuffer(graphics.ELEMENT_ARRAY_BUFFER, r.Core.ElementsVBO) 114 | gfx.BufferData(graphics.ELEMENT_ARRAY_BUFFER, uintSize*len(renderModel.Indexes), gfx.Ptr(&renderModel.Indexes[0]), graphics.STATIC_DRAW) 115 | 116 | // upload the texture 117 | r.Material.DiffuseTex = gfx.GenTexture() 118 | gfx.ActiveTexture(graphics.TEXTURE0) 119 | gfx.BindTexture(graphics.TEXTURE_2D, r.Material.DiffuseTex) 120 | 121 | gfx.TexImage2D(graphics.TEXTURE_2D, 0, graphics.RGBA, int32(renderModel.TextureWidth), int32(renderModel.TextureHeight), 122 | 0, graphics.RGBA, graphics.UNSIGNED_BYTE, gfx.Ptr(renderModel.TextureBytes), len(renderModel.TextureBytes)) 123 | 124 | // If this renders black ask yourself what's wrong. ;p 125 | gfx.GenerateMipmap(graphics.TEXTURE_2D) 126 | 127 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MAG_FILTER, graphics.LINEAR) 128 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MIN_FILTER, graphics.LINEAR_MIPMAP_LINEAR) 129 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_S, graphics.CLAMP_TO_EDGE) 130 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_T, graphics.CLAMP_TO_EDGE) 131 | /* 132 | GLfloat fLargest; 133 | glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest ); 134 | glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest ); 135 | */ 136 | 137 | // setup the attribute bindings 138 | shaderPosition := dr.Shader.GetAttribLocation("position") 139 | if shaderPosition >= 0 { 140 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.VertVBO) 141 | gfx.EnableVertexAttribArray(uint32(shaderPosition)) 142 | gfx.VertexAttribPointer(uint32(shaderPosition), 3, graphics.FLOAT, false, r.Core.VBOStride, gfx.PtrOffset(r.Core.VertVBOOffset)) 143 | } 144 | 145 | shaderVertUv := dr.Shader.GetAttribLocation("texCoord") 146 | if shaderVertUv >= 0 { 147 | gfx.BindBuffer(graphics.ARRAY_BUFFER, r.Core.UvVBO) 148 | gfx.EnableVertexAttribArray(uint32(shaderVertUv)) 149 | gfx.VertexAttribPointer(uint32(shaderVertUv), 2, graphics.FLOAT, false, r.Core.VBOStride, gfx.PtrOffset(r.Core.UvVBOOffset)) 150 | } 151 | 152 | // unbind things to appease the OpenGL gods 153 | gfx.BindTexture(graphics.TEXTURE_2D, 0) 154 | gfx.BindVertexArray(0) 155 | 156 | // store the renderable 157 | dr.renderables[rendermodelName] = r 158 | 159 | // return the new renderable 160 | return r, nil 161 | } 162 | 163 | // RenderDevices will render all connected devices. 164 | func (dr *DeviceRenderables) RenderDevices(vrCompositor *vr.Compositor, perspective mgl.Mat4, view mgl.Mat4, camera fizzle.Camera) { 165 | // only render if we have input focus 166 | if dr.vrSystem.IsInputFocusCapturedByAnotherProcess() { 167 | return 168 | } 169 | 170 | gfx := fizzle.GetGraphics() 171 | shaderMvp := dr.Shader.GetUniformLocation("mvp") 172 | shaderTex0 := dr.Shader.GetUniformLocation("diffuse") 173 | 174 | // loop through all non-HMD devices 175 | for i := vr.TrackedDeviceIndexHmd + 1; i < vr.MaxTrackedDeviceCount; i++ { 176 | // make sure the pose is correct 177 | pose := vrCompositor.GetRenderPose(i) 178 | if !pose.PoseIsValid { 179 | continue 180 | } 181 | 182 | // get the renderable 183 | r, err := dr.GetRenderableForTrackedDevice(int(i)) 184 | if err != nil { 185 | fmt.Printf("DeviceRenderables.RenderDevices(): failed to get the renderable for device #%d: %s\n", i, err.Error()) 186 | continue 187 | } 188 | 189 | gfx.UseProgram(dr.Shader.Prog) 190 | gfx.BindVertexArray(r.Core.Vao) 191 | 192 | // calculate the mvp based off of the model's pose 193 | poseMat := vr.Mat34ToMat4(&pose.DeviceToAbsoluteTracking) 194 | mvp := perspective.Mul4(view).Mul4(poseMat) 195 | 196 | if shaderMvp >= 0 { 197 | gfx.UniformMatrix4fv(shaderMvp, 1, false, mvp) 198 | } 199 | 200 | if shaderTex0 >= 0 { 201 | gfx.ActiveTexture(graphics.Texture(graphics.TEXTURE0)) 202 | gfx.BindTexture(graphics.TEXTURE_2D, r.Material.DiffuseTex) 203 | gfx.Uniform1i(shaderTex0, 0) 204 | } 205 | 206 | gfx.BindBuffer(graphics.ELEMENT_ARRAY_BUFFER, r.Core.ElementsVBO) 207 | gfx.DrawElements(graphics.Enum(graphics.TRIANGLES), int32(r.FaceCount*3), graphics.UNSIGNED_INT, gfx.PtrOffset(0)) 208 | gfx.BindVertexArray(0) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /util/fizzlevr/rendering.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Timothy Bogdala 2 | // See the LICENSE file for more details. 3 | 4 | package fizzlevr 5 | 6 | import ( 7 | fizzle "github.com/tbogdala/fizzle" 8 | graphics "github.com/tbogdala/fizzle/graphicsprovider" 9 | vr "github.com/tbogdala/openvr-go" 10 | ) 11 | 12 | // EyeFramebuffer contains the render buffers and textures 13 | // used to render each eye in VR. 14 | type EyeFramebuffer struct { 15 | DepthBuffer graphics.Buffer 16 | RenderTexture graphics.Texture 17 | RenderFramebuffer graphics.Buffer 18 | ResolveTexture graphics.Texture 19 | ResolveFramebuffer graphics.Buffer 20 | } 21 | 22 | // CreateStereoRenderTargets returns new EyeFramebuffer structs, one for each eye, 23 | // that are used for rendering. 24 | func CreateStereoRenderTargets(width, height uint32) (left, right *EyeFramebuffer) { 25 | left = new(EyeFramebuffer) 26 | right = new(EyeFramebuffer) 27 | 28 | left.Init(width, height) 29 | right.Init(width, height) 30 | 31 | return left, right 32 | } 33 | 34 | // Init creates the necessary render buffers and render textures for an eye. 35 | func (eyeFB *EyeFramebuffer) Init(width, height uint32) { 36 | gfx := fizzle.GetGraphics() 37 | 38 | eyeFB.RenderFramebuffer = gfx.GenFramebuffer() 39 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFB.RenderFramebuffer) 40 | 41 | eyeFB.DepthBuffer = gfx.GenRenderbuffer() 42 | gfx.BindRenderbuffer(graphics.RENDERBUFFER, eyeFB.DepthBuffer) 43 | gfx.RenderbufferStorageMultisample(graphics.RENDERBUFFER, 4, graphics.DEPTH_COMPONENT, int32(width), int32(height)) 44 | gfx.FramebufferRenderbuffer(graphics.FRAMEBUFFER, graphics.DEPTH_ATTACHMENT, graphics.RENDERBUFFER, eyeFB.DepthBuffer) 45 | 46 | eyeFB.RenderTexture = gfx.GenTexture() 47 | gfx.BindTexture(graphics.TEXTURE_2D_MULTISAMPLE, eyeFB.RenderTexture) 48 | gfx.TexImage2DMultisample(graphics.TEXTURE_2D_MULTISAMPLE, 4, graphics.RGBA8, int32(width), int32(height), true) 49 | gfx.FramebufferTexture2D(graphics.FRAMEBUFFER, graphics.COLOR_ATTACHMENT0, graphics.TEXTURE_2D_MULTISAMPLE, eyeFB.RenderTexture, 0) 50 | 51 | // ---------------------------------------------------------------------- // 52 | 53 | eyeFB.ResolveFramebuffer = gfx.GenFramebuffer() 54 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, eyeFB.ResolveFramebuffer) 55 | 56 | eyeFB.ResolveTexture = gfx.GenTexture() 57 | gfx.BindTexture(graphics.TEXTURE_2D, eyeFB.ResolveTexture) 58 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MIN_FILTER, graphics.LINEAR) 59 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MAX_LEVEL, 0) 60 | gfx.TexImage2D(graphics.TEXTURE_2D, 0, graphics.RGBA8, int32(width), int32(height), 0, graphics.RGBA, graphics.UNSIGNED_BYTE, nil, 0) 61 | gfx.FramebufferTexture2D(graphics.FRAMEBUFFER, graphics.COLOR_ATTACHMENT0, graphics.TEXTURE_2D, eyeFB.ResolveTexture, 0) 62 | 63 | gfx.BindFramebuffer(graphics.FRAMEBUFFER, 0) 64 | } 65 | 66 | // DistortionLens is used to render the VR framebuffers out to a window 67 | // based on lens distortionvalues calculated in Init(). 68 | type DistortionLens struct { 69 | VAO uint32 70 | Verts graphics.Buffer 71 | Indices graphics.Buffer 72 | IndexCount int32 73 | Shader *fizzle.RenderShader 74 | EyeRight *EyeFramebuffer 75 | EyeLeft *EyeFramebuffer 76 | } 77 | 78 | // CreateDistortionLens creates a DistortionLens object that can render 79 | // the framebuffers for the left and right eye to a window. 80 | func CreateDistortionLens(vrSystem *vr.System, lensShader *fizzle.RenderShader, eyeLeft, eyeRight *EyeFramebuffer) *DistortionLens { 81 | lens := new(DistortionLens) 82 | lens.Shader = lensShader 83 | lens.EyeLeft = eyeLeft 84 | lens.EyeRight = eyeRight 85 | 86 | const lensGridSegmentCountH = 43 87 | const lensGridSegmentCountV = 43 88 | 89 | w := float32(1.0) / float32(lensGridSegmentCountH-1) 90 | h := float32(1.0) / float32(lensGridSegmentCountV-1) 91 | 92 | u := float32(0.0) 93 | v := float32(0.0) 94 | 95 | var verts []float32 96 | var dc0 vr.DistortionCoordinates 97 | 98 | // left eye distortion verts 99 | Xoffset := float32(-1.0) 100 | for y := 0; y < lensGridSegmentCountV; y++ { 101 | for x := 0; x < lensGridSegmentCountH; x++ { 102 | u = float32(x) * w 103 | v = 1.0 - float32(y)*h 104 | vrSystem.ComputeDistortion(vr.EyeLeft, u, v, &dc0) 105 | verts = append(verts, Xoffset+u) 106 | verts = append(verts, -1.0+2.0*float32(y)*h) 107 | verts = append(verts, dc0.Red[0]) 108 | verts = append(verts, 1.0-dc0.Red[1]) 109 | verts = append(verts, dc0.Green[0]) 110 | verts = append(verts, 1.0-dc0.Green[1]) 111 | verts = append(verts, dc0.Blue[0]) 112 | verts = append(verts, 1.0-dc0.Blue[1]) 113 | } 114 | } 115 | 116 | // right eye distortion verts 117 | Xoffset = float32(0.0) 118 | for y := 0; y < lensGridSegmentCountV; y++ { 119 | for x := 0; x < lensGridSegmentCountH; x++ { 120 | u = float32(x) * w 121 | v = 1.0 - float32(y)*h 122 | vrSystem.ComputeDistortion(vr.EyeRight, u, v, &dc0) 123 | verts = append(verts, Xoffset+u) 124 | verts = append(verts, -1.0+2.0*float32(y)*h) 125 | verts = append(verts, dc0.Red[0]) 126 | verts = append(verts, 1.0-dc0.Red[1]) 127 | verts = append(verts, dc0.Green[0]) 128 | verts = append(verts, 1.0-dc0.Green[1]) 129 | verts = append(verts, dc0.Blue[0]) 130 | verts = append(verts, 1.0-dc0.Blue[1]) 131 | } 132 | } 133 | 134 | var vIndices []uint32 135 | var a, b, c, d uint32 136 | offset := 0 137 | for y := 0; y < lensGridSegmentCountV-1; y++ { 138 | for x := 0; x < lensGridSegmentCountH-1; x++ { 139 | a = uint32(lensGridSegmentCountH*y + x + offset) 140 | b = uint32(lensGridSegmentCountH*y + x + 1 + offset) 141 | c = uint32((y+1)*lensGridSegmentCountH + x + 1 + offset) 142 | d = uint32((y+1)*lensGridSegmentCountH + x + offset) 143 | vIndices = append(vIndices, a) 144 | vIndices = append(vIndices, b) 145 | vIndices = append(vIndices, c) 146 | 147 | vIndices = append(vIndices, a) 148 | vIndices = append(vIndices, c) 149 | vIndices = append(vIndices, d) 150 | } 151 | } 152 | 153 | offset = lensGridSegmentCountH * lensGridSegmentCountV 154 | for y := 0; y < lensGridSegmentCountV-1; y++ { 155 | for x := 0; x < lensGridSegmentCountH-1; x++ { 156 | a = uint32(lensGridSegmentCountH*y + x + offset) 157 | b = uint32(lensGridSegmentCountH*y + x + 1 + offset) 158 | c = uint32((y+1)*lensGridSegmentCountH + x + 1 + offset) 159 | d = uint32((y+1)*lensGridSegmentCountH + x + offset) 160 | vIndices = append(vIndices, a) 161 | vIndices = append(vIndices, b) 162 | vIndices = append(vIndices, c) 163 | 164 | vIndices = append(vIndices, a) 165 | vIndices = append(vIndices, c) 166 | vIndices = append(vIndices, d) 167 | } 168 | } 169 | lens.IndexCount = int32(len(vIndices)) 170 | 171 | const floatSize = 4 172 | const uintSize = 4 173 | 174 | // create the OpenGL objects 175 | gfx := fizzle.GetGraphics() 176 | lens.VAO = gfx.GenVertexArray() 177 | gfx.BindVertexArray(lens.VAO) 178 | 179 | lens.Verts = gfx.GenBuffer() 180 | gfx.BindBuffer(graphics.ARRAY_BUFFER, lens.Verts) 181 | gfx.BufferData(graphics.ARRAY_BUFFER, len(verts)*floatSize, gfx.Ptr(&verts[0]), graphics.STATIC_DRAW) 182 | 183 | lens.Indices = gfx.GenBuffer() 184 | gfx.BindBuffer(graphics.ELEMENT_ARRAY_BUFFER, lens.Indices) 185 | gfx.BufferData(graphics.ELEMENT_ARRAY_BUFFER, len(vIndices)*uintSize, gfx.Ptr(&vIndices[0]), graphics.STATIC_DRAW) 186 | 187 | const lensStride = 8 * floatSize 188 | const offsetPosition = 0 189 | const offsetRed = 2 * floatSize 190 | const offsetGreen = 4 * floatSize 191 | const offsetBlue = 6 * floatSize 192 | 193 | shaderPosition := lensShader.GetAttribLocation("position") 194 | gfx.EnableVertexAttribArray(uint32(shaderPosition)) 195 | gfx.VertexAttribPointer(uint32(shaderPosition), 2, graphics.FLOAT, false, lensStride, gfx.PtrOffset(offsetPosition)) 196 | 197 | shaderRed := lensShader.GetAttribLocation("v2UVredIn") 198 | gfx.EnableVertexAttribArray(uint32(shaderRed)) 199 | gfx.VertexAttribPointer(uint32(shaderRed), 2, graphics.FLOAT, false, lensStride, gfx.PtrOffset(offsetRed)) 200 | 201 | shaderGreen := lensShader.GetAttribLocation("v2UVGreenIn") 202 | gfx.EnableVertexAttribArray(uint32(shaderGreen)) 203 | gfx.VertexAttribPointer(uint32(shaderGreen), 2, graphics.FLOAT, false, lensStride, gfx.PtrOffset(offsetGreen)) 204 | 205 | shaderBlue := lensShader.GetAttribLocation("v2UVblueIn") 206 | gfx.EnableVertexAttribArray(uint32(shaderBlue)) 207 | gfx.VertexAttribPointer(uint32(shaderBlue), 2, graphics.FLOAT, false, lensStride, gfx.PtrOffset(offsetBlue)) 208 | 209 | gfx.BindVertexArray(0) 210 | gfx.BindBuffer(graphics.ARRAY_BUFFER, 0) 211 | gfx.BindBuffer(graphics.ELEMENT_ARRAY_BUFFER, 0) 212 | 213 | return lens 214 | } 215 | 216 | // Render draws the distortion lens view of the left and right eye 217 | // framebuffers to the window. 218 | func (lens *DistortionLens) Render(windowWidth, windowHeight int32) { 219 | gfx := fizzle.GetGraphics() 220 | 221 | gfx.Disable(graphics.CULL_FACE) 222 | gfx.Disable(graphics.DEPTH_TEST) 223 | gfx.Viewport(0, 0, windowWidth, windowHeight) 224 | gfx.ClearColor(0.0, 0.0, 0.0, 1) 225 | gfx.Clear(graphics.COLOR_BUFFER_BIT | graphics.DEPTH_BUFFER_BIT) 226 | 227 | gfx.BindVertexArray(lens.VAO) 228 | gfx.UseProgram(lens.Shader.Prog) 229 | 230 | // render left lens 231 | gfx.ActiveTexture(graphics.TEXTURE0) 232 | gfx.BindTexture(graphics.TEXTURE_2D, lens.EyeLeft.ResolveTexture) 233 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_S, graphics.CLAMP_TO_EDGE) 234 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_T, graphics.CLAMP_TO_EDGE) 235 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MAG_FILTER, graphics.LINEAR) 236 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MIN_FILTER, graphics.LINEAR_MIPMAP_LINEAR) 237 | gfx.DrawElements(graphics.TRIANGLES, (lens.IndexCount / 2), graphics.UNSIGNED_INT, gfx.PtrOffset(0)) 238 | 239 | // render right lens 240 | gfx.BindTexture(graphics.TEXTURE_2D, lens.EyeRight.ResolveTexture) 241 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_S, graphics.CLAMP_TO_EDGE) 242 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_WRAP_T, graphics.CLAMP_TO_EDGE) 243 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MAG_FILTER, graphics.LINEAR) 244 | gfx.TexParameteri(graphics.TEXTURE_2D, graphics.TEXTURE_MIN_FILTER, graphics.LINEAR_MIPMAP_LINEAR) 245 | gfx.DrawElements(graphics.TRIANGLES, (lens.IndexCount / 2), graphics.UNSIGNED_INT, gfx.PtrOffset(int((lens.IndexCount/2)*4))) // uint32size 246 | 247 | gfx.BindVertexArray(0) 248 | gfx.UseProgram(0) 249 | } 250 | -------------------------------------------------------------------------------- /utils_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package openvr 4 | 5 | import "C" 6 | 7 | // convertCBool2Int is a bandaid over the way the bool types end up working out 8 | // with go re: openvr_capi.h. On windows, openvr_capi.h will setup a typedef 9 | // mapping bool to char; on any other platform stdbool.h is used which will boil 10 | // down to using C._Bool in go. 11 | // 12 | // In order to cope with the different types, this function was made to return 13 | // an integer version of the bool value. It is conditionally compiled based on 14 | // platform so it shouldn't create a duplication error. 15 | func convertCBool2Int(b C._Bool) int { 16 | if b == C._Bool(false) { 17 | return 0 18 | } 19 | 20 | return 1 21 | } 22 | -------------------------------------------------------------------------------- /utils_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package openvr 4 | 5 | /* 6 | #include 7 | #include 8 | #include "openvr_capi.h" 9 | */ 10 | import "C" 11 | 12 | // convertCBool2Int is a bandaid over the way the bool types end up working out 13 | // with go re: openvr_capi.h. On windows, openvr_capi.h will setup a typedef 14 | // mapping bool to char; on any other platform stdbool.h is used which will boil 15 | // down to using C._Bool in go. 16 | // 17 | // In order to cope with the different types, this function was made to return 18 | // an integer version of the bool value. It is conditionally compiled based on 19 | // platform so it shouldn't create a duplication error. 20 | func convertCBool2Int(b C.bool) int { 21 | if b == C.bool(0) { 22 | return 0 23 | } 24 | 25 | return 1 26 | } 27 | -------------------------------------------------------------------------------- /vendored/openvr/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Valve Corporation 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendored/openvr/README.md: -------------------------------------------------------------------------------- 1 | OpenVR SDK 2 | --- 3 | 4 | OpenVR is an API and runtime that allows access to VR hardware from multiple 5 | vendors without requiring that applications have specific knowledge of the 6 | hardware they are targeting. This repository is an SDK that contains the API 7 | and samples. The runtime is under SteamVR in Tools on Steam. 8 | 9 | ### Documentation 10 | 11 | Documentation for the API is available on the [Github Wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation) 12 | 13 | More information on OpenVR and SteamVR can be found on http://steamvr.com 14 | -------------------------------------------------------------------------------- /vendored/openvr/bin/linux32/libopenvr_api.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/linux32/libopenvr_api.so -------------------------------------------------------------------------------- /vendored/openvr/bin/linux32/libopenvr_api.so.dbg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/linux32/libopenvr_api.so.dbg -------------------------------------------------------------------------------- /vendored/openvr/bin/linux64/libopenvr_api.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/linux64/libopenvr_api.so -------------------------------------------------------------------------------- /vendored/openvr/bin/linux64/libopenvr_api.so.dbg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/linux64/libopenvr_api.so.dbg -------------------------------------------------------------------------------- /vendored/openvr/bin/osx32/libopenvr_api.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/osx32/libopenvr_api.dylib -------------------------------------------------------------------------------- /vendored/openvr/bin/osx32/libopenvr_api.dylib.dSYM/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleIdentifier 8 | com.apple.xcode.dsym.libopenvr_api.dylib 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundlePackageType 12 | dSYM 13 | CFBundleSignature 14 | ???? 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleVersion 18 | 1 19 | 20 | 21 | -------------------------------------------------------------------------------- /vendored/openvr/bin/osx32/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/osx32/libopenvr_api.dylib.dSYM/Contents/Resources/DWARF/libopenvr_api.dylib -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/OpenVR: -------------------------------------------------------------------------------- 1 | Versions/Current/OpenVR -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/Versions/A/OpenVR: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/osx64/OpenVR.framework/Versions/A/OpenVR -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.valvesoftware.OpenVR.framework 7 | CFBundleInfoDictionaryVersion 8 | 6.0 9 | CFBundleName 10 | OpenVR 11 | CFBundlePackageType 12 | FMWK 13 | CFBundleShortVersionString 14 | 1.0 15 | CFBundleVersion 16 | 1.0 17 | 18 | 19 | -------------------------------------------------------------------------------- /vendored/openvr/bin/osx64/OpenVR.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /vendored/openvr/bin/win32/openvr_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/win32/openvr_api.dll -------------------------------------------------------------------------------- /vendored/openvr/bin/win32/openvr_api.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/win32/openvr_api.pdb -------------------------------------------------------------------------------- /vendored/openvr/bin/win64/openvr_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/win64/openvr_api.dll -------------------------------------------------------------------------------- /vendored/openvr/bin/win64/openvr_api.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/bin/win64/openvr_api.pdb -------------------------------------------------------------------------------- /vendored/openvr/lib/linux32/libopenvr_api.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/lib/linux32/libopenvr_api.so -------------------------------------------------------------------------------- /vendored/openvr/lib/linux64/libopenvr_api.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/lib/linux64/libopenvr_api.so -------------------------------------------------------------------------------- /vendored/openvr/lib/osx32/libopenvr_api.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/lib/osx32/libopenvr_api.dylib -------------------------------------------------------------------------------- /vendored/openvr/lib/win32/openvr_api.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/lib/win32/openvr_api.lib -------------------------------------------------------------------------------- /vendored/openvr/lib/win64/openvr_api.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbogdala/openvr-go/df4d85562c3beaa99799cdd102470d2f83044b75/vendored/openvr/lib/win64/openvr_api.lib -------------------------------------------------------------------------------- /vendored/openvr_version.md: -------------------------------------------------------------------------------- 1 | OpenVR SDK 1.0.10 2 | --------------------------------------------------------------------------------