1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

Merge branch 'master' into vanruesc/postprocessing

This commit is contained in:
Kevin Levron 2021-05-05 00:44:35 +02:00
commit 9baccbd1d5
20 changed files with 138 additions and 326 deletions

View File

@ -39,6 +39,8 @@ I wanted to code something similar to *react-three-fiber* but for VueJS.
I started from scratch, I will rewrite some of my [WebGL demos](https://codepen.io/collection/AGZywR) to see if this little toy can do the job. I started from scratch, I will rewrite some of my [WebGL demos](https://codepen.io/collection/AGZywR) to see if this little toy can do the job.
Next version (*v0.3*, rewrited with Typescript) will be soon released !
*Trois* is a french word, it means *Three*. *Trois* is a french word, it means *Three*.
## Usage (CDN) ## Usage (CDN)
@ -80,6 +82,8 @@ Thanks to VueJS/ViteJS, **TroisJS use watchers and HMR to update ThreeJS objects
## Features ## Features
Take a look at examples : https://troisjs.github.io/
- [ ] Lights - [ ] Lights
- [x] AmbientLight - [x] AmbientLight
- [x] DirectionalLight - [x] DirectionalLight
@ -93,6 +97,7 @@ Thanks to VueJS/ViteJS, **TroisJS use watchers and HMR to update ThreeJS objects
- [x] Mapcap - [x] Mapcap
- [x] Phong - [x] Phong
- [x] Physical - [x] Physical
- [x] Shader
- [x] Standard - [x] Standard
- [x] SubSurface - [x] SubSurface
- [x] Toon - [x] Toon

View File

@ -1,6 +1,6 @@
import { Object3D, Scene } from 'three' import { Object3D, Scene } from 'three'
import { ComponentPublicInstance, defineComponent, PropType, watch } from 'vue' import { ComponentPublicInstance, defineComponent, PropType, watch } from 'vue'
import { bindOptions, bindProp } from '../tools' import { bindObjectProp, bindProp } from '../tools'
import { RendererInjectionKey, RendererInterface } from './Renderer' import { RendererInjectionKey, RendererInterface } from './Renderer'
import { SceneInjectionKey } from './Scene' import { SceneInjectionKey } from './Scene'
@ -83,7 +83,7 @@ export default defineComponent({
bindProp(this, 'userData', o3d.userData) bindProp(this, 'userData', o3d.userData)
bindProp(this, 'visible', o3d) bindProp(this, 'visible', o3d)
bindOptions(o3d, this.props) bindObjectProp(this, 'props', o3d)
this.$emit('created', o3d) this.$emit('created', o3d)

View File

@ -20,6 +20,7 @@ export default defineComponent({
onPointerLeave: { type: Function as PropType<PointerIntersectCallbackType>, default: emptyCallBack }, onPointerLeave: { type: Function as PropType<PointerIntersectCallbackType>, default: emptyCallBack },
onClick: { type: Function as PropType<PointerIntersectCallbackType>, default: emptyCallBack }, onClick: { type: Function as PropType<PointerIntersectCallbackType>, default: emptyCallBack },
intersectMode: { type: String, default: 'move' }, intersectMode: { type: String, default: 'move' },
intersectRecursive: { type: Boolean, default: false },
}, },
setup(): RaycasterSetupInterface { setup(): RaycasterSetupInterface {
const renderer = inject(RendererInjectionKey) const renderer = inject(RendererInjectionKey)
@ -39,6 +40,7 @@ export default defineComponent({
camera: renderer.camera, camera: renderer.camera,
domElement: renderer.canvas, domElement: renderer.canvas,
intersectObjects: this.getIntersectObjects(), intersectObjects: this.getIntersectObjects(),
intersectRecursive: this.intersectRecursive,
onIntersectEnter: this.onPointerEnter, onIntersectEnter: this.onPointerEnter,
onIntersectOver: this.onPointerOver, onIntersectOver: this.onPointerOver,
onIntersectMove: this.onPointerMove, onIntersectMove: this.onPointerMove,

View File

@ -1,8 +1,8 @@
/* eslint-disable no-use-before-define */ /* eslint-disable no-use-before-define */
import { Camera, NoToneMapping, PCFShadowMap, Scene, WebGLRenderer } from 'three' import { Camera, Scene, WebGLRenderer } from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { ComponentPublicInstance, defineComponent, InjectionKey, PropType } from 'vue' import { ComponentPublicInstance, defineComponent, InjectionKey, PropType } from 'vue'
import { bindOptions } from '../tools' import { bindObjectProp } from '../tools'
import { PointerPublicConfigInterface } from './usePointer' import { PointerPublicConfigInterface } from './usePointer'
import useThree, { SizeInterface, ThreeConfigInterface, ThreeInterface } from './useThree' import useThree, { SizeInterface, ThreeConfigInterface, ThreeInterface } from './useThree'
@ -136,7 +136,7 @@ export default defineComponent({
if (props.height) config.height = parseInt(props.height) if (props.height) config.height = parseInt(props.height)
const three = useThree(config) const three = useThree(config)
bindOptions(three.renderer, props.props) bindObjectProp(props, 'props', three.renderer)
const renderFn: {(): void} = () => {} const renderFn: {(): void} = () => {}
@ -196,7 +196,6 @@ export default defineComponent({
if (this.shadow) { if (this.shadow) {
this.renderer.shadowMap.enabled = true this.renderer.shadowMap.enabled = true
this.renderer.shadowMap.type = this.shadowType
} }
this.renderFn = this.three.composer ? this.three.renderC : this.three.render this.renderFn = this.three.composer ? this.three.renderC : this.three.render

View File

@ -22,6 +22,7 @@ export type IntersectObject = Mesh | InstancedMesh
export interface PointerPublicConfigInterface { export interface PointerPublicConfigInterface {
intersectMode?: 'frame' intersectMode?: 'frame'
intersectRecursive?: boolean
touch?: boolean touch?: boolean
resetOnEnd?: boolean resetOnEnd?: boolean
resetPosition?: Vector2 resetPosition?: Vector2
@ -59,6 +60,7 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
camera, camera,
domElement, domElement,
intersectObjects, intersectObjects,
intersectRecursive = false,
touch = true, touch = true,
resetOnEnd = false, resetOnEnd = false,
resetPosition = new Vector2(0, 0), resetPosition = new Vector2(0, 0),
@ -119,7 +121,7 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
function intersect() { function intersect() {
if (intersectObjects.length) { if (intersectObjects.length) {
const intersects = raycaster.intersect(positionN, intersectObjects) const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
const offObjects: IntersectObject[] = [...intersectObjects] const offObjects: IntersectObject[] = [...intersectObjects]
const iMeshes: InstancedMesh[] = [] const iMeshes: InstancedMesh[] = []

View File

@ -4,7 +4,7 @@ import { IntersectObject } from './usePointer'
export interface RaycasterInterface { export interface RaycasterInterface {
position: Vector3 position: Vector3
updatePosition(coords: Vector2): void updatePosition(coords: Vector2): void
intersect(coords: Vector2, objects: IntersectObject[]): Intersection[], intersect(coords: Vector2, objects: IntersectObject[], recursive: boolean): Intersection[],
} }
export interface RaycasterConfigInterface { export interface RaycasterConfigInterface {
@ -28,9 +28,9 @@ export default function useRaycaster(options: RaycasterConfigInterface): Raycast
raycaster.ray.intersectPlane(plane, position) raycaster.ray.intersectPlane(plane, position)
} }
const intersect = (coords: Vector2, objects: IntersectObject[]) => { const intersect = (coords: Vector2, objects: IntersectObject[], recursive = false) => {
raycaster.setFromCamera(coords, camera) raycaster.setFromCamera(coords, camera)
return raycaster.intersectObjects(objects) return raycaster.intersectObjects(objects, recursive)
} }
return { return {

View File

@ -1,19 +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() {
const material = new MeshBasicMaterial(propsValues(this.$props))
bindProps(this, Object.keys(wireframeProps), material)
return material
},
},
__hmrId: 'BasicMaterial',
})

View File

@ -1,19 +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() {
const material = new MeshLambertMaterial(propsValues(this.$props))
bindProps(this, Object.keys(wireframeProps), material)
return material
},
},
__hmrId: 'LambertMaterial',
})

View File

@ -1,22 +1,17 @@
import { defineComponent } from 'vue'
import { MeshMatcapMaterial, TextureLoader } from 'three' import { MeshMatcapMaterial, TextureLoader } from 'three'
import { propsValues, getMatcapUrl } from '../tools' import { propsValues, getMatcapUrl } from '../tools'
import Material from './Material' import { materialComponent } from './Material'
export default defineComponent({ export default materialComponent(
extends: Material, 'MatcapMaterial',
props: { {
src: String, src: String,
name: { type: String, default: '0404E8_0404B5_0404CB_3333FC' }, name: { type: String, default: '0404E8_0404B5_0404CB_3333FC' },
flatShading: Boolean,
}, },
methods: { (opts) => {
createMaterial() { const src = opts.src ? opts.src : getMatcapUrl(opts.name)
const src = this.src ? this.src : getMatcapUrl(this.name) const props = propsValues(opts, ['src', 'name'])
const opts = propsValues(this.$props, ['src', 'name']) props.matcap = new TextureLoader().load(src)
opts.matcap = new TextureLoader().load(src) return new MeshMatcapMaterial(props)
return new MeshMatcapMaterial(opts) }
}, )
},
__hmrId: 'MatcapMaterial',
})

View File

@ -1,7 +1,7 @@
import { ComponentPublicInstance, defineComponent, InjectionKey, PropType, watch } from 'vue' import { ComponentPropsOptions, ComponentPublicInstance, defineComponent, InjectionKey, watch } from 'vue'
import { FrontSide, Material, NormalBlending, Texture } from 'three' import { Color, Material, MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshToonMaterial, PointsMaterial as TPointsMaterial, Texture } from 'three'
import { MeshInjectionKey, MeshInterface } from '../meshes/Mesh' import { MeshInjectionKey, MeshInterface } from '../meshes/Mesh'
import { bindOptions } from '../tools' import { bindObjectProp } from '../tools'
export interface MaterialSetupInterface { export interface MaterialSetupInterface {
mesh?: MeshInterface mesh?: MeshInterface
@ -10,7 +10,6 @@ export interface MaterialSetupInterface {
} }
export interface MaterialInterface extends MaterialSetupInterface { export interface MaterialInterface extends MaterialSetupInterface {
setProp(key: string, value: unknown, needsUpdate: boolean): void
setTexture(texture: Texture | null, key: string): void setTexture(texture: Texture | null, key: string): void
} }
@ -18,24 +17,14 @@ export interface MaterialPublicInterface extends ComponentPublicInstance, Materi
export const MaterialInjectionKey: InjectionKey<MaterialPublicInterface> = Symbol('Material') export const MaterialInjectionKey: InjectionKey<MaterialPublicInterface> = Symbol('Material')
export default defineComponent({ const BaseMaterial = defineComponent({
// inject for sub components props: {
color: { type: String, default: '#ffffff' },
props: { type: Object, default: () => ({}) },
},
inject: { inject: {
mesh: MeshInjectionKey as symbol, mesh: MeshInjectionKey as symbol,
}, },
props: {
color: { type: [String, Number] as PropType<string | number>, default: '#ffffff' },
blending: { type: Number, default: NormalBlending },
alphaTest: { type: Number, default: 0 },
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,
props: { type: Object, default: () => ({}) },
},
setup(): MaterialSetupInterface { setup(): MaterialSetupInterface {
return {} return {}
}, },
@ -51,39 +40,28 @@ export default defineComponent({
} }
if (this.createMaterial) { if (this.createMaterial) {
this.material = this.createMaterial() const material = this.material = this.createMaterial()
bindOptions(this.material, this.props) // @ts-ignore
this.mesh.setMaterial(this.material) watch(() => this.color, (value) => { material.color.set(value) })
this.addWatchers() bindObjectProp(this, 'props', material, false, this.setProp)
this.mesh.setMaterial(material)
} }
}, },
unmounted() { unmounted() {
this.material?.dispose() this.material?.dispose()
}, },
methods: { methods: {
setProp(key: string, value: any, needsUpdate = false) { getMaterialParams() {
if (this.material) { return { color: this.color, ...this.props }
// @ts-ignore },
this.material[key] = value setProp(material: any, key: string, value: any, needsUpdate = false) {
this.material.needsUpdate = needsUpdate const dstVal = material[key]
} if (dstVal instanceof Color) dstVal.set(value)
else material[key] = value
material.needsUpdate = needsUpdate
}, },
setTexture(texture: Texture | null, key = 'map') { setTexture(texture: Texture | null, key = 'map') {
this.setProp(key, texture, true) this.setProp(this, key, texture, true)
},
addWatchers() {
['color', 'alphaTest', 'blending', 'depthTest', 'depthWrite', 'fog', 'opacity', 'side', 'transparent'].forEach(p => {
// @ts-ignore
watch(() => this[p], (value) => {
if (p === 'color') {
// @ts-ignore
this.material.color.set(value)
} else {
// @ts-ignore
this.material[p] = value
}
})
})
}, },
}, },
render() { render() {
@ -92,10 +70,30 @@ export default defineComponent({
__hmrId: 'Material', __hmrId: 'Material',
}) })
export const wireframeProps = { export default BaseMaterial
wireframe: { type: Boolean, default: false },
// not needed for WebGL // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
// wireframeLinecap: { type: String, default: 'round' }, export function materialComponent<P extends Readonly<ComponentPropsOptions>>(
// wireframeLinejoin: { type: String, default: 'round' }, name: string,
wireframeLinewidth: { type: Number, default: 1 }, // not really useful props: P,
createMaterial: {(opts: any): Material}
) {
return defineComponent({
name,
extends: BaseMaterial,
props,
methods: {
createMaterial() {
return createMaterial(this.getMaterialParams())
},
},
})
} }
export const BasicMaterial = materialComponent('BasicMaterial', {}, (opts) => new MeshBasicMaterial(opts))
export const LambertMaterial = materialComponent('LambertMaterial', {}, (opts) => new MeshLambertMaterial(opts))
export const PhongMaterial = materialComponent('PhongMaterial', {}, (opts) => new MeshPhongMaterial(opts))
export const PhysicalMaterial = materialComponent('PhysicalMaterial', {}, (opts) => new MeshPhysicalMaterial(opts))
export const PointsMaterial = materialComponent('PointsMaterial', {}, (opts) => new TPointsMaterial(opts))
export const StandardMaterial = materialComponent('StandardMaterial', {}, (opts) => new MeshStandardMaterial(opts))
export const ToonMaterial = materialComponent('ToonMaterial', {}, (opts) => new MeshToonMaterial(opts))

View File

@ -1,40 +0,0 @@
import { defineComponent, watch } from 'vue'
import { MeshPhongMaterial } from 'three'
import { bindProps, propsValues } from '../tools'
import Material, { wireframeProps } from './Material'
export default defineComponent({
extends: Material,
props: {
emissive: { type: [Number, String], default: 0 },
emissiveIntensity: { type: Number, default: 1 },
reflectivity: { type: Number, default: 1 },
shininess: { type: Number, default: 30 },
specular: { type: [String, Number], default: 0x111111 },
flatShading: Boolean,
...wireframeProps,
},
methods: {
createMaterial() {
const material = new MeshPhongMaterial(propsValues(this.$props))
// TODO : handle flatShading ?
const watchProps = ['emissive', 'emissiveIntensity', 'reflectivity', 'shininess', 'specular']
watchProps.forEach(p => {
// @ts-ignore
watch(() => this[p], (value) => {
if (p === 'emissive' || p === 'specular') {
material[p].set(value)
} else {
// @ts-ignore
material[p] = value
}
})
})
bindProps(this, Object.keys(wireframeProps), material)
return material
},
},
__hmrId: 'PhongMaterial',
})

View File

@ -1,17 +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() {
return new MeshPhysicalMaterial(propsValues(this.$props))
},
},
__hmrId: 'PhysicalMaterial',
})

View File

@ -1,19 +0,0 @@
import { defineComponent } from 'vue'
import { PointsMaterial } from 'three'
import { propsValues } from '../tools'
import Material from './Material'
export default defineComponent({
extends: Material,
props: {
size: { type: Number, default: 10 },
sizeAttenuation: { type: Boolean, default: true },
},
methods: {
createMaterial() {
const material = new PointsMaterial(propsValues(this.$props))
return material
},
},
__hmrId: 'PointsMaterial',
})

View File

@ -1,6 +1,5 @@
import { defineComponent, watch } from 'vue'
import { ShaderMaterial } from 'three' import { ShaderMaterial } from 'three'
import Material from './Material' import { materialComponent } from './Material'
import { propsValues } from '../tools' import { propsValues } from '../tools'
const defaultVertexShader = ` const defaultVertexShader = `
@ -18,24 +17,14 @@ const defaultFragmentShader = `
} }
` `
export default defineComponent({ export default materialComponent(
extends: Material, 'ShaderMaterial',
props: { {
uniforms: { type: Object, default: () => ({}) }, props: { type: Object, default: () => ({
vertexShader: { type: String, default: defaultVertexShader }, uniforms: {},
fragmentShader: { type: String, default: defaultFragmentShader }, vertexShader: defaultVertexShader,
fragmentShader: defaultFragmentShader,
}) },
}, },
methods: { (opts) => new ShaderMaterial(propsValues(opts, ['color']))
createMaterial() { )
const material = new ShaderMaterial(propsValues(this.$props, ['color']));
['vertexShader', 'fragmentShader'].forEach(p => {
// @ts-ignore
watch(() => this[p], (value) => { material[p] = value; material.needsUpdate = true })
})
return material
},
},
__hmrId: 'ShaderMaterial',
})

View File

@ -1,54 +0,0 @@
import { defineComponent, PropType, watch } from 'vue'
import { MeshStandardMaterial } from 'three'
import { bindProp, bindProps, propsValues } from '../tools'
import Material, { wireframeProps } from './Material'
import { Vector2PropInterface } from '../core/Object3D'
const props = {
aoMapIntensity: { type: Number, default: 1 },
bumpScale: { type: Number, default: 1 },
displacementBias: { type: Number, default: 0 },
displacementScale: { type: Number, default: 1 },
emissive: { type: [String, Number] as PropType<string | number>, default: 0 },
emissiveIntensity: { type: Number, default: 1 },
envMapIntensity: { type: Number, default: 1 },
lightMapIntensity: { type: Number, default: 1 },
metalness: { type: Number, default: 0 },
normalScale: { type: Object as PropType<Vector2PropInterface>, default: () => ({ x: 1, y: 1 }) },
roughness: { type: Number, default: 1 },
refractionRatio: { type: Number, default: 0.98 },
flatShading: Boolean,
} as const
export default defineComponent({
extends: Material,
props: {
...props,
...wireframeProps,
},
methods: {
createMaterial() {
const material = new MeshStandardMaterial(propsValues(this.$props, ['normalScale']))
// TODO : use setProp, handle flatShading ?
Object.keys(props).forEach(p => {
if (p === 'normalScale') return
// @ts-ignore
watch(() => this[p], (value) => {
if (p === 'emissive') {
material[p].set(value)
} else {
// @ts-ignore
material[p] = value
}
})
})
bindProp(this, 'normalScale', material)
bindProps(this, Object.keys(wireframeProps), material)
return material
},
},
__hmrId: 'StandardMaterial',
})

View File

@ -2,47 +2,50 @@ import { defineComponent, PropType } from 'vue'
import { Color, ShaderMaterial, UniformsUtils } from 'three' import { Color, ShaderMaterial, UniformsUtils } from 'three'
import SubsurfaceScatteringShader from './SubsurfaceScatteringShader' import SubsurfaceScatteringShader from './SubsurfaceScatteringShader'
import Material from './Material' import Material from './Material'
// import { bindProps, propsValues } from '../tools' import { bindObjectProp } from '../tools'
const props = { export interface SubSurfaceMaterialUniformsInterface {
color: { type: [String, Number] as PropType<string | number>, default: '#ffffff' }, diffuse?: string | number
thicknessColor: { type: [String, Number] as PropType<string | number>, default: '#ffffff' }, thicknessColor?: string | number
thicknessDistortion: { type: Number, default: 0.4 }, thicknessDistortion?: number
thicknessAmbient: { type: Number, default: 0.01 }, thicknessAmbient?: number
thicknessAttenuation: { type: Number, default: 0.7 }, thicknessAttenuation?: number
thicknessPower: { type: Number, default: 2 }, thicknessPower?: number
thicknessScale: { type: Number, default: 4 }, thicknessScale?: number
} as const }
export default defineComponent({ export default defineComponent({
extends: Material, extends: Material,
props, props: {
uniforms: { type: Object as PropType<SubSurfaceMaterialUniformsInterface>, default: () => ({
diffuse: '#ffffff',
thicknessColor: '#ffffff',
thicknessDistortion: 0.4,
thicknessAmbient: 0.01,
thicknessAttenuation: 0.7,
thicknessPower: 2,
thicknessScale: 4,
}) },
},
methods: { methods: {
createMaterial() { createMaterial() {
const params = SubsurfaceScatteringShader const params = SubsurfaceScatteringShader
const uniforms = UniformsUtils.clone(params.uniforms) const uniforms = UniformsUtils.clone(params.uniforms)
Object.keys(props).forEach((key) => { bindObjectProp(this, 'uniforms', uniforms, true, (dst: any, key: string, value: any) => {
// @ts-ignore const dstVal = dst[key].value
const value = this[key] if (dstVal instanceof Color) dstVal.set(value)
let _key = key, _value = value else dst[key].value = value
if (['color', 'thicknessColor'].includes(key)) {
if (key === 'color') _key = 'diffuse'
_value = new Color(value)
}
uniforms[_key].value = _value
}) })
const material = new ShaderMaterial({ const material = new ShaderMaterial({
...params, ...params,
uniforms,
lights: true, lights: true,
transparent: this.transparent, ...this.props,
vertexColors: this.vertexColors, uniforms,
}) })
return material return material
}, },
}, },
__hmrId: 'SubSurfaceMaterial',
}) })

View File

@ -1,6 +1,6 @@
import { defineComponent, PropType, watch } from 'vue' import { defineComponent, PropType, watch } from 'vue'
import { ShaderMaterial, Texture, TextureLoader } from 'three' import { ShaderMaterial, Texture, TextureLoader } from 'three'
import { bindOptions } from '../tools' import { bindObjectProp } from '../tools'
import { MaterialInjectionKey, MaterialInterface } from './Material' import { MaterialInjectionKey, MaterialInterface } from './Material'
export interface TexureInterface { export interface TexureInterface {
@ -40,7 +40,7 @@ export default defineComponent({
initTexture() { initTexture() {
if (!this.texture) return if (!this.texture) return
bindOptions(this.texture, this.props) bindObjectProp(this, 'props', this.texture)
if (!this.material) return if (!this.material) return
this.material.setTexture(this.texture, this.name) this.material.setTexture(this.texture, this.name)

View File

@ -1,19 +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() {
const material = new MeshToonMaterial(propsValues(this.$props))
bindProps(this, Object.keys(wireframeProps), material)
return material
},
},
__hmrId: 'ToonMaterial',
})

View File

@ -1,17 +1,9 @@
export { default as Material, MaterialInjectionKey } from './Material' export { default as Material, BasicMaterial, LambertMaterial, PhongMaterial, PhysicalMaterial, PointsMaterial, StandardMaterial, ToonMaterial, MaterialInjectionKey } from './Material'
export { default as BasicMaterial } from './BasicMaterial'
export { default as LambertMaterial } from './LambertMaterial'
export { default as MatcapMaterial } from './MatcapMaterial' 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 ShaderMaterial } from './ShaderMaterial'
export { default as StandardMaterial } from './StandardMaterial'
export { default as SubSurfaceMaterial } from './SubSurfaceMaterial' export { default as SubSurfaceMaterial } from './SubSurfaceMaterial'
export { default as ToonMaterial } from './ToonMaterial'
export { default as Texture } from './Texture' export { default as Texture } from './Texture'
export { default as CubeTexture } from './CubeTexture' export { default as CubeTexture } from './CubeTexture'
export { default as PointsMaterial } from './PointsMaterial'
export type { MaterialPublicInterface } from './Material' export type { MaterialPublicInterface } from './Material'

View File

@ -1,16 +1,30 @@
import { toRef, watch } from 'vue' import { toRef, watch, WatchStopHandle } from 'vue'
export function applyOptions(dst: any, options: Record<string, unknown>): void { type OptionSetter = (dst: any, key: string, value: any) => void
export function applyObjectProps(
dst: any,
options: Record<string, unknown>,
setter?: OptionSetter
): void {
if (options instanceof Object) { if (options instanceof Object) {
Object.entries(options).forEach(([key, value]) => { Object.entries(options).forEach(([key, value]) => {
dst[key] = value if (setter) setter(dst, key, value)
else dst[key] = value
}) })
} }
} }
export function bindOptions(dst: any, options: Record<string, unknown>): void { export function bindObjectProp(
applyOptions(dst, options) src: any,
watch(() => options, (value) => { applyOptions(dst, value) }, { deep: true }) prop: string,
dst: any,
apply = true,
setter?: OptionSetter
): WatchStopHandle {
if (apply) applyObjectProps(dst, src[prop], setter)
const r = toRef(src, prop)
return watch(r, (value) => { applyObjectProps(dst, value, setter) })
} }
export function setFromProp(o: Record<string, unknown>, prop: Record<string, unknown>): void { export function setFromProp(o: Record<string, unknown>, prop: Record<string, unknown>): void {