@babylonjs-toolkit/next 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +319 -0
- package/lib/blendtreeposition.d.ts +5 -0
- package/lib/blendtreeposition.d.ts.map +1 -0
- package/lib/blendtreeposition.js +5 -0
- package/lib/channelmixerplugin.d.ts +11 -0
- package/lib/channelmixerplugin.d.ts.map +1 -0
- package/lib/channelmixerplugin.js +34 -0
- package/lib/collisioncontact.d.ts +7 -0
- package/lib/collisioncontact.d.ts.map +1 -0
- package/lib/collisioncontact.js +7 -0
- package/lib/collisionfilters.d.ts +10 -0
- package/lib/collisionfilters.d.ts.map +1 -0
- package/lib/collisionfilters.js +10 -0
- package/lib/collisionflags.d.ts +14 -0
- package/lib/collisionflags.d.ts.map +1 -0
- package/lib/collisionflags.js +14 -0
- package/lib/collisionstate.d.ts +8 -0
- package/lib/collisionstate.d.ts.map +1 -0
- package/lib/collisionstate.js +8 -0
- package/lib/customloadingscreen.d.ts +16 -0
- package/lib/customloadingscreen.d.ts.map +1 -0
- package/lib/customloadingscreen.js +48 -0
- package/lib/entitycontroller.d.ts +11 -0
- package/lib/entitycontroller.d.ts.map +1 -0
- package/lib/entitycontroller.js +41 -0
- package/lib/fontmanifestentry.d.ts +8 -0
- package/lib/fontmanifestentry.d.ts.map +1 -0
- package/lib/fontmanifestentry.js +1 -0
- package/lib/handedness.d.ts +6 -0
- package/lib/handedness.d.ts.map +1 -0
- package/lib/handedness.js +6 -0
- package/lib/ianimationcurve.d.ts +14 -0
- package/lib/ianimationcurve.d.ts.map +1 -0
- package/lib/ianimationcurve.js +1 -0
- package/lib/index.d.ts +43 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +42 -0
- package/lib/intersectionprecision.d.ts +5 -0
- package/lib/intersectionprecision.d.ts.map +1 -0
- package/lib/intersectionprecision.js +5 -0
- package/lib/iparticlesystemtransform.d.ts +9 -0
- package/lib/iparticlesystemtransform.d.ts.map +1 -0
- package/lib/iparticlesystemtransform.js +1 -0
- package/lib/irecastnavigationplugin.d.ts +92 -0
- package/lib/irecastnavigationplugin.d.ts.map +1 -0
- package/lib/irecastnavigationplugin.js +1 -0
- package/lib/linesmeshrenderer.d.ts +22 -0
- package/lib/linesmeshrenderer.d.ts.map +1 -0
- package/lib/linesmeshrenderer.js +79 -0
- package/lib/localmessagebus.d.ts +9 -0
- package/lib/localmessagebus.d.ts.map +1 -0
- package/lib/localmessagebus.js +44 -0
- package/lib/lutblendplugin.d.ts +13 -0
- package/lib/lutblendplugin.d.ts.map +1 -0
- package/lib/lutblendplugin.js +153 -0
- package/lib/mousebuttonmode.d.ts +6 -0
- package/lib/mousebuttonmode.d.ts.map +1 -0
- package/lib/mousebuttonmode.js +6 -0
- package/lib/movementtype.d.ts +5 -0
- package/lib/movementtype.d.ts.map +1 -0
- package/lib/movementtype.js +5 -0
- package/lib/noisefunction2d.d.ts +2 -0
- package/lib/noisefunction2d.d.ts.map +1 -0
- package/lib/noisefunction2d.js +1 -0
- package/lib/noisefunction3d.d.ts +2 -0
- package/lib/noisefunction3d.d.ts.map +1 -0
- package/lib/noisefunction3d.js +1 -0
- package/lib/noisefunction4d.d.ts +2 -0
- package/lib/noisefunction4d.d.ts.map +1 -0
- package/lib/noisefunction4d.js +1 -0
- package/lib/perlin2d.d.ts +9 -0
- package/lib/perlin2d.d.ts.map +1 -0
- package/lib/perlin2d.js +45 -0
- package/lib/playercontrol.d.ts +5 -0
- package/lib/playercontrol.d.ts.map +1 -0
- package/lib/playercontrol.js +5 -0
- package/lib/prefabobjectpool.d.ts +20 -0
- package/lib/prefabobjectpool.d.ts.map +1 -0
- package/lib/prefabobjectpool.js +96 -0
- package/lib/randomfn.d.ts +2 -0
- package/lib/randomfn.d.ts.map +1 -0
- package/lib/randomfn.js +1 -0
- package/lib/raycasthitresult.d.ts +21 -0
- package/lib/raycasthitresult.d.ts.map +1 -0
- package/lib/raycasthitresult.js +36 -0
- package/lib/recastclassctor.d.ts +2 -0
- package/lib/recastclassctor.d.ts.map +1 -0
- package/lib/recastclassctor.js +1 -0
- package/lib/roomerrormessage.d.ts +5 -0
- package/lib/roomerrormessage.d.ts.map +1 -0
- package/lib/roomerrormessage.js +2 -0
- package/lib/scenemanager.d.ts +4141 -0
- package/lib/scenemanager.d.ts.map +1 -0
- package/lib/scenemanager.js +29628 -0
- package/lib/simplexnoise.d.ts +21 -0
- package/lib/simplexnoise.d.ts.map +1 -0
- package/lib/simplexnoise.js +362 -0
- package/lib/touchjoystickhandler.d.ts +39 -0
- package/lib/touchjoystickhandler.d.ts.map +1 -0
- package/lib/touchjoystickhandler.js +175 -0
- package/lib/touchmousebutton.d.ts +6 -0
- package/lib/touchmousebutton.d.ts.map +1 -0
- package/lib/touchmousebutton.js +6 -0
- package/lib/treebranchmaterial.d.ts +31 -0
- package/lib/treebranchmaterial.d.ts.map +1 -0
- package/lib/treebranchmaterial.js +369 -0
- package/lib/triggervolume.d.ts +6 -0
- package/lib/triggervolume.d.ts.map +1 -0
- package/lib/triggervolume.js +6 -0
- package/lib/unitydropdownmenu.d.ts +20 -0
- package/lib/unitydropdownmenu.d.ts.map +1 -0
- package/lib/unitydropdownmenu.js +144 -0
- package/lib/unityscrollbar.d.ts +11 -0
- package/lib/unityscrollbar.d.ts.map +1 -0
- package/lib/unityscrollbar.js +38 -0
- package/lib/unityslider.d.ts +4 -0
- package/lib/unityslider.d.ts.map +1 -0
- package/lib/unityslider.js +3 -0
- package/lib/universalcharactercontroller.d.ts +3 -0
- package/lib/universalcharactercontroller.d.ts.map +1 -0
- package/lib/universalcharactercontroller.js +1 -0
- package/lib/universalterrainmaterial.d.ts +48 -0
- package/lib/universalterrainmaterial.d.ts.map +1 -0
- package/lib/universalterrainmaterial.js +639 -0
- package/lib/userinputpointer.d.ts +6 -0
- package/lib/userinputpointer.d.ts.map +1 -0
- package/lib/userinputpointer.js +6 -0
- package/lib/xbox360trigger.d.ts +5 -0
- package/lib/xbox360trigger.d.ts.map +1 -0
- package/lib/xbox360trigger.js +5 -0
- package/package.json +211 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import { CustomShaderMaterial, CustomShaderMaterialPlugin, SceneManager } from './scenemanager';
|
|
2
|
+
import { ShaderLanguage } from '@babylonjs/core/Materials';
|
|
3
|
+
export class UniversalTerrainMaterial extends CustomShaderMaterial {
|
|
4
|
+
constructor(name, scene) {
|
|
5
|
+
super(name, scene);
|
|
6
|
+
this.terrainInfo = null;
|
|
7
|
+
this.shader = this.getShaderName();
|
|
8
|
+
this.plugin = new UniversalTerrainMaterialPlugin(this, this.shader);
|
|
9
|
+
}
|
|
10
|
+
awake() {
|
|
11
|
+
}
|
|
12
|
+
update() {
|
|
13
|
+
}
|
|
14
|
+
getShaderName() {
|
|
15
|
+
return "UniversalTerrainMaterial";
|
|
16
|
+
}
|
|
17
|
+
getTerrainInfo() {
|
|
18
|
+
return this.terrainInfo;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class UniversalTerrainMaterialPlugin extends CustomShaderMaterialPlugin {
|
|
22
|
+
constructor(customMaterial, shaderName) {
|
|
23
|
+
super(customMaterial, shaderName, 100, { UNIVERSALTERRAINMATERIAL: false }, true, true, false);
|
|
24
|
+
this.colorName = "surfaceAlbedo";
|
|
25
|
+
this.splatmapSampler = "splatmapSampler";
|
|
26
|
+
this.detailsSampler = "detailsSampler";
|
|
27
|
+
this.normalsSampler = "normalsSampler";
|
|
28
|
+
this.GLSL_CustomFragment = null;
|
|
29
|
+
this.GLSL_CustomVertex = null;
|
|
30
|
+
this.GLSL_VertexMainEnd = null;
|
|
31
|
+
this.GLSL_FragmentUpdateColor = null;
|
|
32
|
+
this.WGSL_CustomFragment = null;
|
|
33
|
+
this.WGSL_CustomVertex = null;
|
|
34
|
+
this.WGSL_VertexMainEnd = null;
|
|
35
|
+
this.WGSL_FragmentUpdateColor = null;
|
|
36
|
+
}
|
|
37
|
+
isCompatible(shaderLanguage) {
|
|
38
|
+
return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
|
|
39
|
+
}
|
|
40
|
+
getClassName() {
|
|
41
|
+
return "UniversalTerrainMaterialPlugin";
|
|
42
|
+
}
|
|
43
|
+
getCustomCode(shaderType, shaderLanguage) {
|
|
44
|
+
const terrainInfo = this.getCustomShaderMaterial().getTerrainInfo();
|
|
45
|
+
if (shaderType === "vertex") {
|
|
46
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
47
|
+
if (this.WGSL_CustomVertex == null)
|
|
48
|
+
this.WGSL_CustomVertex = this.WGSL_FormatTerrainVertexDefintions(terrainInfo);
|
|
49
|
+
if (this.WGSL_VertexMainEnd == null)
|
|
50
|
+
this.WGSL_VertexMainEnd = this.WGSL_FormatTerrainVertexMainEnd(terrainInfo);
|
|
51
|
+
return {
|
|
52
|
+
CUSTOM_VERTEX_DEFINITIONS: this.WGSL_CustomVertex,
|
|
53
|
+
CUSTOM_VERTEX_MAIN_END: this.WGSL_VertexMainEnd,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else if (shaderLanguage === ShaderLanguage.GLSL) {
|
|
57
|
+
if (this.GLSL_CustomVertex == null)
|
|
58
|
+
this.GLSL_CustomVertex = this.GLSL_FormatTerrainVertexDefintions(terrainInfo);
|
|
59
|
+
if (this.GLSL_VertexMainEnd == null)
|
|
60
|
+
this.GLSL_VertexMainEnd = this.GLSL_FormatTerrainVertexMainEnd(terrainInfo);
|
|
61
|
+
return {
|
|
62
|
+
CUSTOM_VERTEX_DEFINITIONS: this.GLSL_CustomVertex,
|
|
63
|
+
CUSTOM_VERTEX_MAIN_END: this.GLSL_VertexMainEnd,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (shaderType === "fragment") {
|
|
68
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
69
|
+
if (this.WGSL_CustomFragment == null)
|
|
70
|
+
this.WGSL_CustomFragment = this.WGSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
|
|
71
|
+
if (this.WGSL_FragmentUpdateColor == null)
|
|
72
|
+
this.WGSL_FragmentUpdateColor = this.WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
|
|
73
|
+
return {
|
|
74
|
+
CUSTOM_FRAGMENT_DEFINITIONS: this.WGSL_CustomFragment,
|
|
75
|
+
CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.WGSL_FragmentUpdateColor,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (shaderLanguage === ShaderLanguage.GLSL) {
|
|
79
|
+
if (this.GLSL_CustomFragment == null)
|
|
80
|
+
this.GLSL_CustomFragment = this.GLSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
|
|
81
|
+
if (this.GLSL_FragmentUpdateColor == null)
|
|
82
|
+
this.GLSL_FragmentUpdateColor = this.GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
|
|
83
|
+
return {
|
|
84
|
+
CUSTOM_FRAGMENT_DEFINITIONS: this.GLSL_CustomFragment,
|
|
85
|
+
CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.GLSL_FragmentUpdateColor,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
getUniforms(shaderLanguage) {
|
|
92
|
+
const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
|
|
93
|
+
this.vertexDefinitions = this.getCustomShaderMaterial().getCustomVertexCode(wgsl);
|
|
94
|
+
this.fragmentDefinitions = (wgsl === true) ? this.getCustomShaderMaterial().getCustomFragmentCode(wgsl) : null;
|
|
95
|
+
return this.getCustomShaderMaterial().getCustomUniforms(wgsl);
|
|
96
|
+
}
|
|
97
|
+
getSamplers(samplers) {
|
|
98
|
+
const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
|
|
99
|
+
if (customSamplers != null && customSamplers.length > 0)
|
|
100
|
+
samplers.push(...customSamplers);
|
|
101
|
+
}
|
|
102
|
+
getAttributes(attributes, scene, mesh) {
|
|
103
|
+
const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
|
|
104
|
+
if (customAttributes != null && customAttributes.length > 0)
|
|
105
|
+
attributes.push(...customAttributes);
|
|
106
|
+
}
|
|
107
|
+
prepareDefines(defines, scene, mesh) {
|
|
108
|
+
if (!this.getIsEnabled())
|
|
109
|
+
return;
|
|
110
|
+
this.getCustomShaderMaterial().prepareCustomDefines(defines);
|
|
111
|
+
}
|
|
112
|
+
bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
|
|
113
|
+
if (!this.getIsEnabled())
|
|
114
|
+
return;
|
|
115
|
+
this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
|
|
116
|
+
}
|
|
117
|
+
WGSL_FormatTerrainVertexDefintions(terrainInfo) {
|
|
118
|
+
let result = "";
|
|
119
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
120
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
121
|
+
+ "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
122
|
+
+ "varying vSplatmapUV: vec2<f32>;\r\n"
|
|
123
|
+
+ "\r\n"
|
|
124
|
+
+ "#endif\r\n\r\n");
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
WGSL_FormatTerrainVertexMainEnd(terrainInfo) {
|
|
129
|
+
let result = "";
|
|
130
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
131
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
132
|
+
+ "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
133
|
+
+ "#ifdef UV1\r\n"
|
|
134
|
+
+ "vertexOutputs.vSplatmapUV = uvUpdated;\r\n"
|
|
135
|
+
+ "#endif\r\n"
|
|
136
|
+
+ "\r\n"
|
|
137
|
+
+ "#endif\r\n\r\n");
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
WGSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
|
|
142
|
+
let result = "";
|
|
143
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
144
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
145
|
+
+ "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
146
|
+
+ "varying vSplatmapUV: vec2<f32>;\r\n"
|
|
147
|
+
+ "\r\n"
|
|
148
|
+
+ "fn srgb_to_linear(c: vec3<f32>) -> vec3<f32>\r\n"
|
|
149
|
+
+ "{\r\n"
|
|
150
|
+
+ " return pow(c, vec3<f32>(2.2));\r\n"
|
|
151
|
+
+ "}\r\n"
|
|
152
|
+
+ "\r\n"
|
|
153
|
+
+ "fn linear_to_srgb(c: vec3<f32>) -> vec3<f32>\r\n"
|
|
154
|
+
+ "{\r\n"
|
|
155
|
+
+ " return pow(c, vec3<f32>(1.0 / 2.2));\r\n"
|
|
156
|
+
+ "}\r\n"
|
|
157
|
+
+ "\r\n"
|
|
158
|
+
+ "fn calculateMipmapLevel(uvs: vec2<f32>, size: vec2<f32>) -> f32\r\n"
|
|
159
|
+
+ "{\r\n"
|
|
160
|
+
+ " let dx: vec2<f32> = dpdx(uvs * size.x);\r\n"
|
|
161
|
+
+ " let dy: vec2<f32> = dpdy(uvs * size.y);\r\n"
|
|
162
|
+
+ " let d: f32 = max(dot(dx, dx), dot(dy, dy));\r\n"
|
|
163
|
+
+ " return (0.4 * log2(d));\r\n"
|
|
164
|
+
+ "}\r\n"
|
|
165
|
+
+ "\r\n"
|
|
166
|
+
+ "fn sampleTextureAtlas2D(atlas: texture_2d<f32>, sampler: sampler, gamma: f32, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>, lod: f32) -> vec4<f32>\r\n"
|
|
167
|
+
+ "{\r\n"
|
|
168
|
+
+ " var level: f32 = lod;\r\n"
|
|
169
|
+
+ " if (level < 0.0) {\r\n"
|
|
170
|
+
+ " level = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y);\r\n"
|
|
171
|
+
+ " }\r\n"
|
|
172
|
+
+ " let size: f32 = pow(2.0, tile.y - level);\r\n"
|
|
173
|
+
+ " let sizex: f32 = size / rect.z;\r\n"
|
|
174
|
+
+ " let sizey: f32 = size / rect.w;\r\n"
|
|
175
|
+
+ " var uv: vec2<f32> = fract(uvs);\r\n"
|
|
176
|
+
+ " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
|
|
177
|
+
+ " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
|
|
178
|
+
+ " var color: vec4<f32> = textureSampleLevel(atlas, sampler, uv, level);\r\n"
|
|
179
|
+
+ " if (gamma != 1.0) {\r\n"
|
|
180
|
+
+ " color.r = pow(color.r, gamma);\r\n"
|
|
181
|
+
+ " color.g = pow(color.g, gamma);\r\n"
|
|
182
|
+
+ " color.b = pow(color.b, gamma);\r\n"
|
|
183
|
+
+ " }\r\n"
|
|
184
|
+
+ " return color;\r\n"
|
|
185
|
+
+ "}\r\n"
|
|
186
|
+
+ "\r\n"
|
|
187
|
+
+ "fn sampleSplatmapAtlas2D(atlas: texture_2d<f32>, sampler: sampler, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>) -> vec4<f32>\r\n"
|
|
188
|
+
+ "{\r\n"
|
|
189
|
+
+ " let size: f32 = pow(2.0, tile.y);\r\n"
|
|
190
|
+
+ " let sizex: f32 = size / rect.z;\r\n"
|
|
191
|
+
+ " let sizey: f32 = size / rect.w;\r\n"
|
|
192
|
+
+ " var uv: vec2<f32> = uvs;\r\n"
|
|
193
|
+
+ " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
|
|
194
|
+
+ " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
|
|
195
|
+
+ " return textureSample(atlas, sampler, uv);\r\n"
|
|
196
|
+
+ "}\r\n"
|
|
197
|
+
+ "\r\n"
|
|
198
|
+
+ "fn blendSplatmapAtlasColors(splatmap: vec4<f32>, color1: vec4<f32>, color2: vec4<f32>, color3: vec4<f32>, color4: vec4<f32>, mixbuffer: vec3<f32>) -> vec3<f32>\r\n"
|
|
199
|
+
+ "{\r\n"
|
|
200
|
+
+ " let buffer1: vec3<f32> = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
|
|
201
|
+
+ " let buffer2: vec3<f32> = mix(buffer1, color2.rgb, splatmap.g);\r\n"
|
|
202
|
+
+ " let buffer3: vec3<f32> = mix(buffer2, color3.rgb, splatmap.b);\r\n"
|
|
203
|
+
+ " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
|
|
204
|
+
+ "}\r\n"
|
|
205
|
+
+ "\r\n"
|
|
206
|
+
+ "fn perturbNormalSamplerColor(cotangentFrame: mat3x3<f32>, samplerColor: vec3<f32>, scale: f32) -> vec3<f32>\r\n"
|
|
207
|
+
+ "{\r\n"
|
|
208
|
+
+ " var map: vec3<f32> = samplerColor.xyz;\r\n"
|
|
209
|
+
+ " map = map * 2.00787402 - 1.00787402;\r\n"
|
|
210
|
+
+ " #ifdef NORMALXYSCALE\r\n"
|
|
211
|
+
+ " map = normalize(map * vec3<f32>(scale, scale, 1.0));\r\n"
|
|
212
|
+
+ " #endif\r\n"
|
|
213
|
+
+ " return normalize(cotangentFrame * map);\r\n"
|
|
214
|
+
+ "}\r\n"
|
|
215
|
+
+ "#endif\r\n\r\n");
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
|
|
220
|
+
let result = "";
|
|
221
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
222
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
223
|
+
+ "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
224
|
+
+ "var normalsColor: vec3<f32> = vec3<f32>(0.5, 0.5, 1.0);\r\n"
|
|
225
|
+
+ "var normalsBuffer: vec3<f32> = normalW.rgb;\r\n"
|
|
226
|
+
+ "var splatmapBuffer: vec3<f32> = " + colorName + ".rgb;\r\n"
|
|
227
|
+
+ "var autoMipMapLevel: f32 = -1.0;\r\n"
|
|
228
|
+
+ "var normalCorrection: f32 = 1.0;\r\n"
|
|
229
|
+
+ "var detailCorrection: f32 = " + colorCorrection.toFixed(4) + ";\r\n"
|
|
230
|
+
+ "\r\n"
|
|
231
|
+
+ "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
|
|
232
|
+
+ "\r\n"
|
|
233
|
+
+ "// Reset Normal Values\r\n"
|
|
234
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
235
|
+
+ " uvOffset = vec2<f32>(0.0, 0.0);\r\n"
|
|
236
|
+
+ " #ifdef NORMAL\r\n"
|
|
237
|
+
+ " normalW = normalize(input.vNormalW);\r\n"
|
|
238
|
+
+ " #else\r\n"
|
|
239
|
+
+ " normalW = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
|
|
240
|
+
+ " #endif\r\n"
|
|
241
|
+
+ " #ifdef CLEARCOAT\r\n"
|
|
242
|
+
+ " clearCoatNormalW = normalW;\r\n"
|
|
243
|
+
+ " #endif\r\n"
|
|
244
|
+
+ " #if defined(BUMP) || defined(PARALLAX)\r\n"
|
|
245
|
+
+ " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
|
|
246
|
+
+ " TBN = vTBN;\r\n"
|
|
247
|
+
+ " #else\r\n"
|
|
248
|
+
+ " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
|
|
249
|
+
+ " #endif\r\n"
|
|
250
|
+
+ " #elif defined(ANISOTROPIC)\r\n"
|
|
251
|
+
+ " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
|
|
252
|
+
+ " TBN = vTBN;\r\n"
|
|
253
|
+
+ " #else\r\n"
|
|
254
|
+
+ " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
|
|
255
|
+
+ " #endif\r\n"
|
|
256
|
+
+ " #endif\r\n"
|
|
257
|
+
+ " #ifdef PARALLAX\r\n"
|
|
258
|
+
+ " invTBN = transposeMat3(TBN);\r\n"
|
|
259
|
+
+ " #endif\r\n"
|
|
260
|
+
+ " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
|
|
261
|
+
+ "#endif\r\n"
|
|
262
|
+
+ "\r\n"
|
|
263
|
+
+ "// Global Atlas Values\r\n"
|
|
264
|
+
+ "let splatTileSize: f32 = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
|
|
265
|
+
+ "let splatTileBits: f32 = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
|
|
266
|
+
+ "let detailTileSize: f32 = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
|
|
267
|
+
+ "let detailTileBits: f32 = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
|
|
268
|
+
+ "\r\n"
|
|
269
|
+
+ "// Sample splatmap textures\r\n");
|
|
270
|
+
if (terrainInfo.splatmapCount > 0) {
|
|
271
|
+
let counter = 0;
|
|
272
|
+
result += "normalsBuffer = vec3<f32>(0.0,0.0,0.0);\r\n";
|
|
273
|
+
for (let index = 0; index < terrainInfo.splatmapCount; index++) {
|
|
274
|
+
counter = (index * 4);
|
|
275
|
+
const splatmapRect = terrainInfo["splatmapRect" + index];
|
|
276
|
+
result += "var splatmapRect" + index + ": vec4<f32> = vec4<f32>(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
|
|
277
|
+
result += "var splatmapAlbedo" + index + ": vec4<f32> = sampleSplatmapAtlas2D(" + splatmapSampler + ", " + splatmapSampler + "Sampler, vec2<f32>(splatTileSize, splatTileBits), splatmapRect" + index + ", (fragmentInputs.vSplatmapUV + uvOffset));\r\n";
|
|
278
|
+
result += "var textureAlbedo" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
279
|
+
result += "var textureAlbedo" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
280
|
+
result += "var textureAlbedo" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
281
|
+
result += "var textureAlbedo" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
282
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
283
|
+
const textureRect = terrainInfo["textureRect" + (counter + 0)];
|
|
284
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
|
|
285
|
+
result += "var textureRect" + (counter + 0) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
286
|
+
result += "var textureScale" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
287
|
+
result += "var textureOffset" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
288
|
+
result += "var textureTileUV" + (counter + 0) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
|
|
289
|
+
result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
290
|
+
}
|
|
291
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
292
|
+
const textureRect = terrainInfo["textureRect" + (counter + 1)];
|
|
293
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
|
|
294
|
+
result += "var textureRect" + (counter + 1) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
295
|
+
result += "var textureScale" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
296
|
+
result += "var textureOffset" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
297
|
+
result += "var textureTileUV" + (counter + 1) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
|
|
298
|
+
result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
299
|
+
}
|
|
300
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
301
|
+
const textureRect = terrainInfo["textureRect" + (counter + 2)];
|
|
302
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
|
|
303
|
+
result += "var textureRect" + (counter + 2) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
304
|
+
result += "var textureScale" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
305
|
+
result += "var textureOffset" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
306
|
+
result += "var textureTileUV" + (counter + 2) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
|
|
307
|
+
result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
308
|
+
}
|
|
309
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
310
|
+
const textureRect = terrainInfo["textureRect" + (counter + 3)];
|
|
311
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
|
|
312
|
+
result += "var textureRect" + (counter + 3) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
313
|
+
result += "var textureScale" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
314
|
+
result += "var textureOffset" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
315
|
+
result += "var textureTileUV" + (counter + 3) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
|
|
316
|
+
result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
317
|
+
}
|
|
318
|
+
result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
|
|
319
|
+
result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
|
|
320
|
+
result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
|
|
321
|
+
result += " var normalColor" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
322
|
+
result += " var normalColor" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
323
|
+
result += " var normalColor" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
324
|
+
result += " var normalColor" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
325
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
326
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 0)];
|
|
327
|
+
result += " var normalScale" + (counter + 0) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
328
|
+
result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
329
|
+
result += " normalColor" + (counter + 0) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + "), 1.0);\r\n";
|
|
330
|
+
}
|
|
331
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
332
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 1)];
|
|
333
|
+
result += " var normalScale" + (counter + 1) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
334
|
+
result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
335
|
+
result += " normalColor" + (counter + 1) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + "), 1.0);\r\n";
|
|
336
|
+
}
|
|
337
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
338
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 2)];
|
|
339
|
+
result += " var normalScale" + (counter + 2) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
340
|
+
result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
341
|
+
result += " normalColor" + (counter + 2) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + "), 1.0);\r\n";
|
|
342
|
+
}
|
|
343
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
344
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 3)];
|
|
345
|
+
result += " var normalScale" + (counter + 3) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
346
|
+
result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
347
|
+
result += " normalColor" + (counter + 3) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + "), 1.0);\r\n";
|
|
348
|
+
}
|
|
349
|
+
result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
|
|
350
|
+
result += " #endif\r\n";
|
|
351
|
+
result += "#endif\r\n";
|
|
352
|
+
result += "\r\n";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
result += ("// Update Color Values\r\n"
|
|
356
|
+
+ colorName + " = splatmapBuffer.rgb;\r\n"
|
|
357
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
358
|
+
+ " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
|
|
359
|
+
+ " normalW = normalsBuffer.rgb;\r\n"
|
|
360
|
+
+ " #endif\r\n"
|
|
361
|
+
+ " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
|
|
362
|
+
+ " var faceNormal: vec3<f32> = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
|
|
363
|
+
+ " #if defined(TWOSIDEDLIGHTING)\r\n"
|
|
364
|
+
+ " faceNormal = select(-faceNormal, faceNormal, fragmentInputs.frontFacing)\r\n"
|
|
365
|
+
+ " #endif\r\n"
|
|
366
|
+
+ " normalW *= sign(dot(normalW, faceNormal));\r\n"
|
|
367
|
+
+ " #endif\r\n"
|
|
368
|
+
+ " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
|
|
369
|
+
+ " normalW = select(-normalW, normalW, fragmentInputs.frontFacing);\r\n"
|
|
370
|
+
+ " #endif\r\n"
|
|
371
|
+
+ "#endif\r\n"
|
|
372
|
+
+ "\r\n"
|
|
373
|
+
+ "#endif\r\n"
|
|
374
|
+
+ "\r\n"
|
|
375
|
+
+ "#endif\r\n\r\n");
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
GLSL_FormatTerrainVertexDefintions(terrainInfo) {
|
|
380
|
+
let result = "";
|
|
381
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
382
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
383
|
+
+ "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
384
|
+
+ "varying vec2 vSplatmapUV;\r\n"
|
|
385
|
+
+ "\r\n"
|
|
386
|
+
+ "#endif\r\n\r\n");
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
GLSL_FormatTerrainVertexMainEnd(terrainInfo) {
|
|
391
|
+
let result = "";
|
|
392
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
393
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
394
|
+
+ "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
395
|
+
+ "#ifdef UV1\r\n"
|
|
396
|
+
+ "vSplatmapUV = uv;\r\n"
|
|
397
|
+
+ "#endif\r\n"
|
|
398
|
+
+ "\r\n"
|
|
399
|
+
+ "#endif\r\n\r\n");
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
GLSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
|
|
404
|
+
let result = "";
|
|
405
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
406
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
407
|
+
+ "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
408
|
+
+ "varying vec2 vSplatmapUV;\r\n"
|
|
409
|
+
+ "\r\n"
|
|
410
|
+
+ "vec3 srgb_to_linear(const in vec3 c)\r\n"
|
|
411
|
+
+ "{\r\n"
|
|
412
|
+
+ " return pow(c, vec3(2.2));\r\n"
|
|
413
|
+
+ "}\r\n"
|
|
414
|
+
+ "\r\n"
|
|
415
|
+
+ "vec3 linear_to_srgb(const in vec3 c)\r\n"
|
|
416
|
+
+ "{\r\n"
|
|
417
|
+
+ " return pow(c, vec3(1.0 / 2.2));\r\n"
|
|
418
|
+
+ "}\r\n"
|
|
419
|
+
+ "\r\n"
|
|
420
|
+
+ "float calculateMipmapLevel(const in vec2 uvs, const in vec2 size)\r\n"
|
|
421
|
+
+ "{\r\n"
|
|
422
|
+
+ " vec2 dx = dFdx(uvs * size.x);\r\n"
|
|
423
|
+
+ " vec2 dy = dFdy(uvs * size.y);\r\n"
|
|
424
|
+
+ " float d = max(dot(dx, dx), dot(dy, dy));\r\n"
|
|
425
|
+
+ " return 0.4 * log2(d);\r\n"
|
|
426
|
+
+ "}\r\n"
|
|
427
|
+
+ "\r\n"
|
|
428
|
+
+ "vec4 sampleTextureAtlas2D(const in sampler2D atlas, const in float gamma, const in vec2 tile, const in vec4 rect, in vec2 uvs, in float lod)\r\n"
|
|
429
|
+
+ "{\r\n"
|
|
430
|
+
+ " if (lod < 0.0) lod = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y); // Tile Info (tile.xy)\r\n"
|
|
431
|
+
+ " float size = pow(2.0, tile.y - lod); // Tile Bits (tile.y)\r\n"
|
|
432
|
+
+ " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
|
|
433
|
+
+ " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
|
|
434
|
+
+ " uvs = fract(uvs); // Perfrom Tiling (fract)\r\n"
|
|
435
|
+
+ " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
|
|
436
|
+
+ " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
|
|
437
|
+
+ " vec4 color = texture2DLodEXT(atlas, uvs, lod);\r\n"
|
|
438
|
+
+ " if (gamma != 1.0) {\r\n"
|
|
439
|
+
+ " color.r = pow(color.r, gamma);\r\n"
|
|
440
|
+
+ " color.g = pow(color.g, gamma);\r\n"
|
|
441
|
+
+ " color.b = pow(color.b, gamma);\r\n"
|
|
442
|
+
+ " }\r\n"
|
|
443
|
+
+ " return color;\r\n"
|
|
444
|
+
+ "}\r\n"
|
|
445
|
+
+ "\r\n"
|
|
446
|
+
+ "vec4 sampleSplatmapAtlas2D(const in sampler2D atlas, const in vec2 tile, const in vec4 rect, in vec2 uvs)\r\n"
|
|
447
|
+
+ "{\r\n"
|
|
448
|
+
+ " float size = pow(2.0, tile.y); // Tile Bits (tile.y)\r\n"
|
|
449
|
+
+ " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
|
|
450
|
+
+ " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
|
|
451
|
+
+ " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
|
|
452
|
+
+ " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
|
|
453
|
+
+ " return texture2D(atlas, uvs);\r\n"
|
|
454
|
+
+ "}\r\n"
|
|
455
|
+
+ "\r\n"
|
|
456
|
+
+ "vec3 blendSplatmapAtlasColors(const in vec4 splatmap, in vec4 color1, in vec4 color2, in vec4 color3, in vec4 color4, in vec3 mixbuffer)\r\n"
|
|
457
|
+
+ "{\r\n"
|
|
458
|
+
+ " vec3 buffer1 = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
|
|
459
|
+
+ " vec3 buffer2 = mix(buffer1, color2.rgb, splatmap.g);\r\n"
|
|
460
|
+
+ " vec3 buffer3 = mix(buffer2, color3.rgb, splatmap.b);\r\n"
|
|
461
|
+
+ " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
|
|
462
|
+
+ "}\r\n"
|
|
463
|
+
+ "\r\n"
|
|
464
|
+
+ "vec3 perturbNormalSamplerColor(mat3 cotangentFrame, vec3 samplerColor, float scale)\r\n"
|
|
465
|
+
+ "{\r\n"
|
|
466
|
+
+ " vec3 map = samplerColor.xyz;\r\n"
|
|
467
|
+
+ " map = map * 2.00787402 - 1.00787402;\r\n"
|
|
468
|
+
+ " #ifdef NORMALXYSCALE\r\n"
|
|
469
|
+
+ " map = normalize(map * vec3(scale, scale, 1.0));\r\n"
|
|
470
|
+
+ " #endif\r\n"
|
|
471
|
+
+ " return normalize(cotangentFrame * map);\r\n"
|
|
472
|
+
+ "}\r\n"
|
|
473
|
+
+ "\r\n"
|
|
474
|
+
+ "\r\n"
|
|
475
|
+
+ "#endif\r\n\r\n");
|
|
476
|
+
}
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
|
|
480
|
+
let result = "";
|
|
481
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
482
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
483
|
+
+ "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
484
|
+
+ "vec3 normalsColor = vec3(0.5, 0.5, 1.0);\r\n"
|
|
485
|
+
+ "vec3 normalsBuffer = normalW.rgb;\r\n"
|
|
486
|
+
+ "vec3 splatmapBuffer = " + colorName + ".rgb;\r\n"
|
|
487
|
+
+ "float autoMipMapLevel = -1.0;\r\n"
|
|
488
|
+
+ "float normalCorrection = 1.0;\r\n"
|
|
489
|
+
+ "float detailCorrection = " + colorCorrection.toFixed(4) + ";\r\n"
|
|
490
|
+
+ "\r\n"
|
|
491
|
+
+ "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
|
|
492
|
+
+ "\r\n"
|
|
493
|
+
+ "// Reset Normal Values\r\n"
|
|
494
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
495
|
+
+ " uvOffset = vec2(0.0, 0.0);\r\n"
|
|
496
|
+
+ " #ifdef NORMAL\r\n"
|
|
497
|
+
+ " normalW = normalize(vNormalW);\r\n"
|
|
498
|
+
+ " #else\r\n"
|
|
499
|
+
+ " normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
|
|
500
|
+
+ " #endif\r\n"
|
|
501
|
+
+ " #ifdef CLEARCOAT\r\n"
|
|
502
|
+
+ " clearCoatNormalW = normalW;\r\n"
|
|
503
|
+
+ " #endif\r\n"
|
|
504
|
+
+ " #if defined(BUMP) || defined(PARALLAX)\r\n"
|
|
505
|
+
+ " #if defined(TANGENT) && defined(NORMAL)\r\n"
|
|
506
|
+
+ " TBN = vTBN;\r\n"
|
|
507
|
+
+ " #else\r\n"
|
|
508
|
+
+ " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV);\r\n"
|
|
509
|
+
+ " #endif\r\n"
|
|
510
|
+
+ " #elif defined(ANISOTROPIC)\r\n"
|
|
511
|
+
+ " #if defined(TANGENT) && defined(NORMAL)\r\n"
|
|
512
|
+
+ " TBN = vTBN;\r\n"
|
|
513
|
+
+ " #else\r\n"
|
|
514
|
+
+ " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV, vec2(1.0, 1.0));\r\n"
|
|
515
|
+
+ " #endif\r\n"
|
|
516
|
+
+ " #endif\r\n"
|
|
517
|
+
+ " #ifdef PARALLAX\r\n"
|
|
518
|
+
+ " invTBN = transposeMat3(TBN);\r\n"
|
|
519
|
+
+ " #endif\r\n"
|
|
520
|
+
+ " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
|
|
521
|
+
+ "#endif\r\n"
|
|
522
|
+
+ "\r\n"
|
|
523
|
+
+ "// Global Atlas Values\r\n"
|
|
524
|
+
+ "float splatTileSize = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
|
|
525
|
+
+ "float splatTileBits = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
|
|
526
|
+
+ "float detailTileSize = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
|
|
527
|
+
+ "float detailTileBits = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
|
|
528
|
+
+ "\r\n"
|
|
529
|
+
+ "// Sample splatmap textures\r\n");
|
|
530
|
+
if (terrainInfo.splatmapCount > 0) {
|
|
531
|
+
let counter = 0;
|
|
532
|
+
result += "normalsBuffer = vec3(0.0,0.0,0.0);\r\n";
|
|
533
|
+
for (let index = 0; index < terrainInfo.splatmapCount; index++) {
|
|
534
|
+
counter = (index * 4);
|
|
535
|
+
const splatmapRect = terrainInfo["splatmapRect" + index];
|
|
536
|
+
result += "vec4 splatmapRect" + index + " = vec4(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
|
|
537
|
+
result += "vec4 splatmapAlbedo" + index + " = sampleSplatmapAtlas2D(" + splatmapSampler + ", vec2(splatTileSize, splatTileBits), splatmapRect" + index + ", (vSplatmapUV + uvOffset));\r\n";
|
|
538
|
+
result += "vec4 textureAlbedo" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
539
|
+
result += "vec4 textureAlbedo" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
540
|
+
result += "vec4 textureAlbedo" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
541
|
+
result += "vec4 textureAlbedo" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
542
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
543
|
+
const textureRect = terrainInfo["textureRect" + (counter + 0)];
|
|
544
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
|
|
545
|
+
result += "vec4 textureRect" + (counter + 0) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
546
|
+
result += "vec2 textureScale" + (counter + 0) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
547
|
+
result += "vec2 textureOffset" + (counter + 0) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
548
|
+
result += "vec2 textureTileUV" + (counter + 0) + " = ((vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
|
|
549
|
+
result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
550
|
+
}
|
|
551
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
552
|
+
const textureRect = terrainInfo["textureRect" + (counter + 1)];
|
|
553
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
|
|
554
|
+
result += "vec4 textureRect" + (counter + 1) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
555
|
+
result += "vec2 textureScale" + (counter + 1) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
556
|
+
result += "vec2 textureOffset" + (counter + 1) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
557
|
+
result += "vec2 textureTileUV" + (counter + 1) + " = ((vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
|
|
558
|
+
result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
559
|
+
}
|
|
560
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
561
|
+
const textureRect = terrainInfo["textureRect" + (counter + 2)];
|
|
562
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
|
|
563
|
+
result += "vec4 textureRect" + (counter + 2) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
564
|
+
result += "vec2 textureScale" + (counter + 2) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
565
|
+
result += "vec2 textureOffset" + (counter + 2) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
566
|
+
result += "vec2 textureTileUV" + (counter + 2) + " = ((vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
|
|
567
|
+
result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
568
|
+
}
|
|
569
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
570
|
+
const textureRect = terrainInfo["textureRect" + (counter + 3)];
|
|
571
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
|
|
572
|
+
result += "vec4 textureRect" + (counter + 3) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
573
|
+
result += "vec2 textureScale" + (counter + 3) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
574
|
+
result += "vec2 textureOffset" + (counter + 3) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
575
|
+
result += "vec2 textureTileUV" + (counter + 3) + " = ((vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
|
|
576
|
+
result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
577
|
+
}
|
|
578
|
+
result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
|
|
579
|
+
result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
|
|
580
|
+
result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
|
|
581
|
+
result += " vec4 normalColor" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
582
|
+
result += " vec4 normalColor" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
583
|
+
result += " vec4 normalColor" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
584
|
+
result += " vec4 normalColor" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
585
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
586
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 0)];
|
|
587
|
+
result += " float normalScale" + (counter + 0) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
588
|
+
result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
589
|
+
result += " normalColor" + (counter + 0) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + ");\r\n";
|
|
590
|
+
}
|
|
591
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
592
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 1)];
|
|
593
|
+
result += " float normalScale" + (counter + 1) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
594
|
+
result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
595
|
+
result += " normalColor" + (counter + 1) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + ");\r\n";
|
|
596
|
+
}
|
|
597
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
598
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 2)];
|
|
599
|
+
result += " float normalScale" + (counter + 2) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
600
|
+
result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
601
|
+
result += " normalColor" + (counter + 2) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + ");\r\n";
|
|
602
|
+
}
|
|
603
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
604
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 3)];
|
|
605
|
+
result += " float normalScale" + (counter + 3) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
606
|
+
result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
607
|
+
result += " normalColor" + (counter + 3) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + ");\r\n";
|
|
608
|
+
}
|
|
609
|
+
result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
|
|
610
|
+
result += " #endif\r\n";
|
|
611
|
+
result += "#endif\r\n";
|
|
612
|
+
result += "\r\n";
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
result += ("// Update Color Values\r\n"
|
|
616
|
+
+ colorName + " = splatmapBuffer.rgb;\r\n"
|
|
617
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
618
|
+
+ " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
|
|
619
|
+
+ " normalW = normalsBuffer.rgb;\r\n"
|
|
620
|
+
+ " #endif\r\n"
|
|
621
|
+
+ " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
|
|
622
|
+
+ " vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
|
|
623
|
+
+ " #if defined(TWOSIDEDLIGHTING)\r\n"
|
|
624
|
+
+ " faceNormal = gl_FrontFacing ? faceNormal : -faceNormal;\r\n"
|
|
625
|
+
+ " #endif\r\n"
|
|
626
|
+
+ " normalW *= sign(dot(normalW, faceNormal));\r\n"
|
|
627
|
+
+ " #endif\r\n"
|
|
628
|
+
+ " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
|
|
629
|
+
+ " normalW = gl_FrontFacing ? normalW : -normalW;\r\n"
|
|
630
|
+
+ " #endif\r\n"
|
|
631
|
+
+ "#endif\r\n"
|
|
632
|
+
+ "\r\n"
|
|
633
|
+
+ "#endif\r\n"
|
|
634
|
+
+ "\r\n"
|
|
635
|
+
+ "#endif\r\n\r\n");
|
|
636
|
+
}
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
}
|