@ -0,0 +1,2 @@ |
||||
/node_modules/ |
||||
/uploads/ |
@ -0,0 +1,24 @@ |
||||
var express = require('express'); |
||||
var multer = require('multer'); |
||||
var upload = multer({ dest: 'uploads/' }); |
||||
var app = express(); |
||||
|
||||
app.use(express.static('static')); |
||||
|
||||
//主页加载
|
||||
app.get('/', function (req, res) { |
||||
res.sendFile(__dirname + "/views/createScheme.html"); |
||||
}); |
||||
|
||||
//接受文件上传,并且返回文件名
|
||||
app.post('/upload', upload.single('file'), function (req, res) { |
||||
//console.info(req.file)
|
||||
res.send(req.file); |
||||
}); |
||||
|
||||
app.get('/files/:filename', function (req, res) { |
||||
var filename = req.params['filename']; |
||||
res.sendFile(__dirname + "/uploads/" + filename); |
||||
}) |
||||
|
||||
app.listen(80); |
@ -0,0 +1,3 @@ |
||||
{ |
||||
"lockfileVersion": 1 |
||||
} |
@ -0,0 +1,15 @@ |
||||
{ |
||||
"name": "3dmodelos", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
||||
}, |
||||
"author": "", |
||||
"license": "ISC", |
||||
"dependencies": { |
||||
"express": "^4.16.4", |
||||
"multer": "^1.4.1" |
||||
} |
||||
} |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 1.5 MiB |
@ -0,0 +1,266 @@ |
||||
var renderer, scene, camera; |
||||
|
||||
//整个页面维护的数据
|
||||
var data = { name: '默认方案', components: [] }; |
||||
|
||||
//当前选择的部件
|
||||
var componentIndex = -1; |
||||
|
||||
$().ready(function () { |
||||
initData(); |
||||
initUi(); |
||||
initThree(); |
||||
loadmodel(); |
||||
initEvent(); |
||||
}); |
||||
//初始化Threejs
|
||||
function initThree() { |
||||
initScene(); |
||||
initCamera(); |
||||
initRenderer(); |
||||
render(); |
||||
} |
||||
//获取数据
|
||||
function initData() { |
||||
//todo ajax请求数据
|
||||
} |
||||
//刷新主Ui
|
||||
function initUi() { |
||||
//方案名
|
||||
$('#schemeName').val(data.name); |
||||
//组件列表
|
||||
freshComponentItem(); |
||||
} |
||||
|
||||
function freshComponentItem() { |
||||
//清空原有列表
|
||||
$('.componentItem').remove(); |
||||
for (var index in data.components) { |
||||
//添加一个item并注册了click监听
|
||||
$('#componentList').prepend("<li class='componentItem' onclick='selectComponent(" + index + ")'><a>" + data.components[index].name + "</a></li>"); |
||||
} |
||||
} |
||||
|
||||
//选择部件
|
||||
function selectComponent(index) { |
||||
componentIndex = index; |
||||
$('#componentTitle').text(data.components[componentIndex].name); |
||||
$('#delComponent').show(); |
||||
$('#upload').removeClass("disabled"); |
||||
//加载模型列表
|
||||
freshmodelList(); |
||||
} |
||||
|
||||
//选择模型列表
|
||||
function freshmodelList() { |
||||
$('.list-group-item').remove(); |
||||
if (data.components[componentIndex].models.length == 0) { |
||||
$('#modelList').append('<a class="list-group-item">暂无模型</a>') |
||||
} else { |
||||
// if (data.components[componentIndex].modelIndex == -1)
|
||||
// data.components[componentIndex].modelIndex = 0;
|
||||
for (var index in data.components[componentIndex].models) { |
||||
if (index == data.components[componentIndex].modelIndex) { |
||||
data.components[componentIndex].models[index].modelObj.visible = true; |
||||
$('#modelList').append("<a class='list-group-item active' onclick='selectModel(" + index + ")'>" + data.components[componentIndex].models[index].name + "</a>") |
||||
} else { |
||||
data.components[componentIndex].models[index].modelObj.visible = false; |
||||
$('#modelList').append("<a class='list-group-item' onclick='selectModel(" + index + ")'>" + data.components[componentIndex].models[index].name + "</a>") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
//选择模型
|
||||
function selectModel(index) { |
||||
data.components[componentIndex].modelIndex = index; |
||||
freshmodelList(); |
||||
} |
||||
|
||||
//初始化模型
|
||||
function loadmodel() { |
||||
|
||||
} |
||||
|
||||
function initScene() { |
||||
//场景设置
|
||||
scene = new THREE.Scene(); |
||||
//设置天空盒
|
||||
scene.background = new THREE.CubeTextureLoader() |
||||
.setPath('/img/skybox/') |
||||
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); |
||||
//场景灯光
|
||||
//环境光
|
||||
var light = new THREE.HemisphereLight(0xffffff, 0x444444); |
||||
light.position.set(0, 2, 0); |
||||
scene.add(light); |
||||
//直射光
|
||||
light = new THREE.DirectionalLight(0xffffff); |
||||
light.position.set(0, 2, 1); |
||||
light.castShadow = true; |
||||
light.shadow.camera.top = 180; |
||||
light.shadow.camera.bottom = - 100; |
||||
light.shadow.camera.left = - 120; |
||||
light.shadow.camera.right = 120; |
||||
scene.add(light); |
||||
//grid
|
||||
var grid = new THREE.GridHelper(20, 20, 0x0000ff, 0xff0000); |
||||
grid.material.opacity = 0.2; |
||||
grid.material.transparent = true; |
||||
scene.add(grid); |
||||
|
||||
} |
||||
|
||||
function initCamera() { |
||||
//相机设置
|
||||
camera = new THREE.PerspectiveCamera(45, $('#viewField').innerWidth() / $('#viewField').innerHeight()); |
||||
camera.position.set(0, 5, 10); |
||||
//让相机对着场景中央
|
||||
camera.lookAt(scene.position); |
||||
//相机控制,控制的相机和监听的dom
|
||||
controls = new THREE.OrbitControls(camera, $('#viewField')[0]); |
||||
controls.target.set(0, 0, 0); |
||||
controls.update(); |
||||
|
||||
} |
||||
|
||||
function initRenderer() { |
||||
//初始化渲染器
|
||||
renderer = new THREE.WebGLRenderer(); |
||||
//设置像素值
|
||||
renderer.setPixelRatio(window.devicePixelRatio); |
||||
//设置渲染范围为屏幕的大小
|
||||
renderer.setSize($('#viewField').innerWidth(), $('#viewField').innerHeight()); |
||||
//将渲染结果放到页面中
|
||||
$('#viewField').append(renderer.domElement); |
||||
} |
||||
|
||||
function render() { |
||||
requestAnimationFrame(render); |
||||
renderer.render(scene, camera); |
||||
} |
||||
|
||||
//初始化所有事件
|
||||
function initEvent() { |
||||
//方案名变动监听
|
||||
$('#schemeName').change(function () { |
||||
data.name = $('#schemeName').val(); |
||||
}); |
||||
|
||||
//将fileinput事件注册到uploadbtn上
|
||||
$("#upload").click(function () { |
||||
$("#file").click(); |
||||
}); |
||||
|
||||
//只要file发生改变就上传文件
|
||||
$("#file").change(function () { |
||||
if ($(this).val().length > 0) { |
||||
var formData = new FormData($('#uploadForm')[0]); |
||||
$.ajax({ |
||||
type: 'post', |
||||
url: "/upload", |
||||
data: formData, |
||||
cache: false, |
||||
processData: false, |
||||
contentType: false, |
||||
success: function (fileData) { |
||||
//上传成功后加载模型
|
||||
//加载是异步的
|
||||
var addModelItem = function (modelObj) { |
||||
data.components[componentIndex].models.push({ |
||||
name: fileData.originalname, |
||||
fileId: fileData.filename, |
||||
modelObj: modelObj |
||||
}); |
||||
selectModel(data.components[componentIndex].models.length - 1); |
||||
} |
||||
addModel('/files/' + fileData.filename, addModelItem); |
||||
}, |
||||
error: function () { |
||||
alert("上传失败") |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
//当浏览器大小变化时
|
||||
$(window).resize(function () { |
||||
camera.aspect = $('#viewField').innerWidth() / $('#viewField').innerHeight(); |
||||
camera.updateProjectionMatrix(); |
||||
renderer.setSize($('#viewField').innerWidth(), $('#viewField').innerHeight()); |
||||
}); |
||||
|
||||
//当模态框消失的时候清空text
|
||||
$('#addComponentModal').on('hidden.zui.modal', function () { |
||||
$("#componentName").val("") |
||||
}); |
||||
|
||||
//添加一个部件
|
||||
$('#addComponent').click(function () { |
||||
var componentName = $("#componentName").val().trim(); |
||||
if (componentName.length > 0) { |
||||
var component = { |
||||
name: componentName, |
||||
models: [], |
||||
modelIndex: -1 |
||||
} |
||||
data.components.push(component); |
||||
freshComponentItem(); |
||||
$('#addComponentModal').modal('hide'); |
||||
} |
||||
}); |
||||
|
||||
//删除部件
|
||||
$('#delComponent').click(function () { |
||||
new $.zui.Messager('你确定要删除该部件吗,该部件的所有模型将会被清空!', { |
||||
type: 'danger', |
||||
close: false, |
||||
actions: [{ |
||||
icon: 'ok-sign', |
||||
text: '确定', |
||||
action: function () { // 点击该操作按钮的回调函数
|
||||
if (componentIndex >= 0) { |
||||
for (var index in data.components[componentIndex].models) |
||||
scene.remove(data.components[componentIndex].models[index].modelObj); |
||||
data.components.splice(componentIndex, 1); |
||||
freshComponentItem(); |
||||
componentIndex = -1; |
||||
$('#componentTitle').text('请先选择部件'); |
||||
$('#delComponent').hide(); |
||||
$('#upload').addClass("disabled"); |
||||
$('.list-group-item').remove(); |
||||
//todo 还需要发送ajax清空模型文件
|
||||
} |
||||
} |
||||
}] |
||||
}).show(); |
||||
}); |
||||
} |
||||
|
||||
function addModel(url, callBack) { |
||||
var map = new THREE.TextureLoader().load('/img/texture/001.jpg'); |
||||
//加载obj模型
|
||||
// loader = new THREE.OBJLoader();
|
||||
// loader.load(url, function (object) {
|
||||
// object.position.set(0, -1, 0);
|
||||
// object.scale.set(0.01, 0.01, 0.01);
|
||||
// scene.add(object);
|
||||
// });
|
||||
//加载fbx模型
|
||||
var loader = new THREE.FBXLoader(); |
||||
loader.load(url, function (object) { |
||||
object.traverse(function (child) { |
||||
if (child instanceof THREE.Mesh) { |
||||
//child.material.map = map;
|
||||
child.material.castShadow = true; |
||||
child.material.receiveShadow = true; |
||||
child.material.needsUpdate = true; |
||||
} |
||||
}); |
||||
scene.add(object); |
||||
callBack(object); |
||||
}); |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,797 @@ |
||||
/** |
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/ |
||||
|
||||
THREE.OBJLoader = ( function () { |
||||
|
||||
// o object_name | g group_name
|
||||
var object_pattern = /^[og]\s*(.+)?/; |
||||
// mtllib file_reference
|
||||
var material_library_pattern = /^mtllib /; |
||||
// usemtl material_name
|
||||
var material_use_pattern = /^usemtl /; |
||||
|
||||
function ParserState() { |
||||
|
||||
var state = { |
||||
objects: [], |
||||
object: {}, |
||||
|
||||
vertices: [], |
||||
normals: [], |
||||
colors: [], |
||||
uvs: [], |
||||
|
||||
materialLibraries: [], |
||||
|
||||
startObject: function ( name, fromDeclaration ) { |
||||
|
||||
// If the current object (initial from reset) is not from a g/o declaration in the parsed
|
||||
// file. We need to use it for the first parsed g/o to keep things in sync.
|
||||
if ( this.object && this.object.fromDeclaration === false ) { |
||||
|
||||
this.object.name = name; |
||||
this.object.fromDeclaration = ( fromDeclaration !== false ); |
||||
return; |
||||
|
||||
} |
||||
|
||||
var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined ); |
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) { |
||||
|
||||
this.object._finalize( true ); |
||||
|
||||
} |
||||
|
||||
this.object = { |
||||
name: name || '', |
||||
fromDeclaration: ( fromDeclaration !== false ), |
||||
|
||||
geometry: { |
||||
vertices: [], |
||||
normals: [], |
||||
colors: [], |
||||
uvs: [] |
||||
}, |
||||
materials: [], |
||||
smooth: true, |
||||
|
||||
startMaterial: function ( name, libraries ) { |
||||
|
||||
var previous = this._finalize( false ); |
||||
|
||||
// New usemtl declaration overwrites an inherited material, except if faces were declared
|
||||
// after the material, then it must be preserved for proper MultiMaterial continuation.
|
||||
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) { |
||||
|
||||
this.materials.splice( previous.index, 1 ); |
||||
|
||||
} |
||||
|
||||
var material = { |
||||
index: this.materials.length, |
||||
name: name || '', |
||||
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ), |
||||
smooth: ( previous !== undefined ? previous.smooth : this.smooth ), |
||||
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ), |
||||
groupEnd: - 1, |
||||
groupCount: - 1, |
||||
inherited: false, |
||||
|
||||
clone: function ( index ) { |
||||
|
||||
var cloned = { |
||||
index: ( typeof index === 'number' ? index : this.index ), |
||||
name: this.name, |
||||
mtllib: this.mtllib, |
||||
smooth: this.smooth, |
||||
groupStart: 0, |
||||
groupEnd: - 1, |
||||
groupCount: - 1, |
||||
inherited: false |
||||
}; |
||||
cloned.clone = this.clone.bind( cloned ); |
||||
return cloned; |
||||
|
||||
} |
||||
}; |
||||
|
||||
this.materials.push( material ); |
||||
|
||||
return material; |
||||
|
||||
}, |
||||
|
||||
currentMaterial: function () { |
||||
|
||||
if ( this.materials.length > 0 ) { |
||||
|
||||
return this.materials[ this.materials.length - 1 ]; |
||||
|
||||
} |
||||
|
||||
return undefined; |
||||
|
||||
}, |
||||
|
||||
_finalize: function ( end ) { |
||||
|
||||
var lastMultiMaterial = this.currentMaterial(); |
||||
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) { |
||||
|
||||
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; |
||||
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; |
||||
lastMultiMaterial.inherited = false; |
||||
|
||||
} |
||||
|
||||
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
|
||||
if ( end && this.materials.length > 1 ) { |
||||
|
||||
for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) { |
||||
|
||||
if ( this.materials[ mi ].groupCount <= 0 ) { |
||||
|
||||
this.materials.splice( mi, 1 ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// Guarantee at least one empty material, this makes the creation later more straight forward.
|
||||
if ( end && this.materials.length === 0 ) { |
||||
|
||||
this.materials.push( { |
||||
name: '', |
||||
smooth: this.smooth |
||||
} ); |
||||
|
||||
} |
||||
|
||||
return lastMultiMaterial; |
||||
|
||||
} |
||||
}; |
||||
|
||||
// Inherit previous objects material.
|
||||
// Spec tells us that a declared material must be set to all objects until a new material is declared.
|
||||
// If a usemtl declaration is encountered while this new object is being parsed, it will
|
||||
// overwrite the inherited material. Exception being that there was already face declarations
|
||||
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
|
||||
|
||||
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) { |
||||
|
||||
var declared = previousMaterial.clone( 0 ); |
||||
declared.inherited = true; |
||||
this.object.materials.push( declared ); |
||||
|
||||
} |
||||
|
||||
this.objects.push( this.object ); |
||||
|
||||
}, |
||||
|
||||
finalize: function () { |
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) { |
||||
|
||||
this.object._finalize( true ); |
||||
|
||||
} |
||||
|
||||
}, |
||||
|
||||
parseVertexIndex: function ( value, len ) { |
||||
|
||||
var index = parseInt( value, 10 ); |
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; |
||||
|
||||
}, |
||||
|
||||
parseNormalIndex: function ( value, len ) { |
||||
|
||||
var index = parseInt( value, 10 ); |
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; |
||||
|
||||
}, |
||||
|
||||
parseUVIndex: function ( value, len ) { |
||||
|
||||
var index = parseInt( value, 10 ); |
||||
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2; |
||||
|
||||
}, |
||||
|
||||
addVertex: function ( a, b, c ) { |
||||
|
||||
var src = this.vertices; |
||||
var dst = this.object.geometry.vertices; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); |
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); |
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); |
||||
|
||||
}, |
||||
|
||||
addVertexPoint: function ( a ) { |
||||
|
||||
var src = this.vertices; |
||||
var dst = this.object.geometry.vertices; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); |
||||
|
||||
}, |
||||
|
||||
addVertexLine: function ( a ) { |
||||
|
||||
var src = this.vertices; |
||||
var dst = this.object.geometry.vertices; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); |
||||
|
||||
}, |
||||
|
||||
addNormal: function ( a, b, c ) { |
||||
|
||||
var src = this.normals; |
||||
var dst = this.object.geometry.normals; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); |
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); |
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); |
||||
|
||||
}, |
||||
|
||||
addColor: function ( a, b, c ) { |
||||
|
||||
var src = this.colors; |
||||
var dst = this.object.geometry.colors; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); |
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); |
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); |
||||
|
||||
}, |
||||
|
||||
addUV: function ( a, b, c ) { |
||||
|
||||
var src = this.uvs; |
||||
var dst = this.object.geometry.uvs; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ] ); |
||||
dst.push( src[ b + 0 ], src[ b + 1 ] ); |
||||
dst.push( src[ c + 0 ], src[ c + 1 ] ); |
||||
|
||||
}, |
||||
|
||||
addUVLine: function ( a ) { |
||||
|
||||
var src = this.uvs; |
||||
var dst = this.object.geometry.uvs; |
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ] ); |
||||
|
||||
}, |
||||
|
||||
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) { |
||||
|
||||
var vLen = this.vertices.length; |
||||
|
||||
var ia = this.parseVertexIndex( a, vLen ); |
||||
var ib = this.parseVertexIndex( b, vLen ); |
||||
var ic = this.parseVertexIndex( c, vLen ); |
||||
|
||||
this.addVertex( ia, ib, ic ); |
||||
|
||||
if ( ua !== undefined && ua !== '' ) { |
||||
|
||||
var uvLen = this.uvs.length; |
||||
ia = this.parseUVIndex( ua, uvLen ); |
||||
ib = this.parseUVIndex( ub, uvLen ); |
||||
ic = this.parseUVIndex( uc, uvLen ); |
||||
this.addUV( ia, ib, ic ); |
||||
|
||||
} |
||||
|
||||
if ( na !== undefined && na !== '' ) { |
||||
|
||||
// Normals are many times the same. If so, skip function call and parseInt.
|
||||
var nLen = this.normals.length; |
||||
ia = this.parseNormalIndex( na, nLen ); |
||||
|
||||
ib = na === nb ? ia : this.parseNormalIndex( nb, nLen ); |
||||
ic = na === nc ? ia : this.parseNormalIndex( nc, nLen ); |
||||
|
||||
this.addNormal( ia, ib, ic ); |
||||
|
||||
} |
||||
|
||||
if ( this.colors.length > 0 ) { |
||||
|
||||
this.addColor( ia, ib, ic ); |
||||
|
||||
} |
||||
|
||||
}, |
||||
|
||||
addPointGeometry: function ( vertices ) { |
||||
|
||||
this.object.geometry.type = 'Points'; |
||||
|
||||
var vLen = this.vertices.length; |
||||
|
||||
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { |
||||
|
||||
this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) ); |
||||
|
||||
} |
||||
|
||||
}, |
||||
|
||||
addLineGeometry: function ( vertices, uvs ) { |
||||
|
||||
this.object.geometry.type = 'Line'; |
||||
|
||||
var vLen = this.vertices.length; |
||||
var uvLen = this.uvs.length; |
||||
|
||||
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { |
||||
|
||||
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) ); |
||||
|
||||
} |
||||
|
||||
for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) { |
||||
|
||||
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
}; |
||||
|
||||
state.startObject( '', false ); |
||||
|
||||
return state; |
||||
|
||||
} |
||||
|
||||
//
|
||||
|
||||
function OBJLoader( manager ) { |
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; |
||||
|
||||
this.materials = null; |
||||
|
||||
} |
||||
|
||||
OBJLoader.prototype = { |
||||
|
||||
constructor: OBJLoader, |
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) { |
||||
|
||||
var scope = this; |
||||
|
||||
var loader = new THREE.FileLoader( scope.manager ); |
||||
loader.setPath( this.path ); |
||||
loader.load( url, function ( text ) { |
||||
|
||||
onLoad( scope.parse( text ) ); |
||||
|
||||
}, onProgress, onError ); |
||||
|
||||
}, |
||||
|
||||
setPath: function ( value ) { |
||||
|
||||
this.path = value; |
||||
|
||||
return this; |
||||
|
||||
}, |
||||
|
||||
setMaterials: function ( materials ) { |
||||
|
||||
this.materials = materials; |
||||
|
||||
return this; |
||||
|
||||
}, |
||||
|
||||
parse: function ( text ) { |
||||
|
||||
console.time( 'OBJLoader' ); |
||||
|
||||
var state = new ParserState(); |
||||
|
||||
if ( text.indexOf( '\r\n' ) !== - 1 ) { |
||||
|
||||
// This is faster than String.split with regex that splits on both
|
||||
text = text.replace( /\r\n/g, '\n' ); |
||||
|
||||
} |
||||
|
||||
if ( text.indexOf( '\\\n' ) !== - 1 ) { |
||||
|
||||
// join lines separated by a line continuation character (\)
|
||||
text = text.replace( /\\\n/g, '' ); |
||||
|
||||
} |
||||
|
||||
var lines = text.split( '\n' ); |
||||
var line = '', lineFirstChar = ''; |
||||
var lineLength = 0; |
||||
var result = []; |
||||
|
||||
// Faster to just trim left side of the line. Use if available.
|
||||
var trimLeft = ( typeof ''.trimLeft === 'function' ); |
||||
|
||||
for ( var i = 0, l = lines.length; i < l; i ++ ) { |
||||
|
||||
line = lines[ i ]; |
||||
|
||||
line = trimLeft ? line.trimLeft() : line.trim(); |
||||
|
||||
lineLength = line.length; |
||||
|
||||
if ( lineLength === 0 ) continue; |
||||
|
||||
lineFirstChar = line.charAt( 0 ); |
||||
|
||||
// @todo invoke passed in handler if any
|
||||
if ( lineFirstChar === '#' ) continue; |
||||
|
||||
if ( lineFirstChar === 'v' ) { |
||||
|
||||
var data = line.split( /\s+/ ); |
||||
|
||||
switch ( data[ 0 ] ) { |
||||
|
||||
case 'v': |
||||
state.vertices.push( |
||||
parseFloat( data[ 1 ] ), |
||||
parseFloat( data[ 2 ] ), |
||||
parseFloat( data[ 3 ] ) |
||||
); |
||||
if ( data.length === 8 ) { |
||||
|
||||
state.colors.push( |
||||
parseFloat( data[ 4 ] ), |
||||
parseFloat( data[ 5 ] ), |
||||
parseFloat( data[ 6 ] ) |
||||
|
||||
); |
||||
|
||||
} |
||||
break; |
||||
case 'vn': |
||||
state.normals.push( |
||||
parseFloat( data[ 1 ] ), |
||||
parseFloat( data[ 2 ] ), |
||||
parseFloat( data[ 3 ] ) |
||||
); |
||||
break; |
||||
case 'vt': |
||||
state.uvs.push( |
||||
parseFloat( data[ 1 ] ), |
||||
parseFloat( data[ 2 ] ) |
||||
); |
||||
break; |
||||
|
||||
} |
||||
|
||||
} else if ( lineFirstChar === 'f' ) { |
||||
|
||||
var lineData = line.substr( 1 ).trim(); |
||||
var vertexData = lineData.split( /\s+/ ); |
||||
var faceVertices = []; |
||||
|
||||
// Parse the face vertex data into an easy to work with format
|
||||
|
||||
for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) { |
||||
|
||||
var vertex = vertexData[ j ]; |
||||
|
||||
if ( vertex.length > 0 ) { |
||||
|
||||
var vertexParts = vertex.split( '/' ); |
||||
faceVertices.push( vertexParts ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
|
||||
|
||||
var v1 = faceVertices[ 0 ]; |
||||
|
||||
for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) { |
||||
|
||||
var v2 = faceVertices[ j ]; |
||||
var v3 = faceVertices[ j + 1 ]; |
||||
|
||||
state.addFace( |
||||
v1[ 0 ], v2[ 0 ], v3[ 0 ], |
||||
v1[ 1 ], v2[ 1 ], v3[ 1 ], |
||||
v1[ 2 ], v2[ 2 ], v3[ 2 ] |
||||
); |
||||
|
||||
} |
||||
|
||||
} else if ( lineFirstChar === 'l' ) { |
||||
|
||||
var lineParts = line.substring( 1 ).trim().split( " " ); |
||||
var lineVertices = [], lineUVs = []; |
||||
|
||||
if ( line.indexOf( "/" ) === - 1 ) { |
||||
|
||||
lineVertices = lineParts; |
||||
|
||||
} else { |
||||
|
||||
for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) { |
||||
|
||||
var parts = lineParts[ li ].split( "/" ); |
||||
|
||||
if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] ); |
||||
if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] ); |
||||
|
||||
} |
||||
|
||||
} |
||||
state.addLineGeometry( lineVertices, lineUVs ); |
||||
|
||||
} else if ( lineFirstChar === 'p' ) { |
||||
|
||||
var lineData = line.substr( 1 ).trim(); |
||||
var pointData = lineData.split( " " ); |
||||
|
||||
state.addPointGeometry( pointData ); |
||||
|
||||
} else if ( ( result = object_pattern.exec( line ) ) !== null ) { |
||||
|
||||
// o object_name
|
||||
// or
|
||||
// g group_name
|
||||
|
||||
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
|
||||
// var name = result[ 0 ].substr( 1 ).trim();
|
||||
var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 ); |
||||
|
||||
state.startObject( name ); |
||||
|
||||
} else if ( material_use_pattern.test( line ) ) { |
||||
|
||||
// material
|
||||
|
||||
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries ); |
||||
|
||||
} else if ( material_library_pattern.test( line ) ) { |
||||
|
||||
// mtl file
|
||||
|
||||
state.materialLibraries.push( line.substring( 7 ).trim() ); |
||||
|
||||
} else if ( lineFirstChar === 's' ) { |
||||
|
||||
result = line.split( ' ' ); |
||||
|
||||
// smooth shading
|
||||
|
||||
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
|
||||
// but does not define a usemtl for each face set.
|
||||
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
|
||||
// This requires some care to not create extra material on each smooth value for "normal" obj files.
|
||||
// where explicit usemtl defines geometry groups.
|
||||
// Example asset: examples/models/obj/cerberus/Cerberus.obj
|
||||
|
||||
/* |
||||
* http://paulbourke.net/dataformats/obj/
|
||||
* or |
||||
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
|
||||
* |
||||
* From chapter "Grouping" Syntax explanation "s group_number": |
||||
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off. |
||||
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form |
||||
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater |
||||
* than 0." |
||||
*/ |
||||
if ( result.length > 1 ) { |
||||
|
||||
var value = result[ 1 ].trim().toLowerCase(); |
||||
state.object.smooth = ( value !== '0' && value !== 'off' ); |
||||
|
||||
} else { |
||||
|
||||
// ZBrush can produce "s" lines #11707
|
||||
state.object.smooth = true; |
||||
|
||||
} |
||||
var material = state.object.currentMaterial(); |
||||
if ( material ) material.smooth = state.object.smooth; |
||||
|
||||
} else { |
||||
|
||||
// Handle null terminated files without exception
|
||||
if ( line === '\0' ) continue; |
||||
|
||||
throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
state.finalize(); |
||||
|
||||
var container = new THREE.Group(); |
||||
container.materialLibraries = [].concat( state.materialLibraries ); |
||||
|
||||
for ( var i = 0, l = state.objects.length; i < l; i ++ ) { |
||||
|
||||
var object = state.objects[ i ]; |
||||
var geometry = object.geometry; |
||||
var materials = object.materials; |
||||
var isLine = ( geometry.type === 'Line' ); |
||||
var isPoints = ( geometry.type === 'Points' ); |
||||
var hasVertexColors = false; |
||||
|
||||
// Skip o/g line declarations that did not follow with any faces
|
||||
if ( geometry.vertices.length === 0 ) continue; |
||||
|
||||
var buffergeometry = new THREE.BufferGeometry(); |
||||
|
||||
buffergeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) ); |
||||
|
||||
if ( geometry.normals.length > 0 ) { |
||||
|
||||
buffergeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) ); |
||||
|
||||
} else { |
||||
|
||||
buffergeometry.computeVertexNormals(); |
||||
|
||||
} |
||||
|
||||
if ( geometry.colors.length > 0 ) { |
||||
|
||||
hasVertexColors = true; |
||||
buffergeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) ); |
||||
|
||||
} |
||||
|
||||
if ( geometry.uvs.length > 0 ) { |
||||
|
||||
buffergeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) ); |
||||
|
||||
} |
||||
|
||||
// Create materials
|
||||
|
||||
var createdMaterials = []; |
||||
|
||||
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { |
||||
|
||||
var sourceMaterial = materials[ mi ]; |
||||
var material = undefined; |
||||
|
||||
if ( this.materials !== null ) { |
||||
|
||||
material = this.materials.create( sourceMaterial.name ); |
||||
|
||||
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
|
||||
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) { |
||||
|
||||
var materialLine = new THREE.LineBasicMaterial(); |
||||
THREE.Material.prototype.copy.call( materialLine, material ); |
||||
materialLine.color.copy( material.color ); |
||||
materialLine.lights = false; |
||||
material = materialLine; |
||||
|
||||
} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) { |
||||
|
||||
var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } ); |
||||
THREE.Material.prototype.copy.call( materialPoints, material ); |
||||
materialPoints.color.copy( material.color ); |
||||
materialPoints.map = material.map; |
||||
materialPoints.lights = false; |
||||
material = materialPoints; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
if ( ! material ) { |
||||
|
||||
if ( isLine ) { |
||||
|
||||
material = new THREE.LineBasicMaterial(); |
||||
|
||||
} else if ( isPoints ) { |
||||
|
||||
material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } ); |
||||
|
||||
} else { |
||||
|
||||
material = new THREE.MeshPhongMaterial(); |
||||
|
||||
} |
||||
|
||||
material.name = sourceMaterial.name; |
||||
|
||||
} |
||||
|
||||
material.flatShading = sourceMaterial.smooth ? false : true; |
||||
material.vertexColors = hasVertexColors ? THREE.VertexColors : THREE.NoColors; |
||||
|
||||
createdMaterials.push( material ); |
||||
|
||||
} |
||||
|
||||
// Create mesh
|
||||
|
||||
var mesh; |
||||
|
||||
if ( createdMaterials.length > 1 ) { |
||||
|
||||
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { |
||||
|
||||
var sourceMaterial = materials[ mi ]; |
||||
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi ); |
||||
|
||||
} |
||||
|
||||
if ( isLine ) { |
||||
|
||||
mesh = new THREE.LineSegments( buffergeometry, createdMaterials ); |
||||
|
||||
} else if ( isPoints ) { |
||||
|
||||
mesh = new THREE.Points( buffergeometry, createdMaterials ); |
||||
|
||||
} else { |
||||
|
||||
mesh = new THREE.Mesh( buffergeometry, createdMaterials ); |
||||
|
||||
} |
||||
|
||||
} else { |
||||
|
||||
if ( isLine ) { |
||||
|
||||
mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ); |
||||
|
||||
} else if ( isPoints ) { |
||||
|
||||
mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] ); |
||||
|
||||
} else { |
||||
|
||||
mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
mesh.name = object.name; |
||||
|
||||
container.add( mesh ); |
||||
|
||||
} |
||||
|
||||
console.timeEnd( 'OBJLoader' ); |
||||
|
||||
return container; |
||||
|
||||
} |
||||
|
||||
}; |
||||
|
||||
return OBJLoader; |
||||
|
||||
} )(); |
@ -0,0 +1,548 @@ |
||||
/* |
||||
* @author Daosheng Mu / https://github.com/DaoshengMu/
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author takahirox / https://github.com/takahirox/
|
||||
*/ |
||||
|
||||
THREE.TGALoader = function ( manager ) { |
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; |
||||
|
||||
}; |
||||
|
||||
THREE.TGALoader.prototype = { |
||||
|
||||
constructor: THREE.TGALoader, |
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) { |
||||
|
||||
var scope = this; |
||||
|
||||
var texture = new THREE.Texture(); |
||||
|
||||
var loader = new THREE.FileLoader( this.manager ); |
||||
loader.setResponseType( 'arraybuffer' ); |
||||
loader.setPath( this.path ); |
||||
|
||||
loader.load( url, function ( buffer ) { |
||||
|
||||
texture.image = scope.parse( buffer ); |
||||
texture.needsUpdate = true; |
||||
|
||||
if ( onLoad !== undefined ) { |
||||
|
||||
onLoad( texture ); |
||||
|
||||
} |
||||
|
||||
}, onProgress, onError ); |
||||
|
||||
return texture; |
||||
|
||||
}, |
||||
|
||||
parse: function ( buffer ) { |
||||
|
||||
// reference from vthibault, https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Targa.js
|
||||
|
||||
function tgaCheckHeader( header ) { |
||||
|
||||
switch ( header.image_type ) { |
||||
|
||||
// check indexed type
|
||||
|
||||
case TGA_TYPE_INDEXED: |
||||
case TGA_TYPE_RLE_INDEXED: |
||||
if ( header.colormap_length > 256 || header.colormap_size !== 24 || header.colormap_type !== 1 ) { |
||||
|
||||
console.error( 'THREE.TGALoader: Invalid type colormap data for indexed type.' ); |
||||
|
||||
} |
||||
break; |
||||
|
||||
// check colormap type
|
||||
|
||||
case TGA_TYPE_RGB: |
||||
case TGA_TYPE_GREY: |
||||
case TGA_TYPE_RLE_RGB: |
||||
case TGA_TYPE_RLE_GREY: |
||||
if ( header.colormap_type ) { |
||||
|
||||
console.error( 'THREE.TGALoader: Invalid type colormap data for colormap type.' ); |
||||
|
||||
} |
||||
break; |
||||
|
||||
// What the need of a file without data ?
|
||||
|
||||
case TGA_TYPE_NO_DATA: |
||||
console.error( 'THREE.TGALoader: No data.' ); |
||||
|
||||
// Invalid type ?
|
||||
|
||||
default: |
||||
console.error( 'THREE.TGALoader: Invalid type "%s".', header.image_type ); |
||||
|
||||
} |
||||
|
||||
// check image width and height
|
||||
|
||||
if ( header.width <= 0 || header.height <= 0 ) { |
||||
|
||||
console.error( 'THREE.TGALoader: Invalid image size.' ); |
||||
|
||||
} |
||||
|
||||
// check image pixel size
|
||||
|
||||
if ( header.pixel_size !== 8 && header.pixel_size !== 16 && |
||||
header.pixel_size !== 24 && header.pixel_size !== 32 ) { |
||||
|
||||
console.error( 'THREE.TGALoader: Invalid pixel size "%s".', header.pixel_size ); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// parse tga image buffer
|
||||
|
||||
function tgaParse( use_rle, use_pal, header, offset, data ) { |
||||
|
||||
var pixel_data, |
||||
pixel_size, |
||||
pixel_total, |
||||
palettes; |
||||
|
||||
pixel_size = header.pixel_size >> 3; |
||||
pixel_total = header.width * header.height * pixel_size; |
||||
|
||||
// read palettes
|
||||
|
||||
if ( use_pal ) { |
||||
|
||||
palettes = data.subarray( offset, offset += header.colormap_length * ( header.colormap_size >> 3 ) ); |
||||
|
||||
} |
||||
|
||||
// read RLE
|
||||
|
||||
if ( use_rle ) { |
||||
|
||||
pixel_data = new Uint8Array( pixel_total ); |
||||
|
||||
var c, count, i; |
||||
var shift = 0; |
||||
var pixels = new Uint8Array( pixel_size ); |
||||
|
||||
while ( shift < pixel_total ) { |
||||
|
||||
c = data[ offset ++ ]; |
||||
count = ( c & 0x7f ) + 1; |
||||
|
||||
// RLE pixels
|
||||
|
||||
if ( c & 0x80 ) { |
||||
|
||||
// bind pixel tmp array
|
||||
|
||||
for ( i = 0; i < pixel_size; ++ i ) { |
||||
|
||||
pixels[ i ] = data[ offset ++ ]; |
||||
|
||||
} |
||||
|
||||
// copy pixel array
|
||||
|
||||
for ( i = 0; i < count; ++ i ) { |
||||
|
||||
pixel_data.set( pixels, shift + i * pixel_size ); |
||||
|
||||
} |
||||
|
||||
shift += pixel_size * count; |
||||
|
||||
} else { |
||||
|
||||
// raw pixels
|
||||
|
||||
count *= pixel_size; |
||||
for ( i = 0; i < count; ++ i ) { |
||||
|
||||
pixel_data[ shift + i ] = data[ offset ++ ]; |
||||
|
||||
} |
||||
shift += count; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} else { |
||||
|
||||
// raw pixels
|
||||
|
||||
pixel_data = data.subarray( |
||||
offset, offset += ( use_pal ? header.width * header.height : pixel_total ) |
||||
); |
||||
|
||||
} |
||||
|
||||
return { |
||||
pixel_data: pixel_data, |
||||
palettes: palettes |
||||
}; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageData8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image, palettes ) { |
||||
|
||||
var colormap = palettes; |
||||
var color, i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i ++ ) { |
||||
|
||||
color = image[ i ]; |
||||
imageData[ ( x + width * y ) * 4 + 3 ] = 255; |
||||
imageData[ ( x + width * y ) * 4 + 2 ] = colormap[ ( color * 3 ) + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = colormap[ ( color * 3 ) + 1 ]; |
||||
imageData[ ( x + width * y ) * 4 + 0 ] = colormap[ ( color * 3 ) + 2 ]; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageData16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { |
||||
|
||||
var color, i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { |
||||
|
||||
color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ?
|
||||
imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2; |
||||
imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3; |
||||
imageData[ ( x + width * y ) * 4 + 3 ] = ( color & 0x8000 ) ? 0 : 255; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageData24bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { |
||||
|
||||
var i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i += 3 ) { |
||||
|
||||
imageData[ ( x + width * y ) * 4 + 3 ] = 255; |
||||
imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; |
||||
imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageData32bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { |
||||
|
||||
var i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i += 4 ) { |
||||
|
||||
imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; |
||||
imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; |
||||
imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 3 ]; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageDataGrey8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { |
||||
|
||||
var color, i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i ++ ) { |
||||
|
||||
color = image[ i ]; |
||||
imageData[ ( x + width * y ) * 4 + 0 ] = color; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = color; |
||||
imageData[ ( x + width * y ) * 4 + 2 ] = color; |
||||
imageData[ ( x + width * y ) * 4 + 3 ] = 255; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function tgaGetImageDataGrey16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { |
||||
|
||||
var i = 0, x, y; |
||||
var width = header.width; |
||||
|
||||
for ( y = y_start; y !== y_end; y += y_step ) { |
||||
|
||||
for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { |
||||
|
||||
imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; |
||||
imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 1 ]; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
return imageData; |
||||
|
||||
} |
||||
|
||||
function getTgaRGBA( data, width, height, image, palette ) { |
||||
|
||||
var x_start, |
||||
y_start, |
||||
x_step, |
||||
y_step, |
||||
x_end, |
||||
y_end; |
||||
|
||||
switch ( ( header.flags & TGA_ORIGIN_MASK ) >> TGA_ORIGIN_SHIFT ) { |
||||
|
||||
default: |
||||
case TGA_ORIGIN_UL: |
||||
x_start = 0; |
||||
x_step = 1; |
||||
x_end = width; |
||||
y_start = 0; |
||||
y_step = 1; |
||||
y_end = height; |
||||
break; |
||||
|
||||
case TGA_ORIGIN_BL: |
||||
x_start = 0; |
||||
x_step = 1; |
||||
x_end = width; |
||||
y_start = height - 1; |
||||
y_step = - 1; |
||||
y_end = - 1; |
||||
break; |
||||
|
||||
case TGA_ORIGIN_UR: |
||||
x_start = width - 1; |
||||
x_step = - 1; |
||||
x_end = - 1; |
||||
y_start = 0; |
||||
y_step = 1; |
||||
y_end = height; |
||||
break; |
||||
|
||||
case TGA_ORIGIN_BR: |
||||
x_start = width - 1; |
||||
x_step = - 1; |
||||
x_end = - 1; |
||||
y_start = height - 1; |
||||
y_step = - 1; |
||||
y_end = - 1; |
||||
break; |
||||
|
||||
} |
||||
|
||||
if ( use_grey ) { |
||||
|
||||
switch ( header.pixel_size ) { |
||||
|
||||
case 8: |
||||
tgaGetImageDataGrey8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); |
||||
break; |
||||
|
||||
case 16: |
||||
tgaGetImageDataGrey16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); |
||||
break; |
||||
|
||||
default: |
||||
console.error( 'THREE.TGALoader: Format not supported.' ); |
||||
break; |
||||
|
||||
} |
||||
|
||||
} else { |
||||
|
||||
switch ( header.pixel_size ) { |
||||
|
||||
case 8: |
||||
tgaGetImageData8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image, palette ); |
||||
break; |
||||
|
||||
case 16: |
||||
tgaGetImageData16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); |
||||
break; |
||||
|
||||
case 24: |
||||
tgaGetImageData24bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); |
||||
break; |
||||
|
||||
case 32: |
||||
tgaGetImageData32bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); |
||||
break; |
||||
|
||||
default: |
||||
console.error( 'THREE.TGALoader: Format not supported.' ); |
||||
break; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// Load image data according to specific method
|
||||
// var func = 'tgaGetImageData' + (use_grey ? 'Grey' : '') + (header.pixel_size) + 'bits';
|
||||
// func(data, y_start, y_step, y_end, x_start, x_step, x_end, width, image, palette );
|
||||
return data; |
||||
|
||||
} |
||||
|
||||
// TGA constants
|
||||
|
||||
var TGA_TYPE_NO_DATA = 0, |
||||
TGA_TYPE_INDEXED = 1, |
||||
TGA_TYPE_RGB = 2, |
||||
TGA_TYPE_GREY = 3, |
||||
TGA_TYPE_RLE_INDEXED = 9, |
||||
TGA_TYPE_RLE_RGB = 10, |
||||
TGA_TYPE_RLE_GREY = 11, |
||||
|
||||
TGA_ORIGIN_MASK = 0x30, |
||||
TGA_ORIGIN_SHIFT = 0x04, |
||||
TGA_ORIGIN_BL = 0x00, |
||||
TGA_ORIGIN_BR = 0x01, |
||||
TGA_ORIGIN_UL = 0x02, |
||||
TGA_ORIGIN_UR = 0x03; |
||||
|
||||
if ( buffer.length < 19 ) console.error( 'THREE.TGALoader: Not enough data to contain header.' ); |
||||
|
||||
var content = new Uint8Array( buffer ), |
||||
offset = 0, |
||||
header = { |
||||
id_length: content[ offset ++ ], |
||||
colormap_type: content[ offset ++ ], |
||||
image_type: content[ offset ++ ], |
||||
colormap_index: content[ offset ++ ] | content[ offset ++ ] << 8, |
||||
colormap_length: content[ offset ++ ] | content[ offset ++ ] << 8, |
||||
colormap_size: content[ offset ++ ], |
||||
origin: [ |
||||
content[ offset ++ ] | content[ offset ++ ] << 8, |
||||
content[ offset ++ ] | content[ offset ++ ] << 8 |
||||
], |
||||
width: content[ offset ++ ] | content[ offset ++ ] << 8, |
||||
height: content[ offset ++ ] | content[ offset ++ ] << 8, |
||||
pixel_size: content[ offset ++ ], |
||||
flags: content[ offset ++ ] |
||||
}; |
||||
|
||||
// check tga if it is valid format
|
||||
|
||||
tgaCheckHeader( header ); |
||||
|
||||
if ( header.id_length + offset > buffer.length ) { |
||||
|
||||
console.error( 'THREE.TGALoader: No data.' ); |
||||
|
||||
} |
||||
|
||||
// skip the needn't data
|
||||
|
||||
offset += header.id_length; |
||||
|
||||
// get targa information about RLE compression and palette
|
||||
|
||||
var use_rle = false, |
||||
use_pal = false, |
||||
use_grey = false; |
||||
|
||||
switch ( header.image_type ) { |
||||
|
||||
case TGA_TYPE_RLE_INDEXED: |
||||
use_rle = true; |
||||
use_pal = true; |
||||
break; |
||||
|
||||
case TGA_TYPE_INDEXED: |
||||
use_pal = true; |
||||
break; |
||||
|
||||
case TGA_TYPE_RLE_RGB: |
||||
use_rle = true; |
||||
break; |
||||
|
||||
case TGA_TYPE_RGB: |
||||
break; |
||||
|
||||
case TGA_TYPE_RLE_GREY: |
||||
use_rle = true; |
||||
use_grey = true; |
||||
break; |
||||
|
||||
case TGA_TYPE_GREY: |
||||
use_grey = true; |
||||
break; |
||||
|
||||
} |
||||
|
||||
//
|
||||
|
||||
var canvas = document.createElement( 'canvas' ); |
||||
canvas.width = header.width; |
||||
canvas.height = header.height; |
||||
|
||||
var context = canvas.getContext( '2d' ); |
||||
var imageData = context.createImageData( header.width, header.height ); |
||||
|
||||
var result = tgaParse( use_rle, use_pal, header, offset, content ); |
||||
var rgbaData = getTgaRGBA( imageData.data, header.width, header.height, result.pixel_data, result.palettes ); |
||||
|
||||
context.putImageData( imageData, 0, 0 ); |
||||
|
||||
return canvas; |
||||
|
||||
}, |
||||
|
||||
setPath: function ( value ) { |
||||
|
||||
this.path = value; |
||||
return this; |
||||
|
||||
} |
||||
|
||||
}; |
@ -0,0 +1,15 @@ |
||||
/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';var l=void 0,aa=this;function r(c,d){var a=c.split("."),b=aa;!(a[0]in b)&&b.execScript&&b.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&d!==l?b[e]=d:b=b[e]?b[e]:b[e]={}};var t="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function v(c){var d=c.length,a=0,b=Number.POSITIVE_INFINITY,e,f,g,h,k,m,n,p,s,x;for(p=0;p<d;++p)c[p]>a&&(a=c[p]),c[p]<b&&(b=c[p]);e=1<<a;f=new (t?Uint32Array:Array)(e);g=1;h=0;for(k=2;g<=a;){for(p=0;p<d;++p)if(c[p]===g){m=0;n=h;for(s=0;s<g;++s)m=m<<1|n&1,n>>=1;x=g<<16|p;for(s=m;s<e;s+=k)f[s]=x;++h}++g;h<<=1;k<<=1}return[f,a,b]};function w(c,d){this.g=[];this.h=32768;this.d=this.f=this.a=this.l=0;this.input=t?new Uint8Array(c):c;this.m=!1;this.i=y;this.r=!1;if(d||!(d={}))d.index&&(this.a=d.index),d.bufferSize&&(this.h=d.bufferSize),d.bufferType&&(this.i=d.bufferType),d.resize&&(this.r=d.resize);switch(this.i){case A:this.b=32768;this.c=new (t?Uint8Array:Array)(32768+this.h+258);break;case y:this.b=0;this.c=new (t?Uint8Array:Array)(this.h);this.e=this.z;this.n=this.v;this.j=this.w;break;default:throw Error("invalid inflate mode"); |
||||
}}var A=0,y=1,B={t:A,s:y}; |
||||
w.prototype.k=function(){for(;!this.m;){var c=C(this,3);c&1&&(this.m=!0);c>>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=l,h=l,k=b.length,m=l;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case A:for(;e+ |
||||
g>b.length;){m=k-e;g-=m;if(t)b.set(d.subarray(a,a+m),e),e+=m,a+=m;else for(;m--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case y:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(t)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(ba,ca);break;case 2:for(var n=C(this,5)+257,p=C(this,5)+1,s=C(this,4)+4,x=new (t?Uint8Array:Array)(D.length),S=l,T=l,U=l,u=l,M=l,F=l,z=l,q=l,V=l,q=0;q<s;++q)x[D[q]]= |
||||
C(this,3);if(!t){q=s;for(s=x.length;q<s;++q)x[D[q]]=0}S=v(x);u=new (t?Uint8Array:Array)(n+p);q=0;for(V=n+p;q<V;)switch(M=E(this,S),M){case 16:for(z=3+C(this,2);z--;)u[q++]=F;break;case 17:for(z=3+C(this,3);z--;)u[q++]=0;F=0;break;case 18:for(z=11+C(this,7);z--;)u[q++]=0;F=0;break;default:F=u[q++]=M}T=t?v(u.subarray(0,n)):v(u.slice(0,n));U=t?v(u.subarray(n)):v(u.slice(n));this.j(T,U);break;default:throw Error("unknown BTYPE: "+c);}}return this.n()}; |
||||
var G=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],D=t?new Uint16Array(G):G,H=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],I=t?new Uint16Array(H):H,J=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],K=t?new Uint8Array(J):J,L=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],da=t?new Uint16Array(L):L,ea=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12, |
||||
13,13],N=t?new Uint8Array(ea):ea,O=new (t?Uint8Array:Array)(288),P,fa;P=0;for(fa=O.length;P<fa;++P)O[P]=143>=P?8:255>=P?9:279>=P?7:8;var ba=v(O),Q=new (t?Uint8Array:Array)(30),R,ga;R=0;for(ga=Q.length;R<ga;++R)Q[R]=5;var ca=v(Q);function C(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h;b<d;){if(f>=g)throw Error("input buffer is broken");a|=e[f++]<<b;b+=8}h=a&(1<<d)-1;c.f=a>>>d;c.d=b-d;c.a=f;return h} |
||||
function E(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],m,n;b<k&&!(f>=g);)a|=e[f++]<<b,b+=8;m=h[a&(1<<k)-1];n=m>>>16;if(n>b)throw Error("invalid code length: "+n);c.f=a>>n;c.d=b-n;c.a=f;return m&65535} |
||||
w.prototype.j=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length-258,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=I[g];0<K[g]&&(k+=C(this,K[g]));f=E(this,d);h=da[f];0<N[f]&&(h+=C(this,N[f]));b>=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; |
||||
w.prototype.w=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=I[g];0<K[g]&&(k+=C(this,K[g]));f=E(this,d);h=da[f];0<N[f]&&(h+=C(this,N[f]));b+k>e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; |
||||
w.prototype.e=function(){var c=new (t?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(t)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;a<b;++a)c[a]=e[a+32768]}this.g.push(c);this.l+=c.length;if(t)e.set(e.subarray(d,d+32768));else for(a=0;32768>a;++a)e[a]=e[d+a];this.b=32768;return e}; |
||||
w.prototype.z=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.u&&(a+=c.u));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=f<h.length?h.length+f:h.length<<1):e=h.length*a;t?(d=new Uint8Array(e),d.set(h)):d=h;return this.c=d}; |
||||
w.prototype.n=function(){var c=0,d=this.c,a=this.g,b,e=new (t?Uint8Array:Array)(this.l+(this.b-32768)),f,g,h,k;if(0===a.length)return t?this.c.subarray(32768,this.b):this.c.slice(32768,this.b);f=0;for(g=a.length;f<g;++f){b=a[f];h=0;for(k=b.length;h<k;++h)e[c++]=b[h]}f=32768;for(g=this.b;f<g;++f)e[c++]=d[f];this.g=[];return this.buffer=e}; |
||||
w.prototype.v=function(){var c,d=this.b;t?this.r?(c=new Uint8Array(d),c.set(this.c.subarray(0,d))):c=this.c.subarray(0,d):(this.c.length>d&&(this.c.length=d),c=this.c);return this.buffer=c};function W(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case ha:this.method=ha;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.q=new w(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} |
||||
W.prototype.k=function(){var c=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f<g;f++)e[f]=(e[f].charCodeAt(0)&255)>>>0;b=e}for(var h=1,k=0,m=b.length,n,p=0;0<m;){n=1024<m?1024:m;m-=n;do h+=b[p++],k+=h;while(--n);h%=65521;k%=65521}if(a!==(k<<16|h)>>>0)throw Error("invalid adler-32 checksum");}return d};var ha=8;r("Zlib.Inflate",W);r("Zlib.Inflate.prototype.decompress",W.prototype.k);var X={ADAPTIVE:B.s,BLOCK:B.t},Y,Z,$,ia;if(Object.keys)Y=Object.keys(X);else for(Z in Y=[],$=0,X)Y[$++]=Z;$=0;for(ia=Y.length;$<ia;++$)Z=Y[$],r("Zlib.Inflate.BufferType."+Z,X[Z]);}).call(this); |
@ -0,0 +1,110 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8" /> |
||||
<title>创建方案</title> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> |
||||
<!-- three.js --> |
||||
<script src="/js/three/three.js"></script> |
||||
<script src="/js/three/OrbitControls.js"></script> |
||||
<!-- 加载器 --> |
||||
<script src="/js/three/OBJLoader.js"></script> |
||||
<script src="/js/three/FBXLoader.js"></script> |
||||
<script src="/js/three/TGALoader.js"></script> |
||||
<script src='/js/three/inflate.min.js'></script> |
||||
<!-- ZUI --> |
||||
<link rel="stylesheet" href="//cdn.bootcss.com/zui/1.8.1/css/zui.min.css"> |
||||
<script src="//cdn.bootcss.com/zui/1.8.1/js/zui.min.js"></script> |
||||
<style> |
||||
* { |
||||
margin: 0px; |
||||
padding: 0px; |
||||
} |
||||
html, |
||||
body { |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
} |
||||
#viewField { |
||||
width: 80%; |
||||
height: 100%; |
||||
position: absolute; |
||||
top: 0px; |
||||
right: 0px; |
||||
} |
||||
#editField { |
||||
background-color: #fafafa; |
||||
padding: 10px; |
||||
width: 20%; |
||||
height: 100%; |
||||
position: absolute; |
||||
top: 0px; |
||||
left: 0px; |
||||
} |
||||
</style> |
||||
</head> |
||||
|
||||
<body> |
||||
<!-- threejs容器 --> |
||||
<div id="viewField"></div> |
||||
<!-- 编辑栏 --> |
||||
<div id="editField"> |
||||
<div class="panel"> |
||||
<div class="panel-heading"> |
||||
<div class="input-control has-label-left"> |
||||
<input id="schemeName" type="text" class="form-control" placeholder=""> |
||||
<label for="schemeName" class="input-control-label-left"><i class="icon icon-edit"></i>方案名</label> |
||||
</div> |
||||
</div> |
||||
<div class="panel-body"> |
||||
<div class="dropdown"> |
||||
<button class="btn btn-block" data-toggle="dropdown">组件选择 <span class="caret"></span></button> |
||||
<ul class="dropdown-menu" id="componentList"> |
||||
<li><a data-toggle="modal" data-target="#addComponentModal"><i class="icon icon-plus"></i> 添加新组件</a></li> |
||||
</ul> |
||||
</div> |
||||
<div style="margin-top:20px"> |
||||
<div class="page-header"> |
||||
<h3 id="componentTitle">请先选择部件</h3> |
||||
</div> |
||||
<div class="list-group" id="modelList"> |
||||
|
||||
</div> |
||||
<button id="delComponent" class="btn btn-danger pull-right" style="display: none">删除该组件</button> |
||||
</div> |
||||
|
||||
</div> |
||||
<div class="panel-footer"> |
||||
<form id="uploadForm" enctype="multipart/form-data"> |
||||
<input type="file" name="file" id="file" style="display: none" /> |
||||
</form> |
||||
<button class="btn btn-primary btn-block disabled" id="upload">上传模型</button> |
||||
<button class="btn btn-success btn-block" id="saveScheme">保存方案</button> |
||||
</div> |
||||
</div> |
||||
<!-- 模态框:添加组件 --> |
||||
<div class="modal fade" id="addComponentModal"> |
||||
<div class="modal-dialog modal-sm"> |
||||
<div class="modal-content"> |
||||
<div class="modal-header"> |
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span |
||||
class="sr-only">关闭</span></button> |
||||
<h4 class="modal-title">添加新组件</h4> |
||||
</div> |
||||
<div class="modal-body"> |
||||
<input type="text" class="form-control" id="componentName" placeholder="组件名称"> |
||||
</div> |
||||
<div class="modal-footer"> |
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> |
||||
<button type="button" class="btn btn-primary" id="addComponent">添加</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<script src="/js/createScheme.js"></script> |
||||
</body> |
||||
|
||||
</html> |