processVoxelProperties.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import defined from "../Core/defined.js";
  2. import MetadataType from "./MetadataType.js";
  3. import ShaderDestination from "../Renderer/ShaderDestination.js";
  4. /**
  5. * Update the shader with defines, structs, and functions to handle
  6. * voxel properties and statistics
  7. * @function
  8. *
  9. * @param {VoxelRenderResources} renderResources
  10. * @param {VoxelPrimitive} primitive
  11. *
  12. * @private
  13. */
  14. function processVoxelProperties(renderResources, primitive) {
  15. const { shaderBuilder } = renderResources;
  16. const {
  17. names,
  18. types,
  19. componentTypes,
  20. minimumValues,
  21. maximumValues,
  22. } = primitive._provider;
  23. const attributeLength = types.length;
  24. const hasStatistics = defined(minimumValues) && defined(maximumValues);
  25. shaderBuilder.addDefine(
  26. "METADATA_COUNT",
  27. attributeLength,
  28. ShaderDestination.FRAGMENT
  29. );
  30. if (hasStatistics) {
  31. shaderBuilder.addDefine(
  32. "STATISTICS",
  33. undefined,
  34. ShaderDestination.FRAGMENT
  35. );
  36. }
  37. // PropertyStatistics structs
  38. for (let i = 0; i < attributeLength; i++) {
  39. const name = names[i];
  40. const type = types[i];
  41. const propertyStatisticsStructId = `PropertyStatistics_${name}`;
  42. const propertyStatisticsStructName = `PropertyStatistics_${name}`;
  43. shaderBuilder.addStruct(
  44. propertyStatisticsStructId,
  45. propertyStatisticsStructName,
  46. ShaderDestination.FRAGMENT
  47. );
  48. const glslType = getGlslType(type);
  49. shaderBuilder.addStructField(propertyStatisticsStructId, glslType, "min");
  50. shaderBuilder.addStructField(propertyStatisticsStructId, glslType, "max");
  51. }
  52. // Statistics struct
  53. const statisticsStructId = "Statistics";
  54. const statisticsStructName = "Statistics";
  55. const statisticsFieldName = "statistics";
  56. shaderBuilder.addStruct(
  57. statisticsStructId,
  58. statisticsStructName,
  59. ShaderDestination.FRAGMENT
  60. );
  61. for (let i = 0; i < attributeLength; i++) {
  62. const name = names[i];
  63. const propertyStructName = `PropertyStatistics_${name}`;
  64. const propertyFieldName = name;
  65. shaderBuilder.addStructField(
  66. statisticsStructId,
  67. propertyStructName,
  68. propertyFieldName
  69. );
  70. }
  71. // Metadata struct
  72. const metadataStructId = "Metadata";
  73. const metadataStructName = "Metadata";
  74. const metadataFieldName = "metadata";
  75. shaderBuilder.addStruct(
  76. metadataStructId,
  77. metadataStructName,
  78. ShaderDestination.FRAGMENT
  79. );
  80. shaderBuilder.addStructField(
  81. metadataStructId,
  82. statisticsStructName,
  83. statisticsFieldName
  84. );
  85. for (let i = 0; i < attributeLength; i++) {
  86. const name = names[i];
  87. const type = types[i];
  88. const glslType = getGlslType(type);
  89. shaderBuilder.addStructField(metadataStructId, glslType, name);
  90. }
  91. // VoxelProperty structs
  92. for (let i = 0; i < attributeLength; i++) {
  93. const name = names[i];
  94. const type = types[i];
  95. const glslType = getGlslPartialDerivativeType(type);
  96. const voxelPropertyStructId = `VoxelProperty_${name}`;
  97. const voxelPropertyStructName = `VoxelProperty_${name}`;
  98. shaderBuilder.addStruct(
  99. voxelPropertyStructId,
  100. voxelPropertyStructName,
  101. ShaderDestination.FRAGMENT
  102. );
  103. shaderBuilder.addStructField(
  104. voxelPropertyStructId,
  105. glslType,
  106. "partialDerivativeLocal"
  107. );
  108. shaderBuilder.addStructField(
  109. voxelPropertyStructId,
  110. glslType,
  111. "partialDerivativeWorld"
  112. );
  113. shaderBuilder.addStructField(
  114. voxelPropertyStructId,
  115. glslType,
  116. "partialDerivativeView"
  117. );
  118. shaderBuilder.addStructField(
  119. voxelPropertyStructId,
  120. glslType,
  121. "partialDerivativeValid"
  122. );
  123. }
  124. // Voxel struct
  125. const voxelStructId = "Voxel";
  126. const voxelStructName = "Voxel";
  127. const voxelFieldName = "voxel";
  128. shaderBuilder.addStruct(
  129. voxelStructId,
  130. voxelStructName,
  131. ShaderDestination.FRAGMENT
  132. );
  133. for (let i = 0; i < attributeLength; i++) {
  134. const name = names[i];
  135. const voxelPropertyStructName = `VoxelProperty_${name}`;
  136. shaderBuilder.addStructField(voxelStructId, voxelPropertyStructName, name);
  137. }
  138. shaderBuilder.addStructField(voxelStructId, "vec3", "positionEC");
  139. shaderBuilder.addStructField(voxelStructId, "vec3", "positionUv");
  140. shaderBuilder.addStructField(voxelStructId, "vec3", "positionShapeUv");
  141. shaderBuilder.addStructField(voxelStructId, "vec3", "positionUvLocal");
  142. shaderBuilder.addStructField(voxelStructId, "vec3", "viewDirUv");
  143. shaderBuilder.addStructField(voxelStructId, "vec3", "viewDirWorld");
  144. shaderBuilder.addStructField(voxelStructId, "vec3", "surfaceNormal");
  145. shaderBuilder.addStructField(voxelStructId, "float", "travelDistance");
  146. // FragmentInput struct
  147. const fragmentInputStructId = "FragmentInput";
  148. const fragmentInputStructName = "FragmentInput";
  149. shaderBuilder.addStruct(
  150. fragmentInputStructId,
  151. fragmentInputStructName,
  152. ShaderDestination.FRAGMENT
  153. );
  154. shaderBuilder.addStructField(
  155. fragmentInputStructId,
  156. metadataStructName,
  157. metadataFieldName
  158. );
  159. shaderBuilder.addStructField(
  160. fragmentInputStructId,
  161. voxelStructName,
  162. voxelFieldName
  163. );
  164. // Properties struct
  165. const propertiesStructId = "Properties";
  166. const propertiesStructName = "Properties";
  167. const propertiesFieldName = "properties";
  168. shaderBuilder.addStruct(
  169. propertiesStructId,
  170. propertiesStructName,
  171. ShaderDestination.FRAGMENT
  172. );
  173. for (let i = 0; i < attributeLength; i++) {
  174. const name = names[i];
  175. const type = types[i];
  176. const glslType = getGlslType(type);
  177. shaderBuilder.addStructField(propertiesStructId, glslType, name);
  178. }
  179. // Fragment shader functions
  180. // clearProperties function
  181. {
  182. const functionId = "clearProperties";
  183. shaderBuilder.addFunction(
  184. functionId,
  185. `${propertiesStructName} clearProperties()`,
  186. ShaderDestination.FRAGMENT
  187. );
  188. shaderBuilder.addFunctionLines(functionId, [
  189. `${propertiesStructName} ${propertiesFieldName};`,
  190. ]);
  191. for (let i = 0; i < attributeLength; i++) {
  192. const name = names[i];
  193. const type = types[i];
  194. const componentType = componentTypes[i];
  195. const glslType = getGlslType(type, componentType);
  196. shaderBuilder.addFunctionLines(functionId, [
  197. `${propertiesFieldName}.${name} = ${glslType}(0.0);`,
  198. ]);
  199. }
  200. shaderBuilder.addFunctionLines(functionId, [
  201. `return ${propertiesFieldName};`,
  202. ]);
  203. }
  204. // sumProperties function
  205. {
  206. const functionId = "sumProperties";
  207. shaderBuilder.addFunction(
  208. functionId,
  209. `${propertiesStructName} sumProperties(${propertiesStructName} propertiesA, ${propertiesStructName} propertiesB)`,
  210. ShaderDestination.FRAGMENT
  211. );
  212. shaderBuilder.addFunctionLines(functionId, [
  213. `${propertiesStructName} ${propertiesFieldName};`,
  214. ]);
  215. for (let i = 0; i < attributeLength; i++) {
  216. const name = names[i];
  217. shaderBuilder.addFunctionLines(functionId, [
  218. `${propertiesFieldName}.${name} = propertiesA.${name} + propertiesB.${name};`,
  219. ]);
  220. }
  221. shaderBuilder.addFunctionLines(functionId, [
  222. `return ${propertiesFieldName};`,
  223. ]);
  224. }
  225. // scaleProperties function
  226. {
  227. const functionId = "scaleProperties";
  228. shaderBuilder.addFunction(
  229. functionId,
  230. `${propertiesStructName} scaleProperties(${propertiesStructName} ${propertiesFieldName}, float scale)`,
  231. ShaderDestination.FRAGMENT
  232. );
  233. shaderBuilder.addFunctionLines(functionId, [
  234. `${propertiesStructName} scaledProperties = ${propertiesFieldName};`,
  235. ]);
  236. for (let i = 0; i < attributeLength; i++) {
  237. const name = names[i];
  238. shaderBuilder.addFunctionLines(functionId, [
  239. `scaledProperties.${name} *= scale;`,
  240. ]);
  241. }
  242. shaderBuilder.addFunctionLines(functionId, [`return scaledProperties;`]);
  243. }
  244. // mixProperties
  245. {
  246. const functionId = "mixProperties";
  247. shaderBuilder.addFunction(
  248. functionId,
  249. `${propertiesStructName} mixProperties(${propertiesStructName} propertiesA, ${propertiesStructName} propertiesB, float mixFactor)`,
  250. ShaderDestination.FRAGMENT
  251. );
  252. shaderBuilder.addFunctionLines(functionId, [
  253. `${propertiesStructName} ${propertiesFieldName};`,
  254. ]);
  255. for (let i = 0; i < attributeLength; i++) {
  256. const name = names[i];
  257. shaderBuilder.addFunctionLines(functionId, [
  258. `${propertiesFieldName}.${name} = mix(propertiesA.${name}, propertiesB.${name}, mixFactor);`,
  259. ]);
  260. }
  261. shaderBuilder.addFunctionLines(functionId, [
  262. `return ${propertiesFieldName};`,
  263. ]);
  264. }
  265. // copyPropertiesToMetadata
  266. {
  267. const functionId = "copyPropertiesToMetadata";
  268. shaderBuilder.addFunction(
  269. functionId,
  270. `void copyPropertiesToMetadata(in ${propertiesStructName} ${propertiesFieldName}, inout ${metadataStructName} ${metadataFieldName})`,
  271. ShaderDestination.FRAGMENT
  272. );
  273. for (let i = 0; i < attributeLength; i++) {
  274. const name = names[i];
  275. shaderBuilder.addFunctionLines(functionId, [
  276. `${metadataFieldName}.${name} = ${propertiesFieldName}.${name};`,
  277. ]);
  278. }
  279. }
  280. // setStatistics function
  281. if (hasStatistics) {
  282. const functionId = "setStatistics";
  283. shaderBuilder.addFunction(
  284. functionId,
  285. `void setStatistics(inout ${statisticsStructName} ${statisticsFieldName})`,
  286. ShaderDestination.FRAGMENT
  287. );
  288. for (let i = 0; i < attributeLength; i++) {
  289. const name = names[i];
  290. const type = types[i];
  291. const componentCount = MetadataType.getComponentCount(type);
  292. for (let j = 0; j < componentCount; j++) {
  293. const glslField = getGlslField(type, j);
  294. const minimumValue = minimumValues[i][j];
  295. const maximumValue = maximumValues[i][j];
  296. shaderBuilder.addFunctionLines(functionId, [
  297. `${statisticsFieldName}.${name}.min${glslField} = ${getGlslNumberAsFloat(
  298. minimumValue
  299. )};`,
  300. `${statisticsFieldName}.${name}.max${glslField} = ${getGlslNumberAsFloat(
  301. maximumValue
  302. )};`,
  303. ]);
  304. }
  305. }
  306. }
  307. // getPropertiesFromMegatextureAtUv
  308. {
  309. const functionId = "getPropertiesFromMegatextureAtUv";
  310. shaderBuilder.addFunction(
  311. functionId,
  312. `${propertiesStructName} getPropertiesFromMegatextureAtUv(vec2 texcoord)`,
  313. ShaderDestination.FRAGMENT
  314. );
  315. shaderBuilder.addFunctionLines(functionId, [
  316. `${propertiesStructName} ${propertiesFieldName};`,
  317. ]);
  318. for (let i = 0; i < attributeLength; i++) {
  319. const name = names[i];
  320. const type = types[i];
  321. const componentType = componentTypes[i];
  322. const glslTextureSwizzle = getGlslTextureSwizzle(type, componentType);
  323. shaderBuilder.addFunctionLines(functionId, [
  324. `properties.${name} = texture(u_megatextureTextures[${i}], texcoord)${glslTextureSwizzle};`,
  325. ]);
  326. }
  327. shaderBuilder.addFunctionLines(functionId, [
  328. `return ${propertiesFieldName};`,
  329. ]);
  330. }
  331. }
  332. /**
  333. * Converts a {@link MetadataType} to a GLSL type.
  334. *
  335. * @function
  336. *
  337. * @param {MetadataType} type The {@link MetadataType}.
  338. * @returns {string} The GLSL type.
  339. *
  340. * @private
  341. */
  342. function getGlslType(type) {
  343. if (type === MetadataType.SCALAR) {
  344. return "float";
  345. } else if (type === MetadataType.VEC2) {
  346. return "vec2";
  347. } else if (type === MetadataType.VEC3) {
  348. return "vec3";
  349. } else if (type === MetadataType.VEC4) {
  350. return "vec4";
  351. }
  352. }
  353. /**
  354. * Gets the GLSL swizzle when reading data from a texture.
  355. *
  356. * @function
  357. *
  358. * @param {MetadataType} type The {@link MetadataType}.
  359. * @returns {string} The GLSL swizzle.
  360. *
  361. * @private
  362. */
  363. function getGlslTextureSwizzle(type) {
  364. if (type === MetadataType.SCALAR) {
  365. return ".r";
  366. } else if (type === MetadataType.VEC2) {
  367. return ".ra";
  368. } else if (type === MetadataType.VEC3) {
  369. return ".rgb";
  370. } else if (type === MetadataType.VEC4) {
  371. return "";
  372. }
  373. }
  374. /**
  375. * Gets the GLSL type of the partial derivative of {@link MetadataType}.
  376. *
  377. * @function
  378. *
  379. * @param {MetadataType} type The {@link MetadataType}.
  380. * @returns {string} The GLSL type.
  381. *
  382. * @private
  383. */
  384. function getGlslPartialDerivativeType(type) {
  385. if (type === MetadataType.SCALAR) {
  386. return "vec3";
  387. } else if (type === MetadataType.VEC2) {
  388. return "mat2";
  389. } else if (type === MetadataType.VEC3) {
  390. return "mat3";
  391. } else if (type === MetadataType.VEC4) {
  392. return "mat4";
  393. }
  394. }
  395. /**
  396. * GLSL needs to have `.0` at the end of whole number floats or else it's
  397. * treated like an integer.
  398. *
  399. * @function
  400. *
  401. * @param {number} number The number to convert.
  402. * @returns {string} The number as floating point in GLSL.
  403. *
  404. * @private
  405. */
  406. function getGlslNumberAsFloat(number) {
  407. let numberString = number.toString();
  408. if (numberString.indexOf(".") === -1) {
  409. numberString = `${number}.0`;
  410. }
  411. return numberString;
  412. }
  413. /**
  414. * Gets the GLSL field
  415. *
  416. * @function
  417. *
  418. * @param {MetadataType} type
  419. * @param {number} index
  420. * @returns {string}
  421. *
  422. * @private
  423. */
  424. function getGlslField(type, index) {
  425. if (type === MetadataType.SCALAR) {
  426. return "";
  427. }
  428. return `[${index}]`;
  429. }
  430. export default processVoxelProperties;