box.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. )
  9. // Box represents the geometry of a rectangular cuboid.
  10. // See https://en.wikipedia.org/wiki/Cuboid#Rectangular_cuboid for more details.
  11. // A Box geometry is defined by its width, height, and length and also by the number
  12. // of segments in each dimension.
  13. type Box struct {
  14. Geometry
  15. Width float32
  16. Height float32
  17. Length float32
  18. WidthSegments int // > 0
  19. HeightSegments int // > 0
  20. LengthSegments int // > 0
  21. }
  22. // NewCube creates a new cube geometry of the specified size.
  23. func NewCube(size float32) *Box {
  24. return NewSegmentedBox(size, size, size, 1, 1, 1)
  25. }
  26. // NewSegmentedCube creates a cube geometry of the specified size and number of segments.
  27. func NewSegmentedCube(size float32, segments int) *Box {
  28. return NewSegmentedBox(size, size, size, segments, segments, segments)
  29. }
  30. // NewBox creates a box geometry of the specified width, height, and length.
  31. func NewBox(width, height, length float32) *Box {
  32. return NewSegmentedBox(width, height, length, 1, 1, 1)
  33. }
  34. // NewSegmentedBox creates a box geometry of the specified size and with the specified number
  35. // of segments in each dimension. This is the Box constructor with most tunable parameters.
  36. func NewSegmentedBox(width, height, length float32, widthSegments, heightSegments, lengthSegments int) *Box {
  37. box := new(Box)
  38. box.Geometry.Init()
  39. // Validate arguments
  40. if widthSegments <= 0 || heightSegments <= 0 || lengthSegments <= 0 {
  41. panic("Invalid argument(s). All segment quantities should be greater than zero.")
  42. }
  43. box.Width = width
  44. box.Height = height
  45. box.Length = length
  46. box.WidthSegments = widthSegments
  47. box.HeightSegments = heightSegments
  48. box.LengthSegments = lengthSegments
  49. // Create buffers
  50. positions := math32.NewArrayF32(0, 16)
  51. normals := math32.NewArrayF32(0, 16)
  52. uvs := math32.NewArrayF32(0, 16)
  53. indices := math32.NewArrayU32(0, 16)
  54. // Internal function to build each of the six box planes
  55. buildPlane := func(u, v string, udir, vdir int, width, height, length float32, materialIndex uint) {
  56. offset := positions.Len() / 3
  57. gridX := box.WidthSegments
  58. gridY := box.HeightSegments
  59. var w string
  60. if (u == "x" && v == "y") || (u == "y" && v == "x") {
  61. w = "z"
  62. } else if (u == "x" && v == "z") || (u == "z" && v == "x") {
  63. w = "y"
  64. gridY = box.LengthSegments
  65. } else if (u == "z" && v == "y") || (u == "y" && v == "z") {
  66. w = "x"
  67. gridX = box.LengthSegments
  68. }
  69. var normal math32.Vector3
  70. if length > 0 {
  71. normal.SetByName(w, 1)
  72. } else {
  73. normal.SetByName(w, -1)
  74. }
  75. wHalf := width / 2
  76. hHalf := height / 2
  77. gridX1 := gridX + 1
  78. gridY1 := gridY + 1
  79. segmentWidth := width / float32(gridX)
  80. segmentHeight := height / float32(gridY)
  81. // Generate the plane vertices, normals, and uv coordinates
  82. for iy := 0; iy < gridY1; iy++ {
  83. for ix := 0; ix < gridX1; ix++ {
  84. var vector math32.Vector3
  85. vector.SetByName(u, (float32(ix)*segmentWidth-wHalf)*float32(udir))
  86. vector.SetByName(v, (float32(iy)*segmentHeight-hHalf)*float32(vdir))
  87. vector.SetByName(w, length)
  88. positions.AppendVector3(&vector)
  89. normals.AppendVector3(&normal)
  90. uvs.Append(float32(ix)/float32(gridX), float32(1)-(float32(iy)/float32(gridY)))
  91. }
  92. }
  93. // Generate the indices for the vertices, normals and uv coordinates
  94. gstart := indices.Size()
  95. for iy := 0; iy < gridY; iy++ {
  96. for ix := 0; ix < gridX; ix++ {
  97. a := ix + gridX1*iy
  98. b := ix + gridX1*(iy+1)
  99. c := (ix + 1) + gridX1*(iy+1)
  100. d := (ix + 1) + gridX1*iy
  101. indices.Append(uint32(a+offset), uint32(b+offset), uint32(d+offset), uint32(b+offset), uint32(c+offset), uint32(d+offset))
  102. }
  103. }
  104. gcount := indices.Size() - gstart
  105. box.AddGroup(gstart, gcount, int(materialIndex))
  106. }
  107. wHalf := box.Width / 2
  108. hHalf := box.Height / 2
  109. lHalf := box.Length / 2
  110. buildPlane("z", "y", -1, -1, box.Length, box.Height, wHalf, 0) // px
  111. buildPlane("z", "y", 1, -1, box.Length, box.Height, -wHalf, 1) // nx
  112. buildPlane("x", "z", 1, 1, box.Width, box.Length, hHalf, 2) // py
  113. buildPlane("x", "z", 1, -1, box.Width, box.Length, -hHalf, 3) // ny
  114. buildPlane("x", "y", 1, -1, box.Width, box.Height, lHalf, 4) // pz
  115. buildPlane("x", "y", -1, -1, box.Width, box.Height, -lHalf, 5) // nz
  116. box.SetIndices(indices)
  117. box.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
  118. box.AddVBO(gls.NewVBO(normals).AddAttrib(gls.VertexNormal))
  119. box.AddVBO(gls.NewVBO(uvs).AddAttrib(gls.VertexTexcoord))
  120. // Update bounding box
  121. box.boundingBox.Min = math32.Vector3{-wHalf, -hHalf, -lHalf}
  122. box.boundingBox.Max = math32.Vector3{wHalf, hHalf, lHalf}
  123. box.boundingBoxValid = true
  124. // Update bounding sphere
  125. box.boundingSphere.Radius = math32.Sqrt(math32.Pow(width/2,2) + math32.Pow(height/2,2) + math32.Pow(length/2,2))
  126. box.boundingSphereValid = true
  127. // Update area
  128. box.area = 2*width + 2*height + 2*length
  129. box.areaValid = true
  130. // Update volume
  131. box.volume = width * height * length
  132. box.volumeValid = true
  133. return box
  134. }