morph.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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
  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. // Weights returns the morph target weights.
  47. func (mg *MorphGeometry) AddMorphTargets(morphTargets ...*Geometry) {
  48. mg.targets = append(mg.targets, morphTargets...)
  49. for range morphTargets {
  50. mg.weights = append(mg.weights, 0)
  51. }
  52. }
  53. // ActiveMorphTargets sorts the morph targets by weight and returns the top n morph targets with largest weight.
  54. func (mg *MorphGeometry) ActiveMorphTargets() ([]*Geometry, []float32) {
  55. numTargets := len(mg.targets)
  56. if numTargets == 0 {
  57. return nil, nil
  58. }
  59. sortedMorphTargets := make([]*Geometry, numTargets)
  60. copy(sortedMorphTargets, mg.targets)
  61. sort.Slice(sortedMorphTargets, func(i, j int) bool {
  62. return mg.weights[i] > mg.weights[j]
  63. })
  64. sortedWeights := make([]float32, numTargets)
  65. copy(sortedWeights, mg.weights)
  66. sort.Slice(sortedWeights, func(i, j int) bool {
  67. return mg.weights[i] > mg.weights[j]
  68. })
  69. // TODO check current 0 weights
  70. //if len(mg.targets) < NumMorphTargets-1 {
  71. return sortedMorphTargets, sortedWeights
  72. //} else {
  73. // return sortedMorphTargets[:NumMorphTargets-1]
  74. //}
  75. }
  76. // SetIndices sets the indices array for this geometry.
  77. func (mg *MorphGeometry) SetIndices(indices math32.ArrayU32) {
  78. mg.baseGeometry.SetIndices(indices)
  79. for i := range mg.targets {
  80. mg.targets[i].SetIndices(indices)
  81. }
  82. }
  83. // ComputeMorphed computes a morphed geometry from the provided morph target weights.
  84. // Note that morphing is usually computed by the GPU in shaders.
  85. // This CPU implementation allows users to obtain an instance of a morphed geometry
  86. // if so desired (loosing morphing ability).
  87. func (mg *MorphGeometry) ComputeMorphed(weights []float32) *Geometry {
  88. morphed := NewGeometry()
  89. // TODO
  90. return morphed
  91. }
  92. // Dispose releases, if possible, OpenGL resources, C memory
  93. // and VBOs associated with the base geometry and morph targets.
  94. func (mg *MorphGeometry) Dispose() {
  95. mg.baseGeometry.Dispose()
  96. for i := range mg.targets {
  97. mg.targets[i].Dispose()
  98. }
  99. }
  100. // RenderSetup is called by the renderer before drawing the geometry.
  101. func (mg *MorphGeometry) RenderSetup(gs *gls.GLS) {
  102. mg.baseGeometry.RenderSetup(gs)
  103. // Sort weights and find top 8 morph targets with largest current weight (8 is the max sent to shader)
  104. activeMorphTargets, activeWeights := mg.ActiveMorphTargets()
  105. for i, mt := range activeMorphTargets {
  106. mt.SetAttributeName(gls.VertexPosition, "MorphPosition"+strconv.Itoa(i))
  107. mt.SetAttributeName(gls.VertexNormal, "MorphNormal"+strconv.Itoa(i))
  108. //mt.SetAttributeName(vTangent, fmt.Sprintf("MorphTangent[%d]", i))
  109. // Transfer morphed geometry VBOs
  110. for _, vbo := range mt.VBOs() {
  111. vbo.Transfer(gs)
  112. }
  113. }
  114. // Transfer texture info combined uniform
  115. location := mg.uniWeights.Location(gs)
  116. gs.Uniform1fv(location, int32(len(activeWeights)), activeWeights)
  117. }