Coder Social home page Coder Social logo

wwobjloader's Introduction

OBJLoader2 & OBJLoader2Parallel for three.js

License: MIT WWOBJLoader Github Pages NPM Version Gitpod Ready-to-Code

OBJLoader2 is a loader for the OBJ file format. It is an alternative to OBJLoader included in three.js. The loader and its parser can be used on Main via OBJLoader2 or in parallel inside a web worker via OBJLoader2Parallel.

Project History

New versions of OBJLoader2 and OBJLoader2Parallel are from now on again released as npm modules independent of three.js. The first stable version that was released independent again is 4.0.0. Versions 3.x.y were never released as independent npm and only in combination with three.ts itself.

Between release of version 5.0.0 and 6.0.0 all code has been transformed to TypeScript.

Changelog

Interested in recent changes? Check the CHANGELOG.

Development

Getting Started

There exist three possibilities:

  • Checkout the repository and run npm install, npm run build and then npm run dev to spin up local Vite dev server
  • Press the Gitpod button above and start coding and using the examples directly in the browser
  • Checkout the repository and use docker-compose up -d to spin up local Vite dev server.

Whatever environment you choose to start Vite is used to serve the code and the examples using it. With this setup you are able to change the code and examples without invoking an additional bundler. Vite ensures all imported npm modules are available if previously installed in local environment (see npm install).

If you run Vite locally you require a nodejs LTS and npm. The Gitpod and local docker environment ensure all prerequisites are fulfilled.

In any environment the server is reachable on port 8085.

Examples

If you want to get started see take a look at the following examples. They get more advanced from top to bottom:

  • OBJLoader2 basic: [html] [ts]
  • OBJLoader2 basic (Offscreen Canvas): [html] [ts]
  • OBJLoader2Parallel basic: [html] [ts]
  • OBJLoader2 usage options: [html] [ts]
  • OBJLoader2 / OBJLoader parser capability comparison: [html] [ts]
  • OBJLoader2 in react with a .jpg texture: [html] [tsx]
  • OBJLoader2 in react with a .mtl material: [html] [tsx]
  • AssetPipelineLoader basic example: [html] [ts]

Try out all examples here: https://kaisalmen.github.io/WWOBJLoader

Main Branch

Main development now takes place on branch main. Tags identify the releases. The stable branch has been retired.

Feature Overview

OBJLoader2Parser

The parser OBJLoader2Parser used by OBJLoader2 and OBJLoader2Parallel has all OBJ parsing capabilities of OBJLoader from three.js, plus some extra feature. Please see the following list:

  • The parse methods of OBJLoader2Parser accepts ArrayBuffer or String as input. Text processing is approx. 15-20 pecent slower.
  • In case OBJLoader2Parallel the of Parser OBJLoader2Parser is executed inside a worker.
  • OBJLoader2Parser features indexed rendering including vertex reduction.
  • Indexed rendering is available if switched on via setUseIndices (see useIndices in example OBJLoader2 usage options).
  • Face N-Gons are supported.
  • Multi-Materials are created when needed.
  • Flat smoothing defined by "s 0" or "s off" is supported and Multi-Material is created when one object/group defines both smoothing groups equal and not equal to zero.
  • Support for points and lines is available since V2.3.0.
  • New mesh detection relies on 'g' occurrence or 'f', 'l' or 'p' type change (since V2.3.0). This allows multiple mesh definitions within one group.
  • Negative face indices are supported (issue #28)
  • The parser is now a single class that can be directly stored as string and therefore embedded in module or standard Workers (since V4.0.0).

WorkerTaskDirector Core Library

WorkerTask from wtd-core is used to control everything regarding workers. This library was separated with the 4.0.0 release. It now evolves as independent library that is utilized by OBJLoader2Parallel.

Docs

Run npm run doc to create the documentation in [directory]((./packages/objloader2/docs).

Happy coding!

Kai

wwobjloader's People

Contributors

death111 avatar dependabot[bot] avatar ferrolho avatar jakedluhy avatar kaisalmen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wwobjloader's Issues

The objloader2 show only the last mesh from the group

Thanks for this beautiful lib Kaisalmen''

I need your help I stack with the objloader2

when I try to load a .obj file were I have a group that have 21 children meshes I cant access the meshes only print me this output

I have this code to load the group

loadLargeOBJ() {

    const loader = new THREE.OBJLoader2();

    const callbackOnLoad = ( event ) => {

      this.view.scene.add( event.detail.loaderRootNode );

      const group = event.detail.loaderRootNode;

      this.renderer.render(this.scene, this.camera);
    };

    // load a resource from provided URL synchronously
    const asynchromus = true;
    loader.load( '../../../assets/models/brain.obj', callbackOnLoad, null, null, null, asynchromus );
    loader.loadMTL('../../../assets/models/brain.mtl');
  }
Material with name "defaultMaterial" will be added.
21:20:24.657 Three.es.js:60680 Material with name "defaultVertexColorMaterial" will be added.
21:20:24.658 Three.es.js:60680 Material with name "defaultLineMaterial" will be added.
21:20:24.668 Three.es.js:60680 Material with name "defaultPointMaterial" will be added.
21:20:24.668 Three.es.js:60990 WorkerSupport: Building worker code...
21:20:24.668 Three.es.js:61001 WorkerSupport: Using DEFAULT "LoaderSupport.WorkerRunnerRefImpl" as Runner class for worker.
21:20:24.678 Three.es.js:61040 buildWebWorkerCode: 10.19921875ms
21:20:24.744 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:139 OBJLoader2.Parser configuration:
	materialNames:
		- defaultMaterial
		- defaultVertexColorMaterial
		- defaultLineMaterial
		- defaultPointMaterial
	useAsync: true
	materialPerSmoothingGroup: false
	useIndices: false
	disregardNormals: false
	callbackMeshBuilderName: callbackMeshBuilder
	callbackProgressName: callbackProgress
21:20:27.893 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:820 Global output object count: 1
21:20:27.910 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "3rd-Ventricle_Cube.053_" was defined with unresolvable material "3rd_ventricle_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.912 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "4th_ventricle_Cube.052_" was defined with unresolvable material "4th_ventricle_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.914 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Amygdala_Cube.031_" was defined with unresolvable material "amigdala_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.915 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Caudate_Cube.049_" was defined with unresolvable material "caudate_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.915 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Fornix_Cube.048_" was defined with unresolvable material "fornix_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.916 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Group47710.001_" was defined with unresolvable material "cerebellum_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.917 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "hemi_left_ZBrush_defualt_group.002_" was defined with unresolvable material "left_hemi_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.920 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "hemi_right_ZBrush_defualt_group.003_" was defined with unresolvable material "right_hemi_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.926 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Hipophisys_Cube.046_" was defined with unresolvable material "hypophisis"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.927 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Hippocampus_Cube.044_" was defined with unresolvable material "hyppocampus_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.928 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Lateral-Ventricle_Cube.047_" was defined with unresolvable material "lateral_ventricle_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.928 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Medulla_oblongata_Cube.045_" was defined with unresolvable material "medulla_material.001"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.930 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Middle_brain_Pedunculis__Cube.051_" was defined with unresolvable material "middle_brain_material"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.932 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Nervus_Olphactorius_Cube.057_" was defined with unresolvable material "nervus_olphactorius_material.001"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.933 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Nervus_Ophtalmicus_Cube.055_" was defined with unresolvable material "ophtalmicus_material.001"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.934 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Nucleus_Accumbens_Cube.056_" was defined with unresolvable material "accumbens.002"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.936 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Pallidum_Cube.054_" was defined with unresolvable material "accumbens"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.936 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Putamen_Cube.043_" was defined with unresolvable material "putamen_material.001"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.937 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "Thalamus_Cube.050_" was defined with unresolvable material "Thalamus_material.001"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:27.938 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672 object_group "white_left_ZBrush_defualt_group.001_" was defined with unresolvable material "defaultMat"! Assigning "defaultMaterial".
Parser.buildMesh @ blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:672
21:20:28.111 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:827 Overall counts: 
	Vertices: 1339986
	Faces: 1339986
	Multiple definitions: 0
21:20:28.112 blob:http://localhost:4200/f6f8196b-267f-4e42-9928-62b726551ceb:886 WorkerRunner: Run complete!

Incorrect indexPointerC initialization.

indexPointerV has already be increased twice before, at L1067 and L1068.

var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 );
var vertices = scope.rawMesh.subGroupInUse.vertices;
vertices.push( scope.vertices[ indexPointerV++ ] );
vertices.push( scope.vertices[ indexPointerV++ ] );
vertices.push( scope.vertices[ indexPointerV ] );
var indexPointerC = scope.colors.length > 0 ? indexPointerV + 1 : null;
if ( indexPointerC !== null ) {
var colors = scope.rawMesh.subGroupInUse.colors;
colors.push( scope.colors[ indexPointerC++ ] );
colors.push( scope.colors[ indexPointerC++ ] );
colors.push( scope.colors[ indexPointerC ] );
}

So actually after this initialization, indexPointerC points to the next vertex's color, which is incorrect.

I suppose the following should be correct .

var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 );
var indexPointerC = scope.colors.length > 0 ? indexPointerV : null;

var vertices = scope.rawMesh.subGroupInUse.vertices;
vertices.push( scope.vertices[ indexPointerV++ ] );
vertices.push( scope.vertices[ indexPointerV++ ] );
vertices.push( scope.vertices[ indexPointerV ] );

if ( indexPointerC !== null ) {

    var colors = scope.rawMesh.subGroupInUse.colors;
    colors.push( scope.colors[ indexPointerC++ ] );
    colors.push( scope.colors[ indexPointerC++ ] );
    colors.push( scope.colors[ indexPointerC ] );

}

Not sure about some code typos?

Hi, I followed WWOBJLoader from three.js OBJLoader2, and found some code not very sure are they typos or just wrote intentional, please check it:

function ResourceDescriptor( url, extension ) {
	var urlParts = url.split( '/' );

	if ( urlParts.length < 2 ) {

		this.path = null;
		this.name = this.name = url; // why set double times?
this.workerSupport.run(
	{
               ...
	},
	[ content.buffer ] // but WorkerSupport.run only declares 1 parameter, how could this be passed?
);

Meshes of an object are added to the scene with no parent

Hey,

I load a car (obj+mtl) and it appears in the scene. But each mesh of the car is added to the scene seperatly. So I don't have a parent object with which I can move all meshes of the car. Does not matter if I stream it or not.

Original OBJLoader loader creates a parent mesh, and adds all mesh definitions as children. I see that maybe I would need to have the obj file contain a group?

Thanks
death

Webworker with three-wtm and webpack

We are using three within a nuxtjs/vue application where our code is bundled by webpack. For our web workers we use webpack's worker-loader (https://github.com/webpack-contrib/worker-loader). Before recently we were using three < r0.117.0 and were using OBJLoader2Parallel which shipped with three itself. Now we want to upgrade to WWOBJLoader and three-wtm but are stuck. We want to continue to support Firefox so we are limited to standard webworkers which are constructed as a string of source code in a Blob and supplied to the task manager. Therefore, the static method buildStandardWorkerDependencies() fetches a minified/uglyfied version of a threejs build, adds certain mappings and adds the used serialized classes (*Transport, MaterialUtils, OBJLoader2Parser2, etc). As far as I understand, this is build during runtime. Would it be possible to construct this standard worker during build time and include in the final bundle like worker-loader does? Do I have any alternatives for this kind of build process?

OBJLoader2 contains the correct vertices of my obj, but in the scene extra vertices are added to my object

Hello I've been using this repo for my project for indexed geometry and when I load .obj files extra vertices are added to my object
for example 1 of the objects I use has 750 vertices, OBJloader2.parser.vertices displays them correctly however in OBJloader2.parser.globalCounts it says I have 1510 vertices ? (1 thing I noticed in this specific obj the vt in my file are 1510)

In my second object I have 90481 vertices and in the globalCount 95172 vertices appear, later on BufferGeometry displays the same 95172 vertices

Am I doing something wrong? Am I not using OBJLoader2 properly? Are there conflicts with three js?

Here's how I load my obj

   var loader= new OBJLoader2(); 
   	loader.setUseIndices(true)
var textureLoader = new THREE.TextureLoader(loadingManager);
var map = textureLoader.load('Castle.jpg');	
var material = new THREE.MeshPhysicalMaterial({map: map, vertexColors: true});
loader.load( 'Castle.obj', function ( obj ) {		
	obj.traverse( function ( node ) {

		if ( node.isMesh ) 
		{
		node.material = material;
		
		}

	  } );

	scene.add(obj)
} 
);

Thank you for your time and work

The "position" attribute is likely to have NaN values.

Hi,

Thanks for the plugin.

When I am tried to download the OBJ's, I am getting the below error. Could you please help me on this?

**

THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.

**

Thanks,
Sabari.

Textures with transparent PNG in Webworker

I want to test now to load transparent PNG in webgl_loader_obj2_run_director.html

replace

var prepData;
var modelPrepDatas = [];
prepData = new THREE.LoaderSupport.PrepData( 'male02' );
prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/male02/male02.obj', 'OBJ ') );
prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'models/obj/male02/male02.mtl', 'MTL' ) );
prepData.setLogging( false, false );
modelPrepDatas.push( prepData );

with

var prepData;
var modelPrepDatas = [];
prepData = new THREE.LoaderSupport.PrepData( 'cube' );
prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'cube/cube.obj', 'OBJ ') );
prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( 'cube/cube.mtl', 'MTL' ) );
prepData.setLogging( false, false );
prepData.scale = 100.0;
modelPrepDatas.push( prepData );

The texture rendered is not transparent.

I don't know where to place material.transparent = true
I have placed it in various positions but without any luck.
Do you have any idea ?
In LoaderSupport.js ?

Thanks,
D.

Builder enhancements

Possible enhancements for Builder:

  • Create code for handling serialized materials. In case of OBJ materials are loaded from MTL files and are just assigned to Builder (not handled by buildMeshes, yet).

Further points may be added.

AddResource as ArrayBuffer

Hi Kai,

How can I insert the obj as arraybuffer? My obj is not in a url. It is in client side and it is already loaded as text in a dom.

I have tried this:

prepData.addResource( new THREE.LoaderSupport.ResourceDescriptor( '', 'OBJ ').setContent(objDef.objAsArrayBuffer) );

but no luck.

It seems that the old good PrepDataArrayBuffer was much better.

Thanks,
D.

V3.0.0: OBJLoader2 becomes OBJLoader & LoaderSupport evolves to WorkerLoader

Goals:

  • OBJLoader2 should replace OBJLoader and therefore it needs to become independent of LoaderSupport.

  • WorkerSupport and WorkerDirector should be transformed to a generic WorkerLoader or WorkerParser and WorkerLoaderDirector.

  • LoaderSupport will disappear and all functionality will move in one of the three units

ObjLoader2 async worker code breaks under uglify

The code that gets generated for the blob for the worker ends up mangled. Here is an excerpt

Validator = { isValid: function (e){return null!==e&&void 0!==e}, verifyInput: function (e,t){return null===e||void 0===e?t:e}, }

ConsolConsoleLoggerLogger = (function () {

function e(e,t){this.enabled=e!==!1,this.debug=t===!0}

ConsoleLogger.prototype.setDebug = function (e){this.debug=e===!0};

ConsoleLogger.prototype.isDebug = function (){return this.isEnabled()&&this.debug};

ConsoleLogger.prototype.setEnabled = function (e){this.enabled=e===!0};

ConsoleLogger.prototype.isEnabled = function (){return this.enabled};

ConsoleLogger.prototype.logDebug = function (e){this.enabled&&this.debug&&console.info(e)};

ConsoleLogger.prototype.logInfo = function (e){this.enabled&&console.info(e)};

ConsoleLogger.prototype.logWarn = function (e){console.warn(e)};

ConsoleLogger.prototype.logError = function (e){console.error(e)};

ConsoleLogger.prototype.logTimeStart = function (e){this.enabled&&console.time(e)};

ConsoleLogger.prototype.logTimeEnd = function (e){this.enabled&&console.timeEnd(e)};

return ConsoleLogger;

})();

/**

  • This code was constructed by OBJLoader2 buildCode.
    */

LoadLoaderBaserBase = (function () {

function e(e,o){this.manager=t.verifyInput(e,THREE.DefaultLoadingManager),this.logger=t.verifyInput(o,new n),this.modelName="",this.instanceNo=0,this.path="",this.useIndices=!1,this.disregardNormals=!1,this.loaderRootNode=new THREE.Group,this.builder=new THREE.LoaderSupport.Builder(this.logger),this._createDefaultMaterials(),this.callbacks=new THREE.LoaderSupport.Callbacks}

LoaderBase.prototype._createDefaultMaterials = function (){var e=new THREE.MeshStandardMaterial({color:14479871});e.name="defaultMaterial";var t=new THREE.MeshStandardMaterial({color:14479871});t.name="vertexColorMaterial",t.vertexColors=THREE.VertexColors;var n={};n[e.name]=e,n[t.name]=t,this.builder.updateMaterials({cmd:"materialData",materials:{materialCloneInstructions:null,serializedMaterials:null,runtimeMaterials:n}})};

LoaderBase.prototype._applyPrepData = function (e){t.isValid(e)&&(this.setModelName(e.modelName),this.setStreamMeshesTo(e.streamMeshesTo),this.builder.setMaterials(e.materials),this.setUseIndices(e.useIndices),this.setDisregardNormals(e.disregardNormals),this._setCallbacks(e.getCallbacks()))};

LoaderBase.prototype._setCallbacks = function (e){t.isValid(e.onProgress)&&this.callbacks.setCallbackOnProgress(e.onProgress),t.isValid(e.onMeshAlter)&&this.callbacks.setCallbackOnMeshAlter(e.onMeshAlter),t.isValid(e.onLoad)&&this.callbacks.setCallbackOnLoad(e.onLoad),t.isValid(e.onLoadMaterials)&&this.callbacks.setCallbackOnLoadMaterials(e.onLoadMaterials),this.builder._setCallbacks(this.callbacks)};

LoaderBase.prototype.getLogger = function (){return this.logger};

LoaderBase.prototype.setModelName = function (e){this.modelName=t.verifyInput(e,this.modelName)};

LoaderBase.prototype.setPath = function (e){this.path=t.verifyInput(e,this.path)};

LoaderBase.prototype.setStreamMeshesTo = function (e){this.loaderRootNode=t.verifyInput(e,this.loaderRootNode)};

LoaderBase.prototype.setMaterials = function (e){this.builder.setMaterials(e)};

LoaderBase.prototype.setUseIndices = function (e){this.useIndices=e===!0};

LoaderBase.prototype.setDisregardNormals = function (e){this.disregardNormals=e===!0};

LoaderBase.prototype.onProgress = function (e,n,o){var a=t.isValid(n)?n:"",i={detail:{type:e,modelName:this.modelName,instanceNo:this.instanceNo,text:a,numericalValue:o}};t.isValid(this.callbacks.onProgress)&&this.callbacks.onProgress(i),this.logger.logDebug(a)};

return LoaderBase;

})();

Consts = {
CODE_LF: 10,
CODE_CR: 13,
CODE_SPACE: 32,
CODE_SLASH: 47,
STRING_LF: "\n",
STRING_CR: "\r",
STRING_SPACE: " ",
STRING_SLASH: "/",
LINE_F: "f",
LINE_G: "g",
LINE_L: "l",
LINE_O: "o",
LINE_S: "s",
LINE_V: "v",
LINE_VT: "vt",
LINE_VN: "vn",
LINE_MTLLIB: "mtllib",
LINE_USEMTL: "usemtl",
}

ParsParserr = (function () {

function e(e){this.callbackProgress=null,this.callbackBuilder=null,this.materials={},this.rawMesh=null,this.useAsync=!1,this.materialPerSmoothingGroup=!1,this.useIndices=!1,this.disregardNormals=!1,this.inputObjectCount=1,this.outputObjectCount=1,this.counts={vertices:0,faces:0,doubleIndicesCount:0},this.logger=e,this.totalBytes=0,this.reachedFaces=!1}

Parser.prototype.setUseAsync = function (e){this.useAsync=e};

Parser.prototype.setMaterialPerSmoothingGroup = function (e){this.materialPerSmoothingGroup=e};

Parser.prototype.setUseIndices = function (e){this.useIndices=e};

Parser.prototype.setDisregardNormals = function (e){this.disregardNormals=e};

Parser.prototype.setMaterials = function (e){this.materials=o.verifyInput(e,this.materials),this.materials=o.verifyInput(this.materials,{})};

Parser.prototype.setCallbackBuilder = function (e){if(this.callbackBuilder=e,!o.isValid(this.callbackBuilder))throw'Unable to run as no "builder" callback is set.'};

Parser.prototype.setCallbackProgress = function (e){this.callbackProgress=e};

Parser.prototype.configure = function (){if(this.rawMesh=new r(this.materialPerSmoothingGroup,this.useIndices,this.disregardNormals),this.logger.isEnabled()){var e=Object.keys(this.materials),t=e.length>0?"\n\tmaterialNames:\n\t\t- "+e.join("\n\t\t- "):"\n\tmaterialNames: None",n="OBJLoader2.Parser configuration:"+t+"\n\tuseAsync: "+this.useAsync+"\n\tmaterialPerSmoothingGroup: "+this.materialPerSmoothingGroup+"\n\tuseIndices: "+this.useIndices+"\n\tdisregardNormals: "+this.disregardNormals+"\n\tcallbackBuilderName: "+this.callbackBuilder.name+"\n\tcallbackProgressName: "+this.callbackProgress.name;this.logger.logInfo(n)}};

Parser.prototype.parse = function (e){this.logger.logTimeStart("OBJLoader2.Parser.parse"),this.configure();var t=new Uint8Array(e),n=t.byteLength;this.totalBytes=n;for(var o,i=new Array(128),r=0,s=new Array(16),l=0,c="",d=0;d<n;d++)switch(o=t[d]){case a.CODE_SPACE:c.length>0&&(i[r++]=c),s[l++]=0,c="";break;case a.CODE_SLASH:c.length>0&&(i[r++]=c),s[l++]=1,c="";break;case a.CODE_LF:c.length>0&&(i[r++]=c),c="",this.processLine(i,r,s,l,d),r=0,l=0;break;case a.CODE_CR:break;default:c+=String.fromCharCode(o)}this.finalize(d),this.logger.logTimeEnd("OBJLoader2.Parser.parse")};

Parser.prototype.parseText = function (e){this.logger.logTimeStart("OBJLoader2.Parser.parseText"),this.configure();var t=e.length;this.totalBytes=t;for(var n,o=new Array(128),i=0,r=new Array(16),s=0,l="",c=0;c<t;c++)switch(n=e[c]){case a.STRING_SPACE:l.length>0&&(o[i++]=l),r[s++]=0,l="";break;case a.STRING_SLASH:l.length>0&&(o[i++]=l),r[s++]=1,l="";break;case a.STRING_LF:l.length>0&&(o[i++]=l),l="",this.processLine(o,i,r,s,c),i=0,s=0;break;case a.STRING_CR:break;default:l+=n}this.finalize(c),this.logger.logTimeEnd("OBJLoader2.Parser.parseText")};

Parser.prototype.processLine = function (e,t,n,o,i){if(!(t<1)){var r=function(e,t){for(var n=0,o=0;o<t;o++)n+=e[o];return n},s=function(e,t,n){var o="";if(2===t)o=e[1];else{for(var a=t-1,i=1;i<a;i++)o+=e[i]+(0===n[i]?" ":"/");o+=e[a]}return o},l=function(e,t){for(var n=0;n<t;n++)e[n]=""};switch(e[0]){case a.LINE_V:if(this.reachedFaces){if(this.rawMesh.colors.length>0&&this.rawMesh.colors.length!==this.rawMesh.vertices.length)throw"Vertex Colors were detected, but vertex count and color count do not match!";this.processCompletedMesh(this.rawMesh.objectName,this.rawMesh.groupName,i,!0),this.reachedFaces=!1}4===t?this.rawMesh.pushVertex(e):this.rawMesh.pushVertexAndVertextColors(e);break;case a.LINE_VT:this.rawMesh.pushUv(e);break;case a.LINE_VN:this.rawMesh.pushNormal(e);break;case a.LINE_F:this.reachedFaces=!0,this.rawMesh.processFaces(e,t,r(n,o));break;case a.LINE_L:this.rawMesh.processLines(e,t,r(n,o));break;case a.LINE_S:this.rawMesh.pushSmoothingGroup(e[1]),l(e,t);break;case a.LINE_G:this.processCompletedMesh(this.rawMesh.objectName,s(e,t,n),i,!1),l(e,t);break;case a.LINE_O:this.processCompletedMesh(s(e,t,n),this.rawMesh.groupName,i,!1),l(e,t);break;case a.LINE_MTLLIB:this.rawMesh.pushMtllib(s(e,t,n)),l(e,t);break;case a.LINE_USEMTL:this.rawMesh.pushUsemtl(s(e,t,n)),l(e,t)}}};

Parser.prototype.createRawMeshReport = function (e,t){var n=e.createReport(t);return"Input Object number: "+t+"\n\tObject name: "+n.objectName+"\n\tGroup name: "+n.groupName+"\n\tMtllib name: "+n.mtllibName+"\n\tVertex count: "+n.vertexCount+"\n\tNormal count: "+n.normalCount+"\n\tUV count: "+n.uvCount+"\n\tSmoothingGroup count: "+n.smoothingGroupCount+"\n\tMaterial count: "+n.mtlCount+"\n\tReal RawMeshSubGroup count: "+n.subGroups};

Parser.prototype.processCompletedMesh = function (e,t,n,a){var i=this.rawMesh.finalize();if(o.isValid(i)){this.inputObjectCount++,this.logger.isDebug()&&this.logger.logDebug(this.createRawMeshReport(this.rawMesh,this.inputObjectCount)),this.buildMesh(i,n);var r=n/this.totalBytes;this.callbackProgress("Completed [o: "+this.rawMesh.objectName+" g:"+this.rawMesh.groupName+"] Total progress: "+(100*r).toFixed(2)+"%",r),this.rawMesh=a?this.rawMesh.newInstanceResetOffsets():this.rawMesh.newInstanceKeepOffsets()}this.rawMesh.objectName!==e&&o.isValid(e)&&this.rawMesh.pushObject(e),this.rawMesh.groupName!==t&&o.isValid(t)&&this.rawMesh.pushGroup(t)};

Parser.prototype.finalize = function (e){this.logger.logInfo("Global output object count: "+this.outputObjectCount);var t=o.isValid(this.rawMesh)?this.rawMesh.finalize():null;if(o.isValid(t)){if(this.inputObjectCount++,this.logger.isDebug()&&this.logger.logDebug(this.createRawMeshReport(this.rawMesh,this.inputObjectCount)),this.buildMesh(t,e),this.logger.isEnabled()){var n="Overall counts: \n\tVertices: "+this.counts.vertices+"\n\tFaces: "+this.counts.faces+"\n\tMultiple definitions: "+this.counts.doubleIndicesCount;this.logger.logInfo(n)}var a=e/this.totalBytes;this.callbackProgress("Completed Parsing: 100.00%",a)}};

Parser.prototype.buildMesh = function (e,t){var n=e.subGroups,a=new Float32Array(e.absoluteVertexCount);this.counts.vertices+=e.absoluteVertexCount/3,this.counts.faces+=e.faceCount,this.counts.doubleIndicesCount+=e.doubleIndicesCount;var i,r,s,l,c,d,u,h=e.absoluteIndexCount>0?new Uint32Array(e.absoluteIndexCount):null,p=e.absoluteColorCount>0?new Float32Array(e.absoluteColorCount):null,m=e.absoluteNormalCount>0?new Float32Array(e.absoluteNormalCount):null,f=e.absoluteUvCount>0?new Float32Array(e.absoluteUvCount):null,g=o.isValid(p),v=[],E=n.length>1,b=0,y=[],w=[],T=0,M=0,R=0,H=0,x=0,k=0,L=0;for(var S in n)if(n.hasOwnProperty(S)){if(i=n[S],u=i.materialName,d=u+(g?"_vertexColor":"")+(0===i.smoothingGroup?"_flat":""),l=this.materials[u],c=this.materials[d],!o.isValid(l)&&!o.isValid(c)){var I=g?"vertexColorMaterial":"defaultMaterial";l=this.materials[I],this.logger.logWarn('object_group "'+i.objectName+"_"+i.groupName+'" was defined with unresolvable material "'+u+'"! Assigning "'+I+'".'),u=I,u===d&&(c=l,d=I)}if(!o.isValid(c)){var P={materialNameOrg:u,materialName:d,materialProperties:{vertexColors:g?2:0,flatShading:0===i.smoothingGroup}},C={cmd:"materialData",materials:{materialCloneInstructions:P}};this.callbackBuilder(C),this.useAsync&&(this.materials[d]=P)}if(E?(r=y[d],r||(r=b,y[d]=b,v.push(d),b++),L=this.useIndices?i.indices.length:i.vertices.length/3,s={start:k,count:L,index:r},w.push(s),k+=L):v.push(d),a.set(i.vertices,T),T+=i.vertices.length,h&&(h.set(i.indices,M),M+=i.indices.length),p&&(p.set(i.colors,R),R+=i.colors.length),m&&(m.set(i.normals,H),H+=i.normals.length),f&&(f.set(i.uvs,x),x+=i.uvs.length),this.logger.isDebug()){var A=o.isValid(r)?"\n\t\tmaterialIndex: "+r:"",B="Output Object no.: "+this.outputObjectCount+"\n\t\tobjectName: "+i.objectName+"\n\t\tgroupName: "+i.groupName+"\n\t\tmaterialName: "+i.materialName+A+"\n\t\tsmoothingGroup: "+i.smoothingGroup+"\n\t\t#vertices: "+i.vertices.length/3+"\n\t\t#indices: "+i.indices.length+"\n\t\t#colors: "+i.colors.length/3+"\n\t\t#uvs: "+i.uvs.length/2+"\n\t\t#normals: "+i.normals.length/3;this.logger.logDebug(B)}}this.outputObjectCount++,this.callbackBuilder({cmd:"meshData",progress:{numericalValue:t/this.totalBytes},params:{meshName:e.name},materials:{multiMaterial:E,materialNames:v,materialGroups:w},buffers:{vertices:a,indices:h,colors:p,normals:m,uvs:f}},[a.buffer],o.isValid(h)?[h.buffer]:null,o.isValid(p)?[p.buffer]:null,o.isValid(m)?[m.buffer]:null,o.isValid(f)?[f.buffer]:null)};

return Parser;

})();

RawMRawMeshsh = (function () {

function e(e,t,n,a){this.globalVertexOffset=1,this.globalUvOffset=1,this.globalNormalOffset=1,this.vertices=[],this.colors=[],this.normals=[],this.uvs=[],this.activeMtlName=o.verifyInput(a,""),this.objectName="",this.groupName="",this.mtllibName="",this.smoothingGroup={splitMaterials:e===!0,normalized:-1,real:-1},this.useIndices=t===!0,this.disregardNormals=n===!0,this.mtlCount=0,this.smoothingGroupCount=0,this.subGroups=[],this.subGroupInUse=null,this.pushSmoothingGroup(1),this.doubleIndicesCount=0,this.faceCount=0}

RawMesh.prototype.newInstanceResetOffsets = function (){var t=new e(this.smoothingGroup.splitMaterials,this.useIndices,this.disregardNormals,this.activeMtlName);return t.globalVertexOffset=this.globalVertexOffset+this.vertices.length/3,t.globalUvOffset=this.globalUvOffset+this.uvs.length/2,t.globalNormalOffset=this.globalNormalOffset+this.normals.length/3,t};

RawMesh.prototype.newInstanceKeepOffsets = function (){var t=new e(this.smoothingGroup.splitMaterials,this.useIndices,this.disregardNormals,this.activeMtlName);return t.pushObject(this.objectName),t.vertices=this.vertices,t.colors=this.colors,t.uvs=this.uvs,t.normals=this.normals,t.globalVertexOffset=this.globalVertexOffset,t.globalUvOffset=this.globalUvOffset,t.globalNormalOffset=this.globalNormalOffset,t};

RawMesh.prototype.pushVertex = function (e){this.vertices.push(parseFloat(e[1])),this.vertices.push(parseFloat(e[2])),this.vertices.push(parseFloat(e[3]))};

RawMesh.prototype.pushVertexAndVertextColors = function (e){this.vertices.push(parseFloat(e[1])),this.vertices.push(parseFloat(e[2])),this.vertices.push(parseFloat(e[3])),this.colors.push(parseFloat(e[4])),this.colors.push(parseFloat(e[5])),this.colors.push(parseFloat(e[6]))};

RawMesh.prototype.pushUv = function (e){this.uvs.push(parseFloat(e[1])),this.uvs.push(parseFloat(e[2]))};

RawMesh.prototype.pushNormal = function (e){this.normals.push(parseFloat(e[1])),this.normals.push(parseFloat(e[2])),this.normals.push(parseFloat(e[3]))};

RawMesh.prototype.pushObject = function (e){this.objectName=o.verifyInput(e,"")};

RawMesh.prototype.pushMtllib = function (e){this.mtllibName=o.verifyInput(e,"")};

RawMesh.prototype.pushGroup = function (e){this.groupName=o.verifyInput(e,"")};

RawMesh.prototype.pushUsemtl = function (e){this.activeMtlName!==e&&o.isValid(e)&&(this.activeMtlName=e,this.mtlCount++,this.verifyIndex())};

RawMesh.prototype.pushSmoothingGroup = function (e){var t=parseInt(e);isNaN(t)&&(t="off"===e?0:1);var n=this.smoothingGroup.normalized;this.smoothingGroup.normalized=this.smoothingGroup.splitMaterials?t:0===t?0:1,this.smoothingGroup.real=t,n!==t&&(this.smoothingGroupCount++,this.verifyIndex())};

RawMesh.prototype.verifyIndex = function (){var e=this.activeMtlName+"|"+this.smoothingGroup.normalized;this.subGroupInUse=this.subGroups[e],o.isValid(this.subGroupInUse)||(this.subGroupInUse=new s(this.objectName,this.groupName,this.activeMtlName,this.smoothingGroup.normalized),this.subGroups[e]=this.subGroupInUse)};

RawMesh.prototype.processFaces = function (e,t,n){var o,a,i=t-1;if(0===n)for(o=2,a=i-1;o<a;o++)this.buildFace(e[1]),this.buildFace(e[o]),this.buildFace(e[o+1]);else if(i===2*n)for(o=3,a=i-2;o<a;o+=2)this.buildFace(e[1],e[2]),this.buildFace(e[o],e[o+1]),this.buildFace(e[o+2],e[o+3]);else if(2*i===3*n)for(o=4,a=i-3;o<a;o+=3)this.buildFace(e[1],e[2],e[3]),this.buildFace(e[o],e[o+1],e[o+2]),this.buildFace(e[o+3],e[o+4],e[o+5]);else for(o=3,a=i-2;o<a;o+=2)this.buildFace(e[1],void 0,e[2]),this.buildFace(e[o],void 0,e[o+1]),this.buildFace(e[o+2],void 0,e[o+3])};

RawMesh.prototype.buildFace = function (e,t,n){var a=this.subGroupInUse;this.disregardNormals&&(n=void 0);var i=this,r=function(){var o=3*(parseInt(e)-i.globalVertexOffset),r=i.colors.length>0?o:null,s=a.vertices;if(s.push(i.vertices[o++]),s.push(i.vertices[o++]),s.push(i.vertices[o]),null!==r){var l=a.colors;l.push(i.colors[r++]),l.push(i.colors[r++]),l.push(i.colors[r])}if(t){var c=2*(parseInt(t)-i.globalUvOffset),d=a.uvs;d.push(i.uvs[c++]),d.push(i.uvs[c])}if(n){var u=3*(parseInt(n)-i.globalNormalOffset),h=a.normals;h.push(i.normals[u++]),h.push(i.normals[u++]),h.push(i.normals[u])}};if(this.useIndices){var s=e+(t?"_"+t:"_n")+(n?"_"+n:"_n"),l=a.indexMappings[s];o.isValid(l)?this.doubleIndicesCount++:(l=a.vertices.length/3,r(),a.indexMappings[s]=l,a.indexMappingsCount++),a.indices.push(l)}else r();this.faceCount++};

RawMesh.prototype.processLines = function (e,t,n){var o,a=1,i=t-1;if(i===2*n)for(o=i-2;a<o;a+=2)this.vertices.push(parseInt(e[a])),this.uvs.push(parseInt(e[a+1]));else for(o=i-1;a<o;a++)this.vertices.push(parseInt(e[a]))};

RawMesh.prototype.finalize = function (){var e,t,n=[],o=0,a=0,i=0,r=0,s=0,l=0;for(var c in this.subGroups)if(e=this.subGroups[c],e.vertices.length>0){if(t=e.indices,t.length>0&&a>0)for(var d in t)t[d]=t[d]+a;n.push(e),o+=e.vertices.length,a+=e.indexMappingsCount,i+=e.indices.length,r+=e.colors.length,l+=e.uvs.length,s+=e.normals.length}var u=null;return n.length>0&&(u={name:""!==this.groupName?this.groupName:this.objectName,subGroups:n,absoluteVertexCount:o,absoluteIndexCount:i,absoluteColorCount:r,absoluteNormalCount:s,absoluteUvCount:l,faceCount:this.faceCount,doubleIndicesCount:this.doubleIndicesCount}),u};

RawMesh.prototype.createReport = function (){var e={objectName:this.objectName,groupName:this.groupName,mtllibName:this.mtllibName,vertexCount:this.vertices.length/3,normalCount:this.normals.length/3,uvCount:this.uvs.length/2,smoothingGroupCount:this.smoothingGroupCount,mtlCount:this.mtlCount,subGroups:this.subGroups.length};return e};

return RawMesh;

})();

RawMRawMeshSubGroupshSubGroup = (function () {

function e(e,t,n,o){this.objectName=e,this.groupName=t,this.materialName=n,this.smoothingGroup=o,this._init()}

RawMeshSubGroup.prototype._init = function (){this.vertices=[],this.indexMappingsCount=0,this.indexMappings=[],this.indices=[],this.colors=[],this.uvs=[],this.normals=[]};

return RawMeshSubGroup;

})();

e = (function () {

function e(){var e=this,t=function(t){e.processMessage(t.data)};self.addEventListener("message",t,!1)}

e.prototype.applyProperties = function (e,t){var n,o,a;for(n in t)o="set"+n.substring(0,1).toLocaleUpperCase()+n.substring(1),a=t[n],"function"==typeof e[o]?e[o](a):e.hasOwnProperty(n)&&(e[n]=a)};

e.prototype.processMessage = function (e){var t=new ConsoleLogger;if(Validator.isValid(e.logger)&&(t.setEnabled(e.logger.enabled),t.setDebug(e.logger.debug)),"run"===e.cmd){var n={callbackBuilder:function(e){self.postMessage(e)},callbackProgress:function(e){t.logDebug("WorkerRunner: progress: "+e)}},o=new Parser(t);this.applyProperties(o,e.params),this.applyProperties(o,e.materials),this.applyProperties(o,n),o.workerScope=self,o.parse(e.data.input,e.data.options),t.logInfo("WorkerRunner: Run complete!"),n.callbackBuilder({cmd:"complete",msg:"WorkerRunner completed run."})}else t.logError("WorkerRunner: Received unknown command: "+e.cmd)};

return e;

})();

new e();



This is what the same exfiltration from non minified code looks like

Validator = {
isValid: function ( input ) {
return ( input !== null && input !== undefined );
},
verifyInput: function ( input, defaultValue ) {
return ( input === null || input === undefined ) ? defaultValue : input;
},
}

ConsoleLogger = (function () {

function ConsoleLogger( enabled, debug ) {
	this.enabled = enabled !== false;
	this.debug = debug === true;
}

ConsoleLogger.prototype.setDebug = function ( debug ) {
	this.debug = debug === true;
};

ConsoleLogger.prototype.isDebug = function () {
	return this.isEnabled() && this.debug;
};

ConsoleLogger.prototype.setEnabled = function ( enabled ) {
	this.enabled = enabled === true;
};

ConsoleLogger.prototype.isEnabled = function () {
	return this.enabled;
};

ConsoleLogger.prototype.logDebug = function ( message ) {
	if ( this.enabled && this.debug ) console.info( message );
};

ConsoleLogger.prototype.logInfo = function ( message ) {
	if ( this.enabled ) console.info( message );
};

ConsoleLogger.prototype.logWarn = function ( message ) {
	console.warn( message );
};

ConsoleLogger.prototype.logError = function ( message ) {
	console.error( message );
};

ConsoleLogger.prototype.logTimeStart = function ( id ) {
	if ( this.enabled ) console.time( id );
};

ConsoleLogger.prototype.logTimeEnd = function ( id ) {
	if ( this.enabled ) console.timeEnd( id );
};

return ConsoleLogger;

})();

/**

  • This code was constructed by OBJLoader2 buildCode.
    */

LoaderBase = (function () {

function LoaderBase( manager, logger ) {
	this.manager = Validator.verifyInput( manager, THREE.DefaultLoadingManager );
	this.logger = Validator.verifyInput( logger, new ConsoleLogger() );

	this.modelName = '';
	this.instanceNo = 0;
	this.path = '';
	this.useIndices = false;
	this.disregardNormals = false;

	this.loaderRootNode = new THREE.Group();
	this.builder = new THREE.LoaderSupport.Builder( this.logger );
	this._createDefaultMaterials();
	this.callbacks = new THREE.LoaderSupport.Callbacks();
}

LoaderBase.prototype._createDefaultMaterials = function () {
	var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
	defaultMaterial.name = 'defaultMaterial';

	var vertexColorMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
	vertexColorMaterial.name = 'vertexColorMaterial';
	vertexColorMaterial.vertexColors = THREE.VertexColors;

	var runtimeMaterials = {};
	runtimeMaterials[ defaultMaterial.name ] = defaultMaterial;
	runtimeMaterials[ vertexColorMaterial.name ] = vertexColorMaterial;

	this.builder.updateMaterials(
		{
			cmd: 'materialData',
			materials: {
				materialCloneInstructions: null,
				serializedMaterials: null,
				runtimeMaterials: runtimeMaterials
			}
		}
	);
};

LoaderBase.prototype._applyPrepData = function ( prepData ) {
	if ( Validator.isValid( prepData ) ) {

		this.setModelName( prepData.modelName );
		this.setStreamMeshesTo( prepData.streamMeshesTo );
		this.builder.setMaterials( prepData.materials );
		this.setUseIndices( prepData.useIndices );
		this.setDisregardNormals( prepData.disregardNormals );

		this._setCallbacks( prepData.getCallbacks() );
	}
};

LoaderBase.prototype._setCallbacks = function ( callbacks ) {
	if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
	if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
	if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
	if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );

	this.builder._setCallbacks( this.callbacks );
};

LoaderBase.prototype.getLogger = function () {
	return this.logger;
};

LoaderBase.prototype.setModelName = function ( modelName ) {
	this.modelName = Validator.verifyInput( modelName, this.modelName );
};

LoaderBase.prototype.setPath = function ( path ) {
	this.path = Validator.verifyInput( path, this.path );
};

LoaderBase.prototype.setStreamMeshesTo = function ( streamMeshesTo ) {
	this.loaderRootNode = Validator.verifyInput( streamMeshesTo, this.loaderRootNode );
};

LoaderBase.prototype.setMaterials = function ( materials ) {
	this.builder.setMaterials( materials );
};

LoaderBase.prototype.setUseIndices = function ( useIndices ) {
	this.useIndices = useIndices === true;
};

LoaderBase.prototype.setDisregardNormals = function ( disregardNormals ) {
	this.disregardNormals = disregardNormals === true;
};

LoaderBase.prototype.onProgress = function ( type, text, numericalValue ) {
	var content = Validator.isValid( text ) ? text: '';
	var event = {
		detail: {
			type: type,
			modelName: this.modelName,
			instanceNo: this.instanceNo,
			text: content,
			numericalValue: numericalValue
		}
	};

	if ( Validator.isValid( this.callbacks.onProgress ) ) this.callbacks.onProgress( event );

	this.logger.logDebug( content );
};

return LoaderBase;

})();

Consts = {
CODE_LF: 10,
CODE_CR: 13,
CODE_SPACE: 32,
CODE_SLASH: 47,
STRING_LF: "\n",
STRING_CR: "\r",
STRING_SPACE: " ",
STRING_SLASH: "/",
LINE_F: "f",
LINE_G: "g",
LINE_L: "l",
LINE_O: "o",
LINE_S: "s",
LINE_V: "v",
LINE_VT: "vt",
LINE_VN: "vn",
LINE_MTLLIB: "mtllib",
LINE_USEMTL: "usemtl",
}

Parser = (function () {

function Parser( logger ) {
		this.callbackProgress = null;
		this.callbackBuilder = null;

		this.materials = {};
		this.rawMesh = null;
		this.useAsync = false;
		this.materialPerSmoothingGroup = false;
		this.useIndices = false;
		this.disregardNormals = false;

		this.inputObjectCount = 1;
		this.outputObjectCount = 1;
		this.counts = {
			vertices: 0,
			faces: 0,
			doubleIndicesCount: 0
		};
		this.logger = logger;
		this.totalBytes = 0;
		this.reachedFaces = false;
	}

Parser.prototype.setUseAsync = function ( useAsync ) {
		this.useAsync = useAsync;
	};

Parser.prototype.setMaterialPerSmoothingGroup = function ( materialPerSmoothingGroup ) {
		this.materialPerSmoothingGroup = materialPerSmoothingGroup;
	};

Parser.prototype.setUseIndices = function ( useIndices ) {
		this.useIndices = useIndices;
	};

Parser.prototype.setDisregardNormals = function ( disregardNormals ) {
		this.disregardNormals = disregardNormals;
	};

Parser.prototype.setMaterials = function ( materials ) {
		this.materials = Validator.verifyInput( materials, this.materials );
		this.materials = Validator.verifyInput( this.materials, {} );
	};

Parser.prototype.setCallbackBuilder = function ( callbackBuilder ) {
		this.callbackBuilder = callbackBuilder;
		if ( ! Validator.isValid( this.callbackBuilder ) ) throw 'Unable to run as no "builder" callback is set.';
	};

Parser.prototype.setCallbackProgress = function ( callbackProgress ) {
		this.callbackProgress = callbackProgress;
	};

Parser.prototype.configure = function () {
		this.rawMesh = new RawMesh( this.materialPerSmoothingGroup, this.useIndices, this.disregardNormals );

		if ( this.logger.isEnabled() ) {

			var matKeys = Object.keys( this.materials );
			var matNames = ( matKeys.length > 0 ) ? '\n\tmaterialNames:\n\t\t- ' + matKeys.join( '\n\t\t- ' ) : '\n\tmaterialNames: None';
			var printedConfig = 'OBJLoader2.Parser configuration:'
					+ matNames
					+ '\n\tuseAsync: ' + this.useAsync
					+ '\n\tmaterialPerSmoothingGroup: ' + this.materialPerSmoothingGroup
					+ '\n\tuseIndices: ' + this.useIndices
					+ '\n\tdisregardNormals: ' + this.disregardNormals
					+ '\n\tcallbackBuilderName: ' + this.callbackBuilder.name
					+ '\n\tcallbackProgressName: ' + this.callbackProgress.name;
			this.logger.logInfo( printedConfig );
		}
	};

Parser.prototype.parse = function ( arrayBuffer ) {
		this.logger.logTimeStart( 'OBJLoader2.Parser.parse' );
		this.configure();

		var arrayBufferView = new Uint8Array( arrayBuffer );
		var length = arrayBufferView.byteLength;
		this.totalBytes = length;
		var buffer = new Array( 128 );
		var bufferPointer = 0;
		var slashSpacePattern = new Array( 16 );
		var slashSpacePatternPointer = 0;
		var code;
		var word = '';
		var i = 0;
		for ( ; i < length; i++ ) {

			code = arrayBufferView[ i ];
			switch ( code ) {
				case Consts.CODE_SPACE:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					slashSpacePattern[ slashSpacePatternPointer++ ] = 0;
					word = '';
					break;

				case Consts.CODE_SLASH:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					slashSpacePattern[ slashSpacePatternPointer++ ] = 1;
					word = '';
					break;

				case Consts.CODE_LF:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					word = '';
					this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i );
					bufferPointer = 0;
					slashSpacePatternPointer = 0;
					break;

				case Consts.CODE_CR:
					break;

				default:
					word += String.fromCharCode( code );
					break;
			}
		}
		this.finalize( i );
		this.logger.logTimeEnd( 'OBJLoader2.Parser.parse' );
	};

Parser.prototype.parseText = function ( text ) {
		this.logger.logTimeStart( 'OBJLoader2.Parser.parseText' );
		this.configure();

		var length = text.length;
		this.totalBytes = length;
		var buffer = new Array( 128 );
		var bufferPointer = 0;
		var slashSpacePattern = new Array( 16 );
		var slashSpacePatternPointer = 0;
		var char;
		var word = '';
		var i = 0;
		for ( ; i < length; i++ ) {

			char = text[ i ];
			switch ( char ) {
				case Consts.STRING_SPACE:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					slashSpacePattern[ slashSpacePatternPointer++ ] = 0;
					word = '';
					break;

				case Consts.STRING_SLASH:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					slashSpacePattern[ slashSpacePatternPointer++ ] = 1;
					word = '';
					break;

				case Consts.STRING_LF:
					if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
					word = '';
					this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i );
					bufferPointer = 0;
					slashSpacePatternPointer = 0;
					break;

				case Consts.STRING_CR:
					break;

				default:
					word += char;
			}
		}
		this.finalize( i );
		this.logger.logTimeEnd( 'OBJLoader2.Parser.parseText' );
	};

Parser.prototype.processLine = function ( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, currentByte ) {
		if ( bufferPointer < 1 ) return;

		var countSlashes = function ( slashSpacePattern, slashSpacePatternPointer ) {
			var slashesCount = 0;
			for ( var i = 0; i < slashSpacePatternPointer; i++ ) {
				slashesCount += slashSpacePattern[ i ];
			}
			return slashesCount;
		};

		var concatStringBuffer = function ( buffer, bufferPointer, slashSpacePattern ) {
			var concatBuffer = '';
			if ( bufferPointer === 2 ) {

				concatBuffer = buffer[ 1 ];

			} else {

				var bufferLength = bufferPointer - 1;
				for ( var i = 1; i < bufferLength; i++ ) {

					concatBuffer += buffer[ i ] + ( slashSpacePattern[ i ] === 0 ? ' ' : '/' );

				}
				concatBuffer += buffer[ bufferLength ];

			}
			return concatBuffer;
		};

		var flushStringBuffer = function ( buffer, bufferPointer ) {
			for ( var i = 0; i < bufferPointer; i++ ) {
				buffer[ i ] = '';
			}
		};

		switch ( buffer[ 0 ] ) {
			case Consts.LINE_V:
				// object complete instance required if reached faces already (= reached next block of v)
				if ( this.reachedFaces ) {

					if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) {

						throw 'Vertex Colors were detected, but vertex count and color count do not match!';

					}
					// only when new vertices after faces are detected complete new mesh is prepared (reset offsets, etc)
					this.processCompletedMesh( this.rawMesh.objectName, this.rawMesh.groupName, currentByte, true );
					this.reachedFaces = false;

				}
				if ( bufferPointer === 4 ) {

					this.rawMesh.pushVertex( buffer )

				} else {

					this.rawMesh.pushVertexAndVertextColors( buffer );

				}
				break;

			case Consts.LINE_VT:
				this.rawMesh.pushUv( buffer );
				break;

			case Consts.LINE_VN:
				this.rawMesh.pushNormal( buffer );
				break;

			case Consts.LINE_F:
				this.reachedFaces = true;
				this.rawMesh.processFaces( buffer, bufferPointer, countSlashes( slashSpacePattern, slashSpacePatternPointer ) );
				break;

			case Consts.LINE_L:
				this.rawMesh.processLines( buffer, bufferPointer, countSlashes( slashSpacePattern, slashSpacePatternPointer ) );
				break;

			case Consts.LINE_S:
				this.rawMesh.pushSmoothingGroup( buffer[ 1 ] );
				flushStringBuffer( buffer, bufferPointer );
				break;

			case Consts.LINE_G:
				this.processCompletedMesh( this.rawMesh.objectName, concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), currentByte, false );
				flushStringBuffer( buffer, bufferPointer );
				break;

			case Consts.LINE_O:
				this.processCompletedMesh( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), this.rawMesh.groupName, currentByte, false );
				flushStringBuffer( buffer, bufferPointer );
				break;

			case Consts.LINE_MTLLIB:
				this.rawMesh.pushMtllib( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) );
				flushStringBuffer( buffer, bufferPointer );
				break;

			case Consts.LINE_USEMTL:
				this.rawMesh.pushUsemtl( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) );
				flushStringBuffer( buffer, bufferPointer );
				break;

			default:
				break;
		}
	};

Parser.prototype.createRawMeshReport = function ( rawMesh , inputObjectCount ) {
		var report = rawMesh.createReport( inputObjectCount );
		return 'Input Object number: ' + inputObjectCount +
			'\n\tObject name: ' + report.objectName +
			'\n\tGroup name: ' + report.groupName +
			'\n\tMtllib name: ' + report.mtllibName +
			'\n\tVertex count: ' + report.vertexCount +
			'\n\tNormal count: ' + report.normalCount +
			'\n\tUV count: ' + report.uvCount +
			'\n\tSmoothingGroup count: ' + report.smoothingGroupCount +
			'\n\tMaterial count: ' + report.mtlCount +
			'\n\tReal RawMeshSubGroup count: ' + report.subGroups;
	};

Parser.prototype.processCompletedMesh = function ( objectName, groupName, currentByte, beginNewObject ) {
		var result = this.rawMesh.finalize();
		if ( Validator.isValid( result ) ) {

			this.inputObjectCount++;
			if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
			this.buildMesh( result, currentByte );
			var progressBytesPercent = currentByte / this.totalBytes;
			this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
			this.rawMesh = beginNewObject ? this.rawMesh.newInstanceResetOffsets() : this.rawMesh.newInstanceKeepOffsets();

		}
		// Always update group an object name in case they have changed and are valid
		if ( this.rawMesh.objectName !== objectName && Validator.isValid( objectName ) ) this.rawMesh.pushObject( objectName );
		if ( this.rawMesh.groupName !== groupName && Validator.isValid( groupName ) ) this.rawMesh.pushGroup( groupName );
	};

Parser.prototype.finalize = function ( currentByte ) {
		this.logger.logInfo( 'Global output object count: ' + this.outputObjectCount );
		var result = Validator.isValid( this.rawMesh ) ? this.rawMesh.finalize() : null;
		if ( Validator.isValid( result ) ) {

			this.inputObjectCount++;
			if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
			this.buildMesh( result, currentByte );

			if ( this.logger.isEnabled() ) {

				var parserFinalReport = 'Overall counts: ' +
					'\n\tVertices: ' + this.counts.vertices +
					'\n\tFaces: ' + this.counts.faces +
					'\n\tMultiple definitions: ' + this.counts.doubleIndicesCount;
				this.logger.logInfo( parserFinalReport );

			}
			var progressBytesPercent = currentByte / this.totalBytes;
			this.callbackProgress( 'Completed Parsing: 100.00%', progressBytesPercent );

		}
	};

Parser.prototype.buildMesh = function ( result, currentByte ) {
		var rawObjectDescriptions = result.subGroups;

		var vertexFA = new Float32Array( result.absoluteVertexCount );
		this.counts.vertices += result.absoluteVertexCount / 3;
		this.counts.faces += result.faceCount;
		this.counts.doubleIndicesCount += result.doubleIndicesCount;
		var indexUA = ( result.absoluteIndexCount > 0 ) ? new Uint32Array( result.absoluteIndexCount ) : null;
		var colorFA = ( result.absoluteColorCount > 0 ) ? new Float32Array( result.absoluteColorCount ) : null;
		var normalFA = ( result.absoluteNormalCount > 0 ) ? new Float32Array( result.absoluteNormalCount ) : null;
		var uvFA = ( result.absoluteUvCount > 0 ) ? new Float32Array( result.absoluteUvCount ) : null;
		var haveVertexColors = Validator.isValid( colorFA );

		var rawObjectDescription;
		var materialNames = [];

		var createMultiMaterial = ( rawObjectDescriptions.length > 1 );
		var materialIndex = 0;
		var materialIndexMapping = [];
		var selectedMaterialIndex;
		var materialGroup;
		var materialGroups = [];

		var vertexFAOffset = 0;
		var indexUAOffset = 0;
		var colorFAOffset = 0;
		var normalFAOffset = 0;
		var uvFAOffset = 0;
		var materialGroupOffset = 0;
		var materialGroupLength = 0;

		var materialOrg, material, materialName, materialNameOrg;
		for ( var oodIndex in rawObjectDescriptions ) {

			if ( ! rawObjectDescriptions.hasOwnProperty( oodIndex ) ) continue;
			rawObjectDescription = rawObjectDescriptions[ oodIndex ];

			materialNameOrg = rawObjectDescription.materialName;
			materialName = materialNameOrg + ( haveVertexColors ? '_vertexColor' : '' ) + ( rawObjectDescription.smoothingGroup === 0 ? '_flat' : '' );
			materialOrg = this.materials[ materialNameOrg ];
			material = this.materials[ materialName ];

			// both original and derived names do not lead to an existing material => need to use a default material
			if ( ! Validator.isValid( materialOrg ) && ! Validator.isValid( material ) ) {

				var defaultMaterialName = haveVertexColors ? 'vertexColorMaterial' : 'defaultMaterial';
				materialOrg = this.materials[ defaultMaterialName ];
				this.logger.logWarn( 'object_group "' + rawObjectDescription.objectName + '_' +
					rawObjectDescription.groupName + '" was defined with unresolvable material "' +
					materialNameOrg + '"! Assigning "' + defaultMaterialName + '".' );
				materialNameOrg = defaultMaterialName;

				// if names are identical then there is no need for later manipulation
				if ( materialNameOrg === materialName ) {

					material = materialOrg;
					materialName = defaultMaterialName;

				}

			}
			if ( ! Validator.isValid( material ) ) {

				var materialCloneInstructions = {
					materialNameOrg: materialNameOrg,
					materialName: materialName,
					materialProperties: {
						vertexColors: haveVertexColors ? 2 : 0,
						flatShading: rawObjectDescription.smoothingGroup === 0
					}
				};
				var payload = {
					cmd: 'materialData',
					materials: {
						materialCloneInstructions: materialCloneInstructions
					}
				};
				this.callbackBuilder( payload );

				// fake entry for async; sync Parser always works on material references (Builder update directly visible here)
				if ( this.useAsync ) this.materials[ materialName ] = materialCloneInstructions;

			}

			if ( createMultiMaterial ) {

				// re-use material if already used before. Reduces materials array size and eliminates duplicates
				selectedMaterialIndex = materialIndexMapping[ materialName ];
				if ( ! selectedMaterialIndex ) {

					selectedMaterialIndex = materialIndex;
					materialIndexMapping[ materialName ] = materialIndex;
					materialNames.push( materialName );
					materialIndex++;

				}
				materialGroupLength = this.useIndices ? rawObjectDescription.indices.length : rawObjectDescription.vertices.length / 3;
				materialGroup = {
					start: materialGroupOffset,
					count: materialGroupLength,
					index: selectedMaterialIndex
				};
				materialGroups.push( materialGroup );
				materialGroupOffset += materialGroupLength;

			} else {

				materialNames.push( materialName );

			}

			vertexFA.set( rawObjectDescription.vertices, vertexFAOffset );
			vertexFAOffset += rawObjectDescription.vertices.length;

			if ( indexUA ) {

				indexUA.set( rawObjectDescription.indices, indexUAOffset );
				indexUAOffset += rawObjectDescription.indices.length;

			}

			if ( colorFA ) {

				colorFA.set( rawObjectDescription.colors, colorFAOffset );
				colorFAOffset += rawObjectDescription.colors.length;

			}

			if ( normalFA ) {

				normalFA.set( rawObjectDescription.normals, normalFAOffset );
				normalFAOffset += rawObjectDescription.normals.length;

			}
			if ( uvFA ) {

				uvFA.set( rawObjectDescription.uvs, uvFAOffset );
				uvFAOffset += rawObjectDescription.uvs.length;

			}

			if ( this.logger.isDebug() ) {
				var materialIndexLine = Validator.isValid( selectedMaterialIndex ) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : '';
				var createdReport = 'Output Object no.: ' + this.outputObjectCount +
					'\n\t\tobjectName: ' + rawObjectDescription.objectName +
					'\n\t\tgroupName: ' + rawObjectDescription.groupName +
					'\n\t\tmaterialName: ' + rawObjectDescription.materialName +
					materialIndexLine +
					'\n\t\tsmoothingGroup: ' + rawObjectDescription.smoothingGroup +
					'\n\t\t#vertices: ' + rawObjectDescription.vertices.length / 3 +
					'\n\t\t#indices: ' + rawObjectDescription.indices.length +
					'\n\t\t#colors: ' + rawObjectDescription.colors.length / 3 +
					'\n\t\t#uvs: ' + rawObjectDescription.uvs.length / 2 +
					'\n\t\t#normals: ' + rawObjectDescription.normals.length / 3;
				this.logger.logDebug( createdReport );
			}

		}

		this.outputObjectCount++;
		this.callbackBuilder(
			{
				cmd: 'meshData',
				progress: {
					numericalValue: currentByte / this.totalBytes
				},
				params: {
					meshName: result.name
				},
				materials: {
					multiMaterial: createMultiMaterial,
					materialNames: materialNames,
					materialGroups: materialGroups
				},
				buffers: {
					vertices: vertexFA,
					indices: indexUA,
					colors: colorFA,
					normals: normalFA,
					uvs: uvFA
				}
			},
			[ vertexFA.buffer ],
			Validator.isValid( indexUA ) ? [ indexUA.buffer ] : null,
			Validator.isValid( colorFA ) ? [ colorFA.buffer ] : null,
			Validator.isValid( normalFA ) ? [ normalFA.buffer ] : null,
			Validator.isValid( uvFA ) ? [ uvFA.buffer ] : null
		);
	};

return Parser;

})();

RawMesh = (function () {

function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals, activeMtlName ) {
		this.globalVertexOffset = 1;
		this.globalUvOffset = 1;
		this.globalNormalOffset = 1;

		this.vertices = [];
		this.colors = [];
		this.normals = [];
		this.uvs = [];

		// faces are stored according combined index of group, material and smoothingGroup (0 or not)
		this.activeMtlName = Validator.verifyInput( activeMtlName, '' );
		this.objectName = '';
		this.groupName = '';
		this.mtllibName = '';
		this.smoothingGroup = {
			splitMaterials: materialPerSmoothingGroup === true,
			normalized: -1,
			real: -1
		};
		this.useIndices = useIndices === true;
		this.disregardNormals = disregardNormals === true;

		this.mtlCount = 0;
		this.smoothingGroupCount = 0;

		this.subGroups = [];
		this.subGroupInUse = null;
		// this default index is required as it is possible to define faces without 'g' or 'usemtl'
		this.pushSmoothingGroup( 1 );

		this.doubleIndicesCount = 0;
		this.faceCount = 0;
	}

RawMesh.prototype.newInstanceResetOffsets = function () {
		var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );

		// move indices forward
		newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3;
		newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2;
		newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3;

		return newRawObject;
	};

RawMesh.prototype.newInstanceKeepOffsets = function () {
		var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );
		// keep objectName
		newRawObject.pushObject( this.objectName );

		// keep current buffers and indices forward
		newRawObject.vertices = this.vertices;
		newRawObject.colors = this.colors;
		newRawObject.uvs = this.uvs;
		newRawObject.normals = this.normals;
		newRawObject.globalVertexOffset = this.globalVertexOffset;
		newRawObject.globalUvOffset = this.globalUvOffset;
		newRawObject.globalNormalOffset = this.globalNormalOffset;

		return newRawObject;
	};

RawMesh.prototype.pushVertex = function ( buffer ) {
		this.vertices.push( parseFloat( buffer[ 1 ] ) );
		this.vertices.push( parseFloat( buffer[ 2 ] ) );
		this.vertices.push( parseFloat( buffer[ 3 ] ) );
	};

RawMesh.prototype.pushVertexAndVertextColors = function ( buffer ) {
		this.vertices.push( parseFloat( buffer[ 1 ] ) );
		this.vertices.push( parseFloat( buffer[ 2 ] ) );
		this.vertices.push( parseFloat( buffer[ 3 ] ) );
		this.colors.push( parseFloat( buffer[ 4 ] ) );
		this.colors.push( parseFloat( buffer[ 5 ] ) );
		this.colors.push( parseFloat( buffer[ 6 ] ) );
	};

RawMesh.prototype.pushUv = function ( buffer ) {
		this.uvs.push( parseFloat( buffer[ 1 ] ) );
		this.uvs.push( parseFloat( buffer[ 2 ] ) );
	};

RawMesh.prototype.pushNormal = function ( buffer ) {
		this.normals.push( parseFloat( buffer[ 1 ] ) );
		this.normals.push( parseFloat( buffer[ 2 ] ) );
		this.normals.push( parseFloat( buffer[ 3 ] ) );
	};

RawMesh.prototype.pushObject = function ( objectName ) {
		this.objectName = Validator.verifyInput( objectName, '' );
	};

RawMesh.prototype.pushMtllib = function ( mtllibName ) {
		this.mtllibName = Validator.verifyInput( mtllibName, '' );
	};

RawMesh.prototype.pushGroup = function ( groupName ) {
		this.groupName = Validator.verifyInput( groupName, '' );
	};

RawMesh.prototype.pushUsemtl = function ( mtlName ) {
		if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return;
		this.activeMtlName = mtlName;
		this.mtlCount++;

		this.verifyIndex();
	};

RawMesh.prototype.pushSmoothingGroup = function ( smoothingGroup ) {
		var smoothingGroupInt = parseInt( smoothingGroup );
		if ( isNaN( smoothingGroupInt ) ) {
			smoothingGroupInt = smoothingGroup === "off" ? 0 : 1;
		}

		var smoothCheck = this.smoothingGroup.normalized;
		this.smoothingGroup.normalized = this.smoothingGroup.splitMaterials ? smoothingGroupInt : ( smoothingGroupInt === 0 ) ? 0 : 1;
		this.smoothingGroup.real = smoothingGroupInt;

		if ( smoothCheck !== smoothingGroupInt ) {

			this.smoothingGroupCount++;
			this.verifyIndex();

		}
	};

RawMesh.prototype.verifyIndex = function () {
		var index = this.activeMtlName + '|' + this.smoothingGroup.normalized;
		this.subGroupInUse = this.subGroups[ index ];
		if ( ! Validator.isValid( this.subGroupInUse ) ) {

			this.subGroupInUse = new RawMeshSubGroup( this.objectName, this.groupName, this.activeMtlName, this.smoothingGroup.normalized );
			this.subGroups[ index ] = this.subGroupInUse;

		}
	};

RawMesh.prototype.processFaces = function ( buffer, bufferPointer, slashesCount ) {
		var bufferLength = bufferPointer - 1;
		var i, length;

		// "f vertex ..."
		if ( slashesCount === 0 ) {

			for ( i = 2, length = bufferLength - 1; i < length; i ++ ) {

				this.buildFace( buffer[ 1 ] );
				this.buildFace( buffer[ i ] );
				this.buildFace( buffer[ i + 1 ] );

			}

			// "f vertex/uv ..."
		} else if  ( bufferLength === slashesCount * 2 ) {

			for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) {

				this.buildFace( buffer[ 1 ], buffer[ 2 ] );
				this.buildFace( buffer[ i ], buffer[ i + 1 ] );
				this.buildFace( buffer[ i + 2 ], buffer[ i + 3 ] );

			}

			// "f vertex/uv/normal ..."
		} else if  ( bufferLength * 2 === slashesCount * 3 ) {

			for ( i = 4, length = bufferLength - 3; i < length; i += 3 ) {

				this.buildFace( buffer[ 1 ], buffer[ 2 ], buffer[ 3 ] );
				this.buildFace( buffer[ i ], buffer[ i + 1 ], buffer[ i + 2 ] );
				this.buildFace( buffer[ i + 3 ], buffer[ i + 4 ], buffer[ i + 5 ] );

			}

			// "f vertex//normal ..."
		} else {

			for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) {

				this.buildFace( buffer[ 1 ], undefined, buffer[ 2 ] );
				this.buildFace( buffer[ i ], undefined, buffer[ i + 1 ] );
				this.buildFace( buffer[ i + 2 ], undefined, buffer[ i + 3 ] );

			}

		}
	};

RawMesh.prototype.buildFace = function ( faceIndexV, faceIndexU, faceIndexN ) {
		var sgiu = this.subGroupInUse;
		if ( this.disregardNormals ) faceIndexN = undefined;
		var scope = this;
		var updateRawObjectDescriptionInUse = function () {

			var indexPointerV = ( parseInt( faceIndexV ) - scope.globalVertexOffset ) * 3;
			var indexPointerC = scope.colors.length > 0 ? indexPointerV : null;

			var vertices = sgiu.vertices;
			vertices.push( scope.vertices[ indexPointerV++ ] );
			vertices.push( scope.vertices[ indexPointerV++ ] );
			vertices.push( scope.vertices[ indexPointerV ] );

			if ( indexPointerC !== null ) {

				var colors = sgiu.colors;
				colors.push( scope.colors[ indexPointerC++ ] );
				colors.push( scope.colors[ indexPointerC++ ] );
				colors.push( scope.colors[ indexPointerC ] );

			}

			if ( faceIndexU ) {

				var indexPointerU = ( parseInt( faceIndexU ) - scope.globalUvOffset ) * 2;
				var uvs = sgiu.uvs;
				uvs.push( scope.uvs[ indexPointerU++ ] );
				uvs.push( scope.uvs[ indexPointerU ] );

			}
			if ( faceIndexN ) {

				var indexPointerN = ( parseInt( faceIndexN ) - scope.globalNormalOffset ) * 3;
				var normals = sgiu.normals;
				normals.push( scope.normals[ indexPointerN++ ] );
				normals.push( scope.normals[ indexPointerN++ ] );
				normals.push( scope.normals[ indexPointerN ] );

			}
		};

		if ( this.useIndices ) {

			var mappingName = faceIndexV + ( faceIndexU ? '_' + faceIndexU : '_n' ) + ( faceIndexN ? '_' + faceIndexN : '_n' );
			var indicesPointer = sgiu.indexMappings[ mappingName ];
			if ( Validator.isValid( indicesPointer ) ) {

				this.doubleIndicesCount++;

			} else {

				indicesPointer = sgiu.vertices.length / 3;
				updateRawObjectDescriptionInUse();
				sgiu.indexMappings[ mappingName ] = indicesPointer;
				sgiu.indexMappingsCount++;

			}
			sgiu.indices.push( indicesPointer );

		} else {

			updateRawObjectDescriptionInUse();

		}
		this.faceCount++;
	};

RawMesh.prototype.processLines = function ( buffer, bufferPointer, slashCount ) {
		var i = 1;
		var length;
		var bufferLength = bufferPointer - 1;

		if ( bufferLength === slashCount * 2 ) {

			for ( length = bufferLength - 2; i < length; i += 2 ) {

				this.vertices.push( parseInt( buffer[ i ] ) );
				this.uvs.push( parseInt( buffer[ i + 1 ] ) );

			}

		} else {

			for ( length = bufferLength - 1; i < length; i ++ ) {

				this.vertices.push( parseInt( buffer[ i ] ) );

			}

		}
	};

RawMesh.prototype.finalize = function () {
		var rawObjectDescriptionsTemp = [];
		var rawObjectDescription;
		var absoluteVertexCount = 0;
		var absoluteIndexMappingsCount = 0;
		var absoluteIndexCount = 0;
		var absoluteColorCount = 0;
		var absoluteNormalCount = 0;
		var absoluteUvCount = 0;
		var indices;
		for ( var name in this.subGroups ) {

			rawObjectDescription = this.subGroups[ name ];
			if ( rawObjectDescription.vertices.length > 0 ) {

				indices = rawObjectDescription.indices;
				if ( indices.length > 0 && absoluteIndexMappingsCount > 0 ) {

					for ( var i in indices ) indices[ i ] = indices[ i ] + absoluteIndexMappingsCount;

				}
				rawObjectDescriptionsTemp.push( rawObjectDescription );
				absoluteVertexCount += rawObjectDescription.vertices.length;
				absoluteIndexMappingsCount += rawObjectDescription.indexMappingsCount;
				absoluteIndexCount += rawObjectDescription.indices.length;
				absoluteColorCount += rawObjectDescription.colors.length;
				absoluteUvCount += rawObjectDescription.uvs.length;
				absoluteNormalCount += rawObjectDescription.normals.length;

			}
		}

		// do not continue if no result
		var result = null;
		if ( rawObjectDescriptionsTemp.length > 0 ) {

			result = {
				name: this.groupName !== '' ? this.groupName : this.objectName,
				subGroups: rawObjectDescriptionsTemp,
				absoluteVertexCount: absoluteVertexCount,
				absoluteIndexCount: absoluteIndexCount,
				absoluteColorCount: absoluteColorCount,
				absoluteNormalCount: absoluteNormalCount,
				absoluteUvCount: absoluteUvCount,
				faceCount: this.faceCount,
				doubleIndicesCount: this.doubleIndicesCount
			};

		}
		return result;
	};

RawMesh.prototype.createReport = function () {
		var report = {
			objectName: this.objectName,
			groupName: this.groupName,
			mtllibName: this.mtllibName,
			vertexCount: this.vertices.length / 3,
			normalCount: this.normals.length / 3,
			uvCount: this.uvs.length / 2,
			smoothingGroupCount: this.smoothingGroupCount,
			mtlCount: this.mtlCount,
			subGroups: this.subGroups.length
		};

		return report;
	};

return RawMesh;

})();

RawMeshSubGroup = (function () {

function RawMeshSubGroup( objectName, groupName, materialName, smoothingGroup ) {
		this.objectName = objectName;
		this.groupName = groupName;
		this.materialName = materialName;
		this.smoothingGroup = smoothingGroup;
		this._init();
	}

RawMeshSubGroup.prototype._init = function () {
		this.vertices = [];
		this.indexMappingsCount = 0;
		this.indexMappings = [];
		this.indices = [];
		this.colors = [];
		this.uvs = [];
		this.normals = [];
	};

return RawMeshSubGroup;

})();

WorkerRunnerRefImpl = (function () {

function WorkerRunnerRefImpl() {
	var scope = this;
	var scopedRunner = function( event ) {
		scope.processMessage( event.data );
	};
	self.addEventListener( 'message', scopedRunner, false );
}

WorkerRunnerRefImpl.prototype.applyProperties = function ( parser, params ) {
	var property, funcName, values;
	for ( property in params ) {
		funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 );
		values = params[ property ];

		if ( typeof parser[ funcName ] === 'function' ) {

			parser[ funcName ]( values );

		} else if ( parser.hasOwnProperty( property ) ) {

			parser[ property ] = values;

		}
	}
};

WorkerRunnerRefImpl.prototype.processMessage = function ( payload ) {
	var logger = new ConsoleLogger();
	if ( Validator.isValid( payload.logger ) ) {

		logger.setEnabled( payload.logger.enabled );
		logger.setDebug( payload.logger.debug );

	}
	if ( payload.cmd === 'run' ) {

		var callbacks = {
			callbackBuilder: function ( payload ) {
				self.postMessage( payload );
			},
			callbackProgress: function ( text ) {
				logger.logDebug( 'WorkerRunner: progress: ' + text );
			}
		};

		// Parser is expected to be named as such
		var parser = new Parser( logger );
		this.applyProperties( parser, payload.params );
		this.applyProperties( parser, payload.materials );
		this.applyProperties( parser, callbacks );
		parser.workerScope = self;
		parser.parse( payload.data.input, payload.data.options );

		logger.logInfo( 'WorkerRunner: Run complete!' );

		callbacks.callbackBuilder( {
			cmd: 'complete',
			msg: 'WorkerRunner completed run.'
		} );

	} else {

		logger.logError( 'WorkerRunner: Received unknown command: ' + payload.cmd );

	}
};

return WorkerRunnerRefImpl;

})();

new WorkerRunnerRefImpl();


I'm going to be completely honest, I would never have expected this to work. After all there is some metaprogramming going on in there! 

But upon closer inspection, the logic appears to all be proper, some of the variables got renamed to minified variable names, but the right ones seem to be called. 

The ONLY strange stuff is that many of the toplevel names are mangled as though something went wrong with concurrent serialization somehow. `RawMRawMeshSubGroupshSubGroup` instead of `RawMeshSubGroup`, note the `e` got replaced with an entire instance of itself. Holy shit. I think that's the key to this. Something is causing the name to be injected into itself in place of its first `e` letter. 

OK so anyway. Somehow this all seems to ALMOST work under uglify so maybe with some slight robustification i can build a minified three.js that uses objloader2. 

Meanwhile, the good news is that i'm quite satisfied to be simply deploying unminified three.js to production. 
</details>

Add ES6 modules compatibility

Would you be open to a PR adding compatibility for ES6 modules (or commonjs modules at least)?
Currently it's a bit of a hassle to use this loader without the window global THREE set.

Of course that should be doable in a backwards compatible way :)

Abort request

When the user navigates away from the current page, or selects another scene, I want WorkerDirector.tearDown to abort xhr requests which have already started.

Could worker support be used in other model loaders?

I want to rewrite Three.js other model loaders and support webworkers. I want to know whether the LoaderWorkerSupport.js could be used in other format loader or it contains methods that only support obj format.

Fix MeshCreator debug log

Debug logging in OBJLoader2.MeshCreator with regard to MultiMaterials is wrong. It always says it creates MultiMaterials even if it is not the case and it logs materialIndex for regular materials.

Progress should be returned as/with a numeric

It would be more useful if the progress callback passed a numeric or an object containing a numeric instead of just passing strings.

Currently if to map the progress amount to something like a loading bar, you have to rely on a specific message format and parse the message for the value using parseFloat(). Even then, the messages with progress only seem to be emitted for downloading the model, not parsing.

For a starting point, THREE.js loaders already have a progress format which returns an object (emphasis mine):

onProgress โ€” Will be called while load progresses. The argument will be the XMLHttpRequest instance, which contains .total and .loaded bytes.

Browser limits download

Hi Kai,

I am getting one more issue. For example, I have 5 worker, when the objects are downloading the materials were downloaded and started to downloading textures. In my case, I have 5 to 10 texture for each 3D object. Because of texture started download, browser is limiting the worker download.

Is there any other alternative way to download texture after 3D object downloaded?

Thanks,
Sabari.

Option to suppress console messages

There should be an option to suppress all console messages (like a quiet mode). Using THREE.OBJLoader2.WWOBJLoader2.setDebug() only changes the visibility of more fine grained debug messages.

Error handling material loader

  1. The callback this._onProgress does not exist.
  2. When called from here, this is not bound to an OBJLoader2 and thus the call to this.onProgress(.) will fail.
  3. When using OBJLoader2 with the WorkerDirector, and assuming the above issues are fixed, how can I recover from errors related to the material loader when an exception is thrown here?

Each Queue item should have its own callbacks

Each queue item should have its own callbacks. Like the "fileObj" and "fileMtl" parameters, there should be an onSuccess and onFailure parameter aswell.

This way I can use different handlers for different objects. If I wanted, I could still use the same handler for all of them.

NaN Radius in BufferGeometry problem when loading obj with many g (groups) inside

Congrats for the nice loader. It is very helpful. However I found a bug.

I want to load an obj model of 55MB with 20 groups inside. If I unite them as a single group ok it works. However, if they are not united into a single group then I get the following message: THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values. See image below

image

For lighter models, .e.g 12MB with 20 groups it works nice. I guess there are some limitations on the size of each group (g) in the obj file. I like to have the model split into groups because it loads incrementally.

Here you can find the code : https://github.com/DigiArt-project/WordpressUnity3DEditor
at js_libs/wu_webw_3d_view.js

Thanks,
Dimitrios

Verify Parser implementation

Issue #27 revealed that the parser is not behaving correctly with regards to groups.
I have also overlooked to support negative, relative face references. This is already fixed on issue related branch.
A hand-crafted OBJ file should be created to be able to verify many different valid mesh definitions are handled properly.

Support points and lines

Add point support to OBJLoader2 and update verification test to include points and lines.
Lines are parsed, but the both Object type and Materials are not properly created for it.

Generic mesh provision via web worker

Update: 2017-09-08:
This is a copy of the text of three.js PR 12048:

The evolution of OBJLoader2 has now reached a point where your feedback is required (mrdoob, jbaicoianu, fernandojsg, jonnenauha, mikearmstrong001 and please ping anyone interested or of help).

Let me sum up why this all happend:
I started some prototyping on a branch in my WWOBJLoader repo, because I wanted to separate the asynchronous mesh provision of the worker fromWWOBJLoader2. Almost in parallel fernandojsg started #11746. During the discussion mrdoob brought up the idea to have a common approach for executing the Parser part of any Loader in a worker and passing raw results that to a common mesh builder function. With this approach Parsers could be defined without any reference to three.js code allowing quick worker creation.

This PR realizes this with an a new version of OBJLoader2 that fuses OBJLoader2 and WWOBJLoader2 into one, plus the proof of concept MeshSpray example which is not a loader, but it uses all actors and provides a worker executable parser function.
I have extracted LoaderSupport classes (independent of OBJ parsing functionality) that serve as support tools and utilities. Some of the classes make building other worker based loaders a lot easier and are considered required. Some things may be identified as superfluous, but we will see. All extracted code now resides under namespace THREE.LoaderSupport to make it independent from OBJLoader2:

  • Validator: Tools for null/undefined checks and default value assignment
  • Commons (optional): Possible base class for loaders. It bundles common functions and parameters
  • WorkerSupport + WorkerRunnerRefImpl: WorkerSuppport provides helper function to create workers from existing Parser code and it handles the communication with the Worker. WorkerRunnerRefImpl realizes the communication with the front-end, creates, configures and executes the parser inside the worker.
  • Builder: It builds one or many THREE.Mesh from one raw set of Arraybuffers, materialGroup descriptions and further parameters. Supports vertex, vertexColor, normal, uv and index buffers.
  • PrepData + ResourceDescriptor: Description used for automation (fed to run method, see OBJLoader2 description below) and for a unified description resource description in examples
  • WorkerDirector: Creates parsers via reflection and utilizes WorkerSupport to automate loaders via queued run/PrepData instructions with configurable amount of workers. Used to fully automate loaders (MeshSpray and Parallels examples)
  • Callbacks (onProgress, onMeshAlter, onLoad) + LoadedMeshUserOverride (helpful): Used mainly for automation with PrepData. LoadedMeshUserOverride is returned onMeshAlter (see Normal-Helper addition to vive-controller in examples obj2_options)

OBJLoader2 POIs (example obj2_options features six methods how to use it):

  • parse allows synchronous loading of arraybuffer or string data
  • parseAsync allows asynchronous loading of arraybuffer or string data
  • load Allows both synchronous and asynchronous loading of a OBJ file (additional async flag in function)
  • run Allows automated loading of MTL and OBJ files according instructions (sync/async, mesh-streaming, callbacks, etc.)
  • loadMtl makes MTL loading via MTLLoader and provision of materials to OBJLoader2easy
  • Internal Parser is fully serialized, configured and run by generic WorkerSupport
  • Functions initand validate are no longer needed. OBJLoader2 should be re-instantiated when it shall be used again. run has an optional parameter that allows to provide an external WorkerSupport with the cached parser worker (no need for re-build). LoaderDirector uses this mechanism in run_director example
  • UPDATE 2017-09-03: OBJLoader2 now supports setUseIndices which loads data into indexed BufferedGeometry. Loading is roughly a third slower, but up to three quaster less vertices/uvs/normals are created.

Please let me know what you think of all of this and please review the code. This whole approach imposes heavy changes on existing loaders when decided to be ported, but no force to do so is imposed by all of this. We just gain useful options to build asynchronous loaders.

Examples:
obj Indexed
obj2_options
obj2_run_director
ww_meshspray
Bigger OBJ files stage (External) Indexed

TODO for me:

  • Enhance Builder with code for handling serialized materials. In case of OBJ materials are loaded from MTL files and are just assigned to Builder (not handled by buildMeshes, yet).
  • Update the documentation
  • Transform another fairly simply loader like PCDLoader to this generic approach. I am open for suggestions here
  • Done: Polish the examples

Ensure Workers are destroyed as expected

I observed that clean-up of workers is not properly done or leads to unexpected errors when are orchestrated in parallel by LoaderDirector.
WorkerSupport and LoaderDirector should be reviewed and error should be fixed

Don't add objects to the sceneGraph automatically

I want to control the sceneGraph in my own logic. The loader should not add objects to the sceneGraph on its own.
Maybe I want to move the object, or hide it, before its added to the scene graph. Loader should call me, if the object is finished with a reference to the THREE.Object3d and I can proceed how I like with it.

Basically I would remove the sceneGraphBaseNode parameter from the loader.

Provide NPM package

OBJLoader2 and OBJLoader2Parallel were removed from three.js in release 125.

I need to provide an npm package (4.0.0) that provides the removed loaders again.

Required tasks:

  • Provide "npm" package from dev branch
  • Update README and CHANGELOG
  • With 4.0.0 release stable branch will be aligned with dev branch

Obsolete tasks as new npm package will only be release from 4.0.0 onward
- [ ] Provide npm package from release branch
- [ ] Update README on release branch

name property of meshes not set

OBJLoader2 does not set the name property of the meshes it creates. The name stays an empty string.

OBJLoader on the other hand sets the name. Here is an example:
https://threejs.org/examples/?q=mtl#webgl_loader_obj_mtl
The name of the first mesh gets set to
mesh1.002_mesh1-geometry_male-02-1noCullingID_male-02-1noCulling.JP
which comes from the file male02.obj and from this line:
g mesh1.002_mesh1-geometry_male-02-1noCullingID_male-02-1noCulling.JP

I think OBJLoader2 should do the same.

memory leak

This heapened when I use Google Chrome .
When I load a big model (1GB) using the following code ,it uses a lot of memory
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script type="module">
import { OBJLoader2 } from "./jsm/loaders/OBJLoader2.js";
( function () {
let objLoader2 = new OBJLoader2();
let callbackOnLoad = function ( object3d ) {
console.log("success");
};
objLoader2.setModelName( "modelName" );
objLoader2.load( 'test_model/test2.obj', callbackOnLoad, null, null, null );
} )();
</script>
</html>
The garbage collection should clear the memory which stores the model , but it didn't do that,is
it memory leak?
.

When I load using the following code,just add more line " delete objLoader2.parser.contentRef;" in callbackOnLoad function
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script type="module">
import { OBJLoader2 } from "./jsm/loaders/OBJLoader2.js";
( function () {
let objLoader2 = new OBJLoader2();
let callbackOnLoad = function ( object3d ) {
delete objLoader2.parser.contentRef;
console.log("success");
};
objLoader2.setModelName( "modelName" );
objLoader2.load( 'test_model/test2.obj', callbackOnLoad, null, null, null );
} )();
</script>
</html>
the memory which stores the model will be clear;

webpackMissingModule for three-wtm

Webpack is telling me that I'm missing a module and it appears that OBJLoader2 is trying to import this. What is it? If it's a dep is there a reason it isn't in the package file? Do I need to bring it into my project myself?

Got an error in webpack project: "ReferenceError: THREE is not defined"

Hello. I'm working on a webpack project to load and render 3d model , and try to integrate wwobjloader2. But I got an error in chrome console: "ReferenceError: THREE is not defined".
image

What I did is following:

  1. npm install three
  2. npm i --save three-obj-mtl-loader
  3. npm install wwobjloader2
  4. add code import OBJLoader2 from 'wwobjloader2'

Help me please.

Error after build

Hi Kai,

I am getting the below error after build. Could you please help me on this?

screen shot 2017-10-26 at 11 35 14 pm

screen shot 2017-10-26 at 11 35 36 pm

Thanks,
Sabari.

Can you please make this package es6 compatible

Hi, I want to try your new updates on objloader2 but I use typescript for my projects
I use to install the full-three.js package but the author of this package doesn't update the code frequently so please if you have the time make this package in es6 module like.

Textures PNG with alpha channel

Hi,

I 've not managed to load a transparent png. The transparency is loaded as black. I have tried the webgl_loader_obj2.html in three.js v91 but no luck. In the normal webgl_loader_obj.html the transparency is ok with

material.transparent = true;
material.side = THREE.DoubleSide;

but the aforementioned parameters are not doing anything in webgl_loader_obj2.html

I am attaching the model files for testing

cube.mtl

newmtl cube
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd cube.png

cube.obj

# Blender v2.77 (sub 0) OBJ File: 'untitled.blend'
# www.blender.org
mtllib cube.mtl
g Cube
v -0.417097 -1.562497 0.256643
v -0.417097 0.437503 0.256643
v -0.417097 -1.562497 -1.743357
v -0.417097 0.437503 -1.743357
v 1.582903 -1.562497 0.256643
v 1.582903 0.437503 0.256643
v 1.582903 -1.562497 -1.743357
v 1.582903 0.437503 -1.743357
vt 0.3333 0.3333
vt 0.3333 0.0000
vt 0.6667 0.0000
vt 0.6667 0.3333
vt 0.0000 0.6667
vt 0.0000 0.3333
vt 0.3333 0.3333
vt 0.3333 0.6667
vt 0.0000 0.0000
vt 0.3333 0.0000
vt 0.3333 0.3333
vt 0.6667 0.3333
vt 0.6667 0.6667
vt 0.3333 0.6667
vt 1.0000 0.0000
vt 1.0000 0.3333
vt 0.6667 0.3333
vt 0.6667 0.0000
vt 0.0000 0.6667
vt 0.3333 0.6667
vt 0.3333 1.0000
vt 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl cube
s off
f 2/1/1 4/2/1 3/3/1 1/4/1
f 4/5/2 8/6/2 7/7/2 3/8/2
f 8/6/3 6/9/3 5/10/3 7/7/3
f 6/11/4 2/12/4 1/13/4 5/14/4
f 1/15/5 3/16/5 7/17/5 5/18/5
f 6/19/6 8/20/6 4/21/6 2/22/6

and cube.png

cube

Can you please confirm that it is not loading properly and if there is a remedy ? I lost 5 hours on this thing.

BR,
jimver04

This is the good loading with webgl_loader_obj.html:
image

This is the bad loading with webgl_loader_obj2.html:
image

When the data is loading, can I terminate it early?

i try to teimanate it when the data is loading by call _finalize('complete'),but i get an error
WWOBJLoader2.js:307 Uncaught DOMException: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 0 is already neutered.
and Can i do that ?

Cache obj/mtl for multiple use

An enhancement would be, that the loader notices, if e.g. the same obj file is requested multiple times. Right now it will load the same file n times. Would be great, if we could specify if the loader should 'cache' the files. If they are requested again, they just return a copy (could be a parameter if you want to clone the mesh, or reuse the one available).
This would save time, as the download and parsing can be spared for files, which were downloaded and parsed in the past already.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.