// Copyright 2016 The G3N Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package graphic import ( "github.com/g3n/engine/core" "github.com/g3n/engine/geometry" "github.com/g3n/engine/gls" "github.com/g3n/engine/material" "github.com/g3n/engine/math32" ) type Mesh struct { Graphic // Embedded graphic mvm gls.UniformMatrix4f // Model view matrix uniform mvpm gls.UniformMatrix4f // Model view projection matrix uniform nm gls.UniformMatrix3f // Normal matrix uniform } // NewMesh creates and returns a pointer to a mesh with the specified geometry and material // If the mesh has multi materials, the material specified here must be nil and the // individual materials must be add using "AddMateria" or AddGroupMaterial" func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh { m := new(Mesh) m.Init(igeom, imat) return m } func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) { m.Graphic.Init(igeom, gls.TRIANGLES) // Initialize uniforms m.mvm.Init("ModelViewMatrix") m.mvpm.Init("MVP") m.nm.Init("NormalMatrix") // Adds single material if not nil if imat != nil { m.AddMaterial(imat, 0, 0) } } func (m *Mesh) AddMaterial(imat material.IMaterial, start, count int) { m.Graphic.AddMaterial(m, imat, start, count) } // Add group material func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) { m.Graphic.AddGroupMaterial(m, imat, gindex) } // RenderSetup is called by the engine before drawing the mesh geometry // It is responsible to updating the current shader uniforms with // the model matrices. func (m *Mesh) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) { // Calculates model view matrix and updates uniform mw := m.MatrixWorld() var mvm math32.Matrix4 mvm.MultiplyMatrices(&rinfo.ViewMatrix, &mw) m.mvm.SetMatrix4(&mvm) m.mvm.Transfer(gs) // Calculates model view projection matrix and updates uniform var mvpm math32.Matrix4 mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm) m.mvpm.SetMatrix4(&mvpm) m.mvpm.Transfer(gs) // Calculates normal matrix and updates uniform var nm math32.Matrix3 nm.GetNormalMatrix(&mvm) m.nm.SetMatrix3(&nm) m.nm.Transfer(gs) } // Raycast checks intersections between this geometry and the specified raycaster // and if any found appends it to the specified intersects array. func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) { // Transform this mesh geometry bounding sphere from model // to world coordinates and checks intersection with raycaster geom := m.GetGeometry() sphere := geom.BoundingSphere() matrixWorld := m.MatrixWorld() sphere.ApplyMatrix4(&matrixWorld) if !rc.IsIntersectionSphere(&sphere) { return } // Copy ray and transform to model coordinates // This ray will will also be used to check intersects with // the geometry, as is much less expensive to transform the // ray to model coordinates than the geometry to world coordinates. var inverseMatrix math32.Matrix4 inverseMatrix.GetInverse(&matrixWorld, true) var ray math32.Ray ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix) bbox := geom.BoundingBox() if !ray.IsIntersectionBox(&bbox) { return } // Local function to check the intersection of the ray from the raycaster with // the specified face defined by three poins. checkIntersection := func(mat *material.Material, pA, pB, pC, point *math32.Vector3) *core.Intersect { var intersect bool switch mat.Side() { case material.SideBack: intersect = ray.IntersectTriangle(pC, pB, pA, true, point) case material.SideFront: intersect = ray.IntersectTriangle(pA, pB, pC, true, point) case material.SideDouble: intersect = ray.IntersectTriangle(pA, pB, pC, false, point) } if !intersect { return nil } // Transform intersection point from model to world coordinates var intersectionPointWorld = *point intersectionPointWorld.ApplyMatrix4(&matrixWorld) // Calculates the distance from the ray origin to intersection point origin := rc.Ray.Origin() distance := origin.DistanceTo(&intersectionPointWorld) // Checks if distance is between the bounds of the raycaster if distance < rc.Near || distance > rc.Far { return nil } return &core.Intersect{ Distance: distance, Point: intersectionPointWorld, Object: m, } } // Get buffer with position vertices vboPos := geom.VBO("VertexPosition") if vboPos == nil { panic("mesh.Raycast(): VertexPosition VBO not found") } positions := vboPos.Buffer() indices := geom.Indices() var vA math32.Vector3 var vB math32.Vector3 var vC math32.Vector3 // Geometry has indexed vertices if indices.Size() > 0 { for i := 0; i < indices.Size(); i += 3 { // Get face indices a := indices[i] b := indices[i+1] c := indices[i+2] // Get face position vectors positions.GetVector3(int(3*a), &vA) positions.GetVector3(int(3*b), &vB) positions.GetVector3(int(3*c), &vC) // Checks intersection of the ray with this face mat := m.GetMaterial(i).GetMaterial() var point math32.Vector3 intersect := checkIntersection(mat, &vA, &vB, &vC, &point) if intersect != nil { intersect.Index = uint32(i) *intersects = append(*intersects, *intersect) } } // Geometry has NO indexed vertices } else { for i := 0; i < positions.Size(); i += 9 { // Get face indices a := i / 3 b := a + 1 c := a + 2 // Set face position vectors positions.GetVector3(int(3*a), &vA) positions.GetVector3(int(3*b), &vB) positions.GetVector3(int(3*c), &vC) // Checks intersection of the ray with this face mat := m.GetMaterial(i).GetMaterial() var point math32.Vector3 intersect := checkIntersection(mat, &vA, &vB, &vC, &point) if intersect != nil { intersect.Index = uint32(a) *intersects = append(*intersects, *intersect) } } } }