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:
commit
9baccbd1d5
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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[] = []
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
|
||||||
|
@ -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))
|
||||||
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
|
||||||
|
@ -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',
|
|
||||||
})
|
|
@ -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',
|
|
||||||
})
|
})
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
|
||||||
})
|
|
@ -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'
|
||||||
|
26
src/tools.ts
26
src/tools.ts
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user