nativeShaderProcessor.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { WebGL2ShaderProcessor } from "../WebGL/webGL2ShaderProcessors";
  2. import { VertexBuffer } from "../../Meshes/buffer";
  3. // These numbers must match the values for bgfx::Attrib::Enum
  4. const attributeLocations: { [kind: string]: number } = {
  5. [VertexBuffer.PositionKind]: 0,
  6. [VertexBuffer.NormalKind]: 1,
  7. [VertexBuffer.TangentKind]: 2,
  8. [VertexBuffer.UVKind]: 10,
  9. [VertexBuffer.UV2Kind]: 11,
  10. [VertexBuffer.UV3Kind]: 12,
  11. [VertexBuffer.UV4Kind]: 13,
  12. [VertexBuffer.ColorKind]: 4,
  13. [VertexBuffer.MatricesIndicesKind]: 8,
  14. [VertexBuffer.MatricesWeightsKind]: 9,
  15. };
  16. // Remap BJS names to bgfx names
  17. const attributeBGFXName: { [kind: string]: string } = {
  18. [VertexBuffer.PositionKind]: "a_position",
  19. [VertexBuffer.NormalKind]: "a_normal",
  20. [VertexBuffer.TangentKind]: "a_tangent",
  21. [VertexBuffer.UVKind]: "a_texcoord0",
  22. [VertexBuffer.UV2Kind]: "a_texcoord1",
  23. [VertexBuffer.UV3Kind]: "a_texcoord2",
  24. [VertexBuffer.UV4Kind]: "a_texcoord3",
  25. [VertexBuffer.ColorKind]: "a_color0",
  26. [VertexBuffer.MatricesIndicesKind]: "a_indices",
  27. [VertexBuffer.MatricesWeightsKind]: "a_weight",
  28. };
  29. // Must match bgfx::Attrib::TexCoord0
  30. const firstGenericAttributeLocation = 10;
  31. // Must match bgfx::Attrib::TexCoord7
  32. const lastGenericAttributeLocation = 17;
  33. /** @hidden */
  34. export class NativeShaderProcessor extends WebGL2ShaderProcessor {
  35. private _genericAttributeLocation: number;
  36. private _varyingLocationCount: number;
  37. private _varyingLocationMap: { [name: string]: number };
  38. private _replacements: Array<{ searchValue: RegExp, replaceValue: string }>;
  39. private _textureCount: number;
  40. private _uniforms: Array<string>;
  41. public lineProcessor(line: string): string {
  42. for (const replacement of this._replacements) {
  43. line = line.replace(replacement.searchValue, replacement.replaceValue);
  44. }
  45. return line;
  46. }
  47. public attributeProcessor(attribute: string): string {
  48. const match = attribute.match(/attribute\s+[^\s]+\s+([^\s]+)\s*(?:\[.+\])?\s*;/)!;
  49. const name = match[1];
  50. let location = attributeLocations[name];
  51. if (location === undefined) {
  52. location = this._genericAttributeLocation++;
  53. if (location > lastGenericAttributeLocation) {
  54. throw new Error("Exceeded maximum custom attributes");
  55. }
  56. }
  57. let newName = attributeBGFXName[name];
  58. if (newName === undefined) {
  59. throw new Error("Can't find bgfx name mapping");
  60. }
  61. attribute = attribute.replace(name, newName);
  62. this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`, 'g'), replaceValue: `${newName}` });
  63. return `layout(location=${location}) ${super.attributeProcessor(attribute)}`;
  64. }
  65. public varyingProcessor(varying: string, isFragment: boolean): string {
  66. let location: number;
  67. if (isFragment) {
  68. location = this._varyingLocationMap[varying];
  69. }
  70. else {
  71. location = this._varyingLocationCount++;
  72. this._varyingLocationMap[varying] = location;
  73. }
  74. return `layout(location=${location}) ${super.varyingProcessor(varying, isFragment)}`;
  75. }
  76. public uniformProcessor(uniform: string): string {
  77. const match = uniform.match(/uniform\s+([^\s]+)\s+([^\s]+)\s*(?:\[.+\])?\s*;/)!;
  78. const type = match[1];
  79. const name = match[2];
  80. switch (type) {
  81. case "sampler2D":
  82. case "samplerCube": {
  83. const suffix = type.substr(7);
  84. const binding = this._textureCount++;
  85. this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `sampler${suffix}(${name}Texture, ${name})` });
  86. return `layout(binding=${binding}) uniform texture${suffix} ${name}Texture;\nlayout(binding=${binding}) uniform sampler ${name};`;
  87. }
  88. case "float": {
  89. this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.x` });
  90. uniform = `uniform vec4 ${name};`;
  91. break;
  92. }
  93. case "vec2": {
  94. this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.xy` });
  95. uniform = `uniform vec4 ${name};`;
  96. break;
  97. }
  98. case "vec3": {
  99. this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.xyz` });
  100. uniform = `uniform vec4 ${name};`;
  101. break;
  102. }
  103. }
  104. this._uniforms.push(uniform);
  105. return this._uniforms.length === 1 ? "<UNIFORM>" : "";
  106. }
  107. public preProcessor(code: string, defines: string[], isFragment: boolean): string {
  108. this._genericAttributeLocation = firstGenericAttributeLocation;
  109. if (!isFragment) {
  110. this._varyingLocationCount = 0;
  111. this._varyingLocationMap = {};
  112. }
  113. this._replacements = [];
  114. this._textureCount = 0;
  115. this._uniforms = [];
  116. return code;
  117. }
  118. public postProcessor(code: string, defines: string[], isFragment: boolean): string {
  119. code = super.postProcessor(code, defines, isFragment);
  120. code = code.replace("<UNIFORM>", `layout(binding=0) uniform Frame {\n${this._uniforms.join("\n")}\n};`);
  121. code = code.replace("out vec4 glFragColor", "layout(location=0) out vec4 glFragColor");
  122. return code;
  123. }
  124. }