Initial commit of VIZ 3D project

This commit is contained in:
balaji
2026-06-10 16:10:18 +05:30
commit 08aea85c2f
37 changed files with 13118 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
import * as THREE from 'three';
import { StandardUVMaterial } from './StandardUVMaterial';
import { TriplanarMaterial } from './TriplanarMaterial';
type MaterialCacheKey = string;
export class MaterialFactory {
private static instance: MaterialFactory;
private cache: Map<MaterialCacheKey, THREE.Material> = new Map();
private constructor() {}
public static getInstance(): MaterialFactory {
if (!MaterialFactory.instance) {
MaterialFactory.instance = new MaterialFactory();
}
return MaterialFactory.instance;
}
public getStandardUVMaterial(
parameters: THREE.MeshPhysicalMaterialParameters,
key: string
): StandardUVMaterial {
const cacheKey = `standard_uv_${key}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey) as StandardUVMaterial;
}
const material = new StandardUVMaterial(parameters);
this.cache.set(cacheKey, material);
return material;
}
public getTriplanarMaterial(
parameters: THREE.MeshPhysicalMaterialParameters,
key: string
): TriplanarMaterial {
const cacheKey = `triplanar_${key}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey) as TriplanarMaterial;
}
const material = new TriplanarMaterial(parameters);
this.cache.set(cacheKey, material);
return material;
}
public clearCache() {
this.cache.forEach((material) => material.dispose());
this.cache.clear();
}
}
+95
View File
@@ -0,0 +1,95 @@
import * as THREE from 'three';
export class StandardUVMaterial extends THREE.MeshPhysicalMaterial {
public uvUniforms: {
uMappingMode: { value: number }; // 0: imported, 1: planar, 2: box, 3: cylindrical
uUVTransform: { value: THREE.Matrix3 };
};
constructor(parameters?: THREE.MeshPhysicalMaterialParameters) {
super(parameters);
this.uvUniforms = {
uMappingMode: { value: 0 },
uUVTransform: { value: new THREE.Matrix3() },
};
this.customProgramCacheKey = () => `standard_uv_v2`;
this.onBeforeCompile = (shader) => {
shader.uniforms.uMappingMode = this.uvUniforms.uMappingMode;
shader.uniforms.uUVTransform = this.uvUniforms.uUVTransform;
shader.vertexShader = `
attribute vec2 uvPlanar;
attribute vec2 uvBox;
attribute vec2 uvCylinder;
uniform int uMappingMode;
uniform mat3 uUVTransform;
varying vec2 vCustomUV;
` + shader.vertexShader;
shader.vertexShader = shader.vertexShader.replace(
`#include <uv_vertex>`,
`
#include <uv_vertex>
vec2 selectedUV = vec2(0.0);
#ifdef USE_UV
selectedUV = uv;
#endif
if (uMappingMode == 1) {
selectedUV = uvPlanar;
} else if (uMappingMode == 2) {
selectedUV = uvBox;
} else if (uMappingMode == 3) {
selectedUV = uvCylinder;
}
vCustomUV = (uUVTransform * vec3(selectedUV, 1.0)).xy;
`
);
shader.fragmentShader = `
varying vec2 vCustomUV;
` + shader.fragmentShader;
const mainStartIndex = shader.fragmentShader.indexOf('void main() {');
if (mainStartIndex !== -1) {
const prefix = shader.fragmentShader.substring(0, mainStartIndex);
let mainBody = shader.fragmentShader.substring(mainStartIndex);
mainBody = mainBody.replace(/\bvUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvNormalMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvRoughnessMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvMetalnessMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvAlphaMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvEmissiveMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvAoMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvClearcoatMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvClearcoatNormalMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvClearcoatRoughnessMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvTransmissionMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvThicknessMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvSpecularMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvSpecularIntensityMapUv\b/g, 'vCustomUV');
mainBody = mainBody.replace(/\bvSpecularColorMapUv\b/g, 'vCustomUV');
shader.fragmentShader = prefix + mainBody;
}
};
}
public updateUVTransform(offset: [number, number], repeat: [number, number], rotation: number) {
this.uvUniforms.uUVTransform.value.setUvTransform(
offset[0], offset[1],
repeat[0], repeat[1],
rotation,
0.5, 0.5
);
}
}
+134
View File
@@ -0,0 +1,134 @@
import * as THREE from 'three';
export class TriplanarMaterial extends THREE.MeshPhysicalMaterial {
public triplanarUniforms: {
tAlbedo: { value: THREE.Texture | null };
tNormal: { value: THREE.Texture | null };
tRoughness: { value: THREE.Texture | null };
tAo: { value: THREE.Texture | null };
uTriplanarScale: { value: number };
uBlendSharpness: { value: number };
};
constructor(parameters?: THREE.MeshPhysicalMaterialParameters) {
super(parameters);
this.triplanarUniforms = {
tAlbedo: { value: null },
tNormal: { value: null },
tRoughness: { value: null },
tAo: { value: null },
uTriplanarScale: { value: 1.0 },
uBlendSharpness: { value: 5.0 },
};
this.customProgramCacheKey = () => `triplanar_mat_v1`;
this.onBeforeCompile = (shader) => {
// Inject Uniforms
shader.uniforms.tAlbedo = this.triplanarUniforms.tAlbedo;
shader.uniforms.tNormal = this.triplanarUniforms.tNormal;
shader.uniforms.tRoughness = this.triplanarUniforms.tRoughness;
shader.uniforms.tAo = this.triplanarUniforms.tAo;
shader.uniforms.uTriplanarScale = this.triplanarUniforms.uTriplanarScale;
shader.uniforms.uBlendSharpness = this.triplanarUniforms.uBlendSharpness;
// Vertex Shader Injections
shader.vertexShader = `
varying vec3 vTriplanarPosition;
varying vec3 vTriplanarNormal;
` + shader.vertexShader;
shader.vertexShader = shader.vertexShader.replace(
`#include <worldpos_vertex>`,
`
#include <worldpos_vertex>
vTriplanarPosition = position; // Object space
vTriplanarNormal = normal; // Object space normal
`
);
// Fragment Shader Injections
shader.fragmentShader = `
uniform sampler2D tAlbedo;
uniform sampler2D tNormal;
uniform sampler2D tRoughness;
uniform sampler2D tAo;
uniform float uTriplanarScale;
uniform float uBlendSharpness;
varying vec3 vTriplanarPosition;
varying vec3 vTriplanarNormal;
// Triplanar blending logic
vec4 getTriplanarBlend(sampler2D tex, vec3 pos, vec3 norm) {
vec3 blendWeights = abs(norm);
blendWeights = pow(blendWeights, vec3(uBlendSharpness));
blendWeights /= dot(blendWeights, vec3(1.0)); // Normalize
vec2 uvX = pos.yz * uTriplanarScale;
vec2 uvY = pos.zx * uTriplanarScale;
vec2 uvZ = pos.xy * uTriplanarScale;
vec4 colX = texture2D(tex, uvX);
vec4 colY = texture2D(tex, uvY);
vec4 colZ = texture2D(tex, uvZ);
return colX * blendWeights.x + colY * blendWeights.y + colZ * blendWeights.z;
}
` + shader.fragmentShader;
// Replace map chunks
shader.fragmentShader = shader.fragmentShader.replace(
`#include <map_fragment>`,
`
#ifdef USE_MAP
vec4 texelColor = getTriplanarBlend(tAlbedo, vTriplanarPosition, vTriplanarNormal);
texelColor = mapTexelToLinear(texelColor);
diffuseColor *= texelColor;
#endif
`
);
shader.fragmentShader = shader.fragmentShader.replace(
`#include <roughnessmap_fragment>`,
`
float roughnessFactor = roughness;
#ifdef USE_ROUGHNESSMAP
vec4 texelRoughness = getTriplanarBlend(tRoughness, vTriplanarPosition, vTriplanarNormal);
roughnessFactor *= texelRoughness.g;
#endif
`
);
shader.fragmentShader = shader.fragmentShader.replace(
`#include <normal_fragment_maps>`,
`
#ifdef USE_NORMALMAP
vec4 texelNormal = getTriplanarBlend(tNormal, vTriplanarPosition, vTriplanarNormal);
// Simplified triplanar normal calculation
vec3 n = texelNormal.rgb * 2.0 - 1.0;
normal = normalize(normalMatrix * n);
#elif defined( USE_BUMPMAP )
// Bump map omitted for brevity
#endif
`
);
shader.fragmentShader = shader.fragmentShader.replace(
`#include <aomap_fragment>`,
`
#ifdef USE_AOMAP
vec4 texelAo = getTriplanarBlend(tAo, vTriplanarPosition, vTriplanarNormal);
float ambientOcclusion = ( texelAo.r - 1.0 ) * aoMapIntensity + 1.0;
reflectedLight.indirectDiffuse *= ambientOcclusion;
#if defined( USE_ENVMAP ) && defined( STANDARD )
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );
#endif
#endif
`
);
};
}
}