Nagram/Tools/BinaryObjConverter.html

185 lines
5.6 KiB
HTML
Raw Permalink Normal View History

2024-03-08 14:32:16 +00:00
<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>