cylinder.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. "math"
  9. )
  10. // Cylinder represents a cylinder geometry
  11. type Cylinder struct {
  12. Geometry
  13. RadiusTop float64
  14. RadiusBottom float64
  15. Height float64
  16. RadialSegments int
  17. HeightSegments int
  18. ThetaStart float64
  19. ThetaLength float64
  20. Top bool
  21. Bottom bool
  22. }
  23. // NewCylinder creates and returns a pointer to a new Cylinder geometry object.
  24. func NewCylinder(radiusTop, radiusBottom, height float64,
  25. radialSegments, heightSegments int,
  26. thetaStart, thetaLength float64, top, bottom bool) *Cylinder {
  27. c := new(Cylinder)
  28. c.Geometry.Init()
  29. c.RadiusTop = radiusTop
  30. c.RadiusBottom = radiusBottom
  31. c.Height = height
  32. c.RadialSegments = radialSegments
  33. c.HeightSegments = heightSegments
  34. c.ThetaStart = thetaStart
  35. c.ThetaLength = thetaLength
  36. c.Top = top
  37. c.Bottom = bottom
  38. heightHalf := height / 2
  39. vertices := [][]int{}
  40. uvsOrig := [][]math32.Vector2{}
  41. // Create buffer for vertex positions
  42. positions := math32.NewArrayF32(0, 0)
  43. for y := 0; y <= heightSegments; y++ {
  44. var verticesRow = []int{}
  45. var uvsRow = []math32.Vector2{}
  46. v := float64(y) / float64(heightSegments)
  47. radius := v*(radiusBottom-radiusTop) + radiusTop
  48. for x := 0; x <= radialSegments; x++ {
  49. u := float64(x) / float64(radialSegments)
  50. var vertex math32.Vector3
  51. vertex.X = float32(radius * math.Sin(u*thetaLength+thetaStart))
  52. vertex.Y = float32(-v*height + heightHalf)
  53. vertex.Z = float32(radius * math.Cos(u*thetaLength+thetaStart))
  54. positions.AppendVector3(&vertex)
  55. verticesRow = append(verticesRow, positions.Size()/3-1)
  56. uvsRow = append(uvsRow, math32.Vector2{float32(u), 1.0 - float32(v)})
  57. }
  58. vertices = append(vertices, verticesRow)
  59. uvsOrig = append(uvsOrig, uvsRow)
  60. }
  61. tanTheta := (radiusBottom - radiusTop) / height
  62. var na, nb math32.Vector3
  63. // Create preallocated buffers for normals and uvs and buffer for indices
  64. npos := positions.Size()
  65. normals := math32.NewArrayF32(npos, npos)
  66. uvs := math32.NewArrayF32(2*npos/3, 2*npos/3)
  67. indices := math32.NewArrayU32(0, 0)
  68. for x := 0; x < radialSegments; x++ {
  69. if radiusTop != 0 {
  70. positions.GetVector3(3*vertices[0][x], &na)
  71. positions.GetVector3(3*vertices[0][x+1], &nb)
  72. } else {
  73. positions.GetVector3(3*vertices[1][x], &na)
  74. positions.GetVector3(3*vertices[1][x+1], &nb)
  75. }
  76. na.SetY(float32(math.Sqrt(float64(na.X*na.X+na.Z*na.Z)) * tanTheta)).Normalize()
  77. nb.SetY(float32(math.Sqrt(float64(nb.X*nb.X+nb.Z*nb.Z)) * tanTheta)).Normalize()
  78. for y := 0; y < heightSegments; y++ {
  79. v1 := vertices[y][x]
  80. v2 := vertices[y+1][x]
  81. v3 := vertices[y+1][x+1]
  82. v4 := vertices[y][x+1]
  83. n1 := na
  84. n2 := na
  85. n3 := nb
  86. n4 := nb
  87. uv1 := uvsOrig[y][x]
  88. uv2 := uvsOrig[y+1][x]
  89. uv3 := uvsOrig[y+1][x+1]
  90. uv4 := uvsOrig[y][x+1]
  91. indices.Append(uint32(v1), uint32(v2), uint32(v4))
  92. normals.SetVector3(3*v1, &n1)
  93. normals.SetVector3(3*v2, &n2)
  94. normals.SetVector3(3*v4, &n4)
  95. indices.Append(uint32(v2), uint32(v3), uint32(v4))
  96. normals.SetVector3(3*v2, &n2)
  97. normals.SetVector3(3*v3, &n3)
  98. normals.SetVector3(3*v4, &n4)
  99. uvs.SetVector2(2*v1, &uv1)
  100. uvs.SetVector2(2*v2, &uv2)
  101. uvs.SetVector2(2*v3, &uv3)
  102. uvs.SetVector2(2*v4, &uv4)
  103. }
  104. }
  105. // First group is the body of the cylinder
  106. // without the caps
  107. c.AddGroup(0, indices.Size(), 0)
  108. nextGroup := indices.Size()
  109. // Top cap
  110. if top && radiusTop > 0 {
  111. // Array of vertex indicesOrig to build used to build the faces.
  112. indicesOrig := []uint32{}
  113. nextidx := positions.Size() / 3
  114. // Appends top segments vertices and builds array of its indicesOrig
  115. var uv1, uv2, uv3 math32.Vector2
  116. for x := 0; x < radialSegments; x++ {
  117. uv1 = uvsOrig[0][x]
  118. uv2 = uvsOrig[0][x+1]
  119. uv3 = math32.Vector2{uv2.X, 0}
  120. // Appends CENTER with its own UV.
  121. positions.Append(0, float32(heightHalf), 0)
  122. normals.Append(0, 1, 0)
  123. uvs.AppendVector2(&uv3)
  124. indicesOrig = append(indicesOrig, uint32(nextidx))
  125. nextidx++
  126. // Appends vertex
  127. v := math32.Vector3{}
  128. vi := vertices[0][x]
  129. positions.GetVector3(3*vi, &v)
  130. positions.AppendVector3(&v)
  131. normals.Append(0, 1, 0)
  132. uvs.AppendVector2(&uv1)
  133. indicesOrig = append(indicesOrig, uint32(nextidx))
  134. nextidx++
  135. }
  136. // Appends copy of first vertex (center)
  137. var vertex, normal math32.Vector3
  138. var uv math32.Vector2
  139. positions.GetVector3(3*int(indicesOrig[0]), &vertex)
  140. normals.GetVector3(3*int(indicesOrig[0]), &normal)
  141. uvs.GetVector2(2*int(indicesOrig[0]), &uv)
  142. positions.AppendVector3(&vertex)
  143. normals.AppendVector3(&normal)
  144. uvs.AppendVector2(&uv)
  145. indicesOrig = append(indicesOrig, uint32(nextidx))
  146. nextidx++
  147. // Appends copy of second vertex (v1) USING LAST UV2
  148. positions.GetVector3(3*int(indicesOrig[1]), &vertex)
  149. normals.GetVector3(3*int(indicesOrig[1]), &normal)
  150. positions.AppendVector3(&vertex)
  151. normals.AppendVector3(&normal)
  152. uvs.AppendVector2(&uv2)
  153. indicesOrig = append(indicesOrig, uint32(nextidx))
  154. nextidx++
  155. // Append faces indicesOrig
  156. for x := 0; x < radialSegments; x++ {
  157. pos := 2 * x
  158. i1 := indicesOrig[pos]
  159. i2 := indicesOrig[pos+1]
  160. i3 := indicesOrig[pos+3]
  161. indices.Append(uint32(i1), uint32(i2), uint32(i3))
  162. }
  163. // Second group is optional top cap of the cylinder
  164. c.AddGroup(nextGroup, indices.Size()-nextGroup, 1)
  165. nextGroup = indices.Size()
  166. }
  167. // Bottom cap
  168. if bottom && radiusBottom > 0 {
  169. // Array of vertex indicesOrig to build used to build the faces.
  170. indicesOrig := []uint32{}
  171. nextidx := positions.Size() / 3
  172. // Appends top segments vertices and builds array of its indicesOrig
  173. var uv1, uv2, uv3 math32.Vector2
  174. for x := 0; x < radialSegments; x++ {
  175. uv1 = uvsOrig[heightSegments][x]
  176. uv2 = uvsOrig[heightSegments][x+1]
  177. uv3 = math32.Vector2{uv2.X, 1}
  178. // Appends CENTER with its own UV.
  179. positions.Append(0, float32(-heightHalf), 0)
  180. normals.Append(0, -1, 0)
  181. uvs.AppendVector2(&uv3)
  182. indicesOrig = append(indicesOrig, uint32(nextidx))
  183. nextidx++
  184. // Appends vertex
  185. v := math32.Vector3{}
  186. vi := vertices[heightSegments][x]
  187. positions.GetVector3(3*vi, &v)
  188. positions.AppendVector3(&v)
  189. normals.Append(0, -1, 0)
  190. uvs.AppendVector2(&uv1)
  191. indicesOrig = append(indicesOrig, uint32(nextidx))
  192. nextidx++
  193. }
  194. // Appends copy of first vertex (center)
  195. var vertex, normal math32.Vector3
  196. var uv math32.Vector2
  197. positions.GetVector3(3*int(indicesOrig[0]), &vertex)
  198. normals.GetVector3(3*int(indicesOrig[0]), &normal)
  199. uvs.GetVector2(2*int(indicesOrig[0]), &uv)
  200. positions.AppendVector3(&vertex)
  201. normals.AppendVector3(&normal)
  202. uvs.AppendVector2(&uv)
  203. indicesOrig = append(indicesOrig, uint32(nextidx))
  204. nextidx++
  205. // Appends copy of second vertex (v1) USING LAST UV2
  206. positions.GetVector3(3*int(indicesOrig[1]), &vertex)
  207. normals.GetVector3(3*int(indicesOrig[1]), &normal)
  208. positions.AppendVector3(&vertex)
  209. normals.AppendVector3(&normal)
  210. uvs.AppendVector2(&uv2)
  211. indicesOrig = append(indicesOrig, uint32(nextidx))
  212. nextidx++
  213. // Appends faces indicesOrig
  214. for x := 0; x < radialSegments; x++ {
  215. pos := 2 * x
  216. i1 := indicesOrig[pos]
  217. i2 := indicesOrig[pos+3]
  218. i3 := indicesOrig[pos+1]
  219. indices.Append(uint32(i1), uint32(i2), uint32(i3))
  220. }
  221. // Third group is optional bottom cap of the cylinder
  222. c.AddGroup(nextGroup, indices.Size()-nextGroup, 2)
  223. }
  224. c.SetIndices(indices)
  225. c.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
  226. c.AddVBO(gls.NewVBO(normals).AddAttrib(gls.VertexNormal))
  227. c.AddVBO(gls.NewVBO(uvs).AddAttrib(gls.VertexTexcoord))
  228. return c
  229. }