beta.r3js.org
Theunis J. Botha 2016-09-30 12:56:58 +02:00
parent b94785d818
commit 0181ff245b
1 changed files with 757 additions and 0 deletions

757
game-lib.js Normal file
View File

@ -0,0 +1,757 @@
function GameLib3d(
Q,
THREE,
BlenderNode,
config,
apiUrl,
editorUrl,
scenes,
worlds
) {
this.Q = Q;
this.THREE = THREE;
this.BlenderNode = BlenderNode;
this.editor = null;
this.config = config;
if (typeof apiUrl == 'undefined') {
apiUrl = this.config.api16.url;
}
this.apiUrl = apiUrl;
if (typeof editorUrl == 'undefined') {
editorUrl = this.config.editor.url;
}
this.editorUrl = editorUrl;
/**
* I disabled MultiMaterial for now - since it has 2 issues so far
* 1. Raycasting doesn't work for culled faces (https://github.com/mrdoob/three.js/issues/9694)
* 2. Warnings in console - three js breaks when intersecting with faces for which uvs arent defined
* @type {boolean} forceMultiMaterial
*/
this.forceMultiMaterial = false;
this.textureLoader = new THREE.TextureLoader();
}
/**
* @param blenderTexture BlenderNode.Texture
* @param threeMaterial
* @param threeMaterialMapType
* @returns {promise.promise|jQuery.promise|*|promise|Promise}
*/
GameLib3d.prototype.loadMap = function(blenderTexture, threeMaterial, threeMaterialMapType) {
var q = this.Q.defer();
var imagePath = null;
if (blenderTexture && blenderTexture.image && blenderTexture.image.apiPath && blenderTexture.image.filename) {
/**
* Load the image from API here if apiPath is defined
*/
imagePath = this.apiUrl + blenderTexture.image.uploadPath + '/' + blenderTexture.image.filename;
}
if (blenderTexture && blenderTexture.image && blenderTexture.image.uploadPath && blenderTexture.image.filename) {
/**
* Else, load from upload source
*/
imagePath = this.editorUrl + blenderTexture.image.uploadPath + '/' + blenderTexture.image.filename;
}
if (imagePath) {
this.textureLoader.load(
imagePath,
function(texture) {
/**
* onLoad
*/
threeMaterial[threeMaterialMapType] = texture;
threeMaterial[threeMaterialMapType].name = blenderTexture.name;
threeMaterial[threeMaterialMapType].anisotropy = blenderTexture.anisotropy;
threeMaterial[threeMaterialMapType].encoding = blenderTexture.encoding;
threeMaterial[threeMaterialMapType].flipY = blenderTexture.flipY;
// threeMaterial[threeMaterialMapType].format = blenderTexture.format;
threeMaterial[threeMaterialMapType].generateMipmaps = blenderTexture.generateMipmaps;
threeMaterial[threeMaterialMapType].magFilter = blenderTexture.magFilter;
threeMaterial[threeMaterialMapType].minFilter = blenderTexture.minFilter;
threeMaterial[threeMaterialMapType].mapping = blenderTexture.mapping;
threeMaterial[threeMaterialMapType].mipmaps = blenderTexture.mipmaps;
threeMaterial[threeMaterialMapType].offset = new THREE.Vector2(
blenderTexture.offset.x,
blenderTexture.offset.y
);
threeMaterial[threeMaterialMapType].premultiplyAlpha = blenderTexture.premultiplyAlpha;
threeMaterial[threeMaterialMapType].textureType = blenderTexture.textureType;
threeMaterial[threeMaterialMapType].wrapS = blenderTexture.wrapS;
threeMaterial[threeMaterialMapType].wrapT = blenderTexture.wrapT;
threeMaterial[threeMaterialMapType].unpackAlignment = blenderTexture.unpackAlignment;
threeMaterial.needsUpdate = true;
q.resolve(true);
},
function(xhr) {
/**
* onProgress
*/
if (this.editor) {
this.editor.setServerStatus(Math.round(xhr.loaded / xhr.total * 100) + '% complete', 'success');
}
},
function(error) {
/**
* onError
*/
console.log("an error occurred while trying to load the image : " + imagePath);
q.resolve(null);
}
);
} else {
q.resolve(null);
}
return q.promise;
};
/**
* Creates a THREE Mesh from BlenderNode.Mesh
* @param mesh BlenderNode.Mesh
* @param geometry THREE.Geometry
* @param material THREE.Material
*/
GameLib3d.prototype.createThreeMesh = function(mesh, geometry, material) {
var threeMesh = null;
if (mesh.meshType == this.BlenderNode.Mesh.TYPE_NORMAL) {
threeMesh = new this.THREE.Mesh(geometry, material);
}
if (mesh.meshType == this.BlenderNode.Mesh.TYPE_SKINNED) {
var bones = mesh.skeleton.bones;
var skinIndices = mesh.skinIndices;
var skinWeights = mesh.skinWeights;
var threeBones = [];
for (var bi = 0; bi < bones.length; bi++) {
var bone = new this.THREE.Bone();
bone.name = bones[bi].name;
bone.position.x = bones[bi].position.x;
bone.position.y = bones[bi].position.y;
bone.position.z = bones[bi].position.z;
bone.rotation.x = bones[bi].rotation.x;
bone.rotation.y = bones[bi].rotation.y;
bone.rotation.z = bones[bi].rotation.z;
bone.quaternion.x = bones[bi].quaternion.x;
bone.quaternion.y = bones[bi].quaternion.y;
bone.quaternion.z = bones[bi].quaternion.z;
bone.quaternion.w = bones[bi].quaternion.w;
bone.scale.x = bones[bi].scale.x;
bone.scale.y = bones[bi].scale.y;
bone.scale.z = bones[bi].scale.z;
bone.up.x = bones[bi].up.x;
bone.up.y = bones[bi].up.y;
bone.up.z = bones[bi].up.z;
threeBones.push(bone);
}
/**
* Setup the bone relationships
*/
for (var br = 0; br < bones.length; br++) {
for (var cbi = 0; cbi < bones[br].childBoneIds.length; cbi++) {
threeBones[br].add(threeBones[bones[br].childBoneIds[cbi]]);
}
}
/**
* Setup bones (indexes)
*/
for (var si = 0; si < skinIndices.length; si++) {
geometry.skinIndices.push(
new this.THREE.Vector4(
skinIndices[si].x,
skinIndices[si].y,
skinIndices[si].z,
skinIndices[si].w
)
);
}
/**
* Setup bones (weights)
*/
for (var sw = 0; sw < skinWeights.length; sw++) {
geometry.skinWeights.push(
new this.THREE.Vector4(
skinWeights[sw].x,
skinWeights[sw].y,
skinWeights[sw].z,
skinWeights[sw].w
)
);
}
threeMesh = new this.THREE.SkinnedMesh(geometry, material);
var skeleton = new this.THREE.Skeleton(threeBones);
skeleton.useVertexTexture = mesh.skeleton.useVertexTexture;
for (var i = 0; i < bones.length; i++) {
if (bones[i].parentBoneId === null) {
threeMesh.add(threeBones[i]);
break;
}
}
threeMesh.bind(skeleton);
threeMesh.pose();
threeMesh.skeleton.skeletonHelper = new this.THREE.SkeletonHelper(threeMesh);
threeMesh.skeleton.skeletonHelper.material.linewidth = 5;
}
if (threeMesh == null) {
console.log('cannot handle meshes of type ' + mesh.meshType + ' yet.');
}
mesh.threeMeshId = threeMesh.id;
return threeMesh;
};
GameLib3d.prototype.loadMaps = function(blenderMaterial, blenderMaps, threeMaterial) {
var textureMaps = [];
for (var ti = 0; ti < blenderMaps.length; ti++) {
var map = blenderMaps[ti];
if (blenderMaterial.maps.hasOwnProperty(map)) {
var blenderTexture = blenderMaterial.maps[map];
if (
blenderTexture &&
blenderTexture.image &&
blenderTexture.image.filename &&
blenderTexture.image.uploadPath
) {
var threeMap = null;
if (map == 'alpha') {
threeMap = 'alhpaMap';
}
if (map == 'ao') {
threeMap = 'aoMap';
}
if (map == 'bump') {
threeMap = 'bumpMap';
}
if (map == 'displacement') {
threeMap = 'displacementMap';
}
if (map == 'emissive') {
threeMap = 'emissiveMap';
}
if (map == 'environment') {
threeMap = 'envMap';
}
if (map == 'light') {
threeMap = 'lightMap';
}
if (map == 'specular') {
threeMap = 'specularMap';
}
if (map == 'diffuse') {
threeMap = 'map';
}
if (map == 'roughness') {
threeMap = 'roughnessMap';
}
if (map == 'metalness') {
threeMap = 'metalnessMap';
}
if (threeMap == null) {
console.warn("unsupported map type : " + map);
}
textureMaps.push(this.loadMap(blenderMaterial.maps[map], threeMaterial, threeMap));
}
}
}
return textureMaps;
};
/**
* Creates a this.THREE Material from a this.BlenderNode.Material
* @param blenderMaterial this.BlenderNode.Material
*/
GameLib3d.prototype.createThreeMaterial = function(blenderMaterial) {
var defer = this.Q.defer();
var threeMaterial = null;
var blenderMaps = [];
if (blenderMaterial.materialType == this.BlenderNode.Material.TYPE_MESH_STANDARD) {
threeMaterial = new this.THREE.MeshStandardMaterial({
name: blenderMaterial.name,
opacity: blenderMaterial.opacity,
transparent: blenderMaterial.transparent,
blending: blenderMaterial.blending,
blendSrc: blenderMaterial.blendSrc,
blendDst: blenderMaterial.blendDst,
blendEquation: blenderMaterial.blendEquation,
depthTest: blenderMaterial.depthTest,
depthFunc: blenderMaterial.depthFunc,
depthWrite: blenderMaterial.depthWrite,
polygonOffset: blenderMaterial.polygonOffset,
polygonOffsetFactor: blenderMaterial.polygonOffsetFactor,
polygonOffsetUnits: blenderMaterial.polygonOffsetUnits,
alphaTest: blenderMaterial.alphaTest,
clippingPlanes: blenderMaterial.clippingPlanes,
clipShadows: blenderMaterial.clipShadows,
overdraw: blenderMaterial.overdraw,
visible: blenderMaterial.visible,
side: blenderMaterial.side,
color: new this.THREE.Color(
blenderMaterial.color.r,
blenderMaterial.color.g,
blenderMaterial.color.b
),
roughness: blenderMaterial.roughness,
metalness: blenderMaterial.metalness,
lightMapIntensity: blenderMaterial.lightMapIntensity,
aoMapIntensity: blenderMaterial.aoMapIntensity,
emissive: new this.THREE.Color(
blenderMaterial.emissive.r,
blenderMaterial.emissive.g,
blenderMaterial.emissive.b
),
emissiveIntensity: blenderMaterial.emissiveIntensity,
bumpScale: blenderMaterial.bumpScale,
normalScale: blenderMaterial.normalScale,
displacementScale: blenderMaterial.displacementScale,
refractionRatio: blenderMaterial.refractionRatio,
fog: blenderMaterial.fog,
shading: blenderMaterial.shading,
wireframe: blenderMaterial.wireframe,
wireframeLinewidth: blenderMaterial.wireframeLineWidth,
wireframeLinecap: blenderMaterial.wireframeLineCap,
wireframeLinejoin: blenderMaterial.wireframeLineJoin,
vertexColors: blenderMaterial.vertexColors,
skinning: blenderMaterial.skinning,
morphTargets: blenderMaterial.morphTargets,
morphNormals: blenderMaterial.morphNormals
});
blenderMaps.push(
'diffuse',
'light',
'ao',
'emissive',
'bump',
'normal',
'displacement',
'roughness',
'metalness',
'alpha',
'environment'
);
} else if (blenderMaterial.materialType == this.BlenderNode.Material.TYPE_MESH_PHONG) {
threeMaterial = new this.THREE.MeshPhongMaterial({
name: blenderMaterial.name,
opacity: blenderMaterial.opacity,
transparent: blenderMaterial.transparent,
blending: blenderMaterial.blending,
blendSrc: blenderMaterial.blendSrc,
blendDst: blenderMaterial.blendDst,
blendEquation: blenderMaterial.blendEquation,
depthTest: blenderMaterial.depthTest,
depthFunc: blenderMaterial.depthFunc,
depthWrite: blenderMaterial.depthWrite,
polygonOffset: blenderMaterial.polygonOffset,
polygonOffsetFactor: blenderMaterial.polygonOffsetFactor,
polygonOffsetUnits: blenderMaterial.polygonOffsetUnits,
alphaTest: blenderMaterial.alphaTest,
clippingPlanes: blenderMaterial.clippingPlanes,
clipShadows: blenderMaterial.clipShadows,
overdraw: blenderMaterial.overdraw,
visible: blenderMaterial.visible,
side: blenderMaterial.side,
color: new this.THREE.Color(
blenderMaterial.color.r,
blenderMaterial.color.g,
blenderMaterial.color.b
),
specular: new this.THREE.Color(
blenderMaterial.specular.r,
blenderMaterial.specular.g,
blenderMaterial.specular.b
),
shininess: blenderMaterial.shininess,
lightMapIntensity: blenderMaterial.lightMapIntensity,
aoMapIntensity: blenderMaterial.aoMapIntensity,
emissive: new this.THREE.Color(
blenderMaterial.emissive.r,
blenderMaterial.emissive.g,
blenderMaterial.emissive.b
),
emissiveIntensity: blenderMaterial.emissiveIntensity,
bumpScale: blenderMaterial.bumpScale,
normalScale: blenderMaterial.normalScale,
displacementScale: blenderMaterial.displacementScale,
combine: blenderMaterial.combine,
refractionRatio: blenderMaterial.refractionRatio,
fog: blenderMaterial.fog,
shading: blenderMaterial.shading,
wireframe: blenderMaterial.wireframe,
wireframeLinewidth: blenderMaterial.wireframeLineWidth,
wireframeLinecap: blenderMaterial.wireframeLineCap,
wireframeLinejoin: blenderMaterial.wireframeLineJoin,
vertexColors: blenderMaterial.vertexColors,
skinning: blenderMaterial.skinning,
morphTargets: blenderMaterial.morphTargets,
morphNormals: blenderMaterial.morphNormals
});
blenderMaps.push(
'diffuse',
'light',
'ao',
'emissive',
'bump',
'normal',
'displacement',
'specular',
'alpha',
'environment'
);
} else {
console.log("material type is not implemented yet: " + blenderMaterial.materialType + " - material indexes could be screwed up");
}
if (blenderMaps.length > 0) {
var textureMaps = this.loadMaps(blenderMaterial, blenderMaps, threeMaterial);
Q.all(textureMaps).then(
function(){
defer.resolve(threeMaterial);
}
).catch(
function(error){
console.log(error);
defer.reject(error);
}
)
} else {
defer.resolve(threeMaterial);
}
return defer.promise;
};
/**
* Loads a this.BlenderNode.Scene object into a ThreeJS Scene object
* @param blenderScene this.BlenderNode.Scene
* @param onLoaded callback when all meshes have loaded
* @param computeNormals set to true to compute new face and vertex normals during load
*/
GameLib3d.prototype.loadScene = function(blenderScene, onLoaded, computeNormals) {
console.log("loading scene " + blenderScene.name);
var meshQ = [];
for (var m = 0; m < blenderScene.meshes.length; m++) {
var mesh = blenderScene.meshes[m];
console.log("loading mesh " + mesh.name);
var geometry = new this.THREE.Geometry();
var vertices = mesh.vertices;
var faces = mesh.faces;
var faceVertexUvs = mesh.faceVertexUvs;
var materials = mesh.materials;
/**
* Setup vertices
*/
for (var v = 0; v < vertices.length; v++) {
geometry.vertices.push(
new this.THREE.Vector3(
vertices[v].position.x,
vertices[v].position.y,
vertices[v].position.z
)
)
}
/**
* Setup faces
*/
for (var f = 0; f < faces.length; f++) {
var face = new this.THREE.Face3(
faces[f].v0,
faces[f].v1,
faces[f].v2,
new this.THREE.Vector3(
faces[f].normal.x,
faces[f].normal.y,
faces[f].normal.z
),
new this.THREE.Color(
faces[f].color.r,
faces[f].color.g,
faces[f].color.b
),
faces[f].materialIndex
);
face.vertexColors = [
new this.THREE.Color(
faces[f].vertexColors[0].r,
faces[f].vertexColors[0].g,
faces[f].vertexColors[0].b
),
new this.THREE.Color(
faces[f].vertexColors[1].r,
faces[f].vertexColors[1].g,
faces[f].vertexColors[1].b
),
new this.THREE.Color(
faces[f].vertexColors[2].r,
faces[f].vertexColors[2].g,
faces[f].vertexColors[2].b
)
];
face.normal = new this.THREE.Vector3(
faces[f].normal.x,
faces[f].normal.y,
faces[f].normal.z
);
face.vertexNormals = [
new this.THREE.Vector3(
faces[f].vertexNormals[0].x,
faces[f].vertexNormals[0].y,
faces[f].vertexNormals[0].z
),
new this.THREE.Vector3(
faces[f].vertexNormals[1].x,
faces[f].vertexNormals[1].y,
faces[f].vertexNormals[1].z
),
new this.THREE.Vector3(
faces[f].vertexNormals[2].x,
faces[f].vertexNormals[2].y,
faces[f].vertexNormals[2].z
)
];
geometry.faces.push(face);
}
geometry.faceVertexUvs = [];
/**
* Setup face UVs
*/
for (var fm = 0; fm < faceVertexUvs.length; fm++) {
var faceMaterialVertexUvs = faceVertexUvs[fm];
geometry.faceVertexUvs[fm] = [];
for (var fuv = 0; fuv < faceMaterialVertexUvs.length; fuv++) {
geometry.faceVertexUvs[fm][fuv] = [];
geometry.faceVertexUvs[fm][fuv].push(
new this.THREE.Vector2(
faceMaterialVertexUvs[fuv][0].x,
faceMaterialVertexUvs[fuv][0].y
),
new this.THREE.Vector2(
faceMaterialVertexUvs[fuv][1].x,
faceMaterialVertexUvs[fuv][1].y
),
new this.THREE.Vector2(
faceMaterialVertexUvs[fuv][2].x,
faceMaterialVertexUvs[fuv][2].y
)
);
}
}
/**
* Re-calculate normals (if we have to)
* @type {Array}
*/
if (computeNormals) {
geometry.computeFaceNormals();
geometry.computeVertexNormals();
}
var threeMaterialLoaders = [];
/**
* Setup materials
*/
for (var mi = 0; mi < materials.length; mi++) {
threeMaterialLoaders.push(this.createThreeMaterial(materials[mi]));
}
var result = this.Q.all(threeMaterialLoaders).then(
function(gl3d, mesh, geometry) {
return function(materials) {
console.log("loaded all materials maps (" + materials.length + ")");
var material = null;
if (materials.length > 1 && gl3d.forceMultiMaterial) {
material = new gl3d.THREE.MultiMaterial(materials);
/**
* If I don't set the side of the MultiMaterial to DoubleSide, raycasting breaks
*/
//material.side = THREE.DoubleSide;
} else {
material = materials[0];
}
var threeMesh = gl3d.createThreeMesh(mesh, geometry, material);
threeMesh.name = mesh.name;
threeMesh.position.x = mesh.position.x;
threeMesh.position.y = mesh.position.y;
threeMesh.position.z = mesh.position.z;
threeMesh.rotation.x = mesh.rotation.x;
threeMesh.rotation.y = mesh.rotation.y;
threeMesh.rotation.z = mesh.rotation.z;
threeMesh.scale.x = mesh.scale.x;
threeMesh.scale.y = mesh.scale.y;
threeMesh.scale.z = mesh.scale.z;
threeMesh.quaternion.x = mesh.quaternion.x;
threeMesh.quaternion.y = mesh.quaternion.y;
threeMesh.quaternion.z = mesh.quaternion.z;
threeMesh.quaternion.w = mesh.quaternion.w;
return threeMesh;
};
}(this, mesh, geometry)
).catch(function(error){
console.log(error);
});
meshQ.push(result);
}
this.Q.all(meshQ).then(function(threeMeshes){
console.log("all meshes have loaded");
if (typeof onLoaded != 'undefined') {
var threeLights = [];
for (var sli = 0; sli < blenderScene.lights.length; sli++) {
var blenderLight = blenderScene.lights[sli];
var light = null;
if (blenderLight.lightType == 'AmbientLight') {
light = new THREE.AmbientLight(blenderLight.color, blenderLight.intensity);
}
if (blenderLight.lightType == 'DirectionalLight') {
light = new THREE.DirectionalLight(blenderLight.color, blenderLight.intensity);
}
if (blenderLight.lightType == 'PointLight') {
light = new THREE.PointLight(blenderLight.color, blenderLight.intensity);
light.distance = blenderLight.distance;
light.decay = blenderLight.decay;
}
if (blenderLight.lightType == 'SpotLight') {
light = new THREE.SpotLight(blenderLight.color, blenderLight.intensity);
light.distance = blenderLight.distance;
light.angle = blenderLight.angle;
light.penumbra = blenderLight.penumbra;
light.decay = blenderLight.decay;
}
light.position.x = blenderLight.position.x;
light.position.y = blenderLight.position.y;
light.position.z = blenderLight.position.z;
light.rotation.x = blenderLight.rotation.x;
light.rotation.y = blenderLight.rotation.y;
light.rotation.z = blenderLight.rotation.z;
// light.scale.x = blenderLight.scale.x;
// light.scale.y = blenderLight.scale.y;
// light.scale.z = blenderLight.scale.z;
if (light == null) {
console.warn('Does not support lights of type :' + blenderLight.lightType + ', not imported');
} else {
light.name = blenderLight.name;
threeLights.push(light);
}
}
onLoaded(threeMeshes, threeLights);
}
}).catch(function(error){
console.log(error);
});
};
if (typeof module !== 'undefined') {
module.exports = GameLib3d;
}