render voxel models
This commit is contained in:
parent
2dd13ddd5a
commit
e5cacffaee
67
index.html
67
index.html
@ -2,8 +2,18 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>WebGPU</title>
|
<title>WebGPU</title>
|
||||||
<style>
|
<style>
|
||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
div, span {
|
||||||
|
font: 0.95em sans-serif;
|
||||||
|
}
|
||||||
|
select, option {
|
||||||
|
font: 0.95em sans-serif;
|
||||||
|
}
|
||||||
canvas {
|
canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -13,10 +23,67 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.control-root {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
.control-main {
|
||||||
|
width: 250px;
|
||||||
|
float: right;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
width: calc(40% - 0.3rem);
|
||||||
|
float: left;
|
||||||
|
clear: left;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
width: calc(60% - 0.2rem);
|
||||||
|
float: left;
|
||||||
|
margin-top: 0.1rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
|
.row:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
background: #444;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="index.js" type="module"></script>
|
<script src="index.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
|
<div class="control-root">
|
||||||
|
<div class="control-main">
|
||||||
|
<div class="row">
|
||||||
|
<span class="label">model</span>
|
||||||
|
<select class="value" id="model-select"></select>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="label">lighting</span>
|
||||||
|
<select class="value" id="lighting-select">
|
||||||
|
<option>unlit</option>
|
||||||
|
<option>diffuse</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="row">
|
||||||
|
<span class="label">test2</span>
|
||||||
|
<select class="value">
|
||||||
|
<option value="test2">test2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
171
index.js
171
index.js
@ -35,7 +35,6 @@ renderPass.end();
|
|||||||
const commandBuffer = encoder.finish();
|
const commandBuffer = encoder.finish();
|
||||||
device.queue.submit([commandBuffer]);
|
device.queue.submit([commandBuffer]);
|
||||||
|
|
||||||
|
|
||||||
const cubeResponse = await fetch("obj/cube.bin");
|
const cubeResponse = await fetch("obj/cube.bin");
|
||||||
const cubeObj = await cubeResponse.arrayBuffer();
|
const cubeObj = await cubeResponse.arrayBuffer();
|
||||||
|
|
||||||
@ -45,8 +44,8 @@ const indexBufferOffset = cubeView.getUint32(4, true);
|
|||||||
const indexBufferSize = cubeView.getUint32(8, true);
|
const indexBufferSize = cubeView.getUint32(8, true);
|
||||||
const vertexBufferOffset = cubeView.getUint32(12, true);
|
const vertexBufferOffset = cubeView.getUint32(12, true);
|
||||||
const vertexBufferSize = cubeView.getUint32(16, true);
|
const vertexBufferSize = cubeView.getUint32(16, true);
|
||||||
console.log(`indexBufferOffset ${indexBufferOffset} indexBufferSize ${indexBufferSize}`);
|
//console.log(`indexBufferOffset ${indexBufferOffset} indexBufferSize ${indexBufferSize}`);
|
||||||
console.log(`vertexBufferOffset ${vertexBufferOffset} vertexBufferSize ${vertexBufferSize}`);
|
//console.log(`vertexBufferOffset ${vertexBufferOffset} vertexBufferSize ${vertexBufferSize}`);
|
||||||
const cubeIndexCount = indexBufferSize / 2;
|
const cubeIndexCount = indexBufferSize / 2;
|
||||||
|
|
||||||
const cubeVerticesStride = 36;
|
const cubeVerticesStride = 36;
|
||||||
@ -59,7 +58,71 @@ const indexVertexBuffer = device.createBuffer({
|
|||||||
device.queue.writeBuffer(indexVertexBuffer, 0, cubeObj, indexBufferOffset, indexBufferSize);
|
device.queue.writeBuffer(indexVertexBuffer, 0, cubeObj, indexBufferOffset, indexBufferSize);
|
||||||
device.queue.writeBuffer(indexVertexBuffer, indexBufferSize, cubeObj, vertexBufferOffset, vertexBufferSize);
|
device.queue.writeBuffer(indexVertexBuffer, indexBufferSize, cubeObj, vertexBufferOffset, vertexBufferSize);
|
||||||
|
|
||||||
const vertexBufferLayout = {
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// instance buffer
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
async function loadVoxModel(path)
|
||||||
|
{
|
||||||
|
const voxResponse = await fetch(path);
|
||||||
|
const vox = await voxResponse.arrayBuffer();
|
||||||
|
const voxView = new DataView(vox);
|
||||||
|
const sizeX = voxView.getUint32(0, true);
|
||||||
|
const sizeY = voxView.getUint32(4, true);
|
||||||
|
const sizeZ = voxView.getUint32(8, true);
|
||||||
|
const voxelCount = voxView.getUint32(12, true);
|
||||||
|
|
||||||
|
const instanceBuffer = device.createBuffer({
|
||||||
|
label: `instanceBuffer %{}`,
|
||||||
|
size: voxelCount * 4,
|
||||||
|
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
||||||
|
});
|
||||||
|
device.queue.writeBuffer(instanceBuffer, 0, vox, 16, voxelCount * 4);
|
||||||
|
|
||||||
|
const modelConfigurationBuffer = device.createBuffer({
|
||||||
|
label: "modelConfigurationBuffer",
|
||||||
|
size: 4 * 4 + 256 * 4 * 4,
|
||||||
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||||
|
});
|
||||||
|
const paletteOffset = 16 + voxelCount * 4;
|
||||||
|
|
||||||
|
const sizeArray = new Float32Array([sizeX, sizeY, sizeZ, 0]);
|
||||||
|
device.queue.writeBuffer(modelConfigurationBuffer, 0, sizeArray);
|
||||||
|
device.queue.writeBuffer(modelConfigurationBuffer, 4 * 4, vox, paletteOffset, 256 * 4 * 4);
|
||||||
|
|
||||||
|
return {
|
||||||
|
instanceBuffer: instanceBuffer,
|
||||||
|
modelConfigurationBuffer: modelConfigurationBuffer,
|
||||||
|
voxelCount: voxelCount,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelPaths = [
|
||||||
|
"vox/monu1.bin",
|
||||||
|
"vox/monu7.bin",
|
||||||
|
"vox/monu9.bin",
|
||||||
|
"vox/monu16.bin",
|
||||||
|
"vox/chr_fox.bin",
|
||||||
|
"vox/ff3.bin",
|
||||||
|
"vox/dragon.bin",
|
||||||
|
];
|
||||||
|
|
||||||
|
const models = {}
|
||||||
|
for (const path of modelPaths) {
|
||||||
|
models[path] = loadVoxModel(path);
|
||||||
|
}
|
||||||
|
for (const path of modelPaths) {
|
||||||
|
models[path] = await models[path];
|
||||||
|
}
|
||||||
|
var currentModel = modelPaths[0];
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// vertex layout
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const vertexBufferLayouts = [
|
||||||
|
{
|
||||||
arrayStride: cubeVerticesStride,
|
arrayStride: cubeVerticesStride,
|
||||||
attributes: [{
|
attributes: [{
|
||||||
format: "float32x3",
|
format: "float32x3",
|
||||||
@ -79,7 +142,17 @@ const vertexBufferLayout = {
|
|||||||
shaderLocation: 3,
|
shaderLocation: 3,
|
||||||
}],
|
}],
|
||||||
stepMode: "vertex",
|
stepMode: "vertex",
|
||||||
};
|
},
|
||||||
|
{
|
||||||
|
arrayStride: 4,
|
||||||
|
attributes: [{
|
||||||
|
format: "uint8x4",
|
||||||
|
offset: 0,
|
||||||
|
shaderLocation: 4,
|
||||||
|
}],
|
||||||
|
stepMode: "instance",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function getPath(path)
|
function getPath(path)
|
||||||
{
|
{
|
||||||
@ -91,13 +164,17 @@ function getPath(path)
|
|||||||
}).then((blob) => { return blob.text() })
|
}).then((blob) => { return blob.text() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const indexWgsl = getPath("index.wgsl");
|
||||||
|
const computeWgsl = getPath("compute.wgsl");
|
||||||
|
|
||||||
const cellShaderModule = device.createShaderModule({
|
const cellShaderModule = device.createShaderModule({
|
||||||
label: "cell shader",
|
label: "cell shader",
|
||||||
code: await getPath("index.wgsl")
|
code: await indexWgsl,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bindGroupLayout = device.createBindGroupLayout({
|
const bindGroupLayouts = [
|
||||||
label: "bind group layout",
|
device.createBindGroupLayout({
|
||||||
|
label: "bind group layout 0",
|
||||||
entries: [{
|
entries: [{
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
|
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
|
||||||
@ -111,11 +188,20 @@ const bindGroupLayout = device.createBindGroupLayout({
|
|||||||
visibility: GPUShaderStage.COMPUTE,
|
visibility: GPUShaderStage.COMPUTE,
|
||||||
buffer: { type: "storage" }
|
buffer: { type: "storage" }
|
||||||
}]
|
}]
|
||||||
});
|
}),
|
||||||
|
device.createBindGroupLayout({
|
||||||
|
label: "bind group layout 0",
|
||||||
|
entries: [{
|
||||||
|
binding: 0,
|
||||||
|
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
||||||
|
buffer: { type: "uniform" }
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
const pipelineLayout = device.createPipelineLayout({
|
const pipelineLayout = device.createPipelineLayout({
|
||||||
label: "pipeline layout",
|
label: "pipeline layout",
|
||||||
bindGroupLayouts: [ bindGroupLayout ],
|
bindGroupLayouts: bindGroupLayouts,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cellPipeline = device.createRenderPipeline({
|
const cellPipeline = device.createRenderPipeline({
|
||||||
@ -124,7 +210,7 @@ const cellPipeline = device.createRenderPipeline({
|
|||||||
vertex: {
|
vertex: {
|
||||||
module: cellShaderModule,
|
module: cellShaderModule,
|
||||||
entryPoint: "vertexMain",
|
entryPoint: "vertexMain",
|
||||||
buffers: [vertexBufferLayout],
|
buffers: vertexBufferLayouts,
|
||||||
},
|
},
|
||||||
fragment: {
|
fragment: {
|
||||||
module: cellShaderModule,
|
module: cellShaderModule,
|
||||||
@ -145,12 +231,17 @@ const cellPipeline = device.createRenderPipeline({
|
|||||||
|
|
||||||
const computeShaderModule = device.createShaderModule({
|
const computeShaderModule = device.createShaderModule({
|
||||||
label: "compute shader",
|
label: "compute shader",
|
||||||
code: await getPath("compute.wgsl")
|
code: await computeWgsl,
|
||||||
|
});
|
||||||
|
|
||||||
|
const computePipelineLayout = device.createPipelineLayout({
|
||||||
|
label: "compute pipeline layout",
|
||||||
|
bindGroupLayouts: [ bindGroupLayouts[0] ],
|
||||||
});
|
});
|
||||||
|
|
||||||
const computePipeline = device.createComputePipeline({
|
const computePipeline = device.createComputePipeline({
|
||||||
label: "compute pipeline",
|
label: "compute pipeline",
|
||||||
layout: pipelineLayout,
|
layout: computePipelineLayout,
|
||||||
compute: {
|
compute: {
|
||||||
module: computeShaderModule,
|
module: computeShaderModule,
|
||||||
entryPoint: "computeMain",
|
entryPoint: "computeMain",
|
||||||
@ -163,11 +254,11 @@ const workGroupSize = 16;
|
|||||||
//const uniformArray2 = new Float32Array([gridSize, gridSize, 1, 1]);
|
//const uniformArray2 = new Float32Array([gridSize, gridSize, 1, 1]);
|
||||||
const memory = new WebAssembly.Memory({ initial: 1 });
|
const memory = new WebAssembly.Memory({ initial: 1 });
|
||||||
const uniformArray = new Float32Array(memory.buffer);
|
const uniformArray = new Float32Array(memory.buffer);
|
||||||
uniformArray[0] = gridSize;
|
uniformArray[0] = 1;
|
||||||
uniformArray[1] = gridSize;
|
uniformArray[1] = 0;
|
||||||
uniformArray[2] = 1;
|
uniformArray[2] = 0;
|
||||||
uniformArray[3] = 1;
|
uniformArray[3] = 0;
|
||||||
const uniformArraySize = 4 * 4 + 4 * 4 * 4;
|
const uniformArraySize = 4 * 4 + (4 * 4 * 4) * 2;
|
||||||
const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), {
|
const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), {
|
||||||
env: { memory: memory }
|
env: { memory: memory }
|
||||||
});
|
});
|
||||||
@ -203,7 +294,7 @@ device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray);
|
|||||||
const bindGroups = [
|
const bindGroups = [
|
||||||
device.createBindGroup({
|
device.createBindGroup({
|
||||||
label: "cell bind group 0",
|
label: "cell bind group 0",
|
||||||
layout: bindGroupLayout,
|
layout: bindGroupLayouts[0],
|
||||||
entries: [{
|
entries: [{
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: { buffer: uniformBuffer },
|
resource: { buffer: uniformBuffer },
|
||||||
@ -217,7 +308,7 @@ const bindGroups = [
|
|||||||
}),
|
}),
|
||||||
device.createBindGroup({
|
device.createBindGroup({
|
||||||
label: "cell bind group 1",
|
label: "cell bind group 1",
|
||||||
layout: bindGroupLayout,
|
layout: bindGroupLayouts[0],
|
||||||
entries: [{
|
entries: [{
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: { buffer: uniformBuffer },
|
resource: { buffer: uniformBuffer },
|
||||||
@ -231,6 +322,19 @@ const bindGroups = [
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const modelBindGroups = {};
|
||||||
|
for (const path of modelPaths) {
|
||||||
|
const bindGroup = device.createBindGroup({
|
||||||
|
label: "palette bind group",
|
||||||
|
layout: bindGroupLayouts[1],
|
||||||
|
entries: [{
|
||||||
|
binding: 0,
|
||||||
|
resource: { buffer: models[path].modelConfigurationBuffer },
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
modelBindGroups[path] = bindGroup;
|
||||||
|
}
|
||||||
|
|
||||||
let tick = 0;
|
let tick = 0;
|
||||||
let which = 0;
|
let which = 0;
|
||||||
|
|
||||||
@ -244,6 +348,18 @@ function createDepthTexture() {
|
|||||||
|
|
||||||
var depthTexture = undefined;
|
var depthTexture = undefined;
|
||||||
|
|
||||||
|
const modelSelect = document.getElementById("model-select");
|
||||||
|
for (const path of modelPaths) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = path;
|
||||||
|
option.innerHTML = path;
|
||||||
|
modelSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
modelSelect.value = currentModel;
|
||||||
|
|
||||||
|
const lightingSelect = document.getElementById("lighting-select");
|
||||||
|
lightingSelect.value = "unlit";
|
||||||
|
|
||||||
function render()
|
function render()
|
||||||
{
|
{
|
||||||
if (canvas.clientWidth !== canvas.width || canvas.clientWidth !== canvas.height) {
|
if (canvas.clientWidth !== canvas.width || canvas.clientWidth !== canvas.height) {
|
||||||
@ -273,12 +389,10 @@ function render()
|
|||||||
which ^= 1;
|
which ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canvas.width > canvas.height) {
|
if (lightingSelect.value === "diffuse") {
|
||||||
uniformArray[2] = aspect;
|
uniformArray[0] = 1;
|
||||||
uniformArray[3] = 1;
|
|
||||||
} else {
|
} else {
|
||||||
uniformArray[2] = 1;
|
uniformArray[0] = 0;
|
||||||
uniformArray[3] = 1 / aspect;
|
|
||||||
}
|
}
|
||||||
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
|
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
|
||||||
|
|
||||||
@ -297,14 +411,19 @@ function render()
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
currentModel = modelSelect.value;
|
||||||
|
|
||||||
renderPass.setPipeline(cellPipeline);
|
renderPass.setPipeline(cellPipeline);
|
||||||
const vertexOffset = indexBufferSize;
|
const vertexOffset = indexBufferSize;
|
||||||
renderPass.setVertexBuffer(0, indexVertexBuffer, vertexOffset, vertexBufferSize);
|
renderPass.setVertexBuffer(0, indexVertexBuffer, vertexOffset, vertexBufferSize);
|
||||||
|
renderPass.setVertexBuffer(1, models[currentModel].instanceBuffer);
|
||||||
renderPass.setIndexBuffer(indexVertexBuffer, "uint16", 0, indexBufferSize);
|
renderPass.setIndexBuffer(indexVertexBuffer, "uint16", 0, indexBufferSize);
|
||||||
renderPass.setBindGroup(0, bindGroups[which]);
|
renderPass.setBindGroup(0, bindGroups[which]);
|
||||||
|
renderPass.setBindGroup(1, modelBindGroups[currentModel]);
|
||||||
//renderPass.draw(cubeVerticesCount, gridSize * gridSize);
|
//renderPass.draw(cubeVerticesCount, gridSize * gridSize);
|
||||||
renderPass.drawIndexed(cubeIndexCount, gridSize * gridSize);
|
//renderPass.drawIndexed(cubeIndexCount, gridSize * gridSize);
|
||||||
//renderPass.drawIndexed(cubeIndexCount);
|
//renderPass.drawIndexed(cubeIndexCount);
|
||||||
|
renderPass.drawIndexed(cubeIndexCount, models[currentModel].voxelCount);
|
||||||
|
|
||||||
renderPass.end();
|
renderPass.end();
|
||||||
|
|
||||||
|
|||||||
94
index.wgsl
94
index.wgsl
@ -1,29 +1,41 @@
|
|||||||
struct Configuration {
|
struct Configuration {
|
||||||
gridSize: vec2f,
|
lighting: vec4f,
|
||||||
aspect: vec2f,
|
matrix: mat4x4f,
|
||||||
matrix: mat4x4f
|
matrixWorld: 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 ModelConfiguration {
|
||||||
|
size: vec4f,
|
||||||
|
color: array<vec4f, 256>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(0) var<uniform> modelConfiguration: ModelConfiguration;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec3f,
|
@location(0) position: vec3f,
|
||||||
@location(1) texture: vec2f,
|
@location(1) texture: vec2f,
|
||||||
@location(2) normal: vec3f,
|
@location(2) normal: vec3f,
|
||||||
@location(3) tangent: vec4f,
|
@location(3) tangent: vec4f,
|
||||||
|
@location(4) instancePosition: vec4<u32>,
|
||||||
@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) positionWorld: vec3f,
|
||||||
@location(1) normal: vec3f,
|
@interpolate(flat) @location(1) colorIndex: i32,
|
||||||
|
@location(2) normal: vec3f,
|
||||||
|
@interpolate(flat) @location(3) lighting: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentInput {
|
struct FragmentInput {
|
||||||
@location(0) cell: vec2f,
|
@location(0) positionWorld: vec3f,
|
||||||
@location(1) normal: vec3f,
|
@interpolate(flat) @location(1) colorIndex: i32,
|
||||||
|
@location(2) normal: vec3f,
|
||||||
|
@interpolate(flat) @location(3) lighting: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
@ -32,23 +44,75 @@ fn vertexMain(input: VertexInput) -> VertexOutput
|
|||||||
let position = input.position * 0.5 + 0.5;
|
let position = input.position * 0.5 + 0.5;
|
||||||
let instance = f32(input.instance);
|
let instance = f32(input.instance);
|
||||||
|
|
||||||
|
/*
|
||||||
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 + vec3f(cell, 0)) / config.gridSize.xxx;
|
let grid = (position * state + vec3f(cell, 0)) / config.gridSize.xxx;
|
||||||
|
*/
|
||||||
|
|
||||||
|
let size = max(max(modelConfiguration.size.x, modelConfiguration.size.y), modelConfiguration.size.z);
|
||||||
|
|
||||||
|
let cell = (position * 1 + vec3f(input.instancePosition.xzy) - (modelConfiguration.size.xzy/2)) / size;
|
||||||
|
|
||||||
var output: VertexOutput;
|
var output: VertexOutput;
|
||||||
//output.position = (config.matrix * vec4f((grid * 2 - 1), 1)) * vec4f(config.aspect, 1, 1);
|
output.position = config.matrix * vec4f(cell, 1);
|
||||||
output.position = config.matrix * vec4f((grid * 2 - 1), 1);
|
output.positionWorld = (config.matrixWorld * vec4f(cell, 1.0f)).xyz;
|
||||||
|
output.colorIndex = i32(input.instancePosition.w);
|
||||||
//output.position = config.matrix * vec4f(input.position, 1);
|
output.normal = (config.matrixWorld * vec4f(input.normal, 0.0f)).xyz;
|
||||||
|
output.lighting = i32(config.lighting.x != 0.0);
|
||||||
output.cell = cell / config.gridSize;
|
|
||||||
output.normal = input.normal;
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn schlickFresnel(R0: vec3f, normal: vec3f, lightVector: vec3f) -> vec3f
|
||||||
|
{
|
||||||
|
let incidentAngle = saturate(dot(normal, lightVector));
|
||||||
|
|
||||||
|
let f0 = 1.0 - incidentAngle;
|
||||||
|
let reflectPercent = R0 + (1.0f - R0) * (f0 * f0 * f0 * f0 * f0);
|
||||||
|
|
||||||
|
return reflectPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blinnPhong(ndotl: f32, lightVector: vec3f, normal: vec3f, toEye: vec3f) -> vec3f
|
||||||
|
{
|
||||||
|
let roughness = 0.1;
|
||||||
|
|
||||||
|
let m = roughness * 256.0;
|
||||||
|
let halfVec = normalize(toEye + lightVector);
|
||||||
|
let roughnessFactor = (m + 8.0) * pow(ndotl, m) / 8.0f;
|
||||||
|
|
||||||
|
let fresnelR0 = vec3f(0.95f, 0.95f, 0.95f);
|
||||||
|
let fresnelFactor = schlickFresnel(fresnelR0, normal, lightVector);
|
||||||
|
|
||||||
|
let specular = fresnelFactor * roughnessFactor;
|
||||||
|
|
||||||
|
return specular;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lighting(position: vec3f, normal: vec3f) -> vec3f
|
||||||
|
{
|
||||||
|
let d = 0.9;
|
||||||
|
let eyePosition = vec3f(d, d / 2, d);
|
||||||
|
let lightPosition = vec3f(1, 1, 1);
|
||||||
|
let lightVector = normalize(lightPosition - position);
|
||||||
|
let ndotl = max(dot(lightVector, normal), 0.0);
|
||||||
|
let intensity = (ndotl + 0.3) / 1.3;
|
||||||
|
|
||||||
|
let toEye = normalize(eyePosition - position);
|
||||||
|
|
||||||
|
return (1.0 + blinnPhong(ndotl, lightVector, normal, toEye)) * intensity;
|
||||||
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f
|
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f
|
||||||
{
|
{
|
||||||
return vec4f(input.normal * 0.5 + 0.5, 1);
|
let baseColor = modelConfiguration.color[input.colorIndex - 1] / 255.0;
|
||||||
|
|
||||||
|
var intensity = vec3f(1.0);
|
||||||
|
|
||||||
|
if (input.lighting == 1) {
|
||||||
|
intensity = lighting(input.positionWorld, normalize(input.normal));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4(baseColor.xyz * intensity, baseColor.w);
|
||||||
}
|
}
|
||||||
|
|||||||
5
rotate.c
5
rotate.c
@ -9,8 +9,8 @@ void rotate(float angle, float aspect, float * buffer)
|
|||||||
//XMMATRIX mat2 = XMMatrixTranslation(0, 0, 1);
|
//XMMATRIX mat2 = XMMatrixTranslation(0, 0, 1);
|
||||||
//XMStoreFloat4x4((XMFLOAT4X4*)buffer, mat * mat2);
|
//XMStoreFloat4x4((XMFLOAT4X4*)buffer, mat * mat2);
|
||||||
|
|
||||||
|
float d = 0.9;
|
||||||
XMVECTOR eye = XMVectorSet(3, 3, 3, 0);
|
XMVECTOR eye = XMVectorSet(d, d / 2, d, 0);
|
||||||
XMVECTOR at = XMVectorSet(0, 0, 0, 0);
|
XMVECTOR at = XMVectorSet(0, 0, 0, 0);
|
||||||
XMVECTOR up = XMVectorSet(0, 1, 0, 0);
|
XMVECTOR up = XMVectorSet(0, 1, 0, 0);
|
||||||
XMMATRIX view = XMMatrixLookAtRH(eye, at, up);
|
XMMATRIX view = XMMatrixLookAtRH(eye, at, up);
|
||||||
@ -22,4 +22,5 @@ void rotate(float angle, float aspect, float * buffer)
|
|||||||
|
|
||||||
XMMATRIX rot = XMMatrixRotationY(angle);
|
XMMATRIX rot = XMMatrixRotationY(angle);
|
||||||
XMStoreFloat4x4((XMFLOAT4X4*)buffer, rot * view * projection);
|
XMStoreFloat4x4((XMFLOAT4X4*)buffer, rot * view * projection);
|
||||||
|
XMStoreFloat4x4((XMFLOAT4X4*)(buffer + 4 * 4), rot);
|
||||||
}
|
}
|
||||||
|
|||||||
140
tools/vox.py
Normal file
140
tools/vox.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import struct
|
||||||
|
from functools import partial
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import sys
|
||||||
|
|
||||||
|
with open(sys.argv[1], "rb") as f:
|
||||||
|
buf = memoryview(f.read())
|
||||||
|
|
||||||
|
class Reader:
|
||||||
|
def __init__(self, buf):
|
||||||
|
self.index = 0
|
||||||
|
self.mem = memoryview(buf)
|
||||||
|
|
||||||
|
def read(self, n, t):
|
||||||
|
v = struct.unpack(t, self.mem[self.index:self.index+n])
|
||||||
|
self.index += n
|
||||||
|
return v
|
||||||
|
|
||||||
|
def read_char4(self):
|
||||||
|
v = self.read(4, "<cccc")
|
||||||
|
return b"".join(v)
|
||||||
|
|
||||||
|
def read_int(self):
|
||||||
|
v, = self.read(4, "<i")
|
||||||
|
return v
|
||||||
|
|
||||||
|
def read_xyzi(self):
|
||||||
|
v = self.read(4, "<BBBB")
|
||||||
|
return v
|
||||||
|
|
||||||
|
def read_memory(self, n):
|
||||||
|
mem = self.mem[self.index:self.index+n]
|
||||||
|
self.index += n
|
||||||
|
return mem
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Chunk:
|
||||||
|
id: str
|
||||||
|
chunk: bytes
|
||||||
|
children: list[Chunk]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SizeChunk:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
z: int
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class XYZIChunk:
|
||||||
|
voxels: tuple[int, int, int, int]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RGBAChunk:
|
||||||
|
colors: tuple[int, int, int, int]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PackChunk:
|
||||||
|
model_count: int
|
||||||
|
|
||||||
|
def validate_chunk(chunk):
|
||||||
|
r = Reader(chunk.chunk)
|
||||||
|
if chunk.id == b"SIZE":
|
||||||
|
x = r.read_int()
|
||||||
|
y = r.read_int()
|
||||||
|
z = r.read_int()
|
||||||
|
assert r.index == len(chunk.chunk)
|
||||||
|
return SizeChunk(x, y, z)
|
||||||
|
elif chunk.id == b"XYZI":
|
||||||
|
voxel_count = r.read_int()
|
||||||
|
voxels = [r.read_xyzi() for _ in range(voxel_count)]
|
||||||
|
assert r.index == len(chunk.chunk)
|
||||||
|
return XYZIChunk(voxels)
|
||||||
|
elif chunk.id == b"RGBA":
|
||||||
|
colors = [r.read_xyzi() for _ in range(256)]
|
||||||
|
assert r.index == len(chunk.chunk)
|
||||||
|
return RGBAChunk(colors)
|
||||||
|
elif chunk.id == b"PACK":
|
||||||
|
model_count = r.read_int()
|
||||||
|
assert r.index == len(chunk.chunk)
|
||||||
|
return PackChunk(model_count)
|
||||||
|
else:
|
||||||
|
assert False, chunk.id
|
||||||
|
|
||||||
|
def parse_chunk(r: Reader):
|
||||||
|
global index
|
||||||
|
chunk_id = r.read_char4()
|
||||||
|
chunk_size = r.read_int()
|
||||||
|
children_size = r.read_int()
|
||||||
|
chunk = r.read_memory(chunk_size)
|
||||||
|
|
||||||
|
children_start = r.index
|
||||||
|
children = []
|
||||||
|
while (r.index - children_start) < children_size:
|
||||||
|
child_chunk = parse_chunk(r)
|
||||||
|
children.append(child_chunk)
|
||||||
|
assert (r.index - children_start) == children_size
|
||||||
|
|
||||||
|
return Chunk(chunk_id, chunk, children)
|
||||||
|
|
||||||
|
reader = Reader(buf)
|
||||||
|
|
||||||
|
magic = reader.read_char4()
|
||||||
|
assert magic == b"VOX ", magic
|
||||||
|
version = reader.read_int()
|
||||||
|
assert version == 150, version
|
||||||
|
|
||||||
|
main = parse_chunk(reader)
|
||||||
|
assert reader.index == len(buf)
|
||||||
|
chunks = []
|
||||||
|
for chunk in main.children:
|
||||||
|
chunk = validate_chunk(chunk)
|
||||||
|
chunks.append(chunk)
|
||||||
|
|
||||||
|
with open(sys.argv[2], 'wb') as f:
|
||||||
|
size_chunk = None
|
||||||
|
for chunk in chunks:
|
||||||
|
if type(chunk) is SizeChunk:
|
||||||
|
print('size')
|
||||||
|
size_chunk = chunk
|
||||||
|
f.write(struct.pack("<III", chunk.x, chunk.y, chunk.z))
|
||||||
|
print(chunk.x, chunk.y, chunk.z)
|
||||||
|
elif type(chunk) is XYZIChunk:
|
||||||
|
print('xyzi')
|
||||||
|
assert size_chunk is not None
|
||||||
|
#print(size_chunk)
|
||||||
|
print(len(chunk.voxels))
|
||||||
|
f.write(struct.pack("<I", len(chunk.voxels)))
|
||||||
|
for voxel in chunk.voxels:
|
||||||
|
#print(voxel)
|
||||||
|
f.write(struct.pack("<BBBB", *voxel))
|
||||||
|
size_chunk = None
|
||||||
|
elif type(chunk) is RGBAChunk:
|
||||||
|
print('rgba')
|
||||||
|
for color in chunk.colors:
|
||||||
|
f.write(struct.pack("<ffff", *color))
|
||||||
|
elif type(chunk) is PackChunk:
|
||||||
|
print("model count", chunk.model_count)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert False, type(chunk)
|
||||||
BIN
vox/chr_fox.bin
Normal file
BIN
vox/chr_fox.bin
Normal file
Binary file not shown.
BIN
vox/dragon.bin
Normal file
BIN
vox/dragon.bin
Normal file
Binary file not shown.
BIN
vox/ff3.bin
Normal file
BIN
vox/ff3.bin
Normal file
Binary file not shown.
BIN
vox/monu1.bin
Normal file
BIN
vox/monu1.bin
Normal file
Binary file not shown.
BIN
vox/monu16.bin
Normal file
BIN
vox/monu16.bin
Normal file
Binary file not shown.
BIN
vox/monu7.bin
Normal file
BIN
vox/monu7.bin
Normal file
Binary file not shown.
BIN
vox/monu9.bin
Normal file
BIN
vox/monu9.bin
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user