185 lines
5.6 KiB
HTML
185 lines
5.6 KiB
HTML
|
<body>
|
||
|
<input type="file">
|
||
|
<script>
|
||
|
const isLittleEndian = () => {
|
||
|
const arrayBuffer = new ArrayBuffer(2);
|
||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||
|
const uint16array = new Uint16Array(arrayBuffer);
|
||
|
uint8Array[0] = 0xAA;
|
||
|
uint8Array[1] = 0xBB;
|
||
|
return uint16array[0] === 0xBBAA;
|
||
|
}
|
||
|
const swapToBigEndian = (buffer) => {
|
||
|
if (!isLittleEndian()) return buffer
|
||
|
const view = new DataView(buffer);
|
||
|
for (let i = 0; i < buffer.byteLength; i += 4) {
|
||
|
const value = view.getUint32(i, false)
|
||
|
view.setUint32(i, value, true)
|
||
|
}
|
||
|
return buffer;
|
||
|
}
|
||
|
const readBINOBJ = async file => {
|
||
|
const buffer = swapToBigEndian(await file.arrayBuffer())
|
||
|
const intArray = new Int32Array(buffer)
|
||
|
const floatArray = new Float32Array(buffer)
|
||
|
|
||
|
let position = 0
|
||
|
const readInt = () => intArray[position++]
|
||
|
const readFloat = () => floatArray[position++]
|
||
|
|
||
|
let n
|
||
|
|
||
|
n = readInt()
|
||
|
const vertices = []
|
||
|
for (let i = 0; i < n; ++i) {
|
||
|
vertices.push(readFloat())
|
||
|
}
|
||
|
|
||
|
n = readInt()
|
||
|
const textures = []
|
||
|
for (let i = 0; i < n; ++i) {
|
||
|
textures.push(readFloat())
|
||
|
}
|
||
|
|
||
|
n = readInt()
|
||
|
const normals = []
|
||
|
for (let i = 0; i < n; ++i) {
|
||
|
normals.push(readFloat())
|
||
|
}
|
||
|
|
||
|
n = readInt()
|
||
|
const faceIndexes = []
|
||
|
for (let i = 0; i < n; ++i) {
|
||
|
faceIndexes.push([
|
||
|
readInt(), // vertex index
|
||
|
readInt(), // texture index
|
||
|
readInt() // normal index
|
||
|
])
|
||
|
}
|
||
|
|
||
|
console.log(position, intArray.length)
|
||
|
|
||
|
return {
|
||
|
vertices,
|
||
|
textures,
|
||
|
normals,
|
||
|
faceIndexes
|
||
|
}
|
||
|
}
|
||
|
const writeBINOBJ = ({ vertices, textures, normals, faceIndexes }) => {
|
||
|
const datasize = 4 + 4 * vertices.length + 4 + 4 * textures.length + 4 + 4 * normals.length + 4 + 4 * 3 * faceIndexes.length
|
||
|
const buffer = new ArrayBuffer(datasize)
|
||
|
const view = new DataView(buffer)
|
||
|
|
||
|
let position = 0
|
||
|
|
||
|
view.setInt32(4 * (position++), vertices.length, false)
|
||
|
for (const v of vertices)
|
||
|
view.setFloat32(4 * (position++), v, false)
|
||
|
|
||
|
view.setInt32(4 * (position++), textures.length, false)
|
||
|
for (const vt of textures)
|
||
|
view.setFloat32(4 * (position++), vt, false)
|
||
|
|
||
|
view.setInt32(4 * (position++), normals.length, false)
|
||
|
for (const vn of normals)
|
||
|
view.setFloat32(4 * (position++), vn, false)
|
||
|
|
||
|
view.setInt32(4 * (position++), faceIndexes.length, false)
|
||
|
for (const [vi, vti, vni] of faceIndexes) {
|
||
|
view.setInt32(4 * (position++), vi, false)
|
||
|
view.setInt32(4 * (position++), vti, false)
|
||
|
view.setInt32(4 * (position++), vni, false)
|
||
|
}
|
||
|
|
||
|
return buffer
|
||
|
}
|
||
|
const readOBJ = async file => {
|
||
|
const text = await file.text()
|
||
|
|
||
|
const vertices = []
|
||
|
const textures = []
|
||
|
const normals = []
|
||
|
const faceIndexes = []
|
||
|
|
||
|
const lines = text.split('\n')
|
||
|
for (const line of lines) {
|
||
|
const args = line.split(' ')
|
||
|
if (args[0] == 'v') {
|
||
|
vertices.push(parseFloat(args[1]), parseFloat(args[2]), parseFloat(args[3]))
|
||
|
} else if (args[0] == 'vt') {
|
||
|
textures.push(parseFloat(args[1]), parseFloat(args[2]))
|
||
|
} else if (args[0] == 'vn') {
|
||
|
normals.push(parseFloat(args[1]), parseFloat(args[2]), parseFloat(args[3]))
|
||
|
} else if (args[0] == 'f') {
|
||
|
const i1 = args[1].split('/')
|
||
|
const i2 = args[2].split('/')
|
||
|
const i3 = args[3].split('/')
|
||
|
faceIndexes.push([
|
||
|
i1[0] - 1,
|
||
|
i1[1] - 1,
|
||
|
i1[2] - 1,
|
||
|
], [
|
||
|
i2[0] - 1,
|
||
|
i2[1] - 1,
|
||
|
i2[2] - 1
|
||
|
], [
|
||
|
i3[0] - 1,
|
||
|
i3[1] - 1,
|
||
|
i3[2] - 1
|
||
|
])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
vertices,
|
||
|
textures,
|
||
|
normals,
|
||
|
faceIndexes
|
||
|
}
|
||
|
}
|
||
|
const writeOBJ = ({ vertices, textures, normals, faceIndexes }) => {
|
||
|
let text = ""
|
||
|
|
||
|
for (let i = 0; i < vertices.length / 3; ++i) {
|
||
|
text += "v " + vertices[3 * i] + " " + vertices[3 * i + 1] + " " + vertices[3 * i + 2] + "\n"
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < textures.length / 2; ++i) {
|
||
|
text += "vt " + textures[2 * i] + " " + textures[2 * i + 1] + "\n"
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < normals.length / 3; ++i) {
|
||
|
text += "vn " + normals[3 * i] + " " + normals[3 * i + 1] + " " + normals[3 * i + 2] + "\n"
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < faceIndexes.length / 3; ++i) {
|
||
|
text += "f " +
|
||
|
(1 + faceIndexes[3 * i][0]) + "/" + (1 + faceIndexes[3 * i][1]) + "/" + (1 + faceIndexes[3 * i][2]) + " " +
|
||
|
(1 + faceIndexes[3 * i + 1][0]) + "/" + (2 + faceIndexes[3 * i + 1][1]) + "/" + (1 + faceIndexes[3 * i + 1][2])+ " " +
|
||
|
(1 + faceIndexes[3 * i + 2][0]) + "/" + (3 + faceIndexes[3 * i + 2][1]) + "/" + (1 + faceIndexes[3 * i + 2][2])+
|
||
|
"\n"
|
||
|
}
|
||
|
|
||
|
return text
|
||
|
}
|
||
|
const save = (blob, filename) => {
|
||
|
const a = document.createElement('a')
|
||
|
a.download = filename
|
||
|
a.href = window.URL.createObjectURL(blob);
|
||
|
document.body.appendChild(a)
|
||
|
a.click()
|
||
|
document.body.removeChild(a)
|
||
|
}
|
||
|
const input = document.querySelector("input")
|
||
|
input.addEventListener('change', async () => {
|
||
|
const file = input.files[0]
|
||
|
if (file.name.endsWith('.binobj')) {
|
||
|
save(new Blob([ writeOBJ(await readBINOBJ(file)) ], { type: 'text/plain' }), file.name.substring(0, file.name.length - 7) + '.obj')
|
||
|
} else if (file.name.endsWith('.obj')) {
|
||
|
console.log(await readOBJ(file))
|
||
|
save(new Blob([ writeBINOBJ(await readOBJ(file)) ], { type: 'text/plain' }), file.name.substring(0, file.name.length - 4) + '.binobj')
|
||
|
}
|
||
|
})
|
||
|
</script>
|
||
|
</body>
|