draw 3d cubes
This commit is contained in:
parent
d55ec71edc
commit
2dd13ddd5a
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.wasm
|
||||||
|
*.pyc
|
||||||
|
__pycache__
|
||||||
29
build.sh
Normal file
29
build.sh
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
clang++ \
|
||||||
|
-v \
|
||||||
|
--target=wasm32 \
|
||||||
|
-O3 \
|
||||||
|
-flto \
|
||||||
|
-nostdlib \
|
||||||
|
-Wl,--no-entry \
|
||||||
|
-Wl,--export=rotate \
|
||||||
|
-Wl,--lto-O3 \
|
||||||
|
-Wl,--print-map \
|
||||||
|
-Wl,--import-memory \
|
||||||
|
-D_XM_NO_INTRINSICS_ \
|
||||||
|
-I. \
|
||||||
|
-o rotate.wasm \
|
||||||
|
rotate.c
|
||||||
|
|
||||||
|
|
||||||
|
# clang \
|
||||||
|
# -O1 \
|
||||||
|
# --target=wasm32 \
|
||||||
|
# -emit-llvm \
|
||||||
|
# -c \
|
||||||
|
# -S \
|
||||||
|
# test.c
|
||||||
|
|
||||||
|
# llc \
|
||||||
|
# -march=wasm32 \
|
||||||
|
# -filetype=obj \
|
||||||
|
# test.ll
|
||||||
@ -1,6 +1,7 @@
|
|||||||
struct Configuration {
|
struct Configuration {
|
||||||
gridSize: vec2f,
|
gridSize: vec2f,
|
||||||
aspect: vec2f
|
aspect: vec2f,
|
||||||
|
matrix: mat4x4f
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
@group(0) @binding(0) var<uniform> config: Configuration;
|
||||||
|
|||||||
2247
directxmath/directxmath.h
Normal file
2247
directxmath/directxmath.h
Normal file
File diff suppressed because it is too large
Load Diff
2187
directxmath/directxmathconvert.inl
Normal file
2187
directxmath/directxmathconvert.inl
Normal file
File diff suppressed because it is too large
Load Diff
3413
directxmath/directxmathmatrix.inl
Normal file
3413
directxmath/directxmathmatrix.inl
Normal file
File diff suppressed because it is too large
Load Diff
2425
directxmath/directxmathmisc.inl
Normal file
2425
directxmath/directxmathmisc.inl
Normal file
File diff suppressed because it is too large
Load Diff
14689
directxmath/directxmathvector.inl
Normal file
14689
directxmath/directxmathvector.inl
Normal file
File diff suppressed because it is too large
Load Diff
121
index.js
121
index.js
@ -15,7 +15,6 @@ canvas.height = canvas.clientHeight
|
|||||||
|
|
||||||
const context = canvas.getContext("webgpu");
|
const context = canvas.getContext("webgpu");
|
||||||
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
|
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
|
||||||
//console.log(canvasFormat);
|
|
||||||
context.configure({
|
context.configure({
|
||||||
device: device,
|
device: device,
|
||||||
format: canvasFormat,
|
format: canvasFormat,
|
||||||
@ -36,30 +35,48 @@ renderPass.end();
|
|||||||
const commandBuffer = encoder.finish();
|
const commandBuffer = encoder.finish();
|
||||||
device.queue.submit([commandBuffer]);
|
device.queue.submit([commandBuffer]);
|
||||||
|
|
||||||
const vertices = new Float32Array([
|
|
||||||
// X, Y,
|
|
||||||
-0.8, -0.8, // Triangle 1 (Blue)
|
|
||||||
0.8, -0.8,
|
|
||||||
0.8, 0.8,
|
|
||||||
|
|
||||||
-0.8, -0.8, // Triangle 2 (Red)
|
const cubeResponse = await fetch("obj/cube.bin");
|
||||||
0.8, 0.8,
|
const cubeObj = await cubeResponse.arrayBuffer();
|
||||||
-0.8, 0.8,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const vertexBuffer = device.createBuffer({
|
const cubeView = new DataView(cubeObj);
|
||||||
|
const indexCount = cubeView.getUint32(0, true);
|
||||||
|
const indexBufferOffset = cubeView.getUint32(4, true);
|
||||||
|
const indexBufferSize = cubeView.getUint32(8, true);
|
||||||
|
const vertexBufferOffset = cubeView.getUint32(12, true);
|
||||||
|
const vertexBufferSize = cubeView.getUint32(16, true);
|
||||||
|
console.log(`indexBufferOffset ${indexBufferOffset} indexBufferSize ${indexBufferSize}`);
|
||||||
|
console.log(`vertexBufferOffset ${vertexBufferOffset} vertexBufferSize ${vertexBufferSize}`);
|
||||||
|
const cubeIndexCount = indexBufferSize / 2;
|
||||||
|
|
||||||
|
const cubeVerticesStride = 36;
|
||||||
|
|
||||||
|
const indexVertexBuffer = device.createBuffer({
|
||||||
label: "cell vertices",
|
label: "cell vertices",
|
||||||
size: vertices.byteLength,
|
size: indexBufferSize + vertexBufferSize,
|
||||||
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
||||||
});
|
});
|
||||||
device.queue.writeBuffer(vertexBuffer, 0, vertices);
|
device.queue.writeBuffer(indexVertexBuffer, 0, cubeObj, indexBufferOffset, indexBufferSize);
|
||||||
|
device.queue.writeBuffer(indexVertexBuffer, indexBufferSize, cubeObj, vertexBufferOffset, vertexBufferSize);
|
||||||
|
|
||||||
const vertexBufferLayout = {
|
const vertexBufferLayout = {
|
||||||
arrayStride: 8,
|
arrayStride: cubeVerticesStride,
|
||||||
attributes: [{
|
attributes: [{
|
||||||
format: "float32x2",
|
format: "float32x3",
|
||||||
offset: 0,
|
offset: 0,
|
||||||
shaderLocation: 0,
|
shaderLocation: 0,
|
||||||
|
}, {
|
||||||
|
format: "float32x2",
|
||||||
|
offset: 12,
|
||||||
|
shaderLocation: 1,
|
||||||
|
}, {
|
||||||
|
format: "float16x4",
|
||||||
|
offset: 20,
|
||||||
|
shaderLocation: 2,
|
||||||
|
}, {
|
||||||
|
format: "float16x4",
|
||||||
|
offset: 28,
|
||||||
|
shaderLocation: 3,
|
||||||
}],
|
}],
|
||||||
stepMode: "vertex",
|
stepMode: "vertex",
|
||||||
};
|
};
|
||||||
@ -115,7 +132,15 @@ const cellPipeline = device.createRenderPipeline({
|
|||||||
targets: [{
|
targets: [{
|
||||||
format: canvasFormat,
|
format: canvasFormat,
|
||||||
}]
|
}]
|
||||||
}
|
},
|
||||||
|
primitive: {
|
||||||
|
topology: 'triangle-list',
|
||||||
|
},
|
||||||
|
depthStencil: {
|
||||||
|
depthWriteEnabled: true,
|
||||||
|
depthCompare: 'less',
|
||||||
|
format: 'depth24plus',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const computeShaderModule = device.createShaderModule({
|
const computeShaderModule = device.createShaderModule({
|
||||||
@ -135,13 +160,24 @@ const computePipeline = device.createComputePipeline({
|
|||||||
const gridSize = 32;
|
const gridSize = 32;
|
||||||
const workGroupSize = 16;
|
const workGroupSize = 16;
|
||||||
|
|
||||||
const uniformArray = new Float32Array([gridSize, gridSize, 1, 1]);
|
//const uniformArray2 = new Float32Array([gridSize, gridSize, 1, 1]);
|
||||||
|
const memory = new WebAssembly.Memory({ initial: 1 });
|
||||||
|
const uniformArray = new Float32Array(memory.buffer);
|
||||||
|
uniformArray[0] = gridSize;
|
||||||
|
uniformArray[1] = gridSize;
|
||||||
|
uniformArray[2] = 1;
|
||||||
|
uniformArray[3] = 1;
|
||||||
|
const uniformArraySize = 4 * 4 + 4 * 4 * 4;
|
||||||
|
const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), {
|
||||||
|
env: { memory: memory }
|
||||||
|
});
|
||||||
|
|
||||||
const uniformBuffer = device.createBuffer({
|
const uniformBuffer = device.createBuffer({
|
||||||
label: "grid uniform",
|
label: "grid uniform",
|
||||||
size: uniformArray.byteLength,
|
size: uniformArraySize,
|
||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||||
});
|
});
|
||||||
device.queue.writeBuffer(uniformBuffer, 0, uniformArray);
|
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
|
||||||
|
|
||||||
const cellStateArray = new Uint32Array(gridSize * gridSize);
|
const cellStateArray = new Uint32Array(gridSize * gridSize);
|
||||||
|
|
||||||
@ -198,8 +234,28 @@ const bindGroups = [
|
|||||||
let tick = 0;
|
let tick = 0;
|
||||||
let which = 0;
|
let which = 0;
|
||||||
|
|
||||||
|
function createDepthTexture() {
|
||||||
|
return device.createTexture({
|
||||||
|
size: [canvas.width, canvas.height],
|
||||||
|
format: 'depth24plus',
|
||||||
|
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var depthTexture = undefined;
|
||||||
|
|
||||||
function render()
|
function render()
|
||||||
{
|
{
|
||||||
|
if (canvas.clientWidth !== canvas.width || canvas.clientWidth !== canvas.height) {
|
||||||
|
canvas.width = canvas.clientWidth;
|
||||||
|
canvas.height = canvas.clientHeight;
|
||||||
|
|
||||||
|
if (depthTexture !== undefined) {
|
||||||
|
depthTexture.destroy();
|
||||||
|
}
|
||||||
|
depthTexture = createDepthTexture();
|
||||||
|
}
|
||||||
|
|
||||||
const encoder = device.createCommandEncoder();
|
const encoder = device.createCommandEncoder();
|
||||||
|
|
||||||
const computePass = encoder.beginComputePass();
|
const computePass = encoder.beginComputePass();
|
||||||
@ -209,15 +265,14 @@ function render()
|
|||||||
computePass.dispatchWorkgroups(workgroupCount);
|
computePass.dispatchWorkgroups(workgroupCount);
|
||||||
computePass.end();
|
computePass.end();
|
||||||
|
|
||||||
|
const aspect = canvas.width / canvas.height;
|
||||||
|
rotate.instance.exports.rotate(tick * 0.01, aspect, 4 * 4);
|
||||||
|
|
||||||
tick += 1;
|
tick += 1;
|
||||||
if ((tick % 5) == 0) {
|
if ((tick % 5) == 0) {
|
||||||
which ^= 1;
|
which ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.width = canvas.clientWidth
|
|
||||||
canvas.height = canvas.clientHeight
|
|
||||||
|
|
||||||
const aspect = canvas.height / canvas.width;
|
|
||||||
if (canvas.width > canvas.height) {
|
if (canvas.width > canvas.height) {
|
||||||
uniformArray[2] = aspect;
|
uniformArray[2] = aspect;
|
||||||
uniformArray[3] = 1;
|
uniformArray[3] = 1;
|
||||||
@ -225,7 +280,7 @@ function render()
|
|||||||
uniformArray[2] = 1;
|
uniformArray[2] = 1;
|
||||||
uniformArray[3] = 1 / aspect;
|
uniformArray[3] = 1 / aspect;
|
||||||
}
|
}
|
||||||
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0);
|
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
|
||||||
|
|
||||||
const renderPass = encoder.beginRenderPass({
|
const renderPass = encoder.beginRenderPass({
|
||||||
colorAttachments: [{
|
colorAttachments: [{
|
||||||
@ -233,13 +288,23 @@ function render()
|
|||||||
loadOp: "clear",
|
loadOp: "clear",
|
||||||
clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 },
|
clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 },
|
||||||
storeOp: "store",
|
storeOp: "store",
|
||||||
}]
|
}],
|
||||||
|
depthStencilAttachment: {
|
||||||
|
view: depthTexture.createView(),
|
||||||
|
depthClearValue: 1.0,
|
||||||
|
depthLoadOp: 'clear',
|
||||||
|
depthStoreOp: 'store',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
renderPass.setPipeline(cellPipeline);
|
renderPass.setPipeline(cellPipeline);
|
||||||
renderPass.setVertexBuffer(0, vertexBuffer);
|
const vertexOffset = indexBufferSize;
|
||||||
|
renderPass.setVertexBuffer(0, indexVertexBuffer, vertexOffset, vertexBufferSize);
|
||||||
|
renderPass.setIndexBuffer(indexVertexBuffer, "uint16", 0, indexBufferSize);
|
||||||
renderPass.setBindGroup(0, bindGroups[which]);
|
renderPass.setBindGroup(0, bindGroups[which]);
|
||||||
renderPass.draw(vertices.length / 2, gridSize * gridSize);
|
//renderPass.draw(cubeVerticesCount, gridSize * gridSize);
|
||||||
|
renderPass.drawIndexed(cubeIndexCount, gridSize * gridSize);
|
||||||
|
//renderPass.drawIndexed(cubeIndexCount);
|
||||||
|
|
||||||
renderPass.end();
|
renderPass.end();
|
||||||
|
|
||||||
|
|||||||
21
index.wgsl
21
index.wgsl
@ -1,23 +1,29 @@
|
|||||||
struct Configuration {
|
struct Configuration {
|
||||||
gridSize: vec2f,
|
gridSize: vec2f,
|
||||||
aspect: vec2f
|
aspect: vec2f,
|
||||||
|
matrix: mat4x4f
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
@group(0) @binding(0) var<uniform> config: Configuration;
|
||||||
@group(0) @binding(1) var<storage> state: array<u32>;
|
@group(0) @binding(1) var<storage> state: array<u32>;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec2f,
|
@location(0) position: vec3f,
|
||||||
|
@location(1) texture: vec2f,
|
||||||
|
@location(2) normal: vec3f,
|
||||||
|
@location(3) tangent: vec4f,
|
||||||
@builtin(instance_index) instance: u32,
|
@builtin(instance_index) instance: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) position: vec4f,
|
@builtin(position) position: vec4f,
|
||||||
@location(0) cell: vec2f,
|
@location(0) cell: vec2f,
|
||||||
|
@location(1) normal: vec3f,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentInput {
|
struct FragmentInput {
|
||||||
@location(0) cell: vec2f,
|
@location(0) cell: vec2f,
|
||||||
|
@location(1) normal: vec3f,
|
||||||
};
|
};
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
@ -28,16 +34,21 @@ fn vertexMain(input: VertexInput) -> VertexOutput
|
|||||||
|
|
||||||
let state = f32(state[input.instance]);
|
let state = f32(state[input.instance]);
|
||||||
let cell = vec2f(instance % config.gridSize.x, floor(instance / config.gridSize.y));
|
let cell = vec2f(instance % config.gridSize.x, floor(instance / config.gridSize.y));
|
||||||
let grid = (position * state + cell) / config.gridSize;
|
let grid = (position * state + vec3f(cell, 0)) / config.gridSize.xxx;
|
||||||
|
|
||||||
var output: VertexOutput;
|
var output: VertexOutput;
|
||||||
output.position = vec4f((grid * 2 - 1) * config.aspect, 0, 1);
|
//output.position = (config.matrix * vec4f((grid * 2 - 1), 1)) * vec4f(config.aspect, 1, 1);
|
||||||
|
output.position = config.matrix * vec4f((grid * 2 - 1), 1);
|
||||||
|
|
||||||
|
//output.position = config.matrix * vec4f(input.position, 1);
|
||||||
|
|
||||||
output.cell = cell / config.gridSize;
|
output.cell = cell / config.gridSize;
|
||||||
|
output.normal = input.normal;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f
|
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f
|
||||||
{
|
{
|
||||||
return vec4f(input.cell, 0, 1);
|
return vec4f(input.normal * 0.5 + 0.5, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
obj/cube.bin
Normal file
BIN
obj/cube.bin
Normal file
Binary file not shown.
36
obj/cube.obj
Normal file
36
obj/cube.obj
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Blender 5.2.0 LTS
|
||||||
|
# www.blender.org
|
||||||
|
mtllib Untitled.mtl
|
||||||
|
o Cube
|
||||||
|
v 1.000000 1.000000 -1.000000
|
||||||
|
v 1.000000 -1.000000 -1.000000
|
||||||
|
v 1.000000 1.000000 1.000000
|
||||||
|
v 1.000000 -1.000000 1.000000
|
||||||
|
v -1.000000 1.000000 -1.000000
|
||||||
|
v -1.000000 -1.000000 -1.000000
|
||||||
|
v -1.000000 1.000000 1.000000
|
||||||
|
v -1.000000 -1.000000 1.000000
|
||||||
|
vn -0.0000 1.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 1.0000
|
||||||
|
vn -1.0000 -0.0000 -0.0000
|
||||||
|
vn -0.0000 -1.0000 -0.0000
|
||||||
|
vn 1.0000 -0.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 -1.0000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 1.000000 0.000000
|
||||||
|
vt 1.000000 1.000000
|
||||||
|
vt 0.000000 0.000000
|
||||||
|
s 0
|
||||||
|
usemtl Material
|
||||||
|
f 5/1/1 3/2/1 1/3/1
|
||||||
|
f 3/3/2 8/4/2 4/2/2
|
||||||
|
f 7/1/3 6/2/3 8/4/3
|
||||||
|
f 2/3/4 8/4/4 6/1/4
|
||||||
|
f 1/3/5 4/4/5 2/2/5
|
||||||
|
f 5/1/6 2/2/6 6/4/6
|
||||||
|
f 5/1/1 7/4/1 3/2/1
|
||||||
|
f 3/3/2 7/1/2 8/4/2
|
||||||
|
f 7/1/3 5/3/3 6/2/3
|
||||||
|
f 2/3/4 4/2/4 8/4/4
|
||||||
|
f 1/3/5 3/1/5 4/4/5
|
||||||
|
f 5/1/6 1/3/6 2/2/6
|
||||||
25
rotate.c
Normal file
25
rotate.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "directxmath/directxmath.h"
|
||||||
|
|
||||||
|
extern "C" void rotate(float angle, float aspect, float * buffer);
|
||||||
|
|
||||||
|
void rotate(float angle, float aspect, float * buffer)
|
||||||
|
{
|
||||||
|
//XMMATRIX mat = XMMatrixRotationZ(angle);
|
||||||
|
//XMMATRIX mat2 = XMMatrixRotationX(angle);
|
||||||
|
//XMMATRIX mat2 = XMMatrixTranslation(0, 0, 1);
|
||||||
|
//XMStoreFloat4x4((XMFLOAT4X4*)buffer, mat * mat2);
|
||||||
|
|
||||||
|
|
||||||
|
XMVECTOR eye = XMVectorSet(3, 3, 3, 0);
|
||||||
|
XMVECTOR at = XMVectorSet(0, 0, 0, 0);
|
||||||
|
XMVECTOR up = XMVectorSet(0, 1, 0, 0);
|
||||||
|
XMMATRIX view = XMMatrixLookAtRH(eye, at, up);
|
||||||
|
|
||||||
|
float fov_angle_y = XMConvertToRadians(45);
|
||||||
|
float near_z = 0.01;
|
||||||
|
float far_z = 1000.0;
|
||||||
|
XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect, near_z, far_z);
|
||||||
|
|
||||||
|
XMMATRIX rot = XMMatrixRotationY(angle);
|
||||||
|
XMStoreFloat4x4((XMFLOAT4X4*)buffer, rot * view * projection);
|
||||||
|
}
|
||||||
128
tools/obj/obj.py
Normal file
128
tools/obj/obj.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
from itertools import chain
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from obj import tangent
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'r') as f:
|
||||||
|
lines = f.read().split('\n')
|
||||||
|
|
||||||
|
position = []
|
||||||
|
texture = []
|
||||||
|
normal = []
|
||||||
|
faces = []
|
||||||
|
|
||||||
|
def parse_vertex(prefix, s):
|
||||||
|
return s.removeprefix(prefix).strip().split()
|
||||||
|
|
||||||
|
def float_vector(l, n):
|
||||||
|
v = list(map(float, l))
|
||||||
|
assert len(v) == n
|
||||||
|
return v
|
||||||
|
|
||||||
|
vertex_types = [
|
||||||
|
("v ", position, 3),
|
||||||
|
("vn ", normal, 3),
|
||||||
|
("vt ", texture, 2),
|
||||||
|
]
|
||||||
|
|
||||||
|
ignore_prefixes = ["#", "mtllib ", "usemtl ", "o ", "s "]
|
||||||
|
|
||||||
|
def parse_line(line):
|
||||||
|
if not line.strip():
|
||||||
|
return
|
||||||
|
|
||||||
|
for prefix in ignore_prefixes:
|
||||||
|
if line.startswith(prefix):
|
||||||
|
return
|
||||||
|
|
||||||
|
for prefix, out, n in vertex_types:
|
||||||
|
if line.startswith(prefix):
|
||||||
|
out.append(float_vector(parse_vertex(prefix, line), n))
|
||||||
|
return
|
||||||
|
|
||||||
|
if line.startswith("f "):
|
||||||
|
vertices = [
|
||||||
|
tuple(int(i)-1 for i in vertex.split("/"))
|
||||||
|
for vertex in parse_vertex("f ", line)
|
||||||
|
]
|
||||||
|
assert len(vertices) == 3, vertices # is not triangle
|
||||||
|
faces.append(vertices)
|
||||||
|
return
|
||||||
|
|
||||||
|
assert False, repr(line) # unhandled line prefix
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
parse_line(line)
|
||||||
|
|
||||||
|
vertex_buffer_keys = dict()
|
||||||
|
vertex_buffer = []
|
||||||
|
index_buffer = []
|
||||||
|
|
||||||
|
for face in faces:
|
||||||
|
for vertex in face:
|
||||||
|
if vertex not in vertex_buffer_keys:
|
||||||
|
vertex_buffer_keys[vertex] = len(vertex_buffer)
|
||||||
|
vertex_buffer.append(tuple(chain(
|
||||||
|
position[vertex[0]],
|
||||||
|
texture[vertex[1]],
|
||||||
|
normal[vertex[2]],
|
||||||
|
)))
|
||||||
|
|
||||||
|
index_buffer.append(vertex_buffer_keys[vertex])
|
||||||
|
|
||||||
|
def render_vertex(f, vertex, tangent):
|
||||||
|
# extra padding zero for normal R16G16B16A16
|
||||||
|
# position texture normal tangent
|
||||||
|
stride = struct.pack("<" + "fff" + "ff" + "eeee" + "eeee", *vertex, 0, *tangent)
|
||||||
|
print(vertex, tangent)
|
||||||
|
#print(len(stride))
|
||||||
|
f.write(stride)
|
||||||
|
|
||||||
|
minv = [0, 0, 0]
|
||||||
|
maxv = [0, 0, 0]
|
||||||
|
for v in vertex_buffer:
|
||||||
|
for i in range(3):
|
||||||
|
if v[i] > maxv[i]:
|
||||||
|
maxv[i] = v[i]
|
||||||
|
if v[i] < minv[i]:
|
||||||
|
minv[i] = v[i]
|
||||||
|
|
||||||
|
with open(sys.argv[2], 'wb') as f:
|
||||||
|
|
||||||
|
header_size = 4 * 5 + 4 * 6
|
||||||
|
|
||||||
|
index_size = 2 * 1
|
||||||
|
index_buffer_size = index_size * len(index_buffer)
|
||||||
|
|
||||||
|
vertex_size = (4 * 3) + (4 * 2) + (2 * 4) + (2 * 4)
|
||||||
|
vertex_buffer_size = vertex_size * len(vertex_buffer)
|
||||||
|
|
||||||
|
index_count = len(index_buffer)
|
||||||
|
index_buffer_offset = header_size
|
||||||
|
vertex_buffer_offset = header_size + index_buffer_size
|
||||||
|
|
||||||
|
tangents = tangent.generate_tangents(vertex_buffer, index_buffer)
|
||||||
|
|
||||||
|
header = struct.pack("<IIIIIffffff",
|
||||||
|
index_count,
|
||||||
|
index_buffer_offset,
|
||||||
|
index_buffer_size,
|
||||||
|
vertex_buffer_offset,
|
||||||
|
vertex_buffer_size,
|
||||||
|
*minv,
|
||||||
|
*maxv)
|
||||||
|
f.write(header)
|
||||||
|
|
||||||
|
assert f.tell() == index_buffer_offset
|
||||||
|
for index in index_buffer:
|
||||||
|
print(index, vertex_buffer[index])
|
||||||
|
f.write(struct.pack("<H", index))
|
||||||
|
|
||||||
|
assert f.tell() == vertex_buffer_offset
|
||||||
|
|
||||||
|
for vertex, tangent in zip(vertex_buffer, tangents):
|
||||||
|
render_vertex(f, vertex, tangent)
|
||||||
|
|
||||||
|
assert f.tell() == vertex_buffer_offset + vertex_buffer_size
|
||||||
81
tools/obj/tangent.py
Normal file
81
tools/obj/tangent.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
PNT = list[tuple[float, float, float, # position
|
||||||
|
float, float, # texture
|
||||||
|
float, float, float, float]] # normal
|
||||||
|
|
||||||
|
def get_position(vertex_buffer: list[PNT], index):
|
||||||
|
return vertex_buffer[index][0:3]
|
||||||
|
|
||||||
|
def get_texture(vertex_buffer: list[PNT], index):
|
||||||
|
return vertex_buffer[index][3:5]
|
||||||
|
|
||||||
|
def get_normal(vertex_buffer: list[PNT], index):
|
||||||
|
return vertex_buffer[index][5:8]
|
||||||
|
|
||||||
|
def normalize(v):
|
||||||
|
return v / np.linalg.norm(v)
|
||||||
|
|
||||||
|
def generate_tangents(vertex_buffer: list[PNT],
|
||||||
|
index_buffer: list[int]):
|
||||||
|
vertex_count = len(vertex_buffer)
|
||||||
|
|
||||||
|
triangle_count = len(index_buffer) // 3
|
||||||
|
|
||||||
|
tan_s = np.zeros((vertex_count, 3,))
|
||||||
|
tan_t = np.zeros((vertex_count, 3,))
|
||||||
|
|
||||||
|
for t in range(triangle_count):
|
||||||
|
i0 = index_buffer[t * 3 + 0]
|
||||||
|
i1 = index_buffer[t * 3 + 1]
|
||||||
|
i2 = index_buffer[t * 3 + 2]
|
||||||
|
|
||||||
|
p0 = get_position(vertex_buffer, i0)
|
||||||
|
p1 = get_position(vertex_buffer, i1)
|
||||||
|
p2 = get_position(vertex_buffer, i2)
|
||||||
|
|
||||||
|
uv0 = get_texture(vertex_buffer, i0)
|
||||||
|
uv1 = get_texture(vertex_buffer, i1)
|
||||||
|
uv2 = get_texture(vertex_buffer, i2)
|
||||||
|
|
||||||
|
x1 = p1[0] - p0[0]
|
||||||
|
x2 = p2[0] - p0[0]
|
||||||
|
y1 = p1[1] - p0[1]
|
||||||
|
y2 = p2[1] - p0[1]
|
||||||
|
z1 = p1[2] - p0[2]
|
||||||
|
z2 = p2[2] - p0[2]
|
||||||
|
|
||||||
|
s1 = uv1[0] - uv0[0]
|
||||||
|
s2 = uv2[0] - uv0[0]
|
||||||
|
|
||||||
|
t1 = uv1[1] - uv0[1]
|
||||||
|
t2 = uv2[1] - uv0[1]
|
||||||
|
|
||||||
|
d = (s1 * t2 - s2 * t1)
|
||||||
|
r = 1.0 / d if d != 0 else 0
|
||||||
|
|
||||||
|
sdir = np.array([(t2 * x1 - t1 * x2) * r,
|
||||||
|
(t2 * y1 - t1 * y2) * r,
|
||||||
|
(t2 * z1 - t1 * z2) * r])
|
||||||
|
|
||||||
|
tdir = np.array([(s1 * x2 - s2 * x1) * r,
|
||||||
|
(s1 * y2 - s2 * y1) * r,
|
||||||
|
(s1 * z2 - s2 * z1) * r])
|
||||||
|
|
||||||
|
tan_s += sdir
|
||||||
|
tan_t += tdir
|
||||||
|
|
||||||
|
tangents = []
|
||||||
|
for v in range(vertex_count):
|
||||||
|
n = np.array(get_normal(vertex_buffer, v))
|
||||||
|
s = tan_s[v]
|
||||||
|
|
||||||
|
# orthagonalize
|
||||||
|
tangent = normalize(s - n * np.dot(n, s))
|
||||||
|
h = np.dot(np.cross(n, s), tan_t[v])
|
||||||
|
w = -1.0 if h < 0.0 else 1.0
|
||||||
|
tangent_w = (tangent[0], tangent[1], tangent[2], w)
|
||||||
|
#print(tangent_w)
|
||||||
|
tangents.append(tangent_w)
|
||||||
|
|
||||||
|
return tangents
|
||||||
Loading…
x
Reference in New Issue
Block a user