|
@@ -201,10 +201,31 @@ module BABYLON {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ type MeshObject = {
|
|
|
+ name: string;
|
|
|
+ indices?: Array<number>;
|
|
|
+ positions?: Array<number>;
|
|
|
+ normals?: Array<number>;
|
|
|
+ colors?: Array<number>;
|
|
|
+ uvs?: Array<number>;
|
|
|
+ materialName: string;
|
|
|
+ };
|
|
|
+
|
|
|
export class OBJFileLoader implements ISceneLoaderPluginAsync {
|
|
|
|
|
|
public static OPTIMIZE_WITH_UV = false;
|
|
|
+ /**
|
|
|
+ * Invert model on y-axis (does a model scaling inversion)
|
|
|
+ */
|
|
|
public static INVERT_Y = false;
|
|
|
+ /**
|
|
|
+ * Include in meshes the vertex colors available in some OBJ files. This is not part of OBJ standard.
|
|
|
+ */
|
|
|
+ public static IMPORT_VERTEX_COLORS = false;
|
|
|
+ /**
|
|
|
+ * Compute the normals for the model, even if normals are present in the file
|
|
|
+ */
|
|
|
+ public static COMPUTE_NORMALS = false;
|
|
|
public name = "obj";
|
|
|
public extensions = ".obj";
|
|
|
public obj = /^o/;
|
|
@@ -212,7 +233,7 @@ module BABYLON {
|
|
|
public mtllib = /^mtllib /;
|
|
|
public usemtl = /^usemtl /;
|
|
|
public smooth = /^s /;
|
|
|
- public vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
|
|
|
+ public vertexPattern = /v( +[\d|\.|\+|\-|e|E]+){3,7}/;
|
|
|
// vn float float float
|
|
|
public normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
|
|
|
// vt float float
|
|
@@ -253,10 +274,10 @@ module BABYLON {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Imports one or more meshes from the loaded glTF data and adds them to the scene
|
|
|
+ * Imports one or more meshes from the loaded OBJ data and adds them to the scene
|
|
|
* @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
|
|
|
* @param scene the scene the meshes should be added to
|
|
|
- * @param data the glTF data to load
|
|
|
+ * @param data the OBJ data to load
|
|
|
* @param rootUrl root url to load from
|
|
|
* @param onProgress event that fires when loading progress has occured
|
|
|
* @param fileName Defines the name of the file to load
|
|
@@ -275,9 +296,10 @@ module BABYLON {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Imports all objects from the loaded glTF data and adds them to the scene
|
|
|
+ * Imports all objects from the loaded OBJ data and adds them to the scene
|
|
|
+ *
|
|
|
* @param scene the scene the objects should be added to
|
|
|
- * @param data the glTF data to load
|
|
|
+ * @param data the OBJ data to load
|
|
|
* @param rootUrl root url to load from
|
|
|
* @param onProgress event that fires when loading progress has occured
|
|
|
* @param fileName Defines the name of the file to load
|
|
@@ -321,20 +343,22 @@ module BABYLON {
|
|
|
* @private
|
|
|
*/
|
|
|
private _parseSolid(meshesNames: any, scene: BABYLON.Scene, data: string, rootUrl: string): Promise<Array<AbstractMesh>> {
|
|
|
-
|
|
|
var positions: Array<BABYLON.Vector3> = []; //values for the positions of vertices
|
|
|
var normals: Array<BABYLON.Vector3> = []; //Values for the normals
|
|
|
var uvs: Array<BABYLON.Vector2> = []; //Values for the textures
|
|
|
- var meshesFromObj: Array<any> = []; //[mesh] Contains all the obj meshes
|
|
|
- var handledMesh: any; //The current mesh of meshes array
|
|
|
+ var colors: Array<BABYLON.Color4> = [];
|
|
|
+ var meshesFromObj: Array<MeshObject> = []; //[mesh] Contains all the obj meshes
|
|
|
+ var handledMesh: MeshObject; //The current mesh of meshes array
|
|
|
var indicesForBabylon: Array<number> = []; //The list of indices for VertexData
|
|
|
var wrappedPositionForBabylon: Array<BABYLON.Vector3> = []; //The list of position in vectors
|
|
|
+ var wrappedColorsForBabylon: Array<BABYLON.Color4> = []; // Array with all color values to match with the indices
|
|
|
var wrappedUvsForBabylon: Array<BABYLON.Vector2> = []; //Array with all value of uvs to match with the indices
|
|
|
var wrappedNormalsForBabylon: Array<BABYLON.Vector3> = []; //Array with all value of normals to match with the indices
|
|
|
var tuplePosNorm: Array<{ normals: Array<number>; idx: Array<number>; uv: Array<number> }> = []; //Create a tuple with indice of Position, Normal, UV [pos, norm, uvs]
|
|
|
var curPositionInIndices = 0;
|
|
|
var hasMeshes: Boolean = false; //Meshes are defined in the file
|
|
|
- var unwrappedPositionsForBabylon: Array<number> = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
|
|
|
+ var unwrappedPositionsForBabylon: Array<number> = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
|
|
|
+ var unwrappedColorsForBabylon: Array<number> = []; // Value of colorForBabylon w/o Color4() [r,g,b,a]
|
|
|
var unwrappedNormalsForBabylon: Array<number> = []; //Value of normalsForBabylon w/o Vector3() [x,y,z]
|
|
|
var unwrappedUVForBabylon: Array<number> = []; //Value of uvsForBabylon w/o Vector3() [x,y,z]
|
|
|
var triangles: Array<string> = []; //Indices from new triangles coming from polygons
|
|
@@ -345,11 +369,13 @@ module BABYLON {
|
|
|
var increment: number = 1; //Id for meshes created by the multimaterial
|
|
|
var isFirstMaterial: boolean = true;
|
|
|
|
|
|
+ var grayColor = new BABYLON.Color4(0.5, 0.5, 0.5, 1);
|
|
|
+
|
|
|
/**
|
|
|
* Search for obj in the given array.
|
|
|
* This function is called to check if a couple of data already exists in an array.
|
|
|
*
|
|
|
- * If found, returns the index of the founded tuple index. Returns -1 if not found
|
|
|
+ * If found, returns the index of the found tuple index. Returns -1 if not found
|
|
|
* @param arr Array<{ normals: Array<number>, idx: Array<number> }>
|
|
|
* @param obj Array<number>
|
|
|
* @returns {boolean}
|
|
@@ -383,7 +409,7 @@ module BABYLON {
|
|
|
* @param textureVectorFromOBJ Vector3 The value of uvs
|
|
|
* @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
|
|
|
*/
|
|
|
- var setData = (indicePositionFromObj: number, indiceUvsFromObj: number, indiceNormalFromObj: number, positionVectorFromOBJ: BABYLON.Vector3, textureVectorFromOBJ: BABYLON.Vector2, normalsVectorFromOBJ: BABYLON.Vector3) => {
|
|
|
+ var setData = (indicePositionFromObj: number, indiceUvsFromObj: number, indiceNormalFromObj: number, positionVectorFromOBJ: BABYLON.Vector3, textureVectorFromOBJ: BABYLON.Vector2, normalsVectorFromOBJ: BABYLON.Vector3, positionColorsFromOBJ?: BABYLON.Color4) => {
|
|
|
//Check if this tuple already exists in the list of tuples
|
|
|
var _index: number;
|
|
|
if (OBJFileLoader.OPTIMIZE_WITH_UV) {
|
|
@@ -421,6 +447,13 @@ module BABYLON {
|
|
|
//Push the normals for Babylon
|
|
|
//Each element is a BABYLON.Vector3(x,y,z)
|
|
|
wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
|
|
|
+
|
|
|
+ if (positionColorsFromOBJ !== undefined) {
|
|
|
+ //Push the colors for Babylon
|
|
|
+ //Each element is a BABYLON.Color4(r,g,b,a)
|
|
|
+ wrappedColorsForBabylon.push(positionColorsFromOBJ);
|
|
|
+ }
|
|
|
+
|
|
|
//Add the tuple in the comparison list
|
|
|
tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
|
|
|
tuplePosNorm[indicePositionFromObj].idx.push(curPositionInIndices++);
|
|
@@ -428,13 +461,13 @@ module BABYLON {
|
|
|
} else {
|
|
|
//The tuple already exists
|
|
|
//Add the index of the already existing tuple
|
|
|
- //At this index we can get the value of position, normal and uvs of vertex
|
|
|
+ //At this index we can get the value of position, normal, color and uvs of vertex
|
|
|
indicesForBabylon.push(_index);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * Transform BABYLON.Vector() object onto 3 digits in an array
|
|
|
+ * Transform BABYLON.Vector() and BABYLON.Color() objects into numbers in an array
|
|
|
*/
|
|
|
var unwrapData = () => {
|
|
|
//Every array has the same length
|
|
@@ -443,11 +476,16 @@ module BABYLON {
|
|
|
unwrappedPositionsForBabylon.push(wrappedPositionForBabylon[l].x, wrappedPositionForBabylon[l].y, wrappedPositionForBabylon[l].z);
|
|
|
unwrappedNormalsForBabylon.push(wrappedNormalsForBabylon[l].x, wrappedNormalsForBabylon[l].y, wrappedNormalsForBabylon[l].z);
|
|
|
unwrappedUVForBabylon.push(wrappedUvsForBabylon[l].x, wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
|
|
|
+ if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
|
|
|
+ //Push the r, g, b, a values of each element in the unwrapped array
|
|
|
+ unwrappedColorsForBabylon.push(wrappedColorsForBabylon[l].r, wrappedColorsForBabylon[l].g, wrappedColorsForBabylon[l].b, wrappedColorsForBabylon[l].a);
|
|
|
+ }
|
|
|
}
|
|
|
// Reset arrays for the next new meshes
|
|
|
wrappedPositionForBabylon = [];
|
|
|
wrappedNormalsForBabylon = [];
|
|
|
wrappedUvsForBabylon = [];
|
|
|
+ wrappedColorsForBabylon = [];
|
|
|
tuplePosNorm = [];
|
|
|
curPositionInIndices = 0;
|
|
|
};
|
|
@@ -504,7 +542,8 @@ module BABYLON {
|
|
|
indicePositionFromObj,
|
|
|
0, 0, //In the pattern 1, normals and uvs are not defined
|
|
|
positions[indicePositionFromObj], //Get the vectors data
|
|
|
- BABYLON.Vector2.Zero(), BABYLON.Vector3.Up() //Create default vectors
|
|
|
+ BABYLON.Vector2.Zero(), BABYLON.Vector3.Up(), //Create default vectors
|
|
|
+ OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
|
|
|
);
|
|
|
}
|
|
|
//Reset variable for the next line
|
|
@@ -535,7 +574,8 @@ module BABYLON {
|
|
|
0, //Default value for normals
|
|
|
positions[indicePositionFromObj], //Get the values for each element
|
|
|
uvs[indiceUvsFromObj],
|
|
|
- BABYLON.Vector3.Up() //Default value for normals
|
|
|
+ BABYLON.Vector3.Up(), //Default value for normals
|
|
|
+ OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -566,7 +606,8 @@ module BABYLON {
|
|
|
|
|
|
setData(
|
|
|
indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
|
|
|
- positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
|
|
|
+ positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
|
|
|
+ OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
|
|
|
);
|
|
|
|
|
|
}
|
|
@@ -597,7 +638,8 @@ module BABYLON {
|
|
|
indiceNormalFromObj,
|
|
|
positions[indicePositionFromObj], //Get each vector of data
|
|
|
BABYLON.Vector2.Zero(),
|
|
|
- normals[indiceNormalFromObj]
|
|
|
+ normals[indiceNormalFromObj],
|
|
|
+ OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
|
|
|
);
|
|
|
}
|
|
|
//Reset variable for the next line
|
|
@@ -627,7 +669,8 @@ module BABYLON {
|
|
|
|
|
|
setData(
|
|
|
indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
|
|
|
- positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
|
|
|
+ positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
|
|
|
+ OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
|
|
|
);
|
|
|
|
|
|
}
|
|
@@ -655,9 +698,14 @@ module BABYLON {
|
|
|
handledMesh.normals = unwrappedNormalsForBabylon.slice();
|
|
|
handledMesh.uvs = unwrappedUVForBabylon.slice();
|
|
|
|
|
|
+ if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
|
|
|
+ handledMesh.colors = unwrappedColorsForBabylon.slice();
|
|
|
+ }
|
|
|
+
|
|
|
//Reset the array for the next mesh
|
|
|
indicesForBabylon = [];
|
|
|
unwrappedPositionsForBabylon = [];
|
|
|
+ unwrappedColorsForBabylon = [];
|
|
|
unwrappedNormalsForBabylon = [];
|
|
|
unwrappedUVForBabylon = [];
|
|
|
}
|
|
@@ -676,10 +724,12 @@ module BABYLON {
|
|
|
continue;
|
|
|
|
|
|
//Get information about one position possible for the vertices
|
|
|
- } else if ((result = this.vertexPattern.exec(line)) !== null) {
|
|
|
+ } else if (this.vertexPattern.test(line)) {
|
|
|
+ result = line.split(' ');
|
|
|
+ //Value of result with line: "v 1.0 2.0 3.0"
|
|
|
+ // ["v", "1.0", "2.0", "3.0"]
|
|
|
+
|
|
|
//Create a Vector3 with the position x, y, z
|
|
|
- //Value of result:
|
|
|
- // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
|
|
|
//Add the Vector in the list of positions
|
|
|
positions.push(new BABYLON.Vector3(
|
|
|
parseFloat(result[1]),
|
|
@@ -687,6 +737,21 @@ module BABYLON {
|
|
|
parseFloat(result[3])
|
|
|
));
|
|
|
|
|
|
+ if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
|
|
|
+ if (result.length >= 7) {
|
|
|
+ // TODO: if these numbers are > 1 we can use Color4.FromInts(r,g,b,a)
|
|
|
+ colors.push(new BABYLON.Color4(
|
|
|
+ parseFloat(result[4]),
|
|
|
+ parseFloat(result[5]),
|
|
|
+ parseFloat(result[6]),
|
|
|
+ (result.length === 7 || result[7] === undefined) ? 1 : parseFloat(result[7])
|
|
|
+ ));
|
|
|
+ } else {
|
|
|
+ // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
|
|
|
+ colors.push(grayColor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
} else if ((result = this.normalPattern.exec(line)) !== null) {
|
|
|
//Create a Vector3 with the normals x, y, z
|
|
|
//Value of result
|
|
@@ -765,20 +830,12 @@ module BABYLON {
|
|
|
} else if (this.group.test(line) || this.obj.test(line)) {
|
|
|
//Create a new mesh corresponding to the name of the group.
|
|
|
//Definition of the mesh
|
|
|
- var objMesh: {
|
|
|
- name: string;
|
|
|
- indices?: Array<number>;
|
|
|
- positions?: Array<number>;
|
|
|
- normals?: Array<number>;
|
|
|
- uvs?: Array<number>;
|
|
|
- materialName: string;
|
|
|
- } =
|
|
|
- //Set the name of the current obj mesh
|
|
|
- {
|
|
|
- name: line.substring(2).trim(),
|
|
|
+ var objMesh: MeshObject = {
|
|
|
+ name: line.substring(2).trim(), //Set the name of the current obj mesh
|
|
|
indices: undefined,
|
|
|
positions: undefined,
|
|
|
normals: undefined,
|
|
|
+ colors: undefined,
|
|
|
uvs: undefined,
|
|
|
materialName: ""
|
|
|
};
|
|
@@ -802,20 +859,12 @@ module BABYLON {
|
|
|
//Set the data for the previous mesh
|
|
|
addPreviousObjMesh();
|
|
|
//Create a new mesh
|
|
|
- var objMesh: {
|
|
|
- name: string;
|
|
|
- indices?: Array<number>;
|
|
|
- positions?: Array<number>;
|
|
|
- normals?: Array<number>;
|
|
|
- uvs?: Array<number>;
|
|
|
- materialName: string;
|
|
|
- } =
|
|
|
- //Set the name of the current obj mesh
|
|
|
- {
|
|
|
- name: objMeshName + "_mm" + increment.toString(),
|
|
|
+ var objMesh: MeshObject = {
|
|
|
+ name: objMeshName + "_mm" + increment.toString(), //Set the name of the current obj mesh
|
|
|
indices: undefined,
|
|
|
positions: undefined,
|
|
|
normals: undefined,
|
|
|
+ colors: undefined,
|
|
|
uvs: undefined,
|
|
|
materialName: materialNameFromObj
|
|
|
};
|
|
@@ -851,7 +900,7 @@ module BABYLON {
|
|
|
//Set the data for the last mesh
|
|
|
handledMesh = meshesFromObj[meshesFromObj.length - 1];
|
|
|
|
|
|
- //Reverse indices for displaying faces in the good sens
|
|
|
+ //Reverse indices for displaying faces in the good sense
|
|
|
indicesForBabylon.reverse();
|
|
|
//Get the good array
|
|
|
unwrapData();
|
|
@@ -860,9 +909,13 @@ module BABYLON {
|
|
|
handledMesh.positions = unwrappedPositionsForBabylon;
|
|
|
handledMesh.normals = unwrappedNormalsForBabylon;
|
|
|
handledMesh.uvs = unwrappedUVForBabylon;
|
|
|
+
|
|
|
+ if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
|
|
|
+ handledMesh.colors = unwrappedColorsForBabylon;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- //If any o or g keyword found, create a mesj with a random id
|
|
|
+ //If any o or g keyword found, create a mesh with a random id
|
|
|
if (!hasMeshes) {
|
|
|
// reverse tab of indices
|
|
|
indicesForBabylon.reverse();
|
|
@@ -873,6 +926,7 @@ module BABYLON {
|
|
|
name: BABYLON.Geometry.RandomId(),
|
|
|
indices: indicesForBabylon,
|
|
|
positions: unwrappedPositionsForBabylon,
|
|
|
+ colors: unwrappedColorsForBabylon,
|
|
|
normals: unwrappedNormalsForBabylon,
|
|
|
uvs: unwrappedUVForBabylon,
|
|
|
materialName: materialNameFromObj
|
|
@@ -910,11 +964,23 @@ module BABYLON {
|
|
|
materialToUse.push(meshesFromObj[j].materialName);
|
|
|
|
|
|
var vertexData: VertexData = new BABYLON.VertexData(); //The container for the values
|
|
|
- //Set the data for the babylonMesh
|
|
|
- vertexData.positions = handledMesh.positions;
|
|
|
- vertexData.normals = handledMesh.normals;
|
|
|
- vertexData.uvs = handledMesh.uvs;
|
|
|
- vertexData.indices = handledMesh.indices;
|
|
|
+ //Set the vertex data for the babylonMesh
|
|
|
+ vertexData.uvs = handledMesh.uvs as FloatArray;
|
|
|
+ vertexData.indices = handledMesh.indices as FloatArray;
|
|
|
+ vertexData.positions = handledMesh.positions as FloatArray;
|
|
|
+
|
|
|
+ if (OBJFileLoader.COMPUTE_NORMALS === true) {
|
|
|
+ let normals: Array<number> = new Array<number>();
|
|
|
+ BABYLON.VertexData.ComputeNormals(handledMesh.positions, handledMesh.indices, normals);
|
|
|
+ vertexData.normals = normals;
|
|
|
+ } else {
|
|
|
+ vertexData.normals = handledMesh.normals as FloatArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
|
|
|
+ vertexData.colors = handledMesh.colors as FloatArray;
|
|
|
+ }
|
|
|
+
|
|
|
//Set the data from the VertexBuffer to the current BABYLON.Mesh
|
|
|
vertexData.applyToMesh(babylonMesh);
|
|
|
if (OBJFileLoader.INVERT_Y) {
|