func (b bbox) Contains(pos mgl32.Vec3) bool { return pos.X() > b[0].X() && pos.Y() > b[0].Y() && pos.Z() > b[0].Z() && pos.X() < b[1].X() && pos.Y() < b[1].Y() && pos.Z() < b[1].Z() }
// Rotate adjusts the direction vectors by a delta vector of {pitch, yaw, roll}. // Roll is ignored for now. func (c *EulerCamera) Rotate(delta mgl.Vec3) { c.yaw += float64(delta.Y()) c.pitch += float64(delta.X()) // Limit vertical rotation to avoid gimbal lock if c.pitch > halfPi { c.pitch = halfPi } else if c.pitch < -halfPi { c.pitch = -halfPi } c.updateVectors() }
func TestTransformSetPosition(t *testing.T) { transform := NewTransform() expectedPosition := mgl32.Vec3{5, 10, 3.5} transform.SetPosition(expectedPosition.X(), expectedPosition.Y(), expectedPosition.Z()) if transform.position != expectedPosition { t.Errorf("Expected position to be %v got %v", expectedPosition, transform.position) } if transform.matrix[12] != expectedPosition.X() || transform.matrix[13] != expectedPosition.Y() || transform.matrix[14] != expectedPosition.Z() { t.Error("Matrix was not updated correctly after translation.") } }
func TestTransformScale(t *testing.T) { transform := NewTransform() scale := mgl32.Vec3{2, 2, 2} transform.Scale(scale.X(), scale.Y(), scale.Z()) if transform.scale != scale { t.Errorf("Expected scale to be %v got %v", scale, transform.scale) } if transform.matrix[0] != scale.X() || transform.matrix[5] != scale.Y() || transform.matrix[10] != scale.Z() { t.Error("Matrix was not updated correctly after translation.") } }
func findFace(bound vmath.AABB, at mgl32.Vec3) direction.Type { switch { case bound.Min.X() == at.X(): return direction.West case bound.Max.X() == at.X(): return direction.East case bound.Min.Y() == at.Y(): return direction.Down case bound.Max.Y() == at.Y(): return direction.Up case bound.Min.Z() == at.Z(): return direction.North case bound.Max.Z() == at.Z(): return direction.South } return direction.Up }
func TestTransformTranslate(t *testing.T) { transform := NewTransform() transform.SetPosition(10, 10, 10) vector := mgl32.Vec3{-5, 6, 3} expected := mgl32.Vec3{5, 16, 13} transform.Translate(vector) if transform.position != expected { t.Errorf("Expected position to be %v got %v", expected, transform.position) } if transform.matrix[12] != expected.X() || transform.matrix[13] != expected.Y() || transform.matrix[14] != expected.Z() { t.Error("Matrix was not updated correctly after translation.") } }
// NewPianoKey returns a key for our piano. func NewPianoKey(pos mgl32.Vec3, lightColor mgl32.Vec3, white bool, freq float32) PianoKey { var color mgl32.Vec4 var keySize float32 if white { color = mgl32.Vec4{0.98, 0.97, 0.94} keySize = 2 } else { color = mgl32.Vec4{0.1, 0.1, 0.1, 1.0} keySize = 1 } pk := PianoKey{Pos: pos, Angle: 0, Color: color, Frequency: freq, Finger: -1, white: white, LightColor: lightColor} pk.BBox[0] = pos.Sub(mgl32.Vec3{0.5, 0.6, keySize}) pk.BBox[1] = pos.Add(mgl32.Vec3{0.5, 0.6, keySize}) pk.source = al.GenSources(1)[0] pk.source.SetGain(1.0) pk.source.SetPosition(al.Vector{pos.X(), pos.Y(), pos.Z()}) pk.source.SetVelocity(al.Vector{}) pk.buffers = al.GenBuffers(3) var samples [1024 * 16]int16 sampleRate := 44100 amplitude := float32(0.8 * 0x7FFF) for i := 0; i < len(samples); i++ { val := f32.Sin((2.0 * math.Pi * freq) / float32(sampleRate) * float32(i)) samples[i] = int16(amplitude * val) } buf := &bytes.Buffer{} binary.Write(buf, binary.LittleEndian, &samples) pk.buffers[0].BufferData(al.FormatMono16, buf.Bytes(), 44100) f, _ := os.Create("audio.raw") binary.Write(f, binary.LittleEndian, &samples) f.Close() return pk }
func (a AABB) MoveOutOf(o AABB, dir mgl32.Vec3) AABB { if dir.X() != 0 { if dir.X() > 0 { ox := a.Max.X() a.Max[0] = o.Min.X() - 0.0001 a.Min[0] += a.Max.X() - ox } else { ox := a.Min.X() a.Min[0] = o.Max.X() + 0.0001 a.Max[0] += a.Min.X() - ox } } if dir.Y() != 0 { if dir.Y() > 0 { oy := a.Max.Y() a.Max[1] = o.Min.Y() - 0.0001 a.Min[1] += a.Max.Y() - oy } else { oy := a.Min.Y() a.Min[1] = o.Max.Y() + 0.0001 a.Max[1] += a.Min.Y() - oy } } if dir.Z() != 0 { if dir.Z() > 0 { oz := a.Max.Z() a.Max[2] = o.Min.Z() - 0.0001 a.Min[2] += a.Max.Z() - oz } else { oz := a.Min.Z() a.Min[2] = o.Max.Z() + 0.0001 a.Max[2] += a.Min.Z() - oz } } return a }
func programLoop(window *win.Window) error { // the linked shader program determines how the data will be rendered vertShader, err := gfx.NewShaderFromFile("shaders/phong.vert", gl.VERTEX_SHADER) if err != nil { return err } fragShader, err := gfx.NewShaderFromFile("shaders/phong.frag", gl.FRAGMENT_SHADER) if err != nil { return err } program, err := gfx.NewProgram(vertShader, fragShader) if err != nil { return err } defer program.Delete() lightFragShader, err := gfx.NewShaderFromFile("shaders/light.frag", gl.FRAGMENT_SHADER) if err != nil { return err } // special shader program so that lights themselves are not affected by lighting lightProgram, err := gfx.NewProgram(vertShader, lightFragShader) if err != nil { return err } VAO := createVAO(cubeVertices, nil) lightVAO := createVAO(cubeVertices, nil) // ensure that triangles that are "behind" others do not draw over top of them gl.Enable(gl.DEPTH_TEST) camera := cam.NewFpsCamera(mgl32.Vec3{0, 0, 3}, mgl32.Vec3{0, 1, 0}, -90, 0, window.InputManager()) for !window.ShouldClose() { // swaps in last buffer, polls for window events, and generally sets up for a new render frame window.StartFrame() // update camera position and direction from input evevnts camera.Update(window.SinceLastFrame()) // background color gl.ClearColor(0, 0, 0, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) // depth buffer needed for DEPTH_TEST // cube rotation matrices rotateX := (mgl32.Rotate3DX(mgl32.DegToRad(-45 * float32(glfw.GetTime())))) rotateY := (mgl32.Rotate3DY(mgl32.DegToRad(-45 * float32(glfw.GetTime())))) rotateZ := (mgl32.Rotate3DZ(mgl32.DegToRad(-45 * float32(glfw.GetTime())))) // creates perspective fov := float32(60.0) projectTransform := mgl32.Perspective(mgl32.DegToRad(fov), float32(window.Width())/float32(window.Height()), 0.1, 100.0) camTransform := camera.GetTransform() lightPos := mgl32.Vec3{0.6, 1, 0.1} lightTransform := mgl32.Translate3D(lightPos.X(), lightPos.Y(), lightPos.Z()).Mul4( mgl32.Scale3D(0.2, 0.2, 0.2)) program.Use() gl.UniformMatrix4fv(program.GetUniformLocation("view"), 1, false, &camTransform[0]) gl.UniformMatrix4fv(program.GetUniformLocation("project"), 1, false, &projectTransform[0]) gl.BindVertexArray(VAO) // draw each cube after all coordinate system transforms are bound // obj is colored, light is white gl.Uniform3f(program.GetUniformLocation("material.ambient"), 1.0, 0.5, 0.31) gl.Uniform3f(program.GetUniformLocation("material.diffuse"), 1.0, 0.5, 0.31) gl.Uniform3f(program.GetUniformLocation("material.specular"), 0.5, 0.5, 0.5) gl.Uniform1f(program.GetUniformLocation("material.shininess"), 32.0) lightColor := mgl32.Vec3{ float32(math.Sin(glfw.GetTime() * 1)), float32(math.Sin(glfw.GetTime() * 0.35)), float32(math.Sin(glfw.GetTime() * 0.65)), } diffuseColor := mgl32.Vec3{ 0.5 * lightColor[0], 0.5 * lightColor[1], 0.5 * lightColor[2], } ambientColor := mgl32.Vec3{ 0.2 * lightColor[0], 0.2 * lightColor[1], 0.2 * lightColor[2], } gl.Uniform3f(program.GetUniformLocation("light.ambient"), ambientColor[0], ambientColor[1], ambientColor[2]) gl.Uniform3f(program.GetUniformLocation("light.diffuse"), diffuseColor[0], diffuseColor[1], diffuseColor[2]) gl.Uniform3f(program.GetUniformLocation("light.specular"), 1.0, 1.0, 1.0) gl.Uniform3f(program.GetUniformLocation("light.position"), lightPos.X(), lightPos.Y(), lightPos.Z()) for _, pos := range cubePositions { // turn the cubes into rectangular prisms for more fun worldTranslate := mgl32.Translate3D(pos[0], pos[1], pos[2]) worldTransform := worldTranslate.Mul4( rotateX.Mul3(rotateY).Mul3(rotateZ).Mat4(), ) gl.UniformMatrix4fv(program.GetUniformLocation("model"), 1, false, &worldTransform[0]) gl.DrawArrays(gl.TRIANGLES, 0, 36) } gl.BindVertexArray(0) // Draw the light obj after the other boxes using its separate shader program // this means that we must re-bind any uniforms lightProgram.Use() gl.BindVertexArray(lightVAO) gl.UniformMatrix4fv(lightProgram.GetUniformLocation("model"), 1, false, &lightTransform[0]) gl.UniformMatrix4fv(lightProgram.GetUniformLocation("view"), 1, false, &camTransform[0]) gl.UniformMatrix4fv(lightProgram.GetUniformLocation("project"), 1, false, &projectTransform[0]) gl.DrawArrays(gl.TRIANGLES, 0, 36) gl.BindVertexArray(0) // end of draw loop } return nil }
func randomVector(min, max mgl32.Vec3) mgl32.Vec3 { r1, r2, r3 := rand.Float32(), rand.Float32(), rand.Float32() return mgl32.Vec3{min.X()*(1.0-r1) + max.X()*r1, min.Y()*(1.0-r2) + max.Y()*r2, min.Z()*(1.0-r3) + max.Z()*r3} }
// // objectToObjectData // Turns the data from .obj format into an internal OpenGL friendly // format. The following information needs to be created for each mesh. // // mesh.V = append(mesh.V, ...4-float32) - indexed from 0 // mesh.N = append(mesh.N, ...3-float32) - indexed from 0 // mesh.T = append(mesh.T, ...2-float32) - indexed from 0 // mesh.F = append(mesh.F, ...3-uint16) - refers to above zero indexed values // // objectData holds the global vertex, texture, and normal point information. // faces are the indexes for this mesh. // // Additionally the normals at each vertex are generated as the sum of the // normals for each face that shares that vertex. // // @param name (string) The Name of the Object. // @param objectData (*objectData) A temporary object data pointer. // @param objectData (*objectData) The array of Faces / Indices. // // @return data (*ObjectData) A pointer to the Object. // @return error (error) the error (if any) // func (loader *Loader) objectToObjectData(name string, objectData *objectData, faces []face) (data *ObjectData, err error) { data = &ObjectData{} data.Name = name vmap := make(map[string]int) // the unique vertex data points for this face. vcnt := -1 // process each vertex of each face. Each one represents a combination vertex, // texture coordinate, and normal. for _, face := range faces { for pi := 0; pi < 3; pi++ { // Load only triangles // for pi, _ := range face.s { v, t, n := -1, -1, -1 if len(face.s) > pi { faceIndex := face.s[pi] if v, t, n, err = parseFaceIndices(faceIndex); err != nil { return data, fmt.Errorf("Could not parse face data %s", err) } // cut down the amount of information passed around by reusing points // where the vertex and the texture coordinate information is the same. vertexIndex := fmt.Sprintf("%d/%d/%d", v, t, n) if _, ok := vmap[vertexIndex]; !ok { // add a new data point. vcnt++ vmap[vertexIndex] = vcnt data.Vertex = append(data.Vertex, objectData.vertices[v].x, objectData.vertices[v].y, objectData.vertices[v].z) // Object might not have normals if n != -1 { data.Normals = append(data.Normals, objectData.normals[n].x, objectData.normals[n].y, objectData.normals[n].z) } // Object might not have texture information if t != -1 { data.Coordinates = append(data.Coordinates, objectData.texture[t].u, objectData.texture[t].v) } } else { // update the normal at the vertex to be a combination of // all the normals of each face that shares the vertex. ni := vmap[vertexIndex] * 3 // Obj might not have normals if n != -1 && len(data.Normals) > (ni+2) { var n1 mgl32.Vec3 = mgl32.Vec3{ float32(data.Normals[ni]), float32(data.Normals[ni+1]), float32(data.Normals[ni+2]), } var n2 mgl32.Vec3 = mgl32.Vec3{ float32(objectData.normals[n].x), float32(objectData.normals[n].y), float32(objectData.normals[n].z), } n2 = n2.Add(n1).Normalize() data.Normals[ni], data.Normals[ni+1], data.Normals[ni+2] = float32(n2.X()), float32(n2.Y()), float32(n2.Z()) } } data.Faces = append(data.Faces, uint16(vmap[vertexIndex])) } } } return data, err }
func (i *Instance) SetPosition(p mgl32.Vec3) { if i.position.X() != p.X() || i.position.Y() != p.Y() || i.position.Z() != p.Z() { i.position = p i.dirty = true } }
func traceRay(max float32, s, d mgl32.Vec3, cb func(x, y, z int) bool) { type gen struct { count int base, d float32 } newGen := func(start, d float32) *gen { g := &gen{} if d > 0 { g.base = (float32(math.Ceil(float64(start))) - start) / d } else if d < 0 { d = float32(math.Abs(float64(d))) g.base = (start - float32(math.Floor(float64(start)))) / d } g.d = d return g } next := func(g *gen) float32 { g.count++ if g.d == 0 { return float32(math.Inf(1)) } return g.base + float32(g.count-1)/g.d } aGen := newGen(s.X(), d.X()) bGen := newGen(s.Y(), d.Y()) cGen := newGen(s.Z(), d.Z()) nextNA := next(aGen) nextNB := next(bGen) nextNC := next(cGen) x, y, z := int(math.Floor(float64(s.X()))), int(math.Floor(float64(s.Y()))), int(math.Floor(float64(s.Z()))) for { if !cb(x, y, z) { return } nextN := float32(0.0) if nextNA <= nextNB { if nextNA <= nextNC { nextN = nextNA nextNA = next(aGen) x += int(math.Copysign(1, float64(d.X()))) } else { nextN = nextNC nextNC = next(cGen) z += int(math.Copysign(1, float64(d.Z()))) } } else { if nextNB <= nextNC { nextN = nextNB nextNB = next(bGen) y += int(math.Copysign(1, float64(d.Y()))) } else { nextN = nextNC nextNC = next(cGen) z += int(math.Copysign(1, float64(d.Z()))) } } if nextN > max { break } } }
func Vector3bytes(w io.Writer, vector mgl32.Vec3) { Float32bytes(w, vector.X()) Float32bytes(w, vector.Y()) Float32bytes(w, vector.Z()) }
func inBox(Hit, B1, B2 mgl32.Vec3, Axis int) bool { if Axis == 1 && Hit.Z() > B1.Z() && Hit.Z() < B2.Z() && Hit.Y() > B1.Y() && Hit.Y() < B2.Y() { return true } if Axis == 2 && Hit.Z() > B1.Z() && Hit.Z() < B2.Z() && Hit.X() > B1.X() && Hit.X() < B2.X() { return true } if Axis == 3 && Hit.X() > B1.X() && Hit.X() < B2.X() && Hit.Y() > B1.Y() && Hit.Y() < B2.Y() { return true } return false }
// returns true if line (L1, L2) intersects with the box (B1, B2) // returns intersection point in Hit func checkLineBox(B1, B2, L1, L2 mgl32.Vec3) (bool, mgl32.Vec3) { var Hit mgl32.Vec3 if L2.X() < B1.X() && L1.X() < B1.X() { return false, mgl32.Vec3{} } if L2.X() > B2.X() && L1.X() > B2.X() { return false, mgl32.Vec3{} } if L2.Y() < B1.Y() && L1.Y() < B1.Y() { return false, mgl32.Vec3{} } if L2.Y() > B2.Y() && L1.Y() > B2.Y() { return false, mgl32.Vec3{} } if L2.Z() < B1.Z() && L1.Z() < B1.Z() { return false, mgl32.Vec3{} } if L2.Z() > B2.Z() && L1.Z() > B2.Z() { return false, mgl32.Vec3{} } if L1.X() > B1.X() && L1.X() < B2.X() && L1.Y() > B1.Y() && L1.Y() < B2.Y() && L1.Z() > B1.Z() && L1.Z() < B2.Z() { Hit = L1 return true, Hit } if (getIntersection(L1.X()-B1.X(), L2.X()-B1.X(), L1, L2, &Hit) && inBox(Hit, B1, B2, 1)) || (getIntersection(L1.Y()-B1.Y(), L2.Y()-B1.Y(), L1, L2, &Hit) && inBox(Hit, B1, B2, 2)) || (getIntersection(L1.Z()-B1.Z(), L2.Z()-B1.Z(), L1, L2, &Hit) && inBox(Hit, B1, B2, 3)) || (getIntersection(L1.X()-B2.X(), L2.X()-B2.X(), L1, L2, &Hit) && inBox(Hit, B1, B2, 1)) || (getIntersection(L1.Y()-B2.Y(), L2.Y()-B2.Y(), L1, L2, &Hit) && inBox(Hit, B1, B2, 2)) || (getIntersection(L1.Z()-B2.Z(), L2.Z()-B2.Z(), L1, L2, &Hit) && inBox(Hit, B1, B2, 3)) { return true, Hit } return false, mgl32.Vec3{} }
func playSoundInternal(cat soundCategory, snd sound, vol, pitch float64, rel bool, pos mgl32.Vec3, cb func()) { vol *= snd.Volume * 100 baseVol := vol vol *= float64(muVolMaster.Value()) / 100 if v, ok := volVars[cat]; ok { vol *= float64(v.Value()) / 100 } if vol <= 0 { if cb != nil { go func() { syncChan <- cb }() } return } name := snd.Name key := pluginKey{"minecraft", name} sb, ok := loadedSounds[key] if !ok { f, err := resource.Open("minecraft", "sounds/"+name+".ogg") if err != nil { v, ok := assets.Objects[fmt.Sprintf("minecraft/sounds/%s.ogg", name)] if !ok { console.Text("Missing sound %s", key) if cb != nil { cb() } return } loc := fmt.Sprintf("./resources/%s", hashPath(v.Hash)) f, err = os.Open(loc) if err != nil { console.Text("Missing sound %s", key) if cb != nil { cb() } return } } if snd.Stream { m := audio.NewMusic(f) m.SetVolume(vol) m.SetPitch(pitch) m.Play() currentMusic = append(currentMusic, music{Music: m, cb: cb, cat: cat, vol: baseVol}) return } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { panic(err) } sb = audio.NewSoundBufferData(data) loadedSounds[key] = sb } var s audio.Sound n := true for _, sn := range soundList { if sn.Status() == audio.StatStopped { s = sn n = false break } } if n { if len(soundList) >= 100 { console.Component( format.Build("WARN: Skipping playing sound due to limit"). Color(format.Yellow).Create(), ) return } s = audio.NewSound() soundList = append(soundList, s) } s.SetBuffer(sb) s.SetVolume(vol) s.SetMinDistance(5) s.SetAttenuation(0.008) s.SetPitch(pitch) s.Play() if rel { s.SetRelative(true) s.SetPosition(pos.X(), pos.Y(), pos.Z()) } else { s.SetRelative(false) } }
// IntersectGeometry - returns a list of line segments resulting from the xz plane intersection of the geometry func IntersectGeometry(geometry *renderer.Geometry) [][2]mgl32.Vec2 { segments := make([][2]mgl32.Vec2, 0) for i := 0; i < len(geometry.Indicies); i = i + 3 { index := geometry.Indicies[i] v1 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]} index = geometry.Indicies[i+1] v2 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]} index = geometry.Indicies[i+2] v3 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]} va, vb, vc := v1, v2, v3 if (v1.Y() < 0 && v2.Y() > 0 && v3.Y() > 0) || (v1.Y() > 0 && v2.Y() < 0 && v3.Y() < 0) { va, vb, vc = v1, v2, v3 } else if (v1.Y() > 0 && v2.Y() < 0 && v3.Y() > 0) || (v1.Y() < 0 && v2.Y() > 0 && v3.Y() < 0) { va, vb, vc = v2, v1, v3 } else if (v1.Y() > 0 && v2.Y() > 0 && v3.Y() < 0) || (v1.Y() < 0 && v2.Y() < 0 && v3.Y() > 0) { va, vb, vc = v3, v2, v1 } else { continue } t_ab := -va.Y() / (va.Y() - vb.Y()) t_ac := -va.Y() / (va.Y() - vc.Y()) segments = append(segments, [2]mgl32.Vec2{ mgl32.Vec2{va.X() + (va.X()-vb.X())*t_ab, va.Z() + (va.Z()-vb.Z())*t_ab}, mgl32.Vec2{va.X() + (va.X()-vc.X())*t_ac, va.Z() + (va.Z()-vc.Z())*t_ac}, }) } return segments }
func (i *Instance) SetScale(s mgl32.Vec3) { if i.scale.X() != s.X() || i.scale.Y() != s.Y() || i.scale.Z() != s.Z() { i.scale = s i.dirty = true } }