Sfoglia il codice sorgente

Added a OptimizeNormals option to the OBJ loader to smooth lighting

Popov72 4 anni fa
parent
commit
4ebb1d5074
2 ha cambiato i file con 68 aggiunte e 0 eliminazioni
  1. 1 0
      dist/preview release/what's new.md
  2. 67 0
      loaders/src/OBJ/objFileLoader.ts

+ 1 - 0
dist/preview release/what's new.md

@@ -25,6 +25,7 @@
 - Changed glTF loader to remove empty animation groups if there are no animation channels loaded with the given options. ([bghgary](https://github.com/bghgary))
 - Changed glTF loader to remove empty animation groups if there are no animation channels loaded with the given options. ([bghgary](https://github.com/bghgary))
 - Update glTF validator to `2.0.0-dev.3.3`. ([bghgary](https://github.com/bghgary))
 - Update glTF validator to `2.0.0-dev.3.3`. ([bghgary](https://github.com/bghgary))
 - Added support for KHR_xmp_json_ld for glTF loader. ([Sebavan](https://github.com/sebavan/), [bghgary](https://github.com/bghgary))
 - Added support for KHR_xmp_json_ld for glTF loader. ([Sebavan](https://github.com/sebavan/), [bghgary](https://github.com/bghgary))
+- Added a `OptimizeNormals` option to the OBJ loader to smooth lighting ([Popov72](https://github.com/Popov72))
 
 
 ### Navigation
 ### Navigation
 
 

+ 67 - 0
loaders/src/OBJ/objFileLoader.ts

@@ -12,6 +12,7 @@ import { AssetContainer } from "babylonjs/assetContainer";
 import { Scene } from "babylonjs/scene";
 import { Scene } from "babylonjs/scene";
 import { WebRequest } from 'babylonjs/Misc/webRequest';
 import { WebRequest } from 'babylonjs/Misc/webRequest';
 import { MTLFileLoader } from './mtlFileLoader';
 import { MTLFileLoader } from './mtlFileLoader';
+import { VertexBuffer } from "babylonjs/Meshes/buffer";
 
 
 type MeshObject = {
 type MeshObject = {
     name: string;
     name: string;
@@ -52,6 +53,11 @@ type MeshLoadOptions = {
      */
      */
     ComputeNormals: boolean,
     ComputeNormals: boolean,
     /**
     /**
+     * Optimize the normals for the model. Lighting can be uneven if you use OptimizeWithUV = true because new vertices can be created for the same location if they pertain to different faces.
+     * Using OptimizehNormals = true will help smoothing the lighting by averaging the normals of those vertices.
+     */
+    OptimizeNormals: boolean,
+    /**
      * Skip loading the materials even if defined in the OBJ file (materials are ignored).
      * Skip loading the materials even if defined in the OBJ file (materials are ignored).
      */
      */
     SkipMaterials: boolean,
     SkipMaterials: boolean,
@@ -95,6 +101,11 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
      */
      */
     public static COMPUTE_NORMALS = false;
     public static COMPUTE_NORMALS = false;
     /**
     /**
+     * Optimize the normals for the model. Lighting can be uneven if you use OptimizeWithUV = true because new vertices can be created for the same location if they pertain to different faces.
+     * Using OptimizehNormals = true will help smoothing the lighting by averaging the normals of those vertices.
+     */
+    public static OPTIMIZE_NORMALS = false;
+    /**
      * Defines custom scaling of UV coordinates of loaded meshes.
      * Defines custom scaling of UV coordinates of loaded meshes.
      */
      */
     public static UV_SCALING = new Vector2(1, 1);
     public static UV_SCALING = new Vector2(1, 1);
@@ -167,6 +178,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
     private static get currentMeshLoadOptions(): MeshLoadOptions {
     private static get currentMeshLoadOptions(): MeshLoadOptions {
         return {
         return {
             ComputeNormals: OBJFileLoader.COMPUTE_NORMALS,
             ComputeNormals: OBJFileLoader.COMPUTE_NORMALS,
+            OptimizeNormals: OBJFileLoader.OPTIMIZE_NORMALS,
             ImportVertexColors: OBJFileLoader.IMPORT_VERTEX_COLORS,
             ImportVertexColors: OBJFileLoader.IMPORT_VERTEX_COLORS,
             InvertY: OBJFileLoader.INVERT_Y,
             InvertY: OBJFileLoader.INVERT_Y,
             InvertTextureY: OBJFileLoader.INVERT_TEXTURE_Y,
             InvertTextureY: OBJFileLoader.INVERT_TEXTURE_Y,
@@ -304,6 +316,58 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
         });
         });
     }
     }
 
 
+    private _optimizeNormals(mesh: AbstractMesh): void {
+        const positions = mesh.getVerticesData(VertexBuffer.PositionKind);
+        const normals = mesh.getVerticesData(VertexBuffer.NormalKind);
+        const mapVertices: { [key: string]: number[] } = {};
+    
+        if (!positions || !normals) {
+            return;
+        }
+
+        for (let i = 0; i < positions.length / 3; i++) {
+            const x = positions[i * 3 + 0];
+            const y = positions[i * 3 + 1];
+            const z = positions[i * 3 + 2];
+            const key = x + "_" + y + "_" + z;
+    
+            let lst = mapVertices[key];
+            if (!lst) {
+                lst = [];
+                mapVertices[key] = lst;
+            }
+            lst.push(i);
+        }
+    
+        const normal = new Vector3();
+        for (const key in mapVertices) {
+            const lst = mapVertices[key];
+            if (lst.length < 2) {
+                continue;
+            }
+
+            const v0Idx = lst[0];
+            for (let i = 1; i < lst.length; ++i) {
+                const vIdx = lst[i];
+                normals[v0Idx * 3 + 0] += normals[vIdx * 3 + 0];
+                normals[v0Idx * 3 + 1] += normals[vIdx * 3 + 1];
+                normals[v0Idx * 3 + 2] += normals[vIdx * 3 + 2];
+            }
+
+            normal.copyFromFloats(normals[v0Idx * 3 + 0], normals[v0Idx * 3 + 1], normals[v0Idx * 3 + 2]);
+            normal.normalize();
+
+            for (let i = 0; i < lst.length; ++i) {
+                const vIdx = lst[i];
+                normals[vIdx * 3 + 0] = normal.x;
+                normals[vIdx * 3 + 1] = normal.y;
+                normals[vIdx * 3 + 2] = normal.z;
+            }
+        }
+    
+        mesh.setVerticesData(VertexBuffer.NormalKind, normals);
+    }   
+ 
     /**
     /**
      * Read the OBJ file and create an Array of meshes.
      * Read the OBJ file and create an Array of meshes.
      * Each mesh contains all information given by the OBJ and the MTL file.
      * Each mesh contains all information given by the OBJ and the MTL file.
@@ -956,6 +1020,9 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
             if (this._meshLoadOptions.InvertY) {
             if (this._meshLoadOptions.InvertY) {
                 babylonMesh.scaling.y *= -1;
                 babylonMesh.scaling.y *= -1;
             }
             }
+            if (this._meshLoadOptions.OptimizeNormals === true) {
+                this._optimizeNormals(babylonMesh);
+            }
 
 
             //Push the mesh into an array
             //Push the mesh into an array
             babylonMeshesArray.push(babylonMesh);
             babylonMeshesArray.push(babylonMesh);