123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- import { GlobalState } from "../../globalState";
- import { NodeMaterial } from "babylonjs/Materials/Node/nodeMaterial";
- import { Nullable } from "babylonjs/types";
- import { Observer } from "babylonjs/Misc/observable";
- import { Engine } from "babylonjs/Engines/engine";
- import { Scene } from "babylonjs/scene";
- import { Mesh } from "babylonjs/Meshes/mesh";
- import { Vector3 } from "babylonjs/Maths/math.vector";
- import { HemisphericLight } from "babylonjs/Lights/hemisphericLight";
- import { ArcRotateCamera } from "babylonjs/Cameras/arcRotateCamera";
- import { PreviewType } from "./previewType";
- import { Animation } from "babylonjs/Animations/animation";
- import { SceneLoader } from "babylonjs/Loading/sceneLoader";
- import { TransformNode } from "babylonjs/Meshes/transformNode";
- import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
- import { FramingBehavior } from "babylonjs/Behaviors/Cameras/framingBehavior";
- import { DirectionalLight } from "babylonjs/Lights/directionalLight";
- import { LogEntry } from "../log/logComponent";
- import { PointerEventTypes } from "babylonjs/Events/pointerEvents";
- import { Color3, Color4 } from "babylonjs/Maths/math.color";
- import { PostProcess } from "babylonjs/PostProcesses/postProcess";
- import { Constants } from "babylonjs/Engines/constants";
- import { CurrentScreenBlock } from "babylonjs/Materials/Node/Blocks/Dual/currentScreenBlock";
- import { NodeMaterialModes } from "babylonjs/Materials/Node/Enums/nodeMaterialModes";
- import { ParticleSystem } from "babylonjs/Particles/particleSystem";
- import { IParticleSystem } from "babylonjs/Particles/IParticleSystem";
- import { ParticleHelper } from "babylonjs/Particles/particleHelper";
- import { Texture } from "babylonjs/Materials/Textures/texture";
- import { ParticleTextureBlock } from "babylonjs/Materials/Node/Blocks/Particle/particleTextureBlock";
- import { FileTools } from "babylonjs/Misc/fileTools";
- import { ProceduralTexture } from "babylonjs/Materials/Textures/Procedurals/proceduralTexture";
- import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
- import { Layer } from "babylonjs/Layers/layer";
- export class PreviewManager {
- private _nodeMaterial: NodeMaterial;
- private _onBuildObserver: Nullable<Observer<NodeMaterial>>;
- private _onPreviewCommandActivatedObserver: Nullable<Observer<boolean>>;
- private _onAnimationCommandActivatedObserver: Nullable<Observer<void>>;
- private _onUpdateRequiredObserver: Nullable<Observer<void>>;
- private _onPreviewBackgroundChangedObserver: Nullable<Observer<void>>;
- private _onBackFaceCullingChangedObserver: Nullable<Observer<void>>;
- private _onDepthPrePassChangedObserver: Nullable<Observer<void>>;
- private _onLightUpdatedObserver: Nullable<Observer<void>>;
- private _engine: Engine;
- private _scene: Scene;
- private _meshes: AbstractMesh[];
- private _camera: ArcRotateCamera;
- private _material: NodeMaterial | StandardMaterial;
- private _globalState: GlobalState;
- private _currentType: number;
- private _lightParent: TransformNode;
- private _postprocess: Nullable<PostProcess>;
- private _proceduralTexture: Nullable<ProceduralTexture>;
- private _particleSystem: Nullable<IParticleSystem>;
- private _layer: Nullable<Layer>;
- public constructor(targetCanvas: HTMLCanvasElement, globalState: GlobalState) {
- this._nodeMaterial = globalState.nodeMaterial;
- this._globalState = globalState;
- this._onBuildObserver = this._nodeMaterial.onBuildObservable.add((nodeMaterial) => {
- let serializationObject = nodeMaterial.serialize();
- this._updatePreview(serializationObject);
- });
- this._onPreviewCommandActivatedObserver = globalState.onPreviewCommandActivated.add((forceRefresh: boolean) => {
- if (forceRefresh) {
- this._currentType = -1;
- }
- this._refreshPreviewMesh();
- });
- this._onLightUpdatedObserver = globalState.onLightUpdated.add(() => {
- this._prepareLights();
- });
- this._onUpdateRequiredObserver = globalState.onUpdateRequiredObservable.add(() => {
- let serializationObject = this._nodeMaterial.serialize();
- this._updatePreview(serializationObject);
- });
- this._onPreviewBackgroundChangedObserver = globalState.onPreviewBackgroundChanged.add(() => {
- this._scene.clearColor = this._globalState.backgroundColor;
- });
- this._onAnimationCommandActivatedObserver = globalState.onAnimationCommandActivated.add(() => {
- this._handleAnimations();
- });
- this._onBackFaceCullingChangedObserver = globalState.onBackFaceCullingChanged.add(() => {
- this._material.backFaceCulling = this._globalState.backFaceCulling;
- });
- this._onDepthPrePassChangedObserver = globalState.onDepthPrePassChanged.add(() => {
- this._material.needDepthPrePass = this._globalState.depthPrePass;
- });
- this._engine = new Engine(targetCanvas, true);
- this._scene = new Scene(this._engine);
- this._scene.clearColor = this._globalState.backgroundColor;
- this._camera = new ArcRotateCamera("Camera", 0, 0.8, 4, Vector3.Zero(), this._scene);
- this._camera.lowerRadiusLimit = 3;
- this._camera.upperRadiusLimit = 10;
- this._camera.wheelPrecision = 20;
- this._camera.minZ = 0.1;
- this._camera.attachControl(false);
- this._lightParent = new TransformNode("LightParent", this._scene);
- this._refreshPreviewMesh();
- this._engine.runRenderLoop(() => {
- this._engine.resize();
- this._scene.render();
- });
- let lastOffsetX: number | undefined = undefined;
- const lightRotationSpeed = 0.01;
- this._scene.onPointerObservable.add((evt) => {
- if (this._globalState.controlCamera) {
- return;
- }
- if (evt.type === PointerEventTypes.POINTERUP) {
- lastOffsetX = undefined;
- return;
- }
- if (evt.event.buttons !== 1) {
- return;
- }
- if (lastOffsetX === undefined) {
- lastOffsetX = evt.event.offsetX;
- }
- var rotateLighting = (lastOffsetX - evt.event.offsetX) * lightRotationSpeed;
- this._lightParent.rotation.y += rotateLighting;
- lastOffsetX = evt.event.offsetX;
- });
- // this._scene.registerBeforeRender(() => {
- // if (this._camera.alpha === cameraLastRotation) {
- // return;
- // }
- // if (!this._globalState.controlCamera) {
- // return;
- // }
- // var rotateLighting = (this._camera.alpha - cameraLastRotation) * lightRotationParallaxSpeed;
- // this._lightParent.rotate(Vector3.Up(), rotateLighting);
- // cameraLastRotation = this._camera.alpha;
- // });
- }
- private _handleAnimations() {
- this._scene.stopAllAnimations();
- if (this._globalState.rotatePreview) {
- for (var root of this._scene.rootNodes) {
- let transformNode = root as TransformNode;
- if (transformNode.getClassName() === "TransformNode" || transformNode.getClassName() === "Mesh" || transformNode.getClassName() === "GroundMesh") {
- if (transformNode.rotationQuaternion) {
- transformNode.rotation = transformNode.rotationQuaternion.toEulerAngles();
- transformNode.rotationQuaternion = null;
- }
- Animation.CreateAndStartAnimation("turnTable", root, "rotation.y", 60, 1200, transformNode.rotation.y, transformNode.rotation.y + 2 * Math.PI, 1);
- }
- }
- }
- }
- private _prepareLights() {
- // Remove current lights
- let currentLights = this._scene.lights.slice(0);
- for (var light of currentLights) {
- light.dispose();
- }
- // Create new lights based on settings
- if (this._globalState.hemisphericLight) {
- new HemisphericLight("Hemispheric light", new Vector3(0, 1, 0), this._scene);
- }
- if (this._globalState.directionalLight0) {
- let dir0 = new DirectionalLight("Directional light #0", new Vector3(0.841626576496605, -0.2193391004130599, -0.49351298337996535), this._scene);
- dir0.intensity = 0.9;
- dir0.diffuse = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549);
- dir0.specular = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549);
- dir0.parent = this._lightParent;
- }
- if (this._globalState.directionalLight1) {
- let dir1 = new DirectionalLight("Directional light #1", new Vector3(-0.9519937437504213, -0.24389315636999764, -0.1849974057546125), this._scene);
- dir1.intensity = 1.2;
- dir1.specular = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432);
- dir1.diffuse = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432);
- dir1.parent = this._lightParent;
- }
- }
- private _prepareScene() {
- this._camera.useFramingBehavior = this._globalState.mode === NodeMaterialModes.Material;
- switch (this._globalState.mode) {
- case NodeMaterialModes.Material: {
- this._prepareLights();
- var framingBehavior = this._camera.getBehaviorByName("Framing") as FramingBehavior;
- setTimeout(() => {
- // Let the behavior activate first
- framingBehavior.framingTime = 0;
- framingBehavior.elevationReturnTime = -1;
- if (this._scene.meshes.length) {
- var worldExtends = this._scene.getWorldExtends();
- this._camera.lowerRadiusLimit = null;
- this._camera.upperRadiusLimit = null;
- framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
- }
- this._camera.pinchPrecision = 200 / this._camera.radius;
- this._camera.upperRadiusLimit = 5 * this._camera.radius;
- });
- this._camera.wheelDeltaPercentage = 0.01;
- this._camera.pinchDeltaPercentage = 0.01;
- // Animations
- this._handleAnimations();
- break;
- }
- case NodeMaterialModes.PostProcess:
- case NodeMaterialModes.ProceduralTexture: {
- this._camera.radius = 4;
- this._camera.upperRadiusLimit = 10;
- break;
- }
- case NodeMaterialModes.Particle: {
- this._camera.radius = this._globalState.previewType === PreviewType.Explosion ? 50 : this._globalState.previewType === PreviewType.DefaultParticleSystem ? 6 : 20;
- this._camera.upperRadiusLimit = 5000;
- break;
- }
- }
- // Material
- let serializationObject = this._nodeMaterial.serialize();
- this._updatePreview(serializationObject);
- }
- private _refreshPreviewMesh() {
- if (this._currentType !== this._globalState.previewType || this._currentType === PreviewType.Custom) {
- this._currentType = this._globalState.previewType;
- if (this._meshes && this._meshes.length) {
- for (var mesh of this._meshes) {
- mesh.dispose();
- }
- }
- this._meshes = [];
- if (this._layer) {
- this._layer.dispose();
- this._layer = null;
- }
- let lights = this._scene.lights.slice(0);
- for (var light of lights) {
- light.dispose();
- }
- this._engine.releaseEffects();
- if (this._particleSystem) {
- this._particleSystem.onBeforeDrawParticlesObservable.clear();
- this._particleSystem.onDisposeObservable.clear();
- this._particleSystem.stop();
- this._particleSystem.dispose();
- this._particleSystem = null;
- }
- SceneLoader.ShowLoadingScreen = false;
- this._globalState.onIsLoadingChanged.notifyObservers(true);
- if (this._globalState.mode === NodeMaterialModes.Material) {
- switch (this._globalState.previewType) {
- case PreviewType.Box:
- SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCube.glb", this._scene).then(() => {
- this._meshes.push(...this._scene.meshes);
- this._prepareScene();
- });
- return;
- case PreviewType.Sphere:
- this._meshes.push(Mesh.CreateSphere("dummy-sphere", 32, 2, this._scene));
- break;
- case PreviewType.Torus:
- this._meshes.push(Mesh.CreateTorus("dummy-torus", 2, 0.5, 32, this._scene));
- break;
- case PreviewType.Cylinder:
- SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCylinder.glb", this._scene).then(() => {
- this._meshes.push(...this._scene.meshes);
- this._prepareScene();
- });
- return;
- case PreviewType.Plane:
- let plane = Mesh.CreateGround("dummy-plane", 2, 2, 128, this._scene);
- plane.scaling.y = -1;
- plane.rotation.x = Math.PI;
- this._meshes.push(plane);
- break;
- case PreviewType.ShaderBall:
- SceneLoader.AppendAsync("https://models.babylonjs.com/", "shaderBall.glb", this._scene).then(() => {
- this._meshes.push(...this._scene.meshes);
- this._prepareScene();
- });
- return;
- case PreviewType.Custom:
- SceneLoader.AppendAsync("file:", this._globalState.previewFile, this._scene).then(() => {
- this._meshes.push(...this._scene.meshes);
- this._prepareScene();
- });
- return;
- }
- } else if (this._globalState.mode === NodeMaterialModes.ProceduralTexture) {
- this._layer = new Layer("proceduralLayer", null, this._scene);
- } else if (this._globalState.mode === NodeMaterialModes.Particle) {
- switch (this._globalState.previewType) {
- case PreviewType.DefaultParticleSystem:
- this._particleSystem = ParticleHelper.CreateDefault(new Vector3(0, 0, 0), 500, this._scene);
- this._particleSystem.start();
- break;
- case PreviewType.Bubbles:
- this._particleSystem = new ParticleSystem("particles", 4000, this._scene);
- this._particleSystem.particleTexture = new Texture("https://assets.babylonjs.com/particles/textures/explosion/Flare.png", this._scene);
- this._particleSystem.minSize = 0.1;
- this._particleSystem.maxSize = 1.0;
- this._particleSystem.minLifeTime = 0.5;
- this._particleSystem.maxLifeTime = 5.0;
- this._particleSystem.minEmitPower = 0.5;
- this._particleSystem.maxEmitPower = 3.0;
- this._particleSystem.createBoxEmitter(new Vector3(-1, 1, -1), new Vector3(1, 1, 1), new Vector3(-0.1, -0.1, -0.1), new Vector3(0.1, 0.1, 0.1));
- this._particleSystem.emitRate = 100;
- this._particleSystem.blendMode = ParticleSystem.BLENDMODE_ONEONE;
- this._particleSystem.color1 = new Color4(1, 1, 0, 1);
- this._particleSystem.color2 = new Color4(1, 0.5, 0, 1);
- this._particleSystem.gravity = new Vector3(0, -1.0, 0);
- this._particleSystem.start();
- break;
- case PreviewType.Explosion:
- this._loadParticleSystem(this._globalState.previewType, 1);
- return;
- case PreviewType.Fire:
- case PreviewType.Rain:
- case PreviewType.Smoke:
- this._loadParticleSystem(this._globalState.previewType);
- return;
- case PreviewType.Custom:
- FileTools.ReadFile(
- this._globalState.previewFile,
- (json) => {
- this._particleSystem = ParticleSystem.Parse(JSON.parse(json), this._scene, "");
- this._particleSystem.start();
- this._prepareScene();
- },
- undefined,
- false,
- (error) => {
- console.log(error);
- }
- );
- return;
- }
- }
- this._prepareScene();
- }
- }
- private _loadParticleSystem(particleNumber: number, systemIndex = 0, prepareScene = true) {
- let name = "";
- switch (particleNumber) {
- case PreviewType.Explosion:
- name = "explosion";
- break;
- case PreviewType.Fire:
- name = "fire";
- break;
- case PreviewType.Rain:
- name = "rain";
- break;
- case PreviewType.Smoke:
- name = "smoke";
- break;
- }
- ParticleHelper.CreateAsync(name, this._scene).then((set) => {
- for (let i = 0; i < set.systems.length; ++i) {
- if (i == systemIndex) {
- this._particleSystem = set.systems[i];
- this._particleSystem.disposeOnStop = true;
- this._particleSystem.onDisposeObservable.add(() => {
- this._loadParticleSystem(particleNumber, systemIndex, false);
- });
- this._particleSystem.start();
- } else {
- set.systems[i].dispose();
- }
- }
- if (prepareScene) {
- this._prepareScene();
- } else {
- let serializationObject = this._nodeMaterial.serialize();
- this._updatePreview(serializationObject);
- }
- });
- }
- private _forceCompilationAsync(material: NodeMaterial, mesh: AbstractMesh): Promise<void> {
- return material.forceCompilationAsync(mesh);
- }
- private _updatePreview(serializationObject: any) {
- try {
- let store = NodeMaterial.IgnoreTexturesAtLoadTime;
- NodeMaterial.IgnoreTexturesAtLoadTime = false;
- let tempMaterial = NodeMaterial.Parse(serializationObject, this._scene);
- NodeMaterial.IgnoreTexturesAtLoadTime = store;
- tempMaterial.backFaceCulling = this._globalState.backFaceCulling;
- tempMaterial.needDepthPrePass = this._globalState.depthPrePass;
- if (this._postprocess) {
- this._postprocess.dispose(this._camera);
- this._postprocess = null;
- }
- if (this._proceduralTexture) {
- this._proceduralTexture.dispose();
- this._proceduralTexture = null;
- }
- switch (this._globalState.mode) {
- case NodeMaterialModes.PostProcess: {
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- this._postprocess = tempMaterial.createPostProcess(this._camera, 1.0, Constants.TEXTURE_NEAREST_SAMPLINGMODE, this._engine);
- const currentScreen = tempMaterial.getBlockByPredicate((block) => block instanceof CurrentScreenBlock);
- if (currentScreen && this._postprocess) {
- this._postprocess.onApplyObservable.add((effect) => {
- effect.setTexture("textureSampler", (currentScreen as CurrentScreenBlock).texture);
- });
- }
- if (this._material) {
- this._material.dispose();
- }
- this._material = tempMaterial;
- break;
- }
- case NodeMaterialModes.ProceduralTexture: {
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- this._proceduralTexture = tempMaterial.createProceduralTexture(512, this._scene);
- if (this._material) {
- this._material.dispose();
- }
- if (this._layer) {
- this._layer.texture = this._proceduralTexture;
- }
- break;
- }
- case NodeMaterialModes.Particle: {
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- this._particleSystem!.onBeforeDrawParticlesObservable.clear();
- this._particleSystem!.onBeforeDrawParticlesObservable.add((effect) => {
- const textureBlock = tempMaterial.getBlockByPredicate((block) => block instanceof ParticleTextureBlock);
- if (textureBlock && (textureBlock as ParticleTextureBlock).texture && effect) {
- effect.setTexture("diffuseSampler", (textureBlock as ParticleTextureBlock).texture);
- }
- });
- tempMaterial.createEffectForParticles(this._particleSystem!);
- if (this._material) {
- this._material.dispose();
- }
- this._material = tempMaterial;
- break;
- }
- default: {
- if (this._meshes.length) {
- let tasks = this._meshes.map((m) => this._forceCompilationAsync(tempMaterial, m));
- Promise.all(tasks)
- .then(() => {
- for (var mesh of this._meshes) {
- mesh.material = tempMaterial;
- }
- if (this._material) {
- this._material.dispose();
- }
- this._material = tempMaterial;
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- })
- .catch((reason) => {
- this._globalState.onLogRequiredObservable.notifyObservers(new LogEntry("Shader compilation error:\r\n" + reason, true));
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- });
- } else {
- this._material = tempMaterial;
- }
- break;
- }
- }
- } catch (err) {
- // Ignore the error
- this._globalState.onIsLoadingChanged.notifyObservers(false);
- }
- }
- public dispose() {
- this._nodeMaterial.onBuildObservable.remove(this._onBuildObserver);
- this._globalState.onPreviewCommandActivated.remove(this._onPreviewCommandActivatedObserver);
- this._globalState.onUpdateRequiredObservable.remove(this._onUpdateRequiredObserver);
- this._globalState.onAnimationCommandActivated.remove(this._onAnimationCommandActivatedObserver);
- this._globalState.onPreviewBackgroundChanged.remove(this._onPreviewBackgroundChangedObserver);
- this._globalState.onBackFaceCullingChanged.remove(this._onBackFaceCullingChangedObserver);
- this._globalState.onDepthPrePassChanged.remove(this._onDepthPrePassChangedObserver);
- this._globalState.onLightUpdated.remove(this._onLightUpdatedObserver);
- if (this._material) {
- this._material.dispose();
- }
- this._camera.dispose();
- for (var mesh of this._meshes) {
- mesh.dispose();
- }
- this._scene.dispose();
- this._engine.dispose();
- }
- }
|