| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- // 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 collision
- import (
- "github.com/g3n/engine/camera"
- "github.com/g3n/engine/core"
- "github.com/g3n/engine/gls"
- "github.com/g3n/engine/graphic"
- "github.com/g3n/engine/material"
- "github.com/g3n/engine/math32"
- "sort"
- )
- // Raycaster represents an empty object that can cast rays and check for ray intersections.
- type Raycaster struct {
- // The distance from the ray origin to the intersected points
- // must be greater than the value of this field to be considered.
- // The defaul value is 0.0
- Near float32
- // The distance from the ray origin to the intersected points
- // must be less than the value of this field to be considered.
- // The defaul value is +Infinity.
- Far float32
- // Minimum distance in world coordinates between the ray and
- // a line segment when checking intersects with lines.
- // The default value is 0.1
- LinePrecision float32
- // Minimum distance in world coordinates between the ray and
- // a point when checking intersects with points.
- // The default value is 0.1
- PointPrecision float32
- // This field must be set with the camera view matrix used
- // when checking for sprite intersections.
- // It is set automatically when using camera.SetRaycaster
- ViewMatrix math32.Matrix4
- // Embedded ray
- math32.Ray
- }
- // Intersect describes the intersection between a ray and an object
- type Intersect struct {
- // Distance between the origin of the ray and the intersect
- Distance float32
- // Point of intersection in world coordinates
- Point math32.Vector3
- // Intersected node
- Object core.INode
- // If the geometry has indices, this field is the
- // index in the Indices buffer of the vertex intersected
- // or the first vertex of the intersected face.
- // If the geometry doesn't have indices, this field is the
- // index in the positions buffer of the vertex intersected
- // or the first vertex of the insersected face.
- Index uint32
- }
- // NewRaycaster creates and returns a pointer to a new raycaster object
- // with the specified origin and direction.
- func NewRaycaster(origin, direction *math32.Vector3) *Raycaster {
- rc := new(Raycaster)
- rc.Ray.Set(origin, direction)
- rc.Near = 0
- rc.Far = math32.Inf(1)
- rc.LinePrecision = 0.1
- rc.PointPrecision = 0.1
- return rc
- }
- // IntersectObject checks intersections between this raycaster and
- // and the specified node. If recursive is true, it also checks
- // the intersection with the node's children.
- // Intersections are returned sorted by distance, closest first.
- func (rc *Raycaster) IntersectObject(inode core.INode, recursive bool) []Intersect {
- intersects := []Intersect{}
- rc.intersectObject(inode, &intersects, recursive)
- sort.Slice(intersects, func(i, j int) bool {
- return intersects[i].Distance < intersects[j].Distance
- })
- return intersects
- }
- // IntersectObjects checks intersections between this raycaster and
- // the specified array of scene nodes. If recursive is true, it also checks
- // the intersection with each nodes' children.
- // Intersections are returned sorted by distance, closest first.
- func (rc *Raycaster) IntersectObjects(inodes []core.INode, recursive bool) []Intersect {
- intersects := []Intersect{}
- for _, inode := range inodes {
- rc.intersectObject(inode, &intersects, recursive)
- }
- sort.Slice(intersects, func(i, j int) bool {
- return intersects[i].Distance < intersects[j].Distance
- })
- return intersects
- }
- func (rc *Raycaster) intersectObject(inode core.INode, intersects *[]Intersect, recursive bool) {
- node := inode.GetNode()
- if !node.Visible() {
- return
- }
- switch in := inode.(type) {
- case *graphic.Sprite:
- rc.RaycastSprite(in, intersects)
- case *graphic.Points:
- rc.RaycastPoints(in, intersects)
- case *graphic.Mesh:
- rc.RaycastMesh(in, intersects)
- case *graphic.Lines:
- rc.RaycastLines(in, intersects)
- case *graphic.LineStrip:
- rc.RaycastLineStrip(in, intersects)
- }
- if recursive {
- for _, child := range node.Children() {
- rc.intersectObject(child, intersects, true)
- }
- }
- return
- }
- // SetRaycaster sets the specified raycaster with this camera position in world coordinates
- // pointing to the direction defined by the specified coordinates unprojected using this camera.
- func (rc *Raycaster) SetFromCamera(cam *camera.Camera, sx, sy float32) error { // TODO maybe use ICamera
- var origin, direction math32.Vector3
- matrixWorld := cam.MatrixWorld()
- origin.SetFromMatrixPosition(&matrixWorld)
- direction.Set(sx, sy, 0.5)
- unproj := cam.Unproject(&direction) // unproj = direction after this point TODO improve clarity
- unproj.Sub(&origin).Normalize()
- rc.Set(&origin, &direction)
- cam.ViewMatrix(&rc.ViewMatrix) // Update the view matrix of the raycaster
- return nil
- }
- // RaycastSprite checks intersections between the raycaster and the specified sprite
- // and if any found appends it to the specified intersects array.
- func (rc *Raycaster) RaycastSprite(s *graphic.Sprite, intersects *[]Intersect) {
- // Copy and convert ray to camera coordinates
- var ray math32.Ray
- ray.Copy(&rc.Ray).ApplyMatrix4(&rc.ViewMatrix)
- // Calculates ViewMatrix * MatrixWorld
- var mv math32.Matrix4
- matrixWorld := s.MatrixWorld()
- mv.MultiplyMatrices(&rc.ViewMatrix, &matrixWorld)
- // Decompose transformation matrix in its components
- var position math32.Vector3
- var quaternion math32.Quaternion
- var scale math32.Vector3
- mv.Decompose(&position, &quaternion, &scale)
- // Remove any rotation in X and Y axis and
- // compose new transformation matrix
- rotation := s.Rotation()
- rotation.X = 0
- rotation.Y = 0
- quaternion.SetFromEuler(&rotation)
- mv.Compose(&position, &quaternion, &scale)
- // Get buffer with vertices and uvs
- geom := s.GetGeometry()
- vboPos := geom.VBO(gls.VertexPosition)
- if vboPos == nil {
- panic("sprite.Raycast(): VertexPosition VBO not found")
- }
- // Get vertex positions, transform to camera coordinates and
- // checks intersection with ray
- buffer := vboPos.Buffer()
- indices := geom.Indices()
- var v1 math32.Vector3
- var v2 math32.Vector3
- var v3 math32.Vector3
- var point math32.Vector3
- intersect := false
- for i := 0; i < indices.Size(); i += 3 {
- pos := indices[i]
- buffer.GetVector3(int(pos*5), &v1)
- v1.ApplyMatrix4(&mv)
- pos = indices[i+1]
- buffer.GetVector3(int(pos*5), &v2)
- v2.ApplyMatrix4(&mv)
- pos = indices[i+2]
- buffer.GetVector3(int(pos*5), &v3)
- v3.ApplyMatrix4(&mv)
- if ray.IntersectTriangle(&v1, &v2, &v3, false, &point) {
- intersect = true
- break
- }
- }
- if !intersect {
- return
- }
- // Get distance from intersection point
- origin := ray.Origin()
- distance := origin.DistanceTo(&point)
- // Checks if distance is between the bounds of the raycaster
- if distance < rc.Near || distance > rc.Far {
- return
- }
- // Appends intersection to received parameter.
- *intersects = append(*intersects, Intersect{
- Distance: distance,
- Point: point,
- Object: s,
- })
- }
- // RaycastPoints
- func (rc *Raycaster) RaycastPoints(p *graphic.Points, intersects *[]Intersect) {
- // Checks intersection with the bounding sphere transformed to world coordinates
- geom := p.GetGeometry()
- sphere := geom.BoundingSphere()
- matrixWorld := p.MatrixWorld()
- sphere.ApplyMatrix4(&matrixWorld)
- if !rc.IsIntersectionSphere(&sphere) {
- return
- }
- // Copy ray and transforms to model coordinates
- var inverseMatrix math32.Matrix4
- var ray math32.Ray
- inverseMatrix.GetInverse(&matrixWorld)
- ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix)
- // Checks intersection with all points
- scale := p.Scale()
- localThreshold := rc.PointPrecision / ((scale.X + scale.Y + scale.Z) / 3)
- localThresholdSq := localThreshold * localThreshold
- // internal function to check intersection with a point
- testPoint := func(point *math32.Vector3, index int) {
- // Get distance from ray to point and if greater than threshold,
- // nothing to do.
- rayPointDistanceSq := ray.DistanceSqToPoint(point)
- if rayPointDistanceSq >= localThresholdSq {
- return
- }
- var intersectPoint math32.Vector3
- ray.ClosestPointToPoint(point, &intersectPoint)
- intersectPoint.ApplyMatrix4(&matrixWorld)
- origin := rc.Ray.Origin()
- distance := origin.DistanceTo(&intersectPoint)
- if distance < rc.Near || distance > rc.Far {
- return
- }
- // Appends intersection of raycaster with this point
- *intersects = append(*intersects, Intersect{
- Distance: distance,
- Point: intersectPoint,
- Index: uint32(index),
- Object: p,
- })
- }
- i := 0
- geom.ReadVertices(func(vertex math32.Vector3) bool {
- testPoint(&vertex, i)
- i++
- return false
- })
- }
- // RaycastMesh
- func (rc *Raycaster) RaycastMesh(m *graphic.Mesh, intersects *[]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)
- 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) *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 &Intersect{
- Distance: distance,
- Point: intersectionPointWorld,
- Object: m,
- }
- }
- i := 0
- geom.ReadFaces(func(vA, vB, vC math32.Vector3) bool {
- // 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)
- }
- i += 3
- return false
- })
- }
- // RaycastLines
- func (rc *Raycaster) RaycastLines(l *graphic.Lines, intersects *[]Intersect) {
- lineRaycast(l, rc, intersects, 2)
- }
- // RaycastLineStrip
- func (rc *Raycaster) RaycastLineStrip(l *graphic.LineStrip, intersects *[]Intersect) {
- lineRaycast(l, rc, intersects, 1)
- }
- // Internal function used by raycasting for Lines and LineStrip.
- func lineRaycast(igr graphic.IGraphic, rc *Raycaster, intersects *[]Intersect, step int) {
- // Get the bounding sphere
- gr := igr.GetGraphic()
- geom := igr.GetGeometry()
- sphere := geom.BoundingSphere()
- // Transform bounding sphere from model to world coordinates and
- // checks intersection with raycaster
- matrixWorld := gr.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
- var ray math32.Ray
- inverseMatrix.GetInverse(&matrixWorld)
- ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix)
- var vstart math32.Vector3
- var vend math32.Vector3
- var interSegment math32.Vector3
- var interRay math32.Vector3
- // Get geometry positions and indices buffers
- vboPos := geom.VBO(gls.VertexPosition)
- if vboPos == nil {
- return
- }
- positions := vboPos.Buffer()
- indices := geom.Indices()
- precisionSq := rc.LinePrecision * rc.LinePrecision
- // Checks intersection with individual lines for indexed geometry
- if indices.Size() > 0 {
- for i := 0; i < indices.Size()-1; i += step {
- // Calculates distance from ray to this line segment
- a := indices[i]
- b := indices[i+1]
- positions.GetVector3(int(3*a), &vstart)
- positions.GetVector3(int(3*b), &vend)
- distSq := ray.DistanceSqToSegment(&vstart, &vend, &interRay, &interSegment)
- if distSq > precisionSq {
- continue
- }
- // Move back to world coordinates for distance calculation
- interRay.ApplyMatrix4(&matrixWorld)
- origin := rc.Ray.Origin()
- distance := origin.DistanceTo(&interRay)
- if distance < rc.Near || distance > rc.Far {
- continue
- }
- interSegment.ApplyMatrix4(&matrixWorld)
- *intersects = append(*intersects, Intersect{
- Distance: distance,
- Point: interSegment,
- Index: uint32(i),
- Object: igr,
- })
- }
- // Checks intersection with individual lines for NON indexed geometry
- } else {
- for i := 0; i < positions.Size()/3-1; i += step {
- positions.GetVector3(int(3*i), &vstart)
- positions.GetVector3(int(3*i+3), &vend)
- distSq := ray.DistanceSqToSegment(&vstart, &vend, &interRay, &interSegment)
- if distSq > precisionSq {
- continue
- }
- // Move back to world coordinates for distance calculation
- interRay.ApplyMatrix4(&matrixWorld)
- origin := rc.Ray.Origin()
- distance := origin.DistanceTo(&interRay)
- if distance < rc.Near || distance > rc.Far {
- continue
- }
- interSegment.ApplyMatrix4(&matrixWorld)
- *intersects = append(*intersects, Intersect{
- Distance: distance,
- Point: interSegment,
- Index: uint32(i),
- Object: igr,
- })
- }
- }
- }
|