Octree.glsl 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // These octree flags must be in sync with GpuOctreeFlag in VoxelTraversal.js
  2. #define OCTREE_FLAG_INTERNAL 0
  3. #define OCTREE_FLAG_LEAF 1
  4. #define OCTREE_FLAG_PACKED_LEAF_FROM_PARENT 2
  5. #define OCTREE_MAX_LEVELS 32 // Harcoded value because GLSL doesn't like variable length loops
  6. uniform sampler2D u_octreeInternalNodeTexture;
  7. uniform vec2 u_octreeInternalNodeTexelSizeUv;
  8. uniform int u_octreeInternalNodeTilesPerRow;
  9. #if (SAMPLE_COUNT > 1)
  10. uniform sampler2D u_octreeLeafNodeTexture;
  11. uniform vec2 u_octreeLeafNodeTexelSizeUv;
  12. uniform int u_octreeLeafNodeTilesPerRow;
  13. #endif
  14. struct OctreeNodeData {
  15. int data;
  16. int flag;
  17. };
  18. struct TraversalData {
  19. ivec4 octreeCoords;
  20. int parentOctreeIndex;
  21. };
  22. struct SampleData {
  23. int megatextureIndex;
  24. ivec4 tileCoords;
  25. vec3 tileUv;
  26. #if (SAMPLE_COUNT > 1)
  27. float weight;
  28. #endif
  29. };
  30. // Integer mod: For WebGL1 only
  31. int intMod(in int a, in int b) {
  32. return a - (b * (a / b));
  33. }
  34. int normU8_toInt(in float value) {
  35. return int(value * 255.0);
  36. }
  37. int normU8x2_toInt(in vec2 value) {
  38. return int(value.x * 255.0) + 256 * int(value.y * 255.0);
  39. }
  40. float normU8x2_toFloat(in vec2 value) {
  41. return float(normU8x2_toInt(value)) / 65535.0;
  42. }
  43. OctreeNodeData getOctreeNodeData(in vec2 octreeUv) {
  44. vec4 texData = texture(u_octreeInternalNodeTexture, octreeUv);
  45. OctreeNodeData data;
  46. data.data = normU8x2_toInt(texData.xy);
  47. data.flag = normU8x2_toInt(texData.zw);
  48. return data;
  49. }
  50. OctreeNodeData getOctreeChildData(in int parentOctreeIndex, in ivec3 childCoord) {
  51. int childIndex = childCoord.z * 4 + childCoord.y * 2 + childCoord.x;
  52. int octreeCoordX = intMod(parentOctreeIndex, u_octreeInternalNodeTilesPerRow) * 9 + 1 + childIndex;
  53. int octreeCoordY = parentOctreeIndex / u_octreeInternalNodeTilesPerRow;
  54. vec2 octreeUv = u_octreeInternalNodeTexelSizeUv * vec2(float(octreeCoordX) + 0.5, float(octreeCoordY) + 0.5);
  55. return getOctreeNodeData(octreeUv);
  56. }
  57. int getOctreeParentIndex(in int octreeIndex) {
  58. int octreeCoordX = intMod(octreeIndex, u_octreeInternalNodeTilesPerRow) * 9;
  59. int octreeCoordY = octreeIndex / u_octreeInternalNodeTilesPerRow;
  60. vec2 octreeUv = u_octreeInternalNodeTexelSizeUv * vec2(float(octreeCoordX) + 0.5, float(octreeCoordY) + 0.5);
  61. vec4 parentData = texture(u_octreeInternalNodeTexture, octreeUv);
  62. int parentOctreeIndex = normU8x2_toInt(parentData.xy);
  63. return parentOctreeIndex;
  64. }
  65. /**
  66. * Convert a position in the uv-space of the tileset bounding shape
  67. * into the uv-space of a tile within the tileset
  68. */
  69. vec3 getTileUv(in vec3 shapePosition, in ivec4 octreeCoords) {
  70. // PERFORMANCE_IDEA: use bit-shifting (only in WebGL2)
  71. float dimAtLevel = pow(2.0, float(octreeCoords.w));
  72. return shapePosition * dimAtLevel - vec3(octreeCoords.xyz);
  73. }
  74. void getOctreeLeafSampleData(in OctreeNodeData data, in ivec4 octreeCoords, out SampleData sampleData) {
  75. sampleData.megatextureIndex = data.data;
  76. sampleData.tileCoords = (data.flag == OCTREE_FLAG_PACKED_LEAF_FROM_PARENT)
  77. ? ivec4(octreeCoords.xyz / 2, octreeCoords.w - 1)
  78. : octreeCoords;
  79. }
  80. #if (SAMPLE_COUNT > 1)
  81. void getOctreeLeafSampleDatas(in OctreeNodeData data, in ivec4 octreeCoords, out SampleData sampleDatas[SAMPLE_COUNT]) {
  82. int leafIndex = data.data;
  83. int leafNodeTexelCount = 2;
  84. // Adding 0.5 moves to the center of the texel
  85. float leafCoordXStart = float(intMod(leafIndex, u_octreeLeafNodeTilesPerRow) * leafNodeTexelCount) + 0.5;
  86. float leafCoordY = float(leafIndex / u_octreeLeafNodeTilesPerRow) + 0.5;
  87. // Get an interpolation weight and a flag to determine whether to read the parent texture
  88. vec2 leafUv0 = u_octreeLeafNodeTexelSizeUv * vec2(leafCoordXStart + 0.0, leafCoordY);
  89. vec4 leafData0 = texture(u_octreeLeafNodeTexture, leafUv0);
  90. float lerp = normU8x2_toFloat(leafData0.xy);
  91. sampleDatas[0].weight = 1.0 - lerp;
  92. sampleDatas[1].weight = lerp;
  93. // TODO: this looks wrong? Should be comparing to OCTREE_FLAG_PACKED_LEAF_FROM_PARENT
  94. sampleDatas[0].tileCoords = (normU8_toInt(leafData0.z) == 1)
  95. ? ivec4(octreeCoords.xyz / 2, octreeCoords.w - 1)
  96. : octreeCoords;
  97. sampleDatas[1].tileCoords = (normU8_toInt(leafData0.w) == 1)
  98. ? ivec4(octreeCoords.xyz / 2, octreeCoords.w - 1)
  99. : octreeCoords;
  100. // Get megatexture indices for both samples
  101. vec2 leafUv1 = u_octreeLeafNodeTexelSizeUv * vec2(leafCoordXStart + 1.0, leafCoordY);
  102. vec4 leafData1 = texture(u_octreeLeafNodeTexture, leafUv1);
  103. sampleDatas[0].megatextureIndex = normU8x2_toInt(leafData1.xy);
  104. sampleDatas[1].megatextureIndex = normU8x2_toInt(leafData1.zw);
  105. }
  106. #endif
  107. OctreeNodeData traverseOctreeDownwards(in vec3 shapePosition, inout TraversalData traversalData) {
  108. float sizeAtLevel = 1.0 / pow(2.0, float(traversalData.octreeCoords.w));
  109. vec3 start = vec3(traversalData.octreeCoords.xyz) * sizeAtLevel;
  110. vec3 end = start + vec3(sizeAtLevel);
  111. OctreeNodeData childData;
  112. for (int i = 0; i < OCTREE_MAX_LEVELS; ++i) {
  113. // Find out which octree child contains the position
  114. // 0 if before center, 1 if after
  115. vec3 center = 0.5 * (start + end);
  116. vec3 childCoord = step(center, shapePosition);
  117. // Get octree coords for the next level down
  118. ivec4 octreeCoords = traversalData.octreeCoords;
  119. traversalData.octreeCoords = ivec4(octreeCoords.xyz * 2 + ivec3(childCoord), octreeCoords.w + 1);
  120. childData = getOctreeChildData(traversalData.parentOctreeIndex, ivec3(childCoord));
  121. if (childData.flag != OCTREE_FLAG_INTERNAL) {
  122. // leaf tile - stop traversing
  123. break;
  124. }
  125. // interior tile - keep going deeper
  126. start = mix(start, center, childCoord);
  127. end = mix(center, end, childCoord);
  128. traversalData.parentOctreeIndex = childData.data;
  129. }
  130. return childData;
  131. }
  132. /**
  133. * Transform a given position to an octree tile coordinate and a position within that tile,
  134. * and find the corresponding megatexture index and texture coordinates
  135. */
  136. void traverseOctreeFromBeginning(in vec3 shapePosition, out TraversalData traversalData, out SampleData sampleDatas[SAMPLE_COUNT]) {
  137. traversalData.octreeCoords = ivec4(0);
  138. traversalData.parentOctreeIndex = 0;
  139. OctreeNodeData nodeData = getOctreeNodeData(vec2(0.0));
  140. if (nodeData.flag != OCTREE_FLAG_LEAF) {
  141. nodeData = traverseOctreeDownwards(shapePosition, traversalData);
  142. }
  143. #if (SAMPLE_COUNT == 1)
  144. getOctreeLeafSampleData(nodeData, traversalData.octreeCoords, sampleDatas[0]);
  145. sampleDatas[0].tileUv = getTileUv(shapePosition, sampleDatas[0].tileCoords);
  146. #else
  147. getOctreeLeafSampleDatas(nodeData, traversalData.octreeCoords, sampleDatas);
  148. sampleDatas[0].tileUv = getTileUv(shapePosition, sampleDatas[0].tileCoords);
  149. sampleDatas[1].tileUv = getTileUv(shapePosition, sampleDatas[1].tileCoords);
  150. #endif
  151. }
  152. bool inRange(in vec3 v, in vec3 minVal, in vec3 maxVal) {
  153. return clamp(v, minVal, maxVal) == v;
  154. }
  155. bool insideTile(in vec3 shapePosition, in ivec4 octreeCoords) {
  156. vec3 tileUv = getTileUv(shapePosition, octreeCoords);
  157. bool inside = inRange(tileUv, vec3(0.0), vec3(1.0));
  158. // Assume (!) the position is always inside the root tile.
  159. return inside || octreeCoords.w == 0;
  160. }
  161. void traverseOctreeFromExisting(in vec3 shapePosition, inout TraversalData traversalData, inout SampleData sampleDatas[SAMPLE_COUNT]) {
  162. if (insideTile(shapePosition, traversalData.octreeCoords)) {
  163. for (int i = 0; i < SAMPLE_COUNT; i++) {
  164. sampleDatas[0].tileUv = getTileUv(shapePosition, sampleDatas[0].tileCoords);
  165. }
  166. return;
  167. }
  168. // Go up tree until we find a parent tile containing shapePosition
  169. for (int i = 0; i < OCTREE_MAX_LEVELS; ++i) {
  170. traversalData.octreeCoords.xyz /= 2;
  171. traversalData.octreeCoords.w -= 1;
  172. if (insideTile(shapePosition, traversalData.octreeCoords)) {
  173. break;
  174. }
  175. traversalData.parentOctreeIndex = getOctreeParentIndex(traversalData.parentOctreeIndex);
  176. }
  177. // Go down tree
  178. OctreeNodeData nodeData = traverseOctreeDownwards(shapePosition, traversalData);
  179. #if (SAMPLE_COUNT == 1)
  180. getOctreeLeafSampleData(nodeData, traversalData.octreeCoords, sampleDatas[0]);
  181. sampleDatas[0].tileUv = getTileUv(shapePosition, sampleDatas[0].tileCoords);
  182. #else
  183. getOctreeLeafSampleDatas(nodeData, traversalData.octreeCoords, sampleDatas);
  184. sampleDatas[0].tileUv = getTileUv(shapePosition, sampleDatas[0].tileCoords);
  185. sampleDatas[1].tileUv = getTileUv(shapePosition, sampleDatas[1].tileCoords);
  186. #endif
  187. }