morph.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package geometry
  5. import (
  6. "github.com/g3n/engine/gls"
  7. "github.com/g3n/engine/math32"
  8. "strconv"
  9. "sort"
  10. )
  11. // MorphGeometry represents a base geometry and its morph targets.
  12. type MorphGeometry struct {
  13. baseGeometry *Geometry // The base geometry
  14. targets []*Geometry // The morph target geometries (containing deltas)
  15. weights []float32 // The weights for each morph target
  16. uniWeights gls.Uniform // Texture unit uniform location cache
  17. morphGeom *Geometry // Cache of the last CPU-morphed geometry
  18. }
  19. // NumMorphInfluencers is the maximum number of active morph targets.
  20. const NumMorphTargets = 8
  21. // NewMorphGeometry creates and returns a pointer to a new MorphGeometry.
  22. func NewMorphGeometry(baseGeometry *Geometry) *MorphGeometry {
  23. mg := new(MorphGeometry)
  24. mg.baseGeometry = baseGeometry
  25. mg.targets = make([]*Geometry, 0)
  26. mg.weights = make([]float32, 0)
  27. mg.baseGeometry.ShaderDefines.Set("MORPHTARGETS", strconv.Itoa(NumMorphTargets))
  28. mg.uniWeights.Init("morphTargetInfluences")
  29. return mg
  30. }
  31. // GetGeometry satisfies the IGeometry interface.
  32. func (mg *MorphGeometry) GetGeometry() *Geometry {
  33. return mg.baseGeometry
  34. }
  35. // SetWeights sets the morph target weights.
  36. func (mg *MorphGeometry) SetWeights(weights []float32) {
  37. if len(weights) != len(mg.weights) {
  38. panic("weights have invalid length")
  39. }
  40. mg.weights = weights
  41. }
  42. // Weights returns the morph target weights.
  43. func (mg *MorphGeometry) Weights() []float32 {
  44. return mg.weights
  45. }
  46. // AddMorphTargets add multiple morph targets to the morph geometry.
  47. // Morph target deltas are calculated internally and the morph target geometries are altered to hold the deltas instead.
  48. func (mg *MorphGeometry) AddMorphTargets(morphTargets ...*Geometry) {
  49. for i := range morphTargets {
  50. mg.weights = append(mg.weights, 0)
  51. // Calculate deltas for VertexPosition
  52. vertexIdx := 0
  53. baseVertices := mg.baseGeometry.VBO(gls.VertexPosition).Buffer()
  54. morphTargets[i].OperateOnVertices(func(vertex *math32.Vector3) bool {
  55. var baseVertex math32.Vector3
  56. baseVertices.GetVector3(vertexIdx*3, &baseVertex)
  57. vertex.Sub(&baseVertex)
  58. vertexIdx++
  59. return false
  60. })
  61. // Calculate deltas for VertexNormal if attribute is present in target geometry
  62. // It is assumed that if VertexNormals are present in a target geometry then they are also present in the base geometry
  63. normalIdx := 0
  64. baseNormalsVBO := mg.baseGeometry.VBO(gls.VertexNormal)
  65. if baseNormalsVBO != nil {
  66. baseNormals := baseNormalsVBO.Buffer()
  67. morphTargets[i].OperateOnVertexNormals(func(normal *math32.Vector3) bool {
  68. var baseNormal math32.Vector3
  69. baseNormals.GetVector3(normalIdx*3, &baseNormal)
  70. normal.Sub(&baseNormal)
  71. normalIdx++
  72. return false
  73. })
  74. }
  75. // TODO Calculate deltas for VertexTangents
  76. }
  77. mg.targets = append(mg.targets, morphTargets...)
  78. // Update all target attributes if we have few enough that we are able to send them
  79. // all to the shader without sorting and choosing the ones with highest current weight
  80. if len(mg.targets) <= NumMorphTargets {
  81. mg.UpdateTargetAttributes(mg.targets)
  82. }
  83. }
  84. // AddMorphTargetDeltas add multiple morph target deltas to the morph geometry.
  85. func (mg *MorphGeometry) AddMorphTargetDeltas(morphTargetDeltas ...*Geometry) {
  86. for range morphTargetDeltas {
  87. mg.weights = append(mg.weights, 0)
  88. }
  89. mg.targets = append(mg.targets, morphTargetDeltas...)
  90. // Update all target attributes if we have few enough that we are able to send them
  91. // all to the shader without sorting and choosing the ones with highest current weight
  92. if len(mg.targets) <= NumMorphTargets {
  93. mg.UpdateTargetAttributes(mg.targets)
  94. }
  95. }
  96. // ActiveMorphTargets sorts the morph targets by weight and returns the top n morph targets with largest weight.
  97. func (mg *MorphGeometry) ActiveMorphTargets() ([]*Geometry, []float32) {
  98. numTargets := len(mg.targets)
  99. if numTargets == 0 {
  100. return nil, nil
  101. }
  102. if numTargets <= NumMorphTargets {
  103. // No need to sort - just return the targets and weights directly
  104. return mg.targets, mg.weights
  105. } else {
  106. // Need to sort them by weight and only return the top N morph targets with largest weight (N = NumMorphTargets)
  107. // TODO test this (more than [NumMorphTargets] morph targets)
  108. sortedMorphTargets := make([]*Geometry, numTargets)
  109. copy(sortedMorphTargets, mg.targets)
  110. sort.Slice(sortedMorphTargets, func(i, j int) bool {
  111. return mg.weights[i] > mg.weights[j]
  112. })
  113. sortedWeights := make([]float32, numTargets)
  114. copy(sortedWeights, mg.weights)
  115. sort.Slice(sortedWeights, func(i, j int) bool {
  116. return mg.weights[i] > mg.weights[j]
  117. })
  118. return sortedMorphTargets, sortedWeights
  119. }
  120. }
  121. // SetIndices sets the indices array for this geometry.
  122. func (mg *MorphGeometry) SetIndices(indices math32.ArrayU32) {
  123. mg.baseGeometry.SetIndices(indices)
  124. for i := range mg.targets {
  125. mg.targets[i].SetIndices(indices)
  126. }
  127. }
  128. // ComputeMorphed computes a morphed geometry from the provided morph target weights.
  129. // Note that morphing is usually computed by the GPU in shaders.
  130. // This CPU implementation allows users to obtain an instance of a morphed geometry
  131. // if so desired (loosing morphing ability).
  132. func (mg *MorphGeometry) ComputeMorphed(weights []float32) *Geometry {
  133. morphed := NewGeometry()
  134. // TODO
  135. return morphed
  136. }
  137. // Dispose releases, if possible, OpenGL resources, C memory
  138. // and VBOs associated with the base geometry and morph targets.
  139. func (mg *MorphGeometry) Dispose() {
  140. mg.baseGeometry.Dispose()
  141. for i := range mg.targets {
  142. mg.targets[i].Dispose()
  143. }
  144. }
  145. // UpdateTargetAttributes updates the attribute names of the specified morph targets in order.
  146. func (mg *MorphGeometry) UpdateTargetAttributes(morphTargets []*Geometry) {
  147. for i, mt := range morphTargets {
  148. mt.SetAttributeName(gls.VertexPosition, "MorphPosition"+strconv.Itoa(i))
  149. mt.SetAttributeName(gls.VertexNormal, "MorphNormal"+strconv.Itoa(i))
  150. mt.SetAttributeName(gls.VertexTangent, "MorphTangent"+strconv.Itoa(i))
  151. }
  152. }
  153. // RenderSetup is called by the renderer before drawing the geometry.
  154. func (mg *MorphGeometry) RenderSetup(gs *gls.GLS) {
  155. mg.baseGeometry.RenderSetup(gs)
  156. // Sort weights and find top 8 morph targets with largest current weight (8 is the max sent to shader)
  157. activeMorphTargets, activeWeights := mg.ActiveMorphTargets()
  158. // If the morph geometry has more targets than the shader supports we need to update attribute names
  159. // as weights change - we only send the top morph targets with highest weight
  160. if len(mg.targets) > NumMorphTargets {
  161. mg.UpdateTargetAttributes(activeMorphTargets)
  162. }
  163. // Transfer morphed geometry VBOs
  164. for _, mt := range activeMorphTargets {
  165. for _, vbo := range mt.VBOs() {
  166. vbo.Transfer(gs)
  167. }
  168. }
  169. // Transfer texture info combined uniform
  170. location := mg.uniWeights.Location(gs)
  171. gs.Uniform1fv(location, int32(len(activeWeights)), activeWeights)
  172. }