diff --git a/src/materials/BasicMaterial.js b/src/materials/BasicMaterial.js deleted file mode 100644 index 798f376..0000000 --- a/src/materials/BasicMaterial.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineComponent } from 'vue'; -import { MeshBasicMaterial } from 'three'; -import { bindProps, propsValues } from '../tools'; -import Material, { wireframeProps } from './Material'; - -export default defineComponent({ - extends: Material, - props: { - ...wireframeProps, - }, - methods: { - createMaterial() { - this.material = new MeshBasicMaterial(propsValues(this.$props)); - }, - addWatchers() { - bindProps(this, Object.keys(wireframeProps), this.material); - }, - }, - __hmrId: 'BasicMaterial', -}); diff --git a/src/materials/BasicMaterial.ts b/src/materials/BasicMaterial.ts new file mode 100644 index 0000000..83a66d5 --- /dev/null +++ b/src/materials/BasicMaterial.ts @@ -0,0 +1,19 @@ +import { defineComponent } from 'vue' +import { MeshBasicMaterial } from 'three' +import { bindProps, propsValues } from '../tools' +import Material, { wireframeProps } from './Material' + +export default defineComponent({ + extends: Material, + props: { + ...wireframeProps, + }, + methods: { + createMaterial() { + const material = new MeshBasicMaterial(propsValues(this.$props)) + bindProps(this, Object.keys(wireframeProps), material) + return material + }, + }, + __hmrId: 'BasicMaterial', +}) diff --git a/src/materials/LambertMaterial.js b/src/materials/LambertMaterial.js deleted file mode 100644 index 2637e49..0000000 --- a/src/materials/LambertMaterial.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineComponent } from 'vue'; -import { MeshLambertMaterial } from 'three'; -import { bindProps, propsValues } from '../tools'; -import Material, { wireframeProps } from './Material'; - -export default defineComponent({ - extends: Material, - props: { - ...wireframeProps, - }, - methods: { - createMaterial() { - this.material = new MeshLambertMaterial(propsValues(this.$props)); - }, - addWatchers() { - bindProps(this, Object.keys(wireframeProps), this.material); - }, - }, - __hmrId: 'LambertMaterial', -}); diff --git a/src/materials/LambertMaterial.ts b/src/materials/LambertMaterial.ts new file mode 100644 index 0000000..084d212 --- /dev/null +++ b/src/materials/LambertMaterial.ts @@ -0,0 +1,19 @@ +import { defineComponent } from 'vue' +import { MeshLambertMaterial } from 'three' +import { bindProps, propsValues } from '../tools' +import Material, { wireframeProps } from './Material' + +export default defineComponent({ + extends: Material, + props: { + ...wireframeProps, + }, + methods: { + createMaterial() { + const material = new MeshLambertMaterial(propsValues(this.$props)) + bindProps(this, Object.keys(wireframeProps), material) + return material + }, + }, + __hmrId: 'LambertMaterial', +}) diff --git a/src/materials/MatcapMaterial.js b/src/materials/MatcapMaterial.js deleted file mode 100644 index 4e5161f..0000000 --- a/src/materials/MatcapMaterial.js +++ /dev/null @@ -1,25 +0,0 @@ -import { defineComponent } from 'vue'; -import { MeshMatcapMaterial, TextureLoader } from 'three'; -import { propsValues, getMatcapUrl } from '../tools'; -import Material from './Material'; - -export default defineComponent({ - extends: Material, - props: { - src: String, - name: String, - flatShading: Boolean, - }, - methods: { - createMaterial() { - const src = this.name ? getMatcapUrl(this.name) : this.src; - const opts = propsValues(this.$props, ['src', 'name']); - opts.matcap = new TextureLoader().load(src); - this.material = new MeshMatcapMaterial(opts); - }, - addWatchers() { - // TODO - }, - }, - __hmrId: 'MatcapMaterial', -}); diff --git a/src/materials/MatcapMaterial.ts b/src/materials/MatcapMaterial.ts new file mode 100644 index 0000000..4a12d39 --- /dev/null +++ b/src/materials/MatcapMaterial.ts @@ -0,0 +1,22 @@ +import { defineComponent } from 'vue' +import { MeshMatcapMaterial, TextureLoader } from 'three' +import { propsValues, getMatcapUrl } from '../tools' +import Material from './Material' + +export default defineComponent({ + extends: Material, + props: { + src: String, + name: { type: String, default: '0404E8_0404B5_0404CB_3333FC' }, + flatShading: Boolean, + }, + methods: { + createMaterial() { + const src = this.src ? this.src : getMatcapUrl(this.name) + const opts = propsValues(this.$props, ['src', 'name']) + opts.matcap = new TextureLoader().load(src) + return new MeshMatcapMaterial(opts) + }, + }, + __hmrId: 'MatcapMaterial', +}) diff --git a/src/materials/Material.js b/src/materials/Material.js deleted file mode 100644 index c2fbc77..0000000 --- a/src/materials/Material.js +++ /dev/null @@ -1,63 +0,0 @@ -import { defineComponent, watch } from 'vue'; -import { FrontSide } from 'three'; - -export default defineComponent({ - inject: ['three', 'mesh'], - props: { - color: { type: [String, Number], default: '#ffffff' }, - depthTest: { type: Boolean, default: true }, - depthWrite: { type: Boolean, default: true }, - fog: { type: Boolean, default: true }, - opacity: { type: Number, default: 1 }, - side: { type: Number, default: FrontSide }, - transparent: Boolean, - vertexColors: Boolean, - }, - provide() { - return { - material: this, - }; - }, - created() { - this.createMaterial(); - this.mesh.setMaterial(this.material); - - this._addWatchers(); - if (this.addWatchers) this.addWatchers(); - }, - unmounted() { - this.material.dispose(); - }, - methods: { - setProp(key, value, needsUpdate = false) { - this.material[key] = value; - this.material.needsUpdate = needsUpdate; - }, - setTexture(texture, key = 'map') { - this.setProp(key, texture, true); - }, - _addWatchers() { - ['color', 'depthTest', 'depthWrite', 'fog', 'opacity', 'side', 'transparent'].forEach(p => { - watch(() => this[p], () => { - if (p === 'color') { - this.material.color.set(this.color); - } else { - this.material[p] = this[p]; - } - }); - }); - }, - }, - render() { - return this.$slots.default ? this.$slots.default() : []; - }, - __hmrId: 'Material', -}); - -export const wireframeProps = { - wireframe: { type: Boolean, default: false }, - // not needed for WebGL - // wireframeLinecap: { type: String, default: 'round' }, - // wireframeLinejoin: { type: String, default: 'round' }, - wireframeLinewidth: { type: Number, default: 1 }, // not really useful -}; diff --git a/src/materials/Material.ts b/src/materials/Material.ts new file mode 100644 index 0000000..3af5d72 --- /dev/null +++ b/src/materials/Material.ts @@ -0,0 +1,85 @@ +import { defineComponent, watch } from 'vue' +import { FrontSide, Material, Texture } from 'three' + +interface MeshInterface { + setMaterial(material: Material): void +} + +interface MaterialInterface { + mesh?: MeshInterface + material?: Material + createMaterial?(): Material + addWatchers?(m: Material): void +} + +export default defineComponent({ + inject: ['three', 'mesh'], + props: { + color: { type: [String, Number], default: '#ffffff' }, + depthTest: { type: Boolean, default: true }, + depthWrite: { type: Boolean, default: true }, + fog: { type: Boolean, default: true }, + opacity: { type: Number, default: 1 }, + side: { type: Number, default: FrontSide }, + transparent: Boolean, + vertexColors: Boolean, + }, + setup(): MaterialInterface { + return {} + }, + provide() { + return { + material: this, + } + }, + created() { + if (!this.mesh) { + console.error('Missing parent Mesh') + return + } + + if (this.createMaterial) { + this.material = this.createMaterial() + this.mesh.setMaterial(this.material) + this._addWatchers() + this.addWatchers?.(this.material) + } + }, + unmounted() { + this.material?.dispose() + }, + methods: { + setProp(key: string, value: any, needsUpdate = false) { + if (this.material) { + this.material[key] = value + this.material.needsUpdate = needsUpdate + } + }, + setTexture(texture: Texture, key = 'map') { + this.setProp(key, texture, true) + }, + _addWatchers() { + ['color', 'depthTest', 'depthWrite', 'fog', 'opacity', 'side', 'transparent'].forEach(p => { + watch(() => this[p], (value) => { + if (p === 'color') { + this.material.color.set(value) + } else { + this.material[p] = value + } + }) + }) + }, + }, + render() { + return this.$slots.default ? this.$slots.default() : [] + }, + __hmrId: 'Material', +}) + +export const wireframeProps = { + wireframe: { type: Boolean, default: false }, + // not needed for WebGL + // wireframeLinecap: { type: String, default: 'round' }, + // wireframeLinejoin: { type: String, default: 'round' }, + wireframeLinewidth: { type: Number, default: 1 }, // not really useful +} diff --git a/src/materials/PhongMaterial.js b/src/materials/PhongMaterial.ts similarity index 52% rename from src/materials/PhongMaterial.js rename to src/materials/PhongMaterial.ts index 1e70b4f..2abcc87 100644 --- a/src/materials/PhongMaterial.js +++ b/src/materials/PhongMaterial.ts @@ -1,7 +1,7 @@ -import { defineComponent, watch } from 'vue'; -import { MeshPhongMaterial } from 'three'; -import { bindProps, propsValues } from '../tools'; -import Material, { wireframeProps } from './Material'; +import { defineComponent, watch } from 'vue' +import { MeshPhongMaterial } from 'three' +import { bindProps, propsValues } from '../tools' +import Material, { wireframeProps } from './Material' export default defineComponent({ extends: Material, @@ -16,21 +16,23 @@ export default defineComponent({ }, methods: { createMaterial() { - this.material = new MeshPhongMaterial(propsValues(this.$props)); - }, - addWatchers() { + const material = new MeshPhongMaterial(propsValues(this.$props)) + // TODO : handle flatShading ? - ['emissive', 'emissiveIntensity', 'reflectivity', 'shininess', 'specular'].forEach(p => { + const watchProps = ['emissive', 'emissiveIntensity', 'reflectivity', 'shininess', 'specular'] + watchProps.forEach(p => { watch(() => this[p], (value) => { if (p === 'emissive' || p === 'specular') { - this.material[p].set(value); + material[p].set(value) } else { - this.material[p] = value; + material[p] = value } - }); - }); - bindProps(this, Object.keys(wireframeProps), this.material); + }) + }) + bindProps(this, Object.keys(wireframeProps), material) + + return material }, }, __hmrId: 'PhongMaterial', -}); +}) diff --git a/src/materials/PhysicalMaterial.js b/src/materials/PhysicalMaterial.js deleted file mode 100644 index 24a4bff..0000000 --- a/src/materials/PhysicalMaterial.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineComponent } from 'vue'; -import { MeshPhysicalMaterial } from 'three'; -import { propsValues } from '../tools'; -import StandardMaterial from './StandardMaterial'; - -export default defineComponent({ - extends: StandardMaterial, - props: { - flatShading: Boolean, - }, - methods: { - createMaterial() { - this.material = new MeshPhysicalMaterial(propsValues(this.$props)); - }, - addWatchers() { - // TODO - }, - }, - __hmrId: 'PhysicalMaterial', -}); diff --git a/src/materials/PhysicalMaterial.ts b/src/materials/PhysicalMaterial.ts new file mode 100644 index 0000000..8f6681f --- /dev/null +++ b/src/materials/PhysicalMaterial.ts @@ -0,0 +1,17 @@ +import { defineComponent } from 'vue' +import { MeshPhysicalMaterial } from 'three' +import { propsValues } from '../tools' +import StandardMaterial from './StandardMaterial' + +export default defineComponent({ + extends: StandardMaterial, + props: { + flatShading: Boolean, + }, + methods: { + createMaterial() { + return new MeshPhysicalMaterial(propsValues(this.$props)) + }, + }, + __hmrId: 'PhysicalMaterial', +}) diff --git a/src/materials/StandardMaterial.js b/src/materials/StandardMaterial.ts similarity index 59% rename from src/materials/StandardMaterial.js rename to src/materials/StandardMaterial.ts index 99b5296..52f85b9 100644 --- a/src/materials/StandardMaterial.js +++ b/src/materials/StandardMaterial.ts @@ -1,7 +1,7 @@ -import { defineComponent, watch } from 'vue'; -import { MeshStandardMaterial } from 'three'; -import { bindProp, bindProps, propsValues } from '../tools'; -import Material, { wireframeProps } from './Material'; +import { defineComponent, watch } from 'vue' +import { MeshStandardMaterial } from 'three' +import { bindProp, bindProps, propsValues } from '../tools' +import Material, { wireframeProps } from './Material' const props = { aoMapIntensity: { type: Number, default: 1 }, @@ -13,11 +13,11 @@ const props = { envMapIntensity: { type: Number, default: 1 }, lightMapIntensity: { type: Number, default: 1 }, metalness: { type: Number, default: 0 }, - normalScale: { type: Object, default: { x: 1, y: 1 } }, + normalScale: { type: Object, default: () => ({ x: 1, y: 1 }) }, roughness: { type: Number, default: 1 }, refractionRatio: { type: Number, default: 0.98 }, flatShading: Boolean, -}; +} export default defineComponent({ extends: Material, @@ -27,23 +27,25 @@ export default defineComponent({ }, methods: { createMaterial() { - this.material = new MeshStandardMaterial(propsValues(this.$props, ['normalScale'])); - }, - addWatchers() { + const material = new MeshStandardMaterial(propsValues(this.$props, ['normalScale'])) + // TODO : use setProp, handle flatShading ? Object.keys(props).forEach(p => { - if (p === 'normalScale') return; + if (p === 'normalScale') return watch(() => this[p], (value) => { if (p === 'emissive') { - this.material[p].set(value); + material[p].set(value) } else { - this.material[p] = value; + material[p] = value } - }); - }); - bindProp(this, 'normalScale', this.material); - bindProps(this, Object.keys(wireframeProps), this.material); + }) + }) + + bindProp(this, 'normalScale', material) + bindProps(this, Object.keys(wireframeProps), material) + + return material }, }, __hmrId: 'StandardMaterial', -}); +}) diff --git a/src/materials/SubsurfaceScatteringShader.js b/src/materials/SubsurfaceScatteringShader.ts similarity index 75% rename from src/materials/SubsurfaceScatteringShader.js rename to src/materials/SubsurfaceScatteringShader.ts index a067846..d5a075c 100644 --- a/src/materials/SubsurfaceScatteringShader.js +++ b/src/materials/SubsurfaceScatteringShader.ts @@ -11,14 +11,14 @@ import { ShaderChunk, ShaderLib, UniformsUtils, -} from 'three'; +} from 'three' -function replaceAll(string, find, replace) { - return string.split(find).join(replace); +function replaceAll(string: string, find: string, replace: string) { + return string.split(find).join(replace) } -const meshphongFragHead = ShaderChunk.meshphong_frag.slice(0, ShaderChunk.meshphong_frag.indexOf('void main() {')); -const meshphongFragBody = ShaderChunk.meshphong_frag.slice(ShaderChunk.meshphong_frag.indexOf('void main() {')); +const meshphongFragHead = ShaderChunk.meshphong_frag.slice(0, ShaderChunk.meshphong_frag.indexOf('void main() {')) +const meshphongFragBody = ShaderChunk.meshphong_frag.slice(ShaderChunk.meshphong_frag.indexOf('void main() {')) const SubsurfaceScatteringShader = { @@ -45,23 +45,23 @@ const SubsurfaceScatteringShader = { ${meshphongFragHead} - uniform float thicknessPower; - uniform float thicknessScale; - uniform float thicknessDistortion; - uniform float thicknessAmbient; - uniform float thicknessAttenuation; - uniform vec3 thicknessColor; + uniform float thicknessPower + uniform float thicknessScale + uniform float thicknessDistortion + uniform float thicknessAmbient + uniform float thicknessAttenuation + uniform vec3 thicknessColor void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in GeometricContext geometry, inout ReflectedLight reflectedLight) { #ifdef USE_COLOR - vec3 thickness = vColor * thicknessColor; + vec3 thickness = vColor * thicknessColor #else - vec3 thickness = thicknessColor; + vec3 thickness = thicknessColor #endif - vec3 scatteringHalf = normalize(directLight.direction + (geometry.normal * thicknessDistortion)); - float scatteringDot = pow(saturate(dot(geometry.viewDir, -scatteringHalf)), thicknessPower) * thicknessScale; - vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * thickness; - reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color; + vec3 scatteringHalf = normalize(directLight.direction + (geometry.normal * thicknessDistortion)) + float scatteringDot = pow(saturate(dot(geometry.viewDir, -scatteringHalf)), thicknessPower) * thicknessScale + vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * thickness + reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color } ` + meshphongFragBody.replace( '#include ', @@ -69,13 +69,13 @@ const SubsurfaceScatteringShader = { ShaderChunk.lights_fragment_begin, 'RE_Direct( directLight, geometry, material, reflectedLight );', ` - RE_Direct( directLight, geometry, material, reflectedLight ); + RE_Direct( directLight, geometry, material, reflectedLight ) #if defined( SUBSURFACE ) && defined( USE_UV ) - RE_Direct_Scattering(directLight, vUv, geometry, reflectedLight); + RE_Direct_Scattering(directLight, vUv, geometry, reflectedLight) #endif ` ) ), -}; +} -export default SubsurfaceScatteringShader; +export default SubsurfaceScatteringShader diff --git a/src/materials/ToonMaterial.js b/src/materials/ToonMaterial.js deleted file mode 100644 index 719e20b..0000000 --- a/src/materials/ToonMaterial.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineComponent } from 'vue'; -import { MeshToonMaterial } from 'three'; -import { bindProps, propsValues } from '../tools'; -import Material, { wireframeProps } from './Material'; - -export default defineComponent({ - extends: Material, - props: { - ...wireframeProps, - }, - methods: { - createMaterial() { - this.material = new MeshToonMaterial(propsValues(this.$props)); - }, - addWatchers() { - bindProps(this, Object.keys(wireframeProps), this.material); - }, - }, - __hmrId: 'ToonMaterial', -}); diff --git a/src/materials/ToonMaterial.ts b/src/materials/ToonMaterial.ts new file mode 100644 index 0000000..25c4078 --- /dev/null +++ b/src/materials/ToonMaterial.ts @@ -0,0 +1,19 @@ +import { defineComponent } from 'vue' +import { MeshToonMaterial } from 'three' +import { bindProps, propsValues } from '../tools' +import Material, { wireframeProps } from './Material' + +export default defineComponent({ + extends: Material, + props: { + ...wireframeProps, + }, + methods: { + createMaterial() { + const material = new MeshToonMaterial(propsValues(this.$props)) + bindProps(this, Object.keys(wireframeProps), material) + return material + }, + }, + __hmrId: 'ToonMaterial', +}) diff --git a/src/materials/index.js b/src/materials/index.js deleted file mode 100644 index af0be33..0000000 --- a/src/materials/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export { default as BasicMaterial } from './BasicMaterial.js'; -export { default as LambertMaterial } from './LambertMaterial.js'; -export { default as MatcapMaterial } from './MatcapMaterial.js'; -export { default as PhongMaterial } from './PhongMaterial.js'; -export { default as PhysicalMaterial } from './PhysicalMaterial.js'; -export { default as ShaderMaterial } from './ShaderMaterial.js'; -export { default as StandardMaterial } from './StandardMaterial.js'; -export { default as SubSurfaceMaterial } from './SubSurfaceMaterial.js'; -export { default as ToonMaterial } from './ToonMaterial.js'; - -export { default as Texture } from './Texture.js'; -export { default as CubeTexture } from './CubeTexture.js'; diff --git a/src/materials/index.ts b/src/materials/index.ts new file mode 100644 index 0000000..80e6196 --- /dev/null +++ b/src/materials/index.ts @@ -0,0 +1,12 @@ +export { default as BasicMaterial } from './BasicMaterial' +export { default as LambertMaterial } from './LambertMaterial' +export { default as MatcapMaterial } from './MatcapMaterial' +export { default as PhongMaterial } from './PhongMaterial' +export { default as PhysicalMaterial } from './PhysicalMaterial' +export { default as ShaderMaterial } from './ShaderMaterial' +export { default as StandardMaterial } from './StandardMaterial' +export { default as SubSurfaceMaterial } from './SubSurfaceMaterial' +export { default as ToonMaterial } from './ToonMaterial' + +export { default as Texture } from './Texture' +export { default as CubeTexture } from './CubeTexture'