diff --git a/CHANGELOG.md b/CHANGELOG.md index ee5d851..3c1a040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Change Log +## Version 0.13.1 +- Fully support MeshStandardMaterial's properties such as envMap on particles. ## version 0.13.0 - Fix the Rotation3dOverLife behavior doesn't work on mesh particle bug diff --git a/README.md b/README.md index 0656306..2c0ae12 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # three.quarks -![npm](https://img.shields.io/npm/v/three.quarks.svg) -![Github Star](https://img.shields.io/github/stars/Alchemist0823/three.quarks.svg?style=social) -[![Discord](https://img.shields.io/discord/1042342883056963664?color=%235865f2&label=Discord)](https://discord.gg/5Tv3kJCrQZ) +[![][npm]][npm-url] +[![][github-star]][github-url] +[![][build-size]][build-size-url] +[![][npm-downloads]][npmtrends-url] +[![][discord]][discord-url] [**three.quarks**](https://quarks.art/) is a high-performance javascript particle system based visual effect library for threejs written in modern **TypeScript**. @@ -196,3 +198,13 @@ If you want the best performance please consider `yarn link` [https://github.com This version of three.js performs much better than official release, because it avoids unnecessary `getProgramCachedKey()` calls and material updates. +[github-star]: https://img.shields.io/github/stars/Alchemist0823/three.quarks.svg?style=social +[github-url]: https://github.com/Alchemist0823/three.quarks +[npm]: https://img.shields.io/npm/v/three.quarks +[npm-url]: https://www.npmjs.com/package/three.quarks +[build-size]: https://badgen.net/bundlephobia/minzip/three.quarks +[build-size-url]: https://bundlephobia.com/result?p=three.quarks +[npm-downloads]: https://img.shields.io/npm/dw/three.quarks +[npmtrends-url]: https://www.npmtrends.com/three.quarks +[discord]: https://img.shields.io/discord/1042342883056963664 +[discord-url]: https://discord.gg/5Tv3kJCrQZ \ No newline at end of file diff --git a/examples/index.html b/examples/index.html index 445cf2f..504d0fb 100644 --- a/examples/index.html +++ b/examples/index.html @@ -199,6 +199,15 @@ window.addEventListener('resize', onResize, false); demoIndex = 0; + if (window.location.hash) { + let hash = window.location.hash.substring(1); + for (let i = 0; i < demos.length; i++) { + if (demos[i].name === hash) { + demoIndex = i; + break; + } + } + } demo = new demos[demoIndex](camera, renderer); document.querySelector("#demo-name").innerText = demo.name; scene = demo.initScene(); @@ -237,6 +246,7 @@ if (demoIndex >= demos.length) { demoIndex = 0; } + window.location.hash = '#' + demos[demoIndex].name; if (demo && demo.deinitScene) { demo.deinitScene(); } diff --git a/examples/meshMaterialDemo.js b/examples/meshMaterialDemo.js index 06edebb..aba6662 100644 --- a/examples/meshMaterialDemo.js +++ b/examples/meshMaterialDemo.js @@ -1,4 +1,14 @@ -import {Group, MeshStandardMaterial, BoxGeometry, Vector4, Color} from 'three'; +import { + Group, + MeshStandardMaterial, + BoxGeometry, + Vector4, + Color, + CubeTextureLoader, + SphereGeometry, + Mesh, + CapsuleGeometry, +} from 'three'; import { BatchedParticleRenderer, RandomQuatGenerator, @@ -14,20 +24,25 @@ import { import {Demo} from './demo.js'; export class MeshMaterialDemo extends Demo { - name = 'Mesh Standard Material'; + name = 'Mesh Standard Material with Environment Map'; initScene() { super.initScene(); this.batchRenderer = new BatchedParticleRenderer(); this.scene.add(this.batchRenderer); - const geo = new BoxGeometry(1.0, 2.0, 1.0); + const loader = new CubeTextureLoader(); + loader.setPath( 'textures/cube/' ); + let textureCube = loader.load( [ 'posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg' ] ); + + const geo = new CapsuleGeometry(1.0, 3.0); const mat = new MeshStandardMaterial({ - color: new Color(1.0, 1.0, 1.0), - roughness: 1.0, - metalness: 0.5, + color: new Color(0.5, 0.5, 0.5), + roughness: 0.2, + metalness: 1.0, + envMap: textureCube, + envMapIntensity: 1.0, }); - //this.scene.add(new Mesh(geo, mat)); let ps = new ParticleSystem({ duration: 1, @@ -37,7 +52,7 @@ export class MeshMaterialDemo extends Demo { startLife: new IntervalValue(2.0, 3.0), startSpeed: new ConstantValue(1), startSize: new ConstantValue(0.1), - startColor: new ConstantColor(new Vector4(1, 0.585716, 0.1691176, 1)), + startColor: new ConstantColor(new Vector4(1, 1, 1, 1)), startRotation: new RandomQuatGenerator(), worldSpace: true, @@ -52,7 +67,7 @@ export class MeshMaterialDemo extends Demo { vTileCount: 10, renderOrder: 0, }); - ps.addBehavior(new Rotation3DOverLife(new EulerGenerator(new ConstantValue(0), new IntervalValue(0, Math.PI), new ConstantValue(0)))); + ps.addBehavior(new Rotation3DOverLife(new EulerGenerator(new IntervalValue(0, Math.PI), new ConstantValue(0), new ConstantValue(0)))); this.batchRenderer.addSystem(ps); this.scene.add(ps.emitter); diff --git a/examples/textures/cube/negx.jpg b/examples/textures/cube/negx.jpg new file mode 100644 index 0000000..992fde5 Binary files /dev/null and b/examples/textures/cube/negx.jpg differ diff --git a/examples/textures/cube/negy.jpg b/examples/textures/cube/negy.jpg new file mode 100644 index 0000000..a51a38d Binary files /dev/null and b/examples/textures/cube/negy.jpg differ diff --git a/examples/textures/cube/negz.jpg b/examples/textures/cube/negz.jpg new file mode 100644 index 0000000..c463f0d Binary files /dev/null and b/examples/textures/cube/negz.jpg differ diff --git a/examples/textures/cube/posx.jpg b/examples/textures/cube/posx.jpg new file mode 100644 index 0000000..106d3a6 Binary files /dev/null and b/examples/textures/cube/posx.jpg differ diff --git a/examples/textures/cube/posy.jpg b/examples/textures/cube/posy.jpg new file mode 100644 index 0000000..1ea42cd Binary files /dev/null and b/examples/textures/cube/posy.jpg differ diff --git a/examples/textures/cube/posz.jpg b/examples/textures/cube/posz.jpg new file mode 100644 index 0000000..69463d0 Binary files /dev/null and b/examples/textures/cube/posz.jpg differ diff --git a/package.json b/package.json index 379f981..b165d1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "three.quarks", - "version": "0.13.0", + "version": "0.13.1", "description": "A General-Purpose Particle System for three.js", "type": "module", "types": "./dist/types/index.d.ts", diff --git a/src/SpriteBatch.ts b/src/SpriteBatch.ts index 07cd5bb..300abb1 100644 --- a/src/SpriteBatch.ts +++ b/src/SpriteBatch.ts @@ -16,6 +16,7 @@ import { Vector4, Scene, PerspectiveCamera, + MeshPhysicalMaterial, } from 'three'; import {SpriteParticle} from './Particle'; @@ -28,6 +29,8 @@ import stretched_bb_particle_vert from './shaders/stretched_bb_particle_vert.gls import {getMaterialUVChannelName} from './util/ThreeUtil'; import {StretchedBillBoardSettings, VFXBatchSettings} from './BatchedRenderer'; import {RenderMode, VFXBatch} from './VFXBatch'; +import {IUniform} from 'three/src/renderers/shaders/UniformsLib'; +import {ParticleMeshPhysicsMaterial, ParticleMeshStandardMaterial} from './materials/ParticleMaterials'; const UP = new Vector3(0, 0, 1); @@ -110,59 +113,13 @@ export class SpriteBatch extends VFXBatch { rebuildMaterial() { this.layers.mask = this.settings.layers.mask; - let uniforms: {[a: string]: Uniform}; + let uniforms: {[a: string]: Uniform} = {}; const defines: {[b: string]: string} = {}; if ( - this.settings.material.type === 'MeshStandardMaterial' || - this.settings.material.type === 'MeshPhysicalMaterial' + this.settings.material.type !== 'MeshStandardMaterial' && + this.settings.material.type !== 'MeshPhysicalMaterial' ) { - const mat = this.settings.material as MeshStandardMaterial; - uniforms = UniformsUtils.merge([ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: {value: /*@__PURE__*/ new Color(0x000000)}, - roughness: {value: 1.0}, - metalness: {value: 0.0}, - envMapIntensity: {value: 1}, // temporary - }, - ]) as {[a: string]: Uniform}; - uniforms['diffuse'].value = mat.color; - uniforms['opacity'].value = mat.opacity; - uniforms['emissive'].value = mat.emissive; - uniforms['roughness'].value = mat.roughness; - uniforms['metalness'].value = mat.metalness; - - if (mat.envMap) { - uniforms['envMap'].value = mat.envMap; - uniforms['envMapIntensity'].value = mat.envMapIntensity; - } - if (mat.normalMap) { - uniforms['normalMap'].value = mat.normalMap; - uniforms['normalScale'].value = mat.normalScale; - } - if (mat.roughnessMap) { - uniforms['roughnessMap'].value = mat.roughnessMap; - } - if (mat.metalnessMap) { - uniforms['metalnessMap'].value = mat.metalnessMap; - } - if (mat.map) { - uniforms['map'] = new Uniform(mat.map); - } - } else { - uniforms = {}; uniforms['map'] = new Uniform((this.settings.material as any).map); } @@ -249,18 +206,38 @@ export class SpriteBatch extends VFXBatch { } else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) { defines['HORIZONTAL'] = ''; } - this.material = new ShaderMaterial({ - uniforms: uniforms, - defines: defines, - vertexShader: vertexShader, - fragmentShader: fragmentShader, - transparent: this.settings.material.transparent, - depthWrite: !this.settings.material.transparent, - blending: this.settings.material.blending, - side: this.settings.material.side, - alphaTest: this.settings.material.alphaTest, - lights: needLights, - }); + + let specialMats = false; + if (this.settings.renderMode === RenderMode.Mesh) { + //const mat = this.settings.material as MeshStandardMaterial; + if (this.settings.material.type === 'MeshStandardMaterial') { + this.material = new ParticleMeshStandardMaterial({}); + this.material.copy(this.settings.material as MeshStandardMaterial); + (this.material as any).uniforms = uniforms; + (this.material as any).defines = defines; + specialMats = true; + } else if (this.settings.material.type === 'MeshPhysicalMaterial') { + this.material = new ParticleMeshPhysicsMaterial({}); + this.material.copy(this.settings.material as MeshPhysicalMaterial); + (this.material as any).uniforms = uniforms; + (this.material as any).defines = defines; + specialMats = true; + } + } + if (!specialMats) { + this.material = new ShaderMaterial({ + uniforms: uniforms, + defines: defines, + vertexShader: vertexShader, + fragmentShader: fragmentShader, + transparent: this.settings.material.transparent, + depthWrite: !this.settings.material.transparent, + blending: this.settings.material.blending, + side: this.settings.material.side, + alphaTest: this.settings.material.alphaTest, + lights: needLights, + }); + } } else if (this.settings.renderMode === RenderMode.StretchedBillBoard) { uniforms['speedFactor'] = new Uniform(1.0); this.material = new ShaderMaterial({ diff --git a/src/VFXBatch.ts b/src/VFXBatch.ts index fc0e4a6..ce639f2 100644 --- a/src/VFXBatch.ts +++ b/src/VFXBatch.ts @@ -51,7 +51,6 @@ export enum RenderMode { export abstract class VFXBatch extends Mesh { type = 'VFXBatch'; systems: Set; - declare material: ShaderMaterial; settings: StoredBatchSettings; protected maxParticles; @@ -91,11 +90,11 @@ export abstract class VFXBatch extends Mesh { } applyDepthTexture(depthTexture: Texture | null): void { - const uniform = this.material.uniforms['depthTexture']; + const uniform = (this.material as ShaderMaterial).uniforms['depthTexture']; if (uniform) { if (uniform.value !== depthTexture) { uniform.value = depthTexture; - this.material.needsUpdate = true; + (this.material as ShaderMaterial).needsUpdate = true; } } } diff --git a/src/functions/ConstantValue.ts b/src/functions/ConstantValue.ts index b7b47ee..a8e7ee4 100644 --- a/src/functions/ConstantValue.ts +++ b/src/functions/ConstantValue.ts @@ -7,7 +7,9 @@ export class ConstantValue implements ValueGenerator { this.type = 'value'; } - genValue(): number { + startGen(memory: GeneratorMemory): void {} + + genValue(memory: GeneratorMemory): number { return this.value; } @@ -25,6 +27,4 @@ export class ConstantValue implements ValueGenerator { clone(): ValueGenerator { return new ConstantValue(this.value); } - - startGen(): void {} } diff --git a/src/materials/ParticleMaterials.ts b/src/materials/ParticleMaterials.ts new file mode 100644 index 0000000..35a469e --- /dev/null +++ b/src/materials/ParticleMaterials.ts @@ -0,0 +1,33 @@ +import { + MeshPhysicalMaterial, + MeshStandardMaterial, + MeshStandardMaterialParameters, + WebGLProgramParametersWithUniforms, + WebGLRenderer, +} from 'three'; +import local_particle_physics_vert from '../shaders/local_particle_physics_vert.glsl'; +import particle_physics_frag from '../shaders/particle_physics_frag.glsl'; + +export class ParticleMeshStandardMaterial extends MeshStandardMaterial { + constructor(parameters: MeshStandardMaterialParameters) { + super(parameters); + } + + onBeforeCompile(parameters: WebGLProgramParametersWithUniforms, renderer: WebGLRenderer) { + super.onBeforeCompile(parameters, renderer); + parameters.vertexShader = local_particle_physics_vert; + parameters.fragmentShader = particle_physics_frag; + } +} + +export class ParticleMeshPhysicsMaterial extends MeshPhysicalMaterial { + constructor(parameters: MeshStandardMaterialParameters) { + super(parameters); + } + + onBeforeCompile(parameters: WebGLProgramParametersWithUniforms, renderer: WebGLRenderer) { + super.onBeforeCompile(parameters, renderer); + parameters.vertexShader = local_particle_physics_vert; + parameters.fragmentShader = particle_physics_frag; + } +}