morph.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. }
  79. // AddMorphTargetDeltas add multiple morph target deltas to the morph geometry.
  80. func (mg *MorphGeometry) AddMorphTargetDeltas(morphTargetDeltas ...*Geometry) {
  81. for range morphTargetDeltas {
  82. mg.weights = append(mg.weights, 0)
  83. }
  84. mg.targets = append(mg.targets, morphTargetDeltas...)
  85. }
  86. // ActiveMorphTargets sorts the morph targets by weight and returns the top n morph targets with largest weight.
  87. func (mg *MorphGeometry) ActiveMorphTargets() ([]*Geometry, []float32) {
  88. numTargets := len(mg.targets)
  89. if numTargets == 0 {
  90. return nil, nil
  91. }
  92. sortedMorphTargets := make([]*Geometry, numTargets)
  93. copy(sortedMorphTargets, mg.targets)
  94. sort.Slice(sortedMorphTargets, func(i, j int) bool {
  95. return mg.weights[i] > mg.weights[j]
  96. })
  97. sortedWeights := make([]float32, numTargets)
  98. copy(sortedWeights, mg.weights)
  99. sort.Slice(sortedWeights, func(i, j int) bool {
  100. return mg.weights[i] > mg.weights[j]
  101. })
  102. // TODO check current 0 weights
  103. //if len(mg.targets) < NumMorphTargets-1 {
  104. return sortedMorphTargets, sortedWeights
  105. //} else {
  106. // return sortedMorphTargets[:NumMorphTargets-1]
  107. //}
  108. }
  109. // SetIndices sets the indices array for this geometry.
  110. func (mg *MorphGeometry) SetIndices(indices math32.ArrayU32) {
  111. mg.baseGeometry.SetIndices(indices)
  112. for i := range mg.targets {
  113. mg.targets[i].SetIndices(indices)
  114. }
  115. }
  116. // ComputeMorphed computes a morphed geometry from the provided morph target weights.
  117. // Note that morphing is usually computed by the GPU in shaders.
  118. // This CPU implementation allows users to obtain an instance of a morphed geometry
  119. // if so desired (loosing morphing ability).
  120. func (mg *MorphGeometry) ComputeMorphed(weights []float32) *Geometry {
  121. morphed := NewGeometry()
  122. // TODO
  123. return morphed
  124. }
  125. // Dispose releases, if possible, OpenGL resources, C memory
  126. // and VBOs associated with the base geometry and morph targets.
  127. func (mg *MorphGeometry) Dispose() {
  128. mg.baseGeometry.Dispose()
  129. for i := range mg.targets {
  130. mg.targets[i].Dispose()
  131. }
  132. }
  133. // RenderSetup is called by the renderer before drawing the geometry.
  134. func (mg *MorphGeometry) RenderSetup(gs *gls.GLS) {
  135. mg.baseGeometry.RenderSetup(gs)
  136. // Sort weights and find top 8 morph targets with largest current weight (8 is the max sent to shader)
  137. activeMorphTargets, activeWeights := mg.ActiveMorphTargets()
  138. for i, mt := range activeMorphTargets {
  139. mt.SetAttributeName(gls.VertexPosition, "MorphPosition"+strconv.Itoa(i))
  140. mt.SetAttributeName(gls.VertexNormal, "MorphNormal"+strconv.Itoa(i))
  141. //mt.SetAttributeName(vTangent, fmt.Sprintf("MorphTangent[%d]", i))
  142. // Transfer morphed geometry VBOs
  143. for _, vbo := range mt.VBOs() {
  144. vbo.Transfer(gs)
  145. }
  146. }
  147. // Transfer texture info combined uniform
  148. location := mg.uniWeights.Location(gs)
  149. gs.Uniform1fv(location, int32(len(activeWeights)), activeWeights)
  150. }