I have nice working obj script but with small bug.
I made group detect rule by adding in blender name with “COLLIDER” and it is a simply non rotated cube.
I also have group detect part.
But i saw in console log that some of (looks like random choose) group always have
all vertices of object.
See printscreen with console logs:

My Obj loader:
'use strict';
import {world} from './matrix-world';
export class constructMesh {
constructor(objectData, inputArg) {
this.inputArg = inputArg;
this.objectData = objectData;
this.create(objectData, inputArg);
this.setScale = (s) => {
this.inputArg.scale = s;
initMeshBuffers(world.GL.gl, this.create(this.objectData, this.inputArg));
};
}
create = (objectData, inputArg, callback) => {
if(typeof callback === 'undefined') callback = function() {};
let initOrientation = [0, 1, 2];
var verts = [],
vertNormals = [],
textures = [],
unpacked = {};
// unpacking stuff
unpacked.verts = [];
unpacked.norms = [];
unpacked.textures = [];
unpacked.hashindices = {};
unpacked.indices = [];
unpacked.index = 0;
// array of lines separated by the newline
var lines = objectData.split('n');
// test group catch data bpund or center
var objGroups = [];
// update swap orientation
if(inputArg.swap[0] !== null) {
swap(inputArg.swap[0], inputArg.swap[1], initOrientation);
}
var VERTEX_RE = /^vs/;
var NORMAL_RE = /^vns/;
var TEXTURE_RE = /^vts/;
var FACE_RE = /^fs/;
var WHITESPACE_RE = /s+/;
for(var i = 0;i < lines.length;i++) {
var line = lines[i].trim();
var elements = line.split(WHITESPACE_RE);
elements.shift();
if(VERTEX_RE.test(line)) {
// if this is a vertex
verts.push.apply(verts, elements);
if(objGroups.length > 0) {
objGroups[objGroups.length - 1].groupVert.push(elements)
}
} else if(NORMAL_RE.test(line)) {
// if this is a vertex normal
vertNormals.push.apply(vertNormals, elements);
} else if(TEXTURE_RE.test(line)) {
// if this is a texture
textures.push.apply(textures, elements);
} else if(FACE_RE.test(line)) {
// if this is a face
/*
split this face into an array of vertex groups
for example:
f 16/92/11 14/101/22 1/69/1
becomes:
['16/92/11', '14/101/22', '1/69/1'];
*/
var quad = false;
for(var j = 0, eleLen = elements.length;j < eleLen;j++) {
// Triangulating quads
// quad: 'f v0/t0/vn0 v1/t1/vn1 v2/t2/vn2 v3/t3/vn3/'
// corresponding triangles:
// 'f v0/t0/vn0 v1/t1/vn1 v2/t2/vn2'
// 'f v2/t2/vn2 v3/t3/vn3 v0/t0/vn0'
if(j === 3 && !quad) {
// add v2/t2/vn2 in again before continuing to 3
j = 2;
quad = true;
}
if(elements[j] in unpacked.hashindices) {
unpacked.indices.push(unpacked.hashindices[elements[j]]);
} else {
var vertex = elements[j].split('/');
if(typeof inputArg.scale == "number") {
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[0]] * inputArg.scale);
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[1]] * inputArg.scale);
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[2]] * inputArg.scale);
} else {
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[0]] * inputArg.scale.x);
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[1]] * inputArg.scale.y);
unpacked.verts.push(+verts[(vertex[0] - 1) * 3 + initOrientation[2]] * inputArg.scale.z);
}
// vertex textures
if(textures.length) {
unpacked.textures.push(+textures[(vertex[1] - 1) * 2 + 0]);
unpacked.textures.push(+textures[(vertex[1] - 1) * 2 + 1]);
}
// vertex normals
unpacked.norms.push(+vertNormals[(vertex[2] - 1) * 3 + 0]);
unpacked.norms.push(+vertNormals[(vertex[2] - 1) * 3 + 1]);
unpacked.norms.push(+vertNormals[(vertex[2] - 1) * 3 + 2]);
// add the newly created vertex to the list of indices
unpacked.hashindices[elements[j]] = unpacked.index;
unpacked.indices.push(unpacked.index);
// increment the counter
unpacked.index += 1;
}
if(j === 3 && quad) {
// add v0/t0/vn0 onto the second triangle
unpacked.indices.push(unpacked.hashindices[elements[0]]);
}
}
} else {
if(line.indexOf('# ') != -1) {
// console.log('obj loader comment:', line)
} else if(line.indexOf('mtllib') != -1) {
// console.log('obj loader MTL file:', line)
} else if(line.indexOf('g ') != -1) {
// console.log('obj loader group :', line)
var nameOFGroup = line.split(' ')[1]
if(nameOFGroup.indexOf('COLLIDER') != -1) {
console.log('obj loader group [SPECIAL CASE COLLIDER]:', nameOFGroup)
objGroups.push({
groupName: nameOFGroup,
groupVert: []
})
}
}
}
}
this.vertices = unpacked.verts;
this.vertexNormals = unpacked.norms;
this.textures = unpacked.textures;
this.indices = unpacked.indices;
if(objGroups.length > 0) {
this.groups = objGroups;
} else {
this.groups = null;
}
callback();
return this;
};
}
var Ajax = function() {
// this is just a helper class to ease ajax calls
var _this = this;
this.xmlhttp = new XMLHttpRequest();
this.get = function(url, callback) {
_this.xmlhttp.onreadystatechange = function() {
if(_this.xmlhttp.readyState === 4) {
callback(_this.xmlhttp.responseText, _this.xmlhttp.status);
}
};
_this.xmlhttp.open('GET', url, true);
_this.xmlhttp.send();
};
};
export var downloadMeshes = function(nameAndURLs, completionCallback, inputArg) {
var semaphore = Object.keys(nameAndURLs).length;
var error = false;
if(typeof inputArg === 'undefined') {
var inputArg = {
scale: 1,
swap: [null]
};
}
if(typeof inputArg.scale === 'undefined') inputArg.scale = 1;
if(typeof inputArg.swap === 'undefined') inputArg.swap = [null];
var meshes = {};
for(var mesh_name in nameAndURLs) {
if(nameAndURLs.hasOwnProperty(mesh_name)) {
new Ajax().get(
nameAndURLs[mesh_name],
(function(name) {
return function(data, status) {
if(status === 200) {
meshes[name] = new constructMesh(data, inputArg);
} else {
error = true;
console.error('An error has occurred and the mesh "' + name + '" could not be downloaded.');
}
// the request has finished, decrement the counter
semaphore--;
if(semaphore === 0) {
if(error) {
// if an error has occurred, the user is notified here and the
// callback is not called
console.error('An error has occurred and one or meshes has not been ' + 'downloaded. The execution of the script has terminated.');
throw '';
}
// there haven't been any errors in retrieving the meshes
// call the callback
completionCallback(meshes);
}
};
})(mesh_name)
);
}
}
};
var _buildBuffer = function(gl, type, data, itemSize) {
var buffer = gl.createBuffer();
var arrayView = type === gl.ARRAY_BUFFER ? Float32Array : Uint16Array;
gl.bindBuffer(type, buffer);
gl.bufferData(type, new arrayView(data), gl.STATIC_DRAW);
buffer.itemSize = itemSize;
buffer.numItems = data.length / itemSize;
return buffer;
};
export var initMeshBuffers = function(gl, mesh) {
mesh.normalBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertexNormals, 3);
mesh.textureBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.textures, 2);
mesh.vertexBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertices, 3);
mesh.indexBuffer = _buildBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, mesh.indices, 1);
};
export var deleteMeshBuffers = function(gl, mesh) {
gl.deleteBuffer(mesh.normalBuffer);
gl.deleteBuffer(mesh.textureBuffer);
gl.deleteBuffer(mesh.vertexBuffer);
gl.deleteBuffer(mesh.indexBuffer);
};
/**
* @description
* Construct sequence list argument for downloadMeshes.
* This is adaptation for blender obj animation export.
* For example:
* matrixEngine.objLoader.downloadMeshes(
matrixEngine.objLoader.makeObjSeqArg(
{
id: objName,
joinMultiPahts: [
{
path: "res/bvh-skeletal-base/swat-guy/seq-walk/low/swat",
id: objName,
from: 1, to: 34
},
{
path: "res/bvh-skeletal-base/swat-guy/seq-walk-pistol/low/swat-walk-pistol",
id: objName,
from: 35, to: 54
}
]
}),
onLoadObj
);
*/
export const makeObjSeqArg = (arg) => {
// Adaptation for blender (animation) obj exporter.
var local = {};
function localCalc(arg, noInitial = false) {
var zeros = '00000';
var l = {};
var helper = arg.from;
for(let j = arg.from, z = 1;j <= arg.to;j++) {
if(z > 9 && z < 99) {
zeros = '0000';
} else if(z > 99 && z < 999) {
zeros = '000';
} // no need more then 999
if(helper == arg.from && noInitial === false) {
l[arg.id] = arg.path + '_' + zeros + z + '.obj';
} else {
l[arg.id + (helper - 1)] = arg.path + '_' + zeros + z + '.obj';
}
helper++;
z++;
}
return l;
}
if(typeof arg.path === 'string') {
local = localCalc(arg);
} else if(typeof arg.path === 'undefined') {
if(typeof arg.joinMultiPahts !== 'undefined') {
console.log("ITS joinMultiPahts!");
var localFinal = {};
arg.joinMultiPahts.forEach((arg, index) => {
if(index === 0) {
localFinal = Object.assign(local, localCalc(arg));
} else {
localFinal = Object.assign(local, localCalc(arg, true));
}
});
console.log("joinMultiPahts LOCAL => ", localFinal);
return localFinal;
}
}
return local;
}
Any suggestion ?