瀏覽代碼

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David `Deltakosh` Catuhe 5 年之前
父節點
當前提交
c0e91a4f67

+ 9 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx

@@ -83,7 +83,7 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
             case "MeshParticleEmitter":
                 return (
                     <MeshEmitterGridComponent  
-                    lockObject={this.props.lockObject} scene={system.getScene()} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    lockObject={this.props.lockObject} scene={system.getScene()!} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                 );                 
             case "PointParticleEmitter":
                 return (
@@ -165,6 +165,10 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
         const system = this.props.system;
         const scene = system.getScene();
 
+        if (!scene) {
+            return;
+        }
+
         Tools.ReadFile(file, (data) => {
             let decoder = new TextDecoder("utf-8");
             let jsonObject = JSON.parse(decoder.decode(data));
@@ -173,19 +177,19 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
             system.dispose();            
             this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
 
-            let newSystem = isGpu ? GPUParticleSystem.Parse(jsonObject, scene, "") : ParticleSystem.Parse(jsonObject, scene, "");
+            let newSystem = isGpu ? GPUParticleSystem.Parse(jsonObject, scene!, "") : ParticleSystem.Parse(jsonObject, scene!, "");
             this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
         }, undefined, true);
     }
 
     loadFromSnippet() {
         const system = this.props.system;
-        const scene = system.getScene();
+        const scene = system.getScene()!;
         let isGpu = system instanceof GPUParticleSystem;
 
         let snippedID = window.prompt("Please enter the snippet ID to use");
 
-        if (!snippedID) {
+        if (!snippedID || !scene) {
             return;
         }
         
@@ -272,7 +276,7 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
         ];
 
 
-        var meshEmitters = this.props.system.getScene().meshes.filter(m => !!m.name);
+        var meshEmitters = this.props.system.getScene()!.meshes.filter(m => !!m.name);
 
         var emitterOptions = [
             { label: "None", value: -1 },

+ 3 - 0
src/Materials/Textures/Procedurals/proceduralTexture.ts

@@ -18,6 +18,7 @@ import "../../../Engines/Extensions/engine.renderTarget";
 import "../../../Engines/Extensions/engine.renderTargetCube";
 import "../../../Shaders/procedural.vertex";
 import { DataBuffer } from '../../../Meshes/dataBuffer';
+import { _TypeStore } from '../../../Misc/typeStore';
 
 /**
  * Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes calmpler' images.
@@ -634,3 +635,5 @@ export class ProceduralTexture extends Texture {
         super.dispose();
     }
 }
+
+_TypeStore.RegisteredTypes["BABYLON.ProceduralTexture"] = ProceduralTexture;

+ 2 - 2
src/Materials/Textures/baseTexture.ts

@@ -375,8 +375,8 @@ export class BaseTexture implements IAnimatable {
      */
     public delayLoadState = Constants.DELAYLOADSTATE_NONE;
 
-    private _scene: Nullable<Scene> = null;
-    private _engine: Nullable<ThinEngine> = null;
+    protected _scene: Nullable<Scene> = null;
+    protected _engine: Nullable<ThinEngine> = null;
 
     /** @hidden */
     public _texture: Nullable<InternalTexture> = null;

+ 28 - 22
src/Materials/Textures/rawTexture.ts

@@ -2,6 +2,8 @@ import { Scene } from "../../scene";
 import { Texture } from "./texture";
 import { Constants } from "../../Engines/constants";
 import "../../Engines/Extensions/engine.rawTexture";
+import { Nullable } from '../../types';
+import { ThinEngine } from '../../Engines/thinEngine';
 
 /**
  * Raw texture can help creating a texture directly from an array of data.
@@ -18,7 +20,7 @@ export class RawTexture extends Texture {
      * @param width define the width of the texture
      * @param height define the height of the texture
      * @param format define the format of the data (RGB, RGBA... Engine.TEXTUREFORMAT_xxx)
-     * @param scene  define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps define whether mip maps should be generated or not
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
@@ -29,10 +31,14 @@ export class RawTexture extends Texture {
          * Define the format of the data (RGB, RGBA... Engine.TEXTUREFORMAT_xxx)
          */
         public format: number,
-        scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
-        super(null, scene, !generateMipMaps, invertY);
+        sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
+        super(null, sceneOrEngine, !generateMipMaps, invertY);
 
-        this._texture = scene.getEngine().createRawTexture(data, width, height, format, generateMipMaps, invertY, samplingMode, null, type);
+        if (!this._engine) {
+            return;
+        }
+
+        this._texture = this._engine.createRawTexture(data, width, height, format, generateMipMaps, invertY, samplingMode, null, type);
 
         this.wrapU = Texture.CLAMP_ADDRESSMODE;
         this.wrapV = Texture.CLAMP_ADDRESSMODE;
@@ -51,14 +57,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the luminance texture
      */
-    public static CreateLuminanceTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateLuminanceTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -66,14 +72,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the luminance alpha texture
      */
-    public static CreateLuminanceAlphaTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE_ALPHA, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateLuminanceAlphaTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE_ALPHA, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -81,14 +87,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the alpha texture
      */
-    public static CreateAlphaTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_ALPHA, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateAlphaTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_ALPHA, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -96,15 +102,15 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the RGB alpha texture
      */
-    public static CreateRGBTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGB, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRGBTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGB, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 
     /**
@@ -112,15 +118,15 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the RGBA texture
      */
-    public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGBA, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGBA, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 
     /**
@@ -128,14 +134,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the R texture
      */
-    public static CreateRTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_FLOAT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_R, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_FLOAT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_R, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 }

+ 1 - 0
src/Materials/Textures/texture.ts

@@ -796,4 +796,5 @@ export class Texture extends BaseTexture {
 }
 
 // References the dependencies.
+_TypeStore.RegisteredTypes["BABYLON.Texture"] = Texture;
 SerializationHelper._TextureParser = Texture.Parse;

+ 3 - 1
src/Meshes/abstractMesh.ts

@@ -30,6 +30,7 @@ import { Epsilon } from '../Maths/math.constants';
 import { Plane } from '../Maths/math.plane';
 import { Axis } from '../Maths/math.axis';
 import { IParticleSystem } from '../Particles/IParticleSystem';
+import { _TypeStore } from '../Misc/typeStore';
 
 declare type Ray = import("../Culling/ray").Ray;
 declare type Collider = import("../Collisions/collider").Collider;
@@ -2185,5 +2186,6 @@ export class AbstractMesh extends TransformNode implements IDisposable, ICullabl
     public getConnectedParticleSystems(): IParticleSystem[] {
         return this._scene.particleSystems.filter((particleSystem) => particleSystem.emitter === this);
     }
-
 }
+
+_TypeStore.RegisteredTypes["BABYLON.AbstractMesh"] = AbstractMesh;

+ 3 - 1
src/Meshes/mesh.ts

@@ -4364,4 +4364,6 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
             this.instances.pop();
         }
     }
-}
+}
+
+_TypeStore.RegisteredTypes["BABYLON.Mesh"] = Mesh;

+ 5 - 2
src/Particles/EmitterTypes/IParticleEmitterType.ts

@@ -1,7 +1,10 @@
 import { Vector3, Matrix } from "../../Maths/math.vector";
 import { Effect } from "../../Materials/effect";
 import { Particle } from "../../Particles/particle";
-import { Scene } from '../../scene';
+import { Nullable } from '../../types';
+
+declare type Scene = import("../../scene").Scene;
+
 /**
  * Particle emitter represents a volume emitting particles.
  * This is the responsibility of the implementation to define the volume shape like cone/sphere/box.
@@ -60,5 +63,5 @@ export interface IParticleEmitterType {
      * @param serializationObject defines the JSON object
      * @param scene defines the hosting scene
      */
-    parse(serializationObject: any, scene: Scene): void;
+    parse(serializationObject: any, scene: Nullable<Scene>): void;
 }

+ 2 - 2
src/Particles/EmitterTypes/meshParticleEmitter.ts

@@ -197,11 +197,11 @@ export class MeshParticleEmitter implements IParticleEmitterType {
      * @param serializationObject defines the JSON object
      * @param scene defines the hosting scene
      */
-    public parse(serializationObject: any, scene: Scene): void {
+    public parse(serializationObject: any, scene: Nullable<Scene>): void {
         Vector3.FromArrayToRef(serializationObject.direction1, 0, this.direction1);
         Vector3.FromArrayToRef(serializationObject.direction2, 0, this.direction2);
 
-        if (serializationObject.meshId) {
+        if (serializationObject.meshId && scene) {
             this.mesh = scene.getLastMeshByID(serializationObject.meshId);
         }
 

+ 7 - 5
src/Particles/IParticleSystem.ts

@@ -1,9 +1,7 @@
 import { Nullable } from "../types";
-import { Vector2, Vector3 } from "../Maths/math.vector";
+import { Vector2, Vector3, Matrix } from "../Maths/math.vector";
 import { Color3, Color4 } from '../Maths/math.color';
-import { AbstractMesh } from "../Meshes/abstractMesh";
 import { BaseTexture } from "../Materials/Textures/baseTexture";
-import { Texture } from "../Materials/Textures/texture";
 import { BoxParticleEmitter, IParticleEmitterType, PointParticleEmitter, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, ConeParticleEmitter } from "../Particles/EmitterTypes/index";
 import { Scene } from "../scene";
 import { ColorGradient, FactorGradient, Color3Gradient } from "../Misc/gradients";
@@ -11,6 +9,7 @@ import { Effect } from "../Materials/effect";
 import { Observable } from "../Misc/observable";
 
 declare type Animation = import("../Animations/animation").Animation;
+declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
 
 /**
  * Interface representing a particle system in Babylon.js.
@@ -60,7 +59,7 @@ export interface IParticleSystem {
     /**
      * The texture used to render each particle. (this can be a spritesheet)
      */
-    particleTexture: Nullable<Texture>;
+    particleTexture: Nullable<BaseTexture>;
 
     /**
      * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE, ParticleSystem.BLENDMODE_STANDARD or ParticleSystem.BLENDMODE_ADD.
@@ -252,6 +251,9 @@ export interface IParticleSystem {
     /** Snippet ID if the particle system was created from the snippet server */
     snippetId: string;
 
+    /** Gets or sets a matrix to use to compute projection */
+    defaultProjectionMatrix: Matrix;
+
     /**
      * Gets the maximum number of particles active at the same time.
      * @returns The max number of active particles.
@@ -691,5 +693,5 @@ export interface IParticleSystem {
      * Get hosting scene
      * @returns the scene
      */
-    getScene(): Scene;
+    getScene(): Nullable<Scene>;
 }

+ 13 - 7
src/Particles/baseParticleSystem.ts

@@ -4,14 +4,15 @@ import { AbstractMesh } from "../Meshes/abstractMesh";
 import { ImageProcessingConfiguration, ImageProcessingConfigurationDefines } from "../Materials/imageProcessingConfiguration";
 import { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralTexture";
 import { RawTexture } from "../Materials/Textures/rawTexture";
-import { Scene } from "../scene";
 import { ColorGradient, FactorGradient, Color3Gradient, IValueGradient } from "../Misc/gradients";
 import { BoxParticleEmitter, IParticleEmitterType, PointParticleEmitter, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, CylinderDirectedParticleEmitter, ConeParticleEmitter } from "../Particles/EmitterTypes/index";
 import { Constants } from "../Engines/constants";
 import { Texture } from '../Materials/Textures/texture';
 import { Color4 } from '../Maths/math.color';
+import { ThinEngine } from '../Engines/thinEngine';
 
 declare type Animation = import("../Animations/animation").Animation;
+declare type Scene = import("../scene").Scene;
 
 /**
  * This represents the base class for particle system in Babylon.
@@ -307,7 +308,7 @@ export class BaseParticleSystem {
      * Get hosting scene
      * @returns the scene
      */
-    public getScene(): Scene {
+    public getScene(): Nullable<Scene> {
         return this._scene;
     }
 
@@ -567,7 +568,12 @@ export class BaseParticleSystem {
     /**
      * The scene the particle system belongs to.
      */
-    protected _scene: Scene;
+    protected _scene: Nullable<Scene>;
+
+    /**
+     * The engine the particle system belongs to.
+     */
+    protected _engine: ThinEngine;
 
     /**
      * Local cache of defines for image processing.
@@ -577,12 +583,12 @@ export class BaseParticleSystem {
     /**
      * Default configuration related to image processing available in the standard Material.
      */
-    protected _imageProcessingConfiguration: ImageProcessingConfiguration;
+    protected _imageProcessingConfiguration: Nullable<ImageProcessingConfiguration>;
 
     /**
      * Gets the image processing configuration used either in this material.
      */
-    public get imageProcessingConfiguration(): ImageProcessingConfiguration {
+    public get imageProcessingConfiguration(): Nullable<ImageProcessingConfiguration> {
         return this._imageProcessingConfiguration;
     }
 
@@ -591,7 +597,7 @@ export class BaseParticleSystem {
      *
      * If sets to null, the scene one is in use.
      */
-    public set imageProcessingConfiguration(value: ImageProcessingConfiguration) {
+    public set imageProcessingConfiguration(value: Nullable<ImageProcessingConfiguration>) {
         this._attachImageProcessingConfiguration(value);
     }
 
@@ -605,7 +611,7 @@ export class BaseParticleSystem {
         }
 
         // Pick the scene configuration if needed.
-        if (!configuration) {
+        if (!configuration && this._scene) {
             this._imageProcessingConfiguration = this._scene.imageProcessingConfiguration;
         }
         else {

+ 85 - 71
src/Particles/gpuParticleSystem.ts

@@ -6,23 +6,24 @@ import { Color4, Color3, TmpColors } from '../Maths/math.color';
 import { Scalar } from "../Maths/math.scalar";
 import { VertexBuffer } from "../Meshes/buffer";
 import { Buffer } from "../Meshes/buffer";
-import { AbstractMesh } from "../Meshes/abstractMesh";
 import { IParticleSystem } from "./IParticleSystem";
 import { BaseParticleSystem } from "./baseParticleSystem";
 import { ParticleSystem } from "./particleSystem";
-import { Engine } from "../Engines/engine";
 import { BoxParticleEmitter } from "../Particles/EmitterTypes/boxParticleEmitter";
-import { Scene, IDisposable } from "../scene";
+import { IDisposable } from "../scene";
 import { Effect, IEffectCreationOptions } from "../Materials/effect";
-import { Material } from "../Materials/material";
 import { MaterialHelper } from "../Materials/materialHelper";
 import { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration";
-import { Texture } from "../Materials/Textures/texture";
 import { RawTexture } from "../Materials/Textures/rawTexture";
 import { Constants } from "../Engines/constants";
 import { EngineStore } from "../Engines/engineStore";
 import { IAnimatable } from '../Animations/animatable.interface';
 import { CustomParticleEmitter } from './EmitterTypes/customParticleEmitter';
+import { ThinEngine } from '../Engines/thinEngine';
+
+declare type Scene = import("../scene").Scene;
+declare type Engine = import("../Engines/engine").Engine;
+declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
 
 import "../Shaders/gpuUpdateParticles.fragment";
 import "../Shaders/gpuUpdateParticles.vertex";
@@ -57,8 +58,6 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
     private _sourceBuffer: Buffer;
     private _targetBuffer: Buffer;
 
-    private _engine: Engine;
-
     private _currentRenderId = -1;
     private _started = false;
     private _stopped = false;
@@ -124,6 +123,9 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      */
     public isLocal = false;
 
+    /** Gets or sets a matrix to use to compute projection */
+    public defaultProjectionMatrix: Matrix;
+
     /**
      * Is this system ready to be used/rendered
      * @return true if the system is ready
@@ -135,7 +137,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             return false;
         }
 
-        if (!this.emitter || !this._updateEffect.isReady() || !this._imageProcessingConfiguration.isReady() || !this._getEffect().isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
+        if (!this.emitter || !this._updateEffect.isReady() || this._imageProcessingConfiguration && !this._imageProcessingConfiguration.isReady() || !this._getEffect().isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
             return false;
         }
 
@@ -193,8 +195,8 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         this._preWarmDone = false;
 
         // Animations
-        if (this.beginAnimationOnStart && this.animations && this.animations.length > 0) {
-            this.getScene().beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
+        if (this.beginAnimationOnStart && this.animations && this.animations.length > 0 && this._scene) {
+            this._scene.beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
         }
     }
 
@@ -697,26 +699,31 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
      * @param name The name of the particle system
      * @param options The options used to create the system
-     * @param scene The scene the particle system belongs to
+     * @param sceneOrEngine The scene the particle system belongs to or the engine to use if no scene
      * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
      * @param customEffect a custom effect used to change the way particles are rendered by default
      */
     constructor(name: string, options: Partial<{
         capacity: number,
         randomTextureSize: number
-    }>, scene: Scene, isAnimationSheetEnabled: boolean = false, customEffect: Nullable<Effect> = null) {
+    }>, sceneOrEngine: Scene | ThinEngine, isAnimationSheetEnabled: boolean = false, customEffect: Nullable<Effect> = null) {
         super(name);
-        this._scene = scene || EngineStore.LastCreatedScene;
 
-        this.uniqueId = this._scene.getUniqueId();
+        if (sceneOrEngine.getClassName() === "Scene") {
+            this._scene = (sceneOrEngine as Scene) || EngineStore.LastCreatedScene;
+            this._engine = this._engine;
+            this.uniqueId = this._scene.getUniqueId();
+            this._scene.particleSystems.push(this);
+        } else {
+            this._engine = (sceneOrEngine as ThinEngine);
+            this.defaultProjectionMatrix = Matrix.PerspectiveFovLH(0.8, 1, 0.1, 100);
+        }
 
         this._customEffect = { 0: customEffect };
 
         // Setup the default processing configuration to the scene.
         this._attachImageProcessingConfiguration(null);
 
-        this._engine = this._scene.getEngine();
-
         if (!options.randomTextureSize) {
             delete options.randomTextureSize;
         }
@@ -737,8 +744,6 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         this._currentActiveCount = 0;
         this._isAnimationSheetEnabled = isAnimationSheetEnabled;
 
-        this._scene.particleSystems.push(this);
-
         this._updateEffectOptions = {
             attributes: ["position", "initialPosition", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex", "cellStartOffset", "noiseCoordinates1", "noiseCoordinates2"],
             uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange", "gravity", "emitPower",
@@ -766,9 +771,9 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             d.push(Math.random());
             d.push(Math.random());
         }
-        this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Constants.TEXTUREFORMAT_RGBA, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
-        this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
-        this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
+        this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Constants.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
+        this._randomTexture.wrapU = Constants.TEXTURE_WRAP_ADDRESSMODE;
+        this._randomTexture.wrapV = Constants.TEXTURE_WRAP_ADDRESSMODE;
 
         d = [];
         for (var i = 0; i < maxTextureSize; ++i) {
@@ -777,9 +782,9 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             d.push(Math.random());
             d.push(Math.random());
         }
-        this._randomTexture2 = new RawTexture(new Float32Array(d), maxTextureSize, 1, Constants.TEXTUREFORMAT_RGBA, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
-        this._randomTexture2.wrapU = Texture.WRAP_ADDRESSMODE;
-        this._randomTexture2.wrapV = Texture.WRAP_ADDRESSMODE;
+        this._randomTexture2 = new RawTexture(new Float32Array(d), maxTextureSize, 1, Constants.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
+        this._randomTexture2.wrapU = Constants.TEXTURE_WRAP_ADDRESSMODE;
+        this._randomTexture2.wrapV = Constants.TEXTURE_WRAP_ADDRESSMODE;
 
         this._randomTextureSize = maxTextureSize;
     }
@@ -915,7 +920,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             return;
         }
 
-        let engine = this._scene.getEngine();
+        let engine = this._engine;
         var data = new Array<float>();
 
         this._attributesStrideSize = 21;
@@ -1144,7 +1149,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         }
 
         this._updateEffectOptions.defines = defines;
-        this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());
+        this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._engine);
     }
 
     private _getEffect(): Effect {
@@ -1157,23 +1162,25 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      * @param blendMode blend mode to take into account when updating the array
      */
     public fillDefines(defines: Array<string>, blendMode: number = 0) {
-        if (this._scene.clipPlane) {
-            defines.push("#define CLIPPLANE");
-        }
-        if (this._scene.clipPlane2) {
-            defines.push("#define CLIPPLANE2");
-        }
-        if (this._scene.clipPlane3) {
-            defines.push("#define CLIPPLANE3");
-        }
-        if (this._scene.clipPlane4) {
-            defines.push("#define CLIPPLANE4");
-        }
-        if (this._scene.clipPlane5) {
-            defines.push("#define CLIPPLANE5");
-        }
-        if (this._scene.clipPlane6) {
-            defines.push("#define CLIPPLANE6");
+        if (this._scene) {
+            if (this._scene.clipPlane) {
+                defines.push("#define CLIPPLANE");
+            }
+            if (this._scene.clipPlane2) {
+                defines.push("#define CLIPPLANE2");
+            }
+            if (this._scene.clipPlane3) {
+                defines.push("#define CLIPPLANE3");
+            }
+            if (this._scene.clipPlane4) {
+                defines.push("#define CLIPPLANE4");
+            }
+            if (this._scene.clipPlane5) {
+                defines.push("#define CLIPPLANE5");
+            }
+            if (this._scene.clipPlane6) {
+                defines.push("#define CLIPPLANE6");
+            }
         }
 
         if (this.blendMode === ParticleSystem.BLENDMODE_MULTIPLY) {
@@ -1262,7 +1269,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         this._renderEffect = new Effect("gpuRenderParticles",
             attributes,
             uniforms,
-            samplers, this._scene.getEngine(), join);
+            samplers, this._engine, join);
 
         return this._renderEffect;
     }
@@ -1272,7 +1279,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      * @param preWarm defines if we are in the pre-warmimg phase
      */
     public animate(preWarm = false): void {
-        this._timeDelta = this.updateSpeed * (preWarm ? this.preWarmStepOffset : this._scene.getAnimationRatio());
+        this._timeDelta = this.updateSpeed * (preWarm ? this.preWarmStepOffset : this._scene?.getAnimationRatio() || 1);
         this._actualFrame += this._timeDelta;
 
         if (!this._stopped) {
@@ -1299,7 +1306,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             });
         }
 
-        (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene || this._engine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
     }
 
     private _createSizeGradientTexture() {
@@ -1344,7 +1351,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
 
         }
 
-        this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
     }
 
     /**
@@ -1371,7 +1378,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             return 0;
         }
 
-        if (!preWarm) {
+        if (!preWarm && this._scene) {
             if (!this._preWarmDone && this.preWarmCycles) {
                 for (var index = 0; index < this.preWarmCycles; index++) {
                     this.animate(true);
@@ -1404,7 +1411,10 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
 
         // Enable update effect
         this._engine.enableEffect(this._updateEffect);
-        this._engine.setState(false);
+        var engine = this._engine as Engine;
+        if (!engine.setState) {
+            throw new Error("GPU particles cannot work with a full Engine. ThinEngine is not supported");
+        }
 
         this._updateEffect.setFloat("currentCount", this._currentActiveCount);
         this._updateEffect.setFloat("timeDelta", this._timeDelta);
@@ -1472,22 +1482,22 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         this._engine.bindVertexArrayObject(this._updateVAO[this._targetIndex], null);
 
         // Update
-        this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
-        this._engine.setRasterizerState(false);
-        this._engine.beginTransformFeedback(true);
-        this._engine.drawArraysType(Material.PointListDrawMode, 0, this._currentActiveCount);
-        this._engine.endTransformFeedback();
-        this._engine.setRasterizerState(true);
-        this._engine.bindTransformFeedbackBuffer(null);
+        engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
+        engine.setRasterizerState(false);
+        engine.beginTransformFeedback(true);
+        engine.drawArraysType(Constants.MATERIAL_PointListDrawMode, 0, this._currentActiveCount);
+        engine.endTransformFeedback();
+        engine.setRasterizerState(true);
+        engine.bindTransformFeedbackBuffer(null);
 
         if (!preWarm) {
             // Enable render effect
             const effect = this._getEffect();
 
             this._engine.enableEffect(effect);
-            let viewMatrix = this._scene.getViewMatrix();
+            let viewMatrix = this._scene?.getViewMatrix() || Matrix.IdentityReadOnly;
             effect.setMatrix("view", viewMatrix);
-            effect.setMatrix("projection", this._scene.getProjectionMatrix());
+            effect.setMatrix("projection", this.defaultProjectionMatrix ?? this._scene!.getProjectionMatrix());
             effect.setTexture("diffuseSampler", this.particleTexture);
             effect.setVector2("translationPivot", this.translationPivot);
             effect.setVector3("worldOffset", this.worldOffset);
@@ -1505,15 +1515,17 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
                 effect.setFloat3("sheetInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, baseSize.width / this.spriteCellWidth);
             }
 
-            if (this._isBillboardBased) {
+            if (this._isBillboardBased && this._scene) {
                 var camera = this._scene.activeCamera!;
                 effect.setVector3("eyePosition", camera.globalPosition);
             }
 
             const defines = effect.defines;
 
-            if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4 || this._scene.clipPlane5 || this._scene.clipPlane6) {
-                MaterialHelper.BindClipPlane(effect, this._scene);
+            if (this._scene) {
+                if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4 || this._scene.clipPlane5 || this._scene.clipPlane6) {
+                    MaterialHelper.BindClipPlane(effect, this._scene);
+                }
             }
 
             if (defines.indexOf("#define BILLBOARDMODE_ALL") >= 0) {
@@ -1544,7 +1556,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             }
 
             if (this.forceDepthWrite) {
-                this._engine.setDepthWrite(true);
+                engine.setDepthWrite(true);
             }
 
             // Bind source VAO
@@ -1555,7 +1567,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
             }
 
             // Render
-            this._engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._currentActiveCount);
+            this._engine.drawArraysType(Constants.MATERIAL_TriangleFanDrawMode, 0, 4, this._currentActiveCount);
             this._engine.setAlphaMode(Constants.ALPHA_DISABLE);
         }
         // Switch VAOs
@@ -1615,9 +1627,11 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      * @param disposeTexture defines if the particule texture must be disposed as well (true by default)
      */
     public dispose(disposeTexture = true): void {
-        var index = this._scene.particleSystems.indexOf(this);
-        if (index > -1) {
-            this._scene.particleSystems.splice(index, 1);
+        if (this._scene) {
+            var index = this._scene.particleSystems.indexOf(this);
+            if (index > -1) {
+                this._scene.particleSystems.splice(index, 1);
+            }
         }
 
         this._releaseBuffers();
@@ -1685,7 +1699,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
      */
     public clone(name: string, newEmitter: any): GPUParticleSystem {
         let serialization = this.serialize();
-        var result = GPUParticleSystem.Parse(serialization, this._scene, "");
+        var result = GPUParticleSystem.Parse(serialization, this._scene || this._engine, "");
         var custom = { ...this._customEffect };
         result.name = name;
         result._customEffect = custom;
@@ -1719,19 +1733,19 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
     /**
      * Parses a JSON object to create a GPU particle system.
      * @param parsedParticleSystem The JSON object to parse
-     * @param scene The scene to create the particle system in
+     * @param sceneOrEngine The scene or the engine to create the particle system in
      * @param rootUrl The root url to use to load external dependencies like texture
      * @param doNotStart Ignore the preventAutoStart attribute and does not start
      * @returns the parsed GPU particle system
      */
-    public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string, doNotStart = false): GPUParticleSystem {
+    public static Parse(parsedParticleSystem: any, sceneOrEngine: Scene | ThinEngine, rootUrl: string, doNotStart = false): GPUParticleSystem {
         var name = parsedParticleSystem.name;
-        var particleSystem = new GPUParticleSystem(name, { capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize }, scene);
+        var particleSystem = new GPUParticleSystem(name, { capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize }, sceneOrEngine);
 
         if (parsedParticleSystem.activeParticleCount) {
             particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
         }
-        ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
+        ParticleSystem._Parse(parsedParticleSystem, particleSystem, sceneOrEngine, rootUrl);
 
         // Auto start
         if (parsedParticleSystem.preventAutoStart) {

+ 3 - 1
src/Particles/particle.ts

@@ -2,10 +2,12 @@ import { Nullable } from "../types";
 import { Vector2, Vector3, TmpVectors, Vector4 } from "../Maths/math.vector";
 import { Color4 } from '../Maths/math.color';
 import { Scalar } from "../Maths/math.scalar";
-import { AbstractMesh } from "../Meshes/abstractMesh";
 import { ParticleSystem } from "./particleSystem";
 import { SubEmitter } from "./subEmitter";
 import { ColorGradient, FactorGradient } from "../Misc/gradients";
+
+declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
+
 /**
  * A particle represents one of the element emitted by a particle system.
  * This is mainly define by its coordinates, direction, velocity and age.

+ 130 - 87
src/Particles/particleSystem.ts

@@ -5,16 +5,12 @@ import { Vector3, Matrix, TmpVectors, Vector4 } from "../Maths/math.vector";
 import { Scalar } from "../Maths/math.scalar";
 import { VertexBuffer } from "../Meshes/buffer";
 import { Buffer } from "../Meshes/buffer";
-import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Material } from "../Materials/material";
 import { MaterialHelper } from "../Materials/materialHelper";
 import { Effect } from "../Materials/effect";
 import { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration";
-import { Texture } from "../Materials/Textures/texture";
 import { RawTexture } from "../Materials/Textures/rawTexture";
-import { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralTexture";
 import { EngineStore } from "../Engines/engineStore";
-import { Scene, IDisposable } from "../scene";
+import { IDisposable } from "../scene";
 import { BoxParticleEmitter, IParticleEmitterType, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, ConeParticleEmitter, PointParticleEmitter, MeshParticleEmitter, CylinderDirectedParticleEmitter } from "../Particles/EmitterTypes/index";
 import { IParticleSystem } from "./IParticleSystem";
 import { BaseParticleSystem } from "./baseParticleSystem";
@@ -30,6 +26,13 @@ import "../Shaders/particles.vertex";
 import { DataBuffer } from '../Meshes/dataBuffer';
 import { Color4, Color3, TmpColors } from '../Maths/math.color';
 import { ISize } from '../Maths/math.size';
+import { BaseTexture } from '../Materials/Textures/baseTexture';
+import { ThinEngine } from '../Engines/thinEngine';
+
+declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
+declare type ProceduralTexture = import("../Materials/Textures/Procedurals/proceduralTexture").ProceduralTexture;
+
+declare type Scene = import("../scene").Scene;
 
 /**
  * This represents a particle system in Babylon.
@@ -137,6 +140,9 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     private _rampGradientsTexture: Nullable<RawTexture>;
     private _useRampGradients = false;
 
+    /** Gets or sets a matrix to use to compute projection */
+    public defaultProjectionMatrix: Matrix;
+
     /** Gets or sets a boolean indicating that ramp gradients must be used
      * @see https://doc.babylonjs.com/babylon101/particles#ramp-gradients
      */
@@ -255,12 +261,12 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
      * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
      * @param name The name of the particle system
      * @param capacity The max number of particles alive at the same time
-     * @param scene The scene the particle system belongs to
+     * @param sceneOrEngine The scene the particle system belongs to or the engine to use if no scene
      * @param customEffect a custom effect used to change the way particles are rendered by default
      * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
      * @param epsilon Offset used to render the particles
      */
-    constructor(name: string, capacity: number, scene: Scene, customEffect: Nullable<Effect> = null, isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
+    constructor(name: string, capacity: number, sceneOrEngine: Scene | ThinEngine, customEffect: Nullable<Effect> = null, isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
         super(name);
 
         this._capacity = capacity;
@@ -268,18 +274,22 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         this._epsilon = epsilon;
         this._isAnimationSheetEnabled = isAnimationSheetEnabled;
 
-        this._scene = scene || EngineStore.LastCreatedScene;
-
-        this.uniqueId = this._scene.getUniqueId();
+        if (!sceneOrEngine || sceneOrEngine.getClassName() === "Scene") {
+            this._scene = (sceneOrEngine as Scene) || EngineStore.LastCreatedScene;
+            this._engine = this._scene.getEngine();
+            this.uniqueId = this._scene.getUniqueId();
+            this._scene.particleSystems.push(this);
+        } else {
+            this._engine = (sceneOrEngine as ThinEngine);
+            this.defaultProjectionMatrix = Matrix.PerspectiveFovLH(0.8, 1, 0.1, 100);
+        }
 
         // Setup the default processing configuration to the scene.
         this._attachImageProcessingConfiguration(null);
 
         this._customEffect = { 0: customEffect };
 
-        this._scene.particleSystems.push(this);
-
-        this._useInstancing = this._scene.getEngine().getCaps().instancedArrays;
+        this._useInstancing = this._engine.getCaps().instancedArrays;
 
         this._createIndexBuffer();
         this._createVertexBuffers();
@@ -793,7 +803,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     }
 
     private _createRampGradientTexture() {
-        if (!this._rampGradients || !this._rampGradients.length || this._rampGradientsTexture) {
+        if (!this._rampGradients || !this._rampGradients.length || this._rampGradientsTexture || !this._scene) {
             return;
         }
 
@@ -814,7 +824,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
         }
 
-        this._rampGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        this._rampGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
     }
 
     /**
@@ -982,7 +992,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             this._vertexBufferSize += 4;
         }
 
-        let engine = this._scene.getEngine();
+        let engine = this._engine;
         this._vertexData = new Float32Array(this._capacity * this._vertexBufferSize * (this._useInstancing ? 1 : 4));
         this._vertexBuffer = new Buffer(engine, this._vertexData, true, this._vertexBufferSize);
 
@@ -1050,7 +1060,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             index += 4;
         }
 
-        this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
+        this._indexBuffer = this._engine.createIndexBuffer(indices);
     }
 
     /**
@@ -1140,8 +1150,8 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
 
         if (this.preWarmCycles) {
-            if (this.emitter instanceof AbstractMesh) {
-                this.emitter.computeWorldMatrix(true);
+            if (this.emitter?.getClassName().indexOf("Mesh") !== -1) {
+                (this.emitter as any).computeWorldMatrix(true);
             }
 
             let noiseTextureAsProcedural = this.noiseTexture as ProceduralTexture;
@@ -1163,8 +1173,8 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
 
         // Animations
-        if (this.beginAnimationOnStart && this.animations && this.animations.length > 0) {
-            this.getScene().beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
+        if (this.beginAnimationOnStart && this.animations && this.animations.length > 0 && this._scene) {
+            this._scene.beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
         }
     }
 
@@ -1607,28 +1617,30 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
      * @param blendMode blend mode to take into account when updating the array
      */
     public fillDefines(defines: Array<string>, blendMode: number) {
-        if (this._scene.clipPlane) {
-            defines.push("#define CLIPPLANE");
-        }
+        if (this._scene) {
+            if (this._scene.clipPlane) {
+                defines.push("#define CLIPPLANE");
+            }
 
-        if (this._scene.clipPlane2) {
-            defines.push("#define CLIPPLANE2");
-        }
+            if (this._scene.clipPlane2) {
+                defines.push("#define CLIPPLANE2");
+            }
 
-        if (this._scene.clipPlane3) {
-            defines.push("#define CLIPPLANE3");
-        }
+            if (this._scene.clipPlane3) {
+                defines.push("#define CLIPPLANE3");
+            }
 
-        if (this._scene.clipPlane4) {
-            defines.push("#define CLIPPLANE4");
-        }
+            if (this._scene.clipPlane4) {
+                defines.push("#define CLIPPLANE4");
+            }
 
-        if (this._scene.clipPlane5) {
-            defines.push("#define CLIPPLANE5");
-        }
+            if (this._scene.clipPlane5) {
+                defines.push("#define CLIPPLANE5");
+            }
 
-        if (this._scene.clipPlane6) {
-            defines.push("#define CLIPPLANE6");
+            if (this._scene.clipPlane6) {
+                defines.push("#define CLIPPLANE6");
+            }
         }
 
         if (this._isAnimationSheetEnabled) {
@@ -1709,7 +1721,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
             this.fillUniformsAttributesAndSamplerNames(effectCreationOption, attributesNamesOrOptions, samplers);
 
-            this._effect = this._scene.getEngine().createEffect(
+            this._effect = this._engine.createEffect(
                 "particles",
                 attributesNamesOrOptions,
                 effectCreationOption,
@@ -1728,7 +1740,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             return;
         }
 
-        if (!preWarmOnly) {
+        if (!preWarmOnly && this._scene) {
             // Check
             if (!this.isReady()) {
                 return;
@@ -1740,7 +1752,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             this._currentRenderId = this._scene.getFrameId();
         }
 
-        this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());
+        this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene?.getAnimationRatio() || 1);
 
         // Determine the number of particles we need to create
         var newParticles;
@@ -1794,7 +1806,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
                 if (this.onAnimationEnd) {
                     this.onAnimationEnd();
                 }
-                if (this.disposeOnStop) {
+                if (this.disposeOnStop && this._scene) {
                     this._scene._toBeDisposed.push(this);
                 }
             }
@@ -1848,7 +1860,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
      * @return true if the system is ready
      */
     public isReady(): boolean {
-        if (!this.emitter || !this._imageProcessingConfiguration.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
+        if (!this.emitter || this._imageProcessingConfiguration && !this._imageProcessingConfiguration.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
             return false;
         }
 
@@ -1871,15 +1883,15 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     private _render(blendMode: number) {
         var effect = this._getEffect(blendMode);
 
-        var engine = this._scene.getEngine();
+        var engine = this._engine;
 
         // Render
         engine.enableEffect(effect);
 
-        var viewMatrix = this._scene.getViewMatrix();
+        var viewMatrix = this._scene?.getViewMatrix() || Matrix.IdentityReadOnly;
         effect.setTexture("diffuseSampler", this.particleTexture);
         effect.setMatrix("view", viewMatrix);
-        effect.setMatrix("projection", this._scene.getProjectionMatrix());
+        effect.setMatrix("projection", this.defaultProjectionMatrix ?? this._scene!.getProjectionMatrix());
 
         if (this._isAnimationSheetEnabled && this.particleTexture) {
             var baseSize = this.particleTexture.getBaseSize();
@@ -1889,7 +1901,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         effect.setVector2("translationPivot", this.translationPivot);
         effect.setFloat4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
 
-        if (this._isBillboardBased) {
+        if (this._isBillboardBased && this._scene) {
             var camera = this._scene.activeCamera!;
             effect.setVector3("eyePosition", camera.globalPosition);
         }
@@ -1904,8 +1916,10 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
         const defines = effect.defines;
 
-        if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4 || this._scene.clipPlane5 || this._scene.clipPlane6) {
-            MaterialHelper.BindClipPlane(effect, this._scene);
+        if (this._scene) {
+            if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4 || this._scene.clipPlane5 || this._scene.clipPlane6) {
+                MaterialHelper.BindClipPlane(effect, this._scene);
+            }
         }
 
         if (defines.indexOf("#define BILLBOARDMODE_ALL") >= 0) {
@@ -1942,9 +1956,9 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
 
         if (this._useInstancing) {
-            engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._particles.length);
+            engine.drawArraysType(Constants.MATERIAL_TriangleFanDrawMode, 0, 4, this._particles.length);
         } else {
-            engine.drawElementsType(Material.TriangleFillMode, 0, this._particles.length * 6);
+            engine.drawElementsType(Constants.MATERIAL_TriangleFillMode, 0, this._particles.length * 6);
         }
 
         return this._particles.length;
@@ -1960,11 +1974,13 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             return 0;
         }
 
-        var engine = this._scene.getEngine();
-        engine.setState(false);
+        var engine = this._engine as any;
+        if (engine.setState) {
+            engine.setState(false);
 
-        if (this.forceDepthWrite) {
-            engine.setDepthWrite(true);
+            if (this.forceDepthWrite) {
+                engine.setDepthWrite(true);
+            }
         }
 
         let outparticles = 0;
@@ -1974,8 +1990,8 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
         outparticles = this._render(this.blendMode);
 
-        engine.unbindInstanceAttributes();
-        engine.setAlphaMode(Constants.ALPHA_DISABLE);
+        this._engine.unbindInstanceAttributes();
+        this._engine.setAlphaMode(Constants.ALPHA_DISABLE);
 
         return outparticles;
     }
@@ -1996,7 +2012,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
 
         if (this._indexBuffer) {
-            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+            this._engine._releaseBuffer(this._indexBuffer);
             this._indexBuffer = null;
         }
 
@@ -2037,12 +2053,14 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         }
 
         // Remove from scene
-        var index = this._scene.particleSystems.indexOf(this);
-        if (index > -1) {
-            this._scene.particleSystems.splice(index, 1);
-        }
+        if (this._scene) {
+            var index = this._scene.particleSystems.indexOf(this);
+            if (index > -1) {
+                this._scene.particleSystems.splice(index, 1);
+            }
 
-        this._scene._activeParticleSystems.dispose();
+            this._scene._activeParticleSystems.dispose();
+        }
 
         // Callback
         this.onDisposeObservable.notifyObservers(this);
@@ -2061,14 +2079,17 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     public clone(name: string, newEmitter: any): ParticleSystem {
         var custom = { ...this._customEffect };
         var program: any = null;
-        if (this.customShader != null) {
-            program = this.customShader;
-            var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
-            custom[0] = this._scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+        var engine = this._engine as any;
+        if (engine.createEffectForParticles) {
+            if (this.customShader != null) {
+                program = this.customShader;
+                var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
+                custom[0] = engine.createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+            }
         }
 
         let serialization = this.serialize();
-        var result = ParticleSystem.Parse(serialization, this._scene, "");
+        var result = ParticleSystem.Parse(serialization, this._scene || this._engine, "");
         result.name = name;
         result.customShader = program;
         result._customEffect = custom;
@@ -2150,7 +2171,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
                 serializationObject.texture = particleSystem.particleTexture.serialize();
             } else {
                 serializationObject.textureName = particleSystem.particleTexture.name;
-                serializationObject.invertY = particleSystem.particleTexture._invertY;
+                serializationObject.invertY = !!(particleSystem.particleTexture as any)._invertY;
             }
         }
 
@@ -2442,20 +2463,31 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     }
 
     /** @hidden */
-    public static _Parse(parsedParticleSystem: any, particleSystem: IParticleSystem, scene: Scene, rootUrl: string) {
-        // Texture
-        if (parsedParticleSystem.texture) {
-            particleSystem.particleTexture = Texture.Parse(parsedParticleSystem.texture, scene, rootUrl) as Texture;
-        } else if (parsedParticleSystem.textureName) {
-            particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene, false, parsedParticleSystem.invertY !== undefined ? parsedParticleSystem.invertY : true);
-            particleSystem.particleTexture.name = parsedParticleSystem.textureName;
+    public static _Parse(parsedParticleSystem: any, particleSystem: IParticleSystem, sceneOrEngine: Scene | ThinEngine, rootUrl: string) {
+        let scene: Nullable<Scene>;
+
+        if (sceneOrEngine instanceof ThinEngine) {
+            scene = null;
+        } else {
+            scene = sceneOrEngine as Scene;
+        }
+
+        const internalClass = _TypeStore.GetClass("BABYLON.Texture");
+        if (internalClass && scene) {
+            // Texture
+            if (parsedParticleSystem.texture) {
+                particleSystem.particleTexture = internalClass.Parse(parsedParticleSystem.texture, scene, rootUrl) as BaseTexture;
+            } else if (parsedParticleSystem.textureName) {
+                particleSystem.particleTexture = new internalClass(rootUrl + parsedParticleSystem.textureName, scene, false, parsedParticleSystem.invertY !== undefined ? parsedParticleSystem.invertY : true);
+                particleSystem.particleTexture!.name = parsedParticleSystem.textureName;
+            }
         }
 
         // Emitter
         if (!parsedParticleSystem.emitterId && parsedParticleSystem.emitterId !== 0 && parsedParticleSystem.emitter === undefined) {
             particleSystem.emitter = Vector3.Zero();
         }
-        else if (parsedParticleSystem.emitterId) {
+        else if (parsedParticleSystem.emitterId && scene) {
             particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
         } else {
             particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
@@ -2491,7 +2523,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             particleSystem.beginAnimationLoop = parsedParticleSystem.beginAnimationLoop;
         }
 
-        if (parsedParticleSystem.autoAnimate) {
+        if (parsedParticleSystem.autoAnimate && scene) {
             scene.beginAnimation(particleSystem, parsedParticleSystem.autoAnimateFrom, parsedParticleSystem.autoAnimateTo, parsedParticleSystem.autoAnimateLoop, parsedParticleSystem.autoAnimateSpeed || 1.0);
         }
 
@@ -2609,8 +2641,9 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             particleSystem.limitVelocityDamping = parsedParticleSystem.limitVelocityDamping;
         }
 
-        if (parsedParticleSystem.noiseTexture) {
-            particleSystem.noiseTexture = ProceduralTexture.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
+        if (parsedParticleSystem.noiseTexture && scene) {
+            const internalClass = _TypeStore.GetClass("BABYLON.ProceduralTexture");
+            particleSystem.noiseTexture = internalClass.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
         }
 
         // Emitter
@@ -2668,21 +2701,31 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
     /**
      * Parses a JSON object to create a particle system.
      * @param parsedParticleSystem The JSON object to parse
-     * @param scene The scene to create the particle system in
+     * @param sceneOrEngine The scene or the engine to create the particle system in
      * @param rootUrl The root url to use to load external dependencies like texture
      * @param doNotStart Ignore the preventAutoStart attribute and does not start
      * @returns the Parsed particle system
      */
-    public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string, doNotStart = false): ParticleSystem {
+    public static Parse(parsedParticleSystem: any, sceneOrEngine: Scene | ThinEngine, rootUrl: string, doNotStart = false): ParticleSystem {
         var name = parsedParticleSystem.name;
         var custom: Nullable<Effect> = null;
         var program: any = null;
-        if (parsedParticleSystem.customShader) {
+        let engine: ThinEngine;
+        let scene: Nullable<Scene>;
+
+        if (sceneOrEngine instanceof ThinEngine) {
+            engine = sceneOrEngine;
+        } else {
+            scene = sceneOrEngine as Scene;
+            engine = scene.getEngine();
+        }
+
+        if (parsedParticleSystem.customShader && (engine as any).createEffectForParticles) {
             program = parsedParticleSystem.customShader;
             var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
-            custom = scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+            custom = (engine as any).createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
         }
-        var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene, custom, parsedParticleSystem.isAnimationSheetEnabled);
+        var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, sceneOrEngine, custom, parsedParticleSystem.isAnimationSheetEnabled);
         particleSystem.customShader = program;
 
         if (parsedParticleSystem.id) {
@@ -2695,14 +2738,14 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             for (var cell of parsedParticleSystem.subEmitters) {
                 let cellArray = [];
                 for (var sub of cell) {
-                    cellArray.push(SubEmitter.Parse(sub, scene, rootUrl));
+                    cellArray.push(SubEmitter.Parse(sub, sceneOrEngine, rootUrl));
                 }
 
                 particleSystem.subEmitters.push(cellArray);
             }
         }
 
-        ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
+        ParticleSystem._Parse(parsedParticleSystem, particleSystem, sceneOrEngine, rootUrl);
 
         if (parsedParticleSystem.textureMask) {
             particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);

+ 13 - 10
src/Particles/subEmitter.ts

@@ -1,9 +1,10 @@
 import { Vector3 } from "../Maths/math.vector";
-import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Mesh } from "../Meshes/mesh";
 import { _DevTools } from '../Misc/devTools';
+import { ThinEngine } from '../Engines/thinEngine';
+import { _TypeStore } from '../Misc/typeStore';
 
 declare type Scene = import("../scene").Scene;
+declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
 declare type ParticleSystem = import("../Particles/particleSystem").ParticleSystem;
 
 /**
@@ -49,7 +50,8 @@ export class SubEmitter {
     ) {
         // Create mesh as emitter to support rotation
         if (!particleSystem.emitter || !(<AbstractMesh>particleSystem.emitter).dispose) {
-            particleSystem.emitter = new AbstractMesh("SubemitterSystemEmitter", particleSystem.getScene());
+            const internalClass = _TypeStore.GetClass("BABYLON.AbstractMesh");
+            particleSystem.emitter = new internalClass("SubemitterSystemEmitter", particleSystem.getScene());
         }
 
         // Automatically dispose of subemitter when system is disposed
@@ -70,9 +72,10 @@ export class SubEmitter {
             emitter = new Vector3();
         } else if (emitter instanceof Vector3) {
             emitter = emitter.clone();
-        } else if (emitter instanceof AbstractMesh) {
-            emitter = new Mesh("", emitter.getScene());
-            emitter.isVisible = false;
+        } else if (emitter.getClassName().indexOf("Mesh") !== -1) {
+            const internalClass = _TypeStore.GetClass("BABYLON.Mesh");
+            emitter = new internalClass("", emitter.getScene());
+            (emitter! as any).isVisible = false;
         }
         var clone = new SubEmitter(this.particleSystem.clone("", emitter));
 
@@ -103,20 +106,20 @@ export class SubEmitter {
     }
 
     /** @hidden */
-    public static _ParseParticleSystem(system: any, scene: Scene, rootUrl: string): ParticleSystem {
+    public static _ParseParticleSystem(system: any, sceneOrEngine: Scene | ThinEngine, rootUrl: string): ParticleSystem {
         throw _DevTools.WarnImport("ParseParticle");
     }
 
     /**
      * Creates a new SubEmitter from a serialized JSON version
      * @param serializationObject defines the JSON object to read from
-     * @param scene defines the hosting scene
+     * @param sceneOrEngine defines the hosting scene or the hosting engine
      * @param rootUrl defines the rootUrl for data loading
      * @returns a new SubEmitter
      */
-    public static Parse(serializationObject: any, scene: Scene, rootUrl: string): SubEmitter {
+    public static Parse(serializationObject: any, sceneOrEngine: Scene | ThinEngine, rootUrl: string): SubEmitter {
         let system = serializationObject.particleSystem;
-        let subEmitter = new SubEmitter(SubEmitter._ParseParticleSystem(system, scene, rootUrl));
+        let subEmitter = new SubEmitter(SubEmitter._ParseParticleSystem(system, sceneOrEngine, rootUrl));
         subEmitter.type = serializationObject.type;
         subEmitter.inheritDirection = serializationObject.inheritDirection;
         subEmitter.inheritedVelocityAmount = serializationObject.inheritedVelocityAmount;