Compare commits
No commits in common. "dc0d6df2057ef22c84f416d9fb460abca9e901eb" and "b198b61bb7373005025c9d3ad298ec318e2fe8cc" have entirely different histories.
dc0d6df205
...
b198b61bb7
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,5 +5,3 @@ __pycache__
|
|||||||
*.data
|
*.data
|
||||||
.gdb_history
|
.gdb_history
|
||||||
*.elf
|
*.elf
|
||||||
favicon.ico
|
|
||||||
tools/ttf_outline/ttf_outline
|
|
||||||
277
flat.js
277
flat.js
@ -27,151 +27,30 @@ function smoothing(radius, distance)
|
|||||||
return value * value * value / volume;
|
return value * value * value / volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
function smoothingSlope(radius, distance)
|
|
||||||
{
|
|
||||||
if (distance >= radius)
|
|
||||||
return 0;
|
|
||||||
const value = radius * radius - distance * distance;
|
|
||||||
const scale = -24.0 / (Math.PI * Math.pow(radius, 8));
|
|
||||||
return scale * distance * value * value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function densityToPressure(density, targetDensity, pressure)
|
|
||||||
{
|
|
||||||
return (density - targetDensity) * pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FlatRenderer {
|
class FlatRenderer {
|
||||||
positionProperty(position, circleRadius, particleCount)
|
createParticles(device)
|
||||||
{
|
|
||||||
var property = 0.0;
|
|
||||||
for (var i = 0; i < particleCount; i++) {
|
|
||||||
const particlePosition = [this.particles[i * this.particleStride + 0],
|
|
||||||
this.particles[i * this.particleStride + 1]];
|
|
||||||
const particleProperty = this.particles[i * this.particleStride + 4];
|
|
||||||
const particleDensity = this.particles[i * this.particleStride + 5];
|
|
||||||
|
|
||||||
const distance = length(sub(position, particlePosition));
|
|
||||||
const influence = smoothing(circleRadius, distance);
|
|
||||||
|
|
||||||
property += particleProperty * influence / particleDensity;
|
|
||||||
}
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
positionGradient(position, circleRadius, particleCount)
|
|
||||||
{
|
|
||||||
var property = new Float32Array([0, 0]);
|
|
||||||
for (var i = 0; i < particleCount; i++) {
|
|
||||||
const particlePosition = [this.particles[i * this.particleStride + 0],
|
|
||||||
this.particles[i * this.particleStride + 1]];
|
|
||||||
const signedDistance = sub(position, particlePosition);
|
|
||||||
const distance = length(signedDistance);
|
|
||||||
|
|
||||||
const direction = [signedDistance[0] / distance, signedDistance[1] / distance];
|
|
||||||
const particleProperty = this.particles[i * this.particleStride + 4];
|
|
||||||
const particleDensity = this.particles[i * this.particleStride + 5];
|
|
||||||
|
|
||||||
const slope = smoothingSlope(circleRadius, distance);
|
|
||||||
|
|
||||||
const weight = particleProperty * slope / particleDensity;
|
|
||||||
property[0] += direction[0] * weight;
|
|
||||||
property[1] += direction[1] * weight;
|
|
||||||
}
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
positionPressureForce(index, circleRadius, particleCount, targetDensity, pressure, mass)
|
|
||||||
{
|
|
||||||
var pressureForce = new Float32Array([0, 0]);
|
|
||||||
|
|
||||||
const position = [this.particles[index * this.particleStride + 0],
|
|
||||||
this.particles[index * this.particleStride + 1]];
|
|
||||||
|
|
||||||
for (var i = 0; i < particleCount; i++) {
|
|
||||||
if (i === index) continue;
|
|
||||||
|
|
||||||
const particlePosition = [this.particles[i * this.particleStride + 0],
|
|
||||||
this.particles[i * this.particleStride + 1]];
|
|
||||||
const signedDistance = sub(position, particlePosition);
|
|
||||||
const distance = length(signedDistance);
|
|
||||||
|
|
||||||
var direction;
|
|
||||||
if (distance === 0) {
|
|
||||||
const x = Math.random();
|
|
||||||
const y = Math.random();
|
|
||||||
const l = length([x, y]);
|
|
||||||
direction = [x / l, y / l];
|
|
||||||
} else {
|
|
||||||
direction = [signedDistance[0] / distance, signedDistance[1] / distance];
|
|
||||||
}
|
|
||||||
const particleDensity = this.particles[i * this.particleStride + 5];
|
|
||||||
|
|
||||||
const slope = smoothingSlope(circleRadius, distance);
|
|
||||||
|
|
||||||
const sharedPressure = (
|
|
||||||
densityToPressure(this.particles[index * this.particleStride * 5], targetDensity, pressure) +
|
|
||||||
densityToPressure(particleDensity, targetDensity, pressure)
|
|
||||||
) / 2;
|
|
||||||
|
|
||||||
const weight = sharedPressure * slope * mass / particleDensity;
|
|
||||||
|
|
||||||
pressureForce[0] += direction[0] * weight;
|
|
||||||
pressureForce[1] += direction[1] * weight;
|
|
||||||
}
|
|
||||||
return pressureForce;
|
|
||||||
}
|
|
||||||
|
|
||||||
particleDensity(aPosition, circleRadius, particleCount)
|
|
||||||
{
|
|
||||||
var density = 0.0;
|
|
||||||
for (var i = 0; i < particleCount; i++) {
|
|
||||||
const bPosition = [this.particles[i * this.particleStride + 0],
|
|
||||||
this.particles[i * this.particleStride + 1]];
|
|
||||||
const distance = length(sub(aPosition, bPosition));
|
|
||||||
const influence = smoothing(circleRadius, distance);
|
|
||||||
density += influence;
|
|
||||||
}
|
|
||||||
return density;
|
|
||||||
}
|
|
||||||
|
|
||||||
createBuffers(device)
|
|
||||||
{
|
{
|
||||||
this.maxDim = 32;
|
this.maxDim = 32;
|
||||||
this.maxParticles = this.maxDim * this.maxDim;
|
this.maxParticles = this.maxDim * this.maxDim;
|
||||||
this.particleStride = this.module.instance.exports.particle__stride(); // bytes
|
this.particleStride = 4 * 2; // in elements
|
||||||
this.particlesSize = this.maxParticles * this.particleStride; // bytes
|
this.particles = new Float32Array(this.maxParticles * this.particleStride);
|
||||||
|
|
||||||
this.maxGradientsDim = 32;
|
|
||||||
this.maxGradients = this.maxGradientsDim * this.maxGradientsDim;
|
|
||||||
this.gradientStride = 4 * 2;
|
|
||||||
|
|
||||||
this.particleConfigurationStride = this.module.instance.exports.particle_configuration__stride();
|
|
||||||
|
|
||||||
this.particleBuffers = [];
|
this.particleBuffers = [];
|
||||||
this.gradientBuffers = [];
|
|
||||||
this.particleConfigurationBuffers = [];
|
|
||||||
|
|
||||||
this.particleBindGroups = [];
|
this.particleBindGroups = [];
|
||||||
|
|
||||||
for (let i = 0; i < 2; i++) {
|
this.particleConfiguration = new Float32Array(3 * 4);
|
||||||
const particleBuffer = device.createBuffer({
|
this.particleConfigurationBuffers = [];
|
||||||
label: `particle buffer ${i}`,
|
|
||||||
size: this.particlesSize,
|
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
this.particleBuffers.push(particleBuffer);
|
|
||||||
|
|
||||||
const gradientBuffer = device.createBuffer({
|
for (let i = 0; i < 2; i++) {
|
||||||
label: `gradient buffer ${i}`,
|
const buffer = device.createBuffer({
|
||||||
size: 4 * 4 * 2,
|
label: `particle buffer ${i}`,
|
||||||
|
size: this.particles.byteLength,
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
||||||
});
|
});
|
||||||
this.gradientBuffers.push(gradientBuffer);
|
this.particleBuffers.push(buffer);
|
||||||
|
|
||||||
const configBuffer = device.createBuffer({
|
const configBuffer = device.createBuffer({
|
||||||
label: `particle configuration buffer ${i}`,
|
label: `particle configuration buffer ${i}`,
|
||||||
size: this.particleConfigurationStride,
|
size: this.particleConfiguration.byteLength,
|
||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||||
});
|
});
|
||||||
this.particleConfigurationBuffers.push(configBuffer);
|
this.particleConfigurationBuffers.push(configBuffer);
|
||||||
@ -184,24 +63,31 @@ class FlatRenderer {
|
|||||||
resource: { buffer: configBuffer },
|
resource: { buffer: configBuffer },
|
||||||
}, {
|
}, {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
resource: { buffer: particleBuffer },
|
resource: { buffer: buffer },
|
||||||
}, {
|
|
||||||
binding: 2,
|
|
||||||
resource: { buffer: gradientBuffer },
|
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
this.particleBindGroups.push(bindGroup);
|
this.particleBindGroups.push(bindGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let y = 0; y < this.maxDim; y++) {
|
||||||
|
for (let x = 0; x < this.maxDim; x++) {
|
||||||
|
const index = y * this.maxDim + x;
|
||||||
|
//this.particles[index * this.particleStride + 0] = (x / this.maxDim) + 0.5 / this.maxDim;
|
||||||
|
//this.particles[index * this.particleStride + 1] = (y / this.maxDim) + 0.5 / this.maxDim;
|
||||||
|
// position
|
||||||
|
const position = [Math.random(), Math.random()];
|
||||||
|
this.particles[index * this.particleStride + 0] = position[0];
|
||||||
|
this.particles[index * this.particleStride + 1] = position[1];
|
||||||
|
this.particles[index * this.particleStride + 2] = 0.0;
|
||||||
|
this.particles[index * this.particleStride + 3] = 1.0;
|
||||||
|
// value.x
|
||||||
|
this.particles[index * this.particleStride + 4] = test(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeState()
|
constructor(device, canvasFormat, viewUniformBuffer, shaderModule)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(device, canvasFormat, viewUniformBuffer, shaderModule, module)
|
|
||||||
{
|
|
||||||
this.module = module;
|
|
||||||
const label = "flat";
|
const label = "flat";
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -247,10 +133,6 @@ class FlatRenderer {
|
|||||||
binding: 1,
|
binding: 1,
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
buffer: { type: "read-only-storage" }
|
buffer: { type: "read-only-storage" }
|
||||||
}, {
|
|
||||||
binding: 2,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
buffer: { type: "read-only-storage" }
|
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -291,7 +173,7 @@ class FlatRenderer {
|
|||||||
// particles
|
// particles
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
this.createBuffers(device);
|
this.createParticles(device);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// bind group
|
// bind group
|
||||||
@ -309,30 +191,17 @@ class FlatRenderer {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGradients(device, frameNumber, configuration)
|
positionDensity(position, circleRadius, particleCount)
|
||||||
{
|
{
|
||||||
const radius = configuration.circleRadius;
|
var density = 0.0;
|
||||||
const count = configuration.particleCount;
|
for (var i = 0; i < particleCount; i++) {
|
||||||
|
const particlePosition = [this.particles[i * this.particleStride + 0],
|
||||||
// calculate particle gradients
|
this.particles[i * this.particleStride + 1]];
|
||||||
for (let y = 0; y < this.maxGradientsDim; y++) {
|
const distance = length(sub(position, particlePosition));
|
||||||
for (let x = 0; x < this.maxGradientsDim; x++) {
|
const influence = smoothing(circleRadius, distance);
|
||||||
const i = y * this.maxGradientsDim + x;
|
density += influence;
|
||||||
const position = [(x / this.maxGradientsDim) + 0.5 / this.maxGradientsDim,
|
|
||||||
(y / this.maxGradientsDim) + 0.5 / this.maxGradientsDim];
|
|
||||||
|
|
||||||
const gradient = this.positionGradient(position, radius, count);
|
|
||||||
|
|
||||||
this.gradients[i * this.gradientStride + 0] = position[0];
|
|
||||||
this.gradients[i * this.gradientStride + 1] = position[1];
|
|
||||||
|
|
||||||
this.gradients[i * this.gradientStride + 4] = gradient[0];
|
|
||||||
this.gradients[i * this.gradientStride + 5] = gradient[1];
|
|
||||||
}
|
}
|
||||||
}
|
return density;
|
||||||
|
|
||||||
device.queue.writeBuffer(this.gradientBuffers[frameNumber], 0,
|
|
||||||
this.gradients, 0, this.gradients.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParticles(device, frameNumber, configuration)
|
updateParticles(device, frameNumber, configuration)
|
||||||
@ -340,7 +209,7 @@ class FlatRenderer {
|
|||||||
for (var i = 0; i < configuration.particleCount; i++) {
|
for (var i = 0; i < configuration.particleCount; i++) {
|
||||||
const position = [this.particles[i * this.particleStride + 0],
|
const position = [this.particles[i * this.particleStride + 0],
|
||||||
this.particles[i * this.particleStride + 1]];
|
this.particles[i * this.particleStride + 1]];
|
||||||
const density = this.particleDensity(position,
|
const density = this.positionDensity(position,
|
||||||
configuration.circleRadius,
|
configuration.circleRadius,
|
||||||
configuration.particleCount);
|
configuration.particleCount);
|
||||||
// value.y
|
// value.y
|
||||||
@ -349,57 +218,10 @@ class FlatRenderer {
|
|||||||
|
|
||||||
device.queue.writeBuffer(this.particleBuffers[frameNumber], 0,
|
device.queue.writeBuffer(this.particleBuffers[frameNumber], 0,
|
||||||
this.particles, 0, this.particles.length);
|
this.particles, 0, this.particles.length);
|
||||||
}
|
|
||||||
|
|
||||||
updateSimulation(device, frameNumber, configuration)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < configuration.particleCount; i++) {
|
|
||||||
const force = this.positionPressureForce(i,
|
|
||||||
configuration.circleRadius,
|
|
||||||
configuration.particleCount,
|
|
||||||
configuration.targetDensity,
|
|
||||||
configuration.pressure,
|
|
||||||
configuration.particleMass1);
|
|
||||||
if (force[0] == NaN)
|
|
||||||
throw new Error("force");
|
|
||||||
|
|
||||||
const particleDensity = this.particles[i * this.particleStride + 5];
|
|
||||||
|
|
||||||
const acceleration = [force[0] / particleDensity, force[1] / particleDensity];
|
|
||||||
|
|
||||||
this.particles[i * this.particleStride + 8] += acceleration[0];
|
|
||||||
this.particles[i * this.particleStride + 9] += acceleration[1] + 0.001;
|
|
||||||
|
|
||||||
// collisions
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < configuration.particleCount; i++) {
|
|
||||||
this.particles[i * this.particleStride + 0] -= this.particles[i * this.particleStride + 8];
|
|
||||||
this.particles[i * this.particleStride + 1] -= this.particles[i * this.particleStride + 9];
|
|
||||||
|
|
||||||
const position = [this.particles[i * this.particleStride + 0],
|
|
||||||
this.particles[i * this.particleStride + 1]];
|
|
||||||
|
|
||||||
for (var c = 0; c < 2; c++) {
|
|
||||||
if (position[c] < 0) {
|
|
||||||
this.particles[i * this.particleStride + c] = 0.01;
|
|
||||||
this.particles[i * this.particleStride + c + 8] *= -1 * 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position[c] > 1) {
|
|
||||||
this.particles[i * this.particleStride + c] = 0.99;
|
|
||||||
this.particles[i * this.particleStride + c + 8] *= -1 * 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateConfiguration(device, frameNumber, configuration)
|
|
||||||
{
|
|
||||||
const PARTICLE = 0;
|
const PARTICLE = 0;
|
||||||
const CIRCLE = 1;
|
const CIRCLE = 1;
|
||||||
const MOUSE = 2;
|
const MOUSE = 2;
|
||||||
const D = 3;
|
|
||||||
|
|
||||||
const config = this.particleConfiguration;
|
const config = this.particleConfiguration;
|
||||||
config[PARTICLE * 4 + 0] = configuration.particleCount;
|
config[PARTICLE * 4 + 0] = configuration.particleCount;
|
||||||
@ -414,13 +236,8 @@ class FlatRenderer {
|
|||||||
|
|
||||||
config[MOUSE * 4 + 0] = configuration.mousePosition[0];
|
config[MOUSE * 4 + 0] = configuration.mousePosition[0];
|
||||||
config[MOUSE * 4 + 1] = configuration.mousePosition[1];
|
config[MOUSE * 4 + 1] = configuration.mousePosition[1];
|
||||||
config[MOUSE * 4 + 2] = configuration.lineThickness;
|
config[MOUSE * 4 + 2] = configuration.mousePosition[2];
|
||||||
config[MOUSE * 4 + 3] = configuration.lineLength;
|
config[MOUSE * 4 + 3] = configuration.mousePosition[3];
|
||||||
|
|
||||||
config[D * 4 + 0] = configuration.targetDensity;
|
|
||||||
config[D * 4 + 1] = configuration.pressure;
|
|
||||||
config[D * 4 + 2] = 0;
|
|
||||||
config[D * 4 + 3] = 0;
|
|
||||||
|
|
||||||
device.queue.writeBuffer(this.particleConfigurationBuffers[frameNumber], 0,
|
device.queue.writeBuffer(this.particleConfigurationBuffers[frameNumber], 0,
|
||||||
this.particleConfiguration, 0, this.particleConfiguration.length)
|
this.particleConfiguration, 0, this.particleConfiguration.length)
|
||||||
@ -428,11 +245,7 @@ class FlatRenderer {
|
|||||||
|
|
||||||
render(device, renderPass, frameNumber, configuration)
|
render(device, renderPass, frameNumber, configuration)
|
||||||
{
|
{
|
||||||
// particle update must be before gradient update
|
this.updateParticles(device, frameNumber, configuration);
|
||||||
//this.updateParticles(device, frameNumber, configuration);
|
|
||||||
//this.updateGradients(device, frameNumber, configuration);
|
|
||||||
//this.updateSimulation(device, frameNumber, configuration);
|
|
||||||
//this.updateConfiguration(device, frameNumber, configuration);
|
|
||||||
|
|
||||||
renderPass.setPipeline(this.renderPipeline);
|
renderPass.setPipeline(this.renderPipeline);
|
||||||
renderPass.setIndexBuffer(this.buffer, "uint16");
|
renderPass.setIndexBuffer(this.buffer, "uint16");
|
||||||
@ -442,7 +255,7 @@ class FlatRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFlat(device, canvasFormat, viewUniformBuffer, module)
|
async function loadFlat(device, canvasFormat, viewUniformBuffer)
|
||||||
{
|
{
|
||||||
const flatWgsl = await getPath("flat.wgsl");
|
const flatWgsl = await getPath("flat.wgsl");
|
||||||
const shaderModule = device.createShaderModule({
|
const shaderModule = device.createShaderModule({
|
||||||
@ -450,7 +263,7 @@ async function loadFlat(device, canvasFormat, viewUniformBuffer, module)
|
|||||||
code: flatWgsl,
|
code: flatWgsl,
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderer = new FlatRenderer(device, canvasFormat, viewUniformBuffer, shaderModule, module);
|
const renderer = new FlatRenderer(device, canvasFormat, viewUniformBuffer, shaderModule);
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
80
flat.wgsl
80
flat.wgsl
@ -8,25 +8,17 @@ struct Configuration {
|
|||||||
struct Particle {
|
struct Particle {
|
||||||
position: vec4f,
|
position: vec4f,
|
||||||
value: vec4f,
|
value: vec4f,
|
||||||
velocity: vec4f,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParticleConfiguration {
|
struct ParticleConfiguration {
|
||||||
particle: vec4f,
|
particle: vec4f,
|
||||||
circle: vec4f,
|
circle: vec4f,
|
||||||
mouse: vec4f,
|
mouse: vec4f,
|
||||||
d: vec4f,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Gradient {
|
|
||||||
position: vec4f,
|
|
||||||
delta: vec4f,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
@group(0) @binding(0) var<uniform> config: Configuration;
|
||||||
@group(1) @binding(0) var<uniform> particleConfiguration: ParticleConfiguration;
|
@group(1) @binding(0) var<uniform> particleConfiguration: ParticleConfiguration;
|
||||||
@group(1) @binding(1) var<storage> particle: array<Particle>;
|
@group(1) @binding(1) var<storage> particle: array<Particle>;
|
||||||
@group(1) @binding(2) var<storage> gradient: array<Gradient>;
|
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@builtin(vertex_index) vertex_index: u32,
|
@builtin(vertex_index) vertex_index: u32,
|
||||||
@ -82,19 +74,6 @@ fn positionDensity(position: vec2f, circleRadius: f32, particleCount: u32) -> f3
|
|||||||
return density;
|
return density;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signedDistanceSegment(p: vec2f, a: vec2f, b: vec2f, r: f32) -> f32
|
|
||||||
{
|
|
||||||
let ba = b - a;
|
|
||||||
let pa = p - a;
|
|
||||||
let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
||||||
return length(pa - h * ba) - r;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn densityToPressure(density: f32, targetDensity: f32, pressure: f32) -> f32
|
|
||||||
{
|
|
||||||
return (density - targetDensity) * pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
||||||
{
|
{
|
||||||
@ -110,22 +89,14 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
|||||||
let testIntensity = particleConfiguration.circle.z;
|
let testIntensity = particleConfiguration.circle.z;
|
||||||
let mass12Mix = particleConfiguration.circle.w;
|
let mass12Mix = particleConfiguration.circle.w;
|
||||||
|
|
||||||
let circlePosition = particleConfiguration.mouse.xy;
|
let circlePosition = particleConfiguration.mouse;
|
||||||
let lineThickness = particleConfiguration.mouse.z;
|
|
||||||
let lineLength = particleConfiguration.mouse.w;
|
|
||||||
|
|
||||||
let targetDensity = particleConfiguration.d.x;
|
|
||||||
let pressure = particleConfiguration.d.y;
|
|
||||||
|
|
||||||
let particleMass = mix(particleMass1, particleMass2, mass12Mix);
|
|
||||||
|
|
||||||
var property: f32 = 0.0;
|
var property: f32 = 0.0;
|
||||||
|
|
||||||
for (var i: u32 = 0; i < particleCount; i++) {
|
for (var i: u32 = 0; i < particleCount; i++) {
|
||||||
let distance = length(input.texture.xy - particle[i].position.xy);
|
let distance = length(input.texture.xy - particle[i].position.xy);
|
||||||
if (distance < particleSize) {
|
if (distance < particleSize) {
|
||||||
//color.x = particle[i].value.x;
|
color.x = particle[i].value.x;
|
||||||
color.x = 1.0;
|
|
||||||
}
|
}
|
||||||
//let circleDistance = length(input.texture.xy - circlePosition.xy);
|
//let circleDistance = length(input.texture.xy - circlePosition.xy);
|
||||||
let influence = smoothing(circleRadius, distance);
|
let influence = smoothing(circleRadius, distance);
|
||||||
@ -133,52 +104,21 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
|||||||
let particleProperty = particle[i].value.x;
|
let particleProperty = particle[i].value.x;
|
||||||
let particleDensity = particle[i].value.y;
|
let particleDensity = particle[i].value.y;
|
||||||
|
|
||||||
//let p1 = particleProperty * influence;
|
let p1 = particleProperty * influence;
|
||||||
let p1 = influence;
|
|
||||||
let p2 = p1 / particleDensity;
|
let p2 = p1 / particleDensity;
|
||||||
|
|
||||||
//property += mix(p1, p2, mass12Mix);
|
property += mix(p1, p2, mass12Mix);
|
||||||
property += influence;
|
|
||||||
}
|
}
|
||||||
|
color.z = property * mix(particleMass1, particleMass2, mass12Mix);
|
||||||
|
|
||||||
let density = property * particleMass;
|
if (circlePosition.w != 0) {
|
||||||
let fragmentPressure = densityToPressure(density, targetDensity, pressure) * 0.01;
|
let thickness = abs(length(input.texture.xy - circlePosition.xy) - circleRadius);
|
||||||
color.z = density;
|
if (thickness < circleThickness) {
|
||||||
|
//color.y = 1.0;
|
||||||
for (var i: u32 = 0; i < 1024; i++) {
|
|
||||||
let a = gradient[i].position.xy;
|
|
||||||
let b = a + gradient[i].delta.xy * particleMass * lineLength;
|
|
||||||
let distance = signedDistanceSegment(input.texture.xy, a, b, 0);
|
|
||||||
let headDistance = length(input.texture.xy - b);
|
|
||||||
if ((distance < lineThickness) || (headDistance < lineThickness * 3.5)) {
|
|
||||||
color.y = 1.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let testColor = test(input.texture.xy);
|
let testColor = test(input.texture.xy);
|
||||||
color.z += testColor * testIntensity;
|
return vec4(color.xz, testColor * testIntensity, 1.0);
|
||||||
|
|
||||||
let r = vec3f(1, 0, 0);
|
|
||||||
let g = vec3f(0, 1, 0);
|
|
||||||
let b = vec3f(0, 0, 1);
|
|
||||||
|
|
||||||
let n = smoothstep(0, -0.01, fragmentPressure);
|
|
||||||
let p = smoothstep(0, 0.01, fragmentPressure);
|
|
||||||
let z = smoothstep(0.01, 0, abs(fragmentPressure));
|
|
||||||
//return vec4(n, z, p, 1);
|
|
||||||
|
|
||||||
let pressureColor = (r * n + g * z + b * p) * density;
|
|
||||||
return vec4(pressureColor.xyz * (1.0 - color.x) + vec3(1, 1, 1) * color.y, 1.0);
|
|
||||||
//return vec4(color.xyz, 1.0);
|
|
||||||
//return vec4(color.x, 0, 0, 1.0);
|
//return vec4(color.x, 0, 0, 1.0);
|
||||||
|
|
||||||
/*
|
|
||||||
if (fragmentPressure < -0.1) {
|
|
||||||
return vec4(1, 0, 0, 1);
|
|
||||||
} else if (fragmentPressure > 0.1) {
|
|
||||||
return vec4(0, 1, 0, 1);
|
|
||||||
} else {
|
|
||||||
return vec4(0, 0, 1, 1);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
265
font.js
265
font.js
@ -1,265 +0,0 @@
|
|||||||
import { getPath } from "./common.js";
|
|
||||||
|
|
||||||
class FontRenderer {
|
|
||||||
createBuffers(device)
|
|
||||||
{
|
|
||||||
this.frames = [];
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// glyph
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const glyphBufferSize = this.module.instance.exports.font_layout__glyph_buffer_size(this.fontLayoutAddress);
|
|
||||||
const glyphBuffer = device.createBuffer({
|
|
||||||
label: `glyph buffer`,
|
|
||||||
size: glyphBufferSize,
|
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
const glyphBufferAddress = this.module.instance.exports.font_layout__glyph_buffer_address(this.fontLayoutAddress);
|
|
||||||
device.queue.writeBuffer(glyphBuffer, 0,
|
|
||||||
this.module.memory.buffer, glyphBufferAddress, glyphBufferSize);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// texture
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const textureWidth = this.module.instance.exports.font_layout__texture_width(this.fontLayoutAddress);
|
|
||||||
const textureHeight = this.module.instance.exports.font_layout__texture_height(this.fontLayoutAddress);
|
|
||||||
const textureAddress = this.module.instance.exports.font_layout__texture_address(this.fontLayoutAddress);
|
|
||||||
|
|
||||||
//console.log(textureWidth, textureHeight, textureAddress);
|
|
||||||
const texture = device.createTexture({
|
|
||||||
size: [textureWidth, textureHeight],
|
|
||||||
format: 'r8unorm',
|
|
||||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
device.queue.writeTexture({ texture: texture },
|
|
||||||
this.module.memory.buffer,
|
|
||||||
{ offset: textureAddress, bytesPerRow: textureWidth },
|
|
||||||
{ width: textureWidth, height: textureHeight });
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// sampler
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const sampler = device.createSampler({
|
|
||||||
magFilter: "linear",
|
|
||||||
minFilter: "linear",
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// bind group
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.perFontBindGroup = device.createBindGroup({
|
|
||||||
label: `font per-font bind group`,
|
|
||||||
layout: this.bindGroupLayout.perFont,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
resource: sampler,
|
|
||||||
}, {
|
|
||||||
binding: 1,
|
|
||||||
resource: texture,
|
|
||||||
}, {
|
|
||||||
binding: 2,
|
|
||||||
resource: { buffer: glyphBuffer },
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// layout
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const layoutBufferSize = this.module.instance.exports.font_layout__layout_buffer_size(this.fontLayoutAddress);
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
const layoutBuffer = device.createBuffer({
|
|
||||||
label: `layout buffer ${i}`,
|
|
||||||
size: layoutBufferSize,
|
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bindGroup = device.createBindGroup({
|
|
||||||
label: `font per-frame bind group ${i}`,
|
|
||||||
layout: this.bindGroupLayout.perFrame,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
resource: { buffer: layoutBuffer },
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
this.frames.push({
|
|
||||||
layoutBuffer: layoutBuffer,
|
|
||||||
bindGroup: bindGroup,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(device, canvasFormat, viewUniformBuffer, wgsl, module, fontBufferSrc)
|
|
||||||
{
|
|
||||||
const label = "font";
|
|
||||||
|
|
||||||
this.module = module;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// font layout
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.fontBufferAddress = this.module.instance.exports.mem_alloc(fontBufferSrc.byteLength);
|
|
||||||
const fontBufferDst = new Uint8Array(this.module.memory.buffer, this.fontBufferAddress, fontBufferSrc.byteLength);
|
|
||||||
fontBufferDst.set(new Uint8Array(fontBufferSrc));
|
|
||||||
this.maxGlyphs = 1024;
|
|
||||||
this.fontLayoutAddress = this.module.instance.exports.font_layout__create(this.fontBufferAddress, this.maxGlyphs);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// shader module
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const shaderModule = device.createShaderModule({
|
|
||||||
label: `${label} shader`,
|
|
||||||
code: wgsl,
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// index buffer
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const array = new Uint16Array(6);
|
|
||||||
array[0] = 0;
|
|
||||||
array[1] = 1;
|
|
||||||
array[2] = 2;
|
|
||||||
array[3] = 0;
|
|
||||||
array[4] = 3;
|
|
||||||
array[5] = 1;
|
|
||||||
|
|
||||||
this.buffer = device.createBuffer({
|
|
||||||
label: `${label} renderer buffer`,
|
|
||||||
size: array.byteLength,
|
|
||||||
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
device.queue.writeBuffer(this.buffer, 0, array, 0, array.length);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// pipeline
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.bindGroupLayout = {
|
|
||||||
view: device.createBindGroupLayout({
|
|
||||||
label: `${label} bind group layout view`,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
visibility: GPUShaderStage.VERTEX,
|
|
||||||
buffer: { type: "uniform" }
|
|
||||||
}]
|
|
||||||
}),
|
|
||||||
perFont: device.createBindGroupLayout({
|
|
||||||
label: `${label} bind group per-font`,
|
|
||||||
entries: [
|
|
||||||
{ // sampler
|
|
||||||
binding: 0,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
sampler: {}
|
|
||||||
}, { // texture
|
|
||||||
binding: 1,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
texture: {},
|
|
||||||
}, { // glyph buffer
|
|
||||||
binding: 2,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
buffer: { type: "read-only-storage" }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
perFrame: device.createBindGroupLayout({
|
|
||||||
label: `${label} bind group per-frame`,
|
|
||||||
entries: [{ // layout buffer
|
|
||||||
binding: 0,
|
|
||||||
visibility: GPUShaderStage.VERTEX,
|
|
||||||
buffer: { type: "read-only-storage" }
|
|
||||||
}
|
|
||||||
/*, { // configuration
|
|
||||||
binding: 1,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
buffer: { type: "uniform" }
|
|
||||||
}*/]
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const pipelineLayout = device.createPipelineLayout({
|
|
||||||
label: `${label} pipeline layout`,
|
|
||||||
bindGroupLayouts: [
|
|
||||||
this.bindGroupLayout.view,
|
|
||||||
this.bindGroupLayout.perFont,
|
|
||||||
this.bindGroupLayout.perFrame,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
this.renderPipeline = device.createRenderPipeline({
|
|
||||||
label: `${label} pipeline`,
|
|
||||||
layout: pipelineLayout,
|
|
||||||
vertex: {
|
|
||||||
module: shaderModule,
|
|
||||||
entryPoint: "vertexMain",
|
|
||||||
},
|
|
||||||
fragment: {
|
|
||||||
module: shaderModule,
|
|
||||||
entryPoint: "fragmentMain",
|
|
||||||
targets: [{
|
|
||||||
format: canvasFormat,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
primitive: {
|
|
||||||
topology: "triangle-list",
|
|
||||||
},
|
|
||||||
depthStencil: {
|
|
||||||
depthWriteEnabled: true,
|
|
||||||
depthCompare: "less",
|
|
||||||
format: "depth24plus",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// buffers
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.createBuffers(device);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// bind group
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.viewBindGroup = device.createBindGroup({
|
|
||||||
label: `${label} bind group`,
|
|
||||||
layout: this.bindGroupLayout.view,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
resource: { buffer: viewUniformBuffer },
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render(renderPass, frameNumber)
|
|
||||||
{
|
|
||||||
renderPass.setPipeline(this.renderPipeline);
|
|
||||||
renderPass.setIndexBuffer(this.buffer, "uint16");
|
|
||||||
renderPass.setBindGroup(0, this.viewBindGroup);
|
|
||||||
renderPass.setBindGroup(1, this.perFontBindGroup);
|
|
||||||
renderPass.setBindGroup(2, this.frames[frameNumber].bindGroup);
|
|
||||||
renderPass.drawIndexed(6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadFont(device, canvasFormat, viewUniformBuffer, module)
|
|
||||||
{
|
|
||||||
const fontWgsl = await getPath("font.wgsl");
|
|
||||||
|
|
||||||
const liberationResponse = await fetch("liberation.data");
|
|
||||||
const liberationBuffer = await liberationResponse.arrayBuffer();
|
|
||||||
|
|
||||||
const renderer = new FontRenderer(device, canvasFormat, viewUniformBuffer, fontWgsl, module, liberationBuffer);
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { loadFont };
|
|
||||||
68
font.wgsl
68
font.wgsl
@ -1,68 +0,0 @@
|
|||||||
struct Configuration {
|
|
||||||
viewProj: mat4x4f,
|
|
||||||
lightViewProj: mat4x4f,
|
|
||||||
lightPosition: vec4f,
|
|
||||||
eyePosition: vec4f,
|
|
||||||
aspect: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlyphBuffer {
|
|
||||||
position: vec2f,
|
|
||||||
size: vec2f,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LayoutBuffer {
|
|
||||||
position: vec4f, // glyph_index w
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
|
||||||
|
|
||||||
@group(1) @binding(0) var linearSampler: sampler;
|
|
||||||
@group(1) @binding(1) var fontTexture: texture_2d<f32>;
|
|
||||||
@group(1) @binding(2) var<storage> glyphBuffer: array<GlyphBuffer>;
|
|
||||||
|
|
||||||
@group(2) @binding(0) var<storage> layoutBuffer: array<LayoutBuffer>;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@builtin(vertex_index) vertex_index: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4f,
|
|
||||||
@location(0) texture: vec2f,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertices = array(
|
|
||||||
vec2f(1.0, 0.0),
|
|
||||||
vec2f(0.0, 1.0),
|
|
||||||
vec2f(0.0, 0.0),
|
|
||||||
vec2f(1.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertexMain(input: VertexInput) -> VertexOutput
|
|
||||||
{
|
|
||||||
let texture = vertices[input.vertex_index];
|
|
||||||
|
|
||||||
let position = vec2f(texture.x, 1.0 - texture.y) * 2.0 - 1.0;
|
|
||||||
|
|
||||||
var output: VertexOutput;
|
|
||||||
output.position = vec4f(position * 0.5, 0.0, 1.0);
|
|
||||||
output.texture = texture;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
|
||||||
{
|
|
||||||
var color = vec3f(input.texture.xy, 0);
|
|
||||||
|
|
||||||
let p = glyphBuffer[66].position;
|
|
||||||
let s = glyphBuffer[66].size;
|
|
||||||
|
|
||||||
let base = textureSample(fontTexture, linearSampler, input.texture * s + p);
|
|
||||||
|
|
||||||
let t = f32(base.x > 0.5);
|
|
||||||
|
|
||||||
return vec4f(t, t, t, 1);
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
// this file is designed to be platform-agnostic
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// metrics are 26.6 fixed point
|
|
||||||
struct glyph_metrics {
|
|
||||||
int32_t horiBearingX;
|
|
||||||
int32_t horiBearingY;
|
|
||||||
int32_t horiAdvance;
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3));
|
|
||||||
|
|
||||||
struct glyph_bitmap {
|
|
||||||
uint16_t x;
|
|
||||||
uint16_t y;
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4));
|
|
||||||
|
|
||||||
struct glyph {
|
|
||||||
struct glyph_bitmap bitmap;
|
|
||||||
struct glyph_metrics metrics;
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics))));
|
|
||||||
|
|
||||||
struct font {
|
|
||||||
uint32_t first_char_code;
|
|
||||||
uint32_t last_char_code;
|
|
||||||
struct face_metrics {
|
|
||||||
int32_t height; // 26.6 fixed point
|
|
||||||
int32_t max_advance; // 26.6 fixed point
|
|
||||||
} face_metrics;
|
|
||||||
uint32_t glyph_count;
|
|
||||||
uint16_t texture_width;
|
|
||||||
uint16_t texture_height;
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct font)) == ((sizeof (uint32_t)) * 6));
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -129,12 +129,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
<!--
|
|
||||||
<div class="row">
|
|
||||||
<span class="label">eye</span>
|
|
||||||
<input type="color" class="value">
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
93
index.js
93
index.js
@ -2,8 +2,6 @@ import { getPath } from "./common.js";
|
|||||||
import { loadGltf } from "./index2.js";
|
import { loadGltf } from "./index2.js";
|
||||||
import { loadLight } from "./light.js";
|
import { loadLight } from "./light.js";
|
||||||
import { loadFlat } from "./flat.js";
|
import { loadFlat } from "./flat.js";
|
||||||
import { loadSnake } from "./snake.js";
|
|
||||||
import { loadFont } from "./font.js";
|
|
||||||
|
|
||||||
if (!navigator.gpu) {
|
if (!navigator.gpu) {
|
||||||
throw new Error("WebGPU not supported on this browser.");
|
throw new Error("WebGPU not supported on this browser.");
|
||||||
@ -47,10 +45,8 @@ const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"),
|
|||||||
env: {
|
env: {
|
||||||
memory: memory,
|
memory: memory,
|
||||||
log: console.log,
|
log: console.log,
|
||||||
logStrInt: console.log,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
module.memory = memory;
|
|
||||||
|
|
||||||
function createDepthTexture() {
|
function createDepthTexture() {
|
||||||
return device.createTexture({
|
return device.createTexture({
|
||||||
@ -85,7 +81,7 @@ function recreateDepth(force)
|
|||||||
const matrixSize = 4 * 4 * 4;
|
const matrixSize = 4 * 4 * 4;
|
||||||
const float4Size = 4 * 4;
|
const float4Size = 4 * 4;
|
||||||
const float3Size = 4 * 3;
|
const float3Size = 4 * 3;
|
||||||
const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 2) + (4 * 4);
|
const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 2);
|
||||||
|
|
||||||
const viewUniformBuffer = device.createBuffer({
|
const viewUniformBuffer = device.createBuffer({
|
||||||
label: "view uniform buffer",
|
label: "view uniform buffer",
|
||||||
@ -102,9 +98,8 @@ module.instance.exports.camera_init(cameraStateAddress);
|
|||||||
|
|
||||||
//const gltfRenderer = await loadGltf(device, canvasFormat, viewUniformBuffer, memory, module);
|
//const gltfRenderer = await loadGltf(device, canvasFormat, viewUniformBuffer, memory, module);
|
||||||
//const lightRenderer = await loadLight(device, canvasFormat, viewUniformBuffer);
|
//const lightRenderer = await loadLight(device, canvasFormat, viewUniformBuffer);
|
||||||
//const flatRenderer = await loadFlat(device, canvasFormat, viewUniformBuffer, module);
|
const flatRenderer = await loadFlat(device, canvasFormat, viewUniformBuffer);
|
||||||
//const snakeRenderer = await loadSnake(device, canvasFormat, viewUniformBuffer, module);
|
|
||||||
const fontRenderer = await loadFont(device, canvasFormat, viewUniformBuffer, module);
|
|
||||||
|
|
||||||
const KEY = {
|
const KEY = {
|
||||||
A: 65,
|
A: 65,
|
||||||
@ -193,6 +188,15 @@ function updateView()
|
|||||||
device.queue.writeBuffer(viewUniformBuffer, 0,
|
device.queue.writeBuffer(viewUniformBuffer, 0,
|
||||||
memory.buffer, viewStateAddress,
|
memory.buffer, viewStateAddress,
|
||||||
viewUniformBufferSize);
|
viewUniformBufferSize);
|
||||||
|
|
||||||
|
const cameraStateF32 = new Float32Array(memory.buffer, cameraStateAddress, cameraStateSize);
|
||||||
|
/*
|
||||||
|
eyeValueX.innerHTML = cameraStateF32[0].toFixed(1);
|
||||||
|
eyeValueY.innerHTML = cameraStateF32[1].toFixed(1);
|
||||||
|
eyeValueZ.innerHTML = cameraStateF32[2].toFixed(1);
|
||||||
|
yawValue.innerHTML = cameraStateF32[9].toFixed(2);
|
||||||
|
pitchValue.innerHTML = cameraStateF32[10].toFixed(2);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const controlMain = document.getElementById("control-main");
|
const controlMain = document.getElementById("control-main");
|
||||||
@ -202,7 +206,16 @@ function remap(low1, high1, low2, high2, value)
|
|||||||
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
|
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sliderDefaults = {"bone length":"32","velocity acc.":"385","velocity damp.":"796","bone count":"40"};
|
const sliderDefaults = {
|
||||||
|
"circle radius": 385,
|
||||||
|
"circle thickness": 284,
|
||||||
|
"mass12 mix": 1,
|
||||||
|
"particle count": 360,
|
||||||
|
"particle mass1": 126,
|
||||||
|
"particle mass2": 417,
|
||||||
|
"particle size": 51,
|
||||||
|
"test intensity": 0,
|
||||||
|
};
|
||||||
|
|
||||||
class Slider {
|
class Slider {
|
||||||
constructor(label, low, high, max = 1024)
|
constructor(label, low, high, max = 1024)
|
||||||
@ -241,8 +254,6 @@ class Slider {
|
|||||||
} else if (this.label in sliderDefaults) {
|
} else if (this.label in sliderDefaults) {
|
||||||
this.input.value = sliderDefaults[this.label];
|
this.input.value = sliderDefaults[this.label];
|
||||||
}
|
}
|
||||||
this.input.value = sliderDefaults[this.label];
|
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,64 +274,49 @@ class Slider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
const sliders = {
|
||||||
const flatSliders = {
|
|
||||||
particleSize: new Slider("particle size", 0.0, 0.05),
|
particleSize: new Slider("particle size", 0.0, 0.05),
|
||||||
particleMass1: new Slider("particle mass1", 0.0, 0.05),
|
particleMass1: new Slider("particle mass1", 0.0, 0.01),
|
||||||
particleMass2: new Slider("particle mass2", 0.0, 1.0),
|
particleMass2: new Slider("particle mass2", 0.0, 1.0),
|
||||||
particleCount: new Slider("particle count", 0.0, 1024.0),
|
particleCount: new Slider("particle count", 0.0, 1024.0),
|
||||||
circleRadius: new Slider("circle radius", 0.0, 0.5),
|
circleRadius: new Slider("circle radius", 0.0, 0.5),
|
||||||
circleThickness: new Slider("circle thickness", 0.0, 0.01),
|
circleThickness: new Slider("circle thickness", 0.0, 0.01),
|
||||||
testIntensity: new Slider("test intensity", 0.0, 2.0),
|
testIntensity: new Slider("test intensity", 0.0, 2.0),
|
||||||
mass12Mix: new Slider("mass12 mix", 0.0, 1.0, 1),
|
mass12Mix: new Slider("mass12 mix", 0.0, 1.0, 1),
|
||||||
lineThickness: new Slider("line thickness", 0.0, 0.005),
|
|
||||||
lineLength: new Slider("line length", 0.0, 9.0),
|
|
||||||
targetDensity: new Slider("target density", -10.0, 10.0),
|
|
||||||
pressure: new Slider("pressure", 0.0, 1.0),
|
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
|
|
||||||
const mousePosition = new Float32Array([0, 0, 0, 0]);
|
const mousePosition = new Float32Array([0, 0, 0, 0]);
|
||||||
|
|
||||||
const snakeSliders = {
|
function updateSliders()
|
||||||
boneCount: new Slider("bone count", 0.0, 100.0, 100),
|
|
||||||
boneLength: new Slider("bone length", 0.0, 0.5),
|
|
||||||
velocityDamping: new Slider("velocity damp.", 0.0, 1.0),
|
|
||||||
velocityAcceleration: new Slider("velocity acc.", 0.0, 1.0),
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateSliders(sliders)
|
|
||||||
{
|
{
|
||||||
const values = {};
|
return {
|
||||||
for (let [key, value] of Object.entries(sliders)) {
|
mousePosition: mousePosition,
|
||||||
values[key] = value.update();
|
particleSize: sliders.particleSize.update(),
|
||||||
}
|
particleMass1: sliders.particleMass1.update(),
|
||||||
return values;
|
particleMass2: sliders.particleMass2.update(),
|
||||||
|
particleCount: sliders.particleCount.update(),
|
||||||
|
circleRadius: sliders.circleRadius.update(),
|
||||||
|
circleThickness: sliders.circleThickness.update(),
|
||||||
|
testIntensity: sliders.testIntensity.update(),
|
||||||
|
mass12Mix: sliders.mass12Mix.update(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(e)
|
function onClick(e)
|
||||||
{
|
{
|
||||||
mousePosition[0] = e.clientX / canvas.width;
|
mousePosition[0] = e.clientX / canvas.width;
|
||||||
mousePosition[1] = 1.0 - (e.clientY / canvas.height);
|
mousePosition[1] = 1.0 - (e.clientY / canvas.height);
|
||||||
|
mousePosition[2] = 0;
|
||||||
|
mousePosition[3] = 1;
|
||||||
}
|
}
|
||||||
//canvas.addEventListener("click", onClick);
|
canvas.addEventListener("click", onClick);
|
||||||
|
|
||||||
function onMouseMove(e)
|
|
||||||
{
|
|
||||||
const aspect = canvas.width / canvas.height;
|
|
||||||
|
|
||||||
mousePosition[0] = ((e.clientX / canvas.width) * 2 - 1) * aspect;
|
|
||||||
mousePosition[1] = (1.0 - (e.clientY / canvas.height)) * 2 - 1;
|
|
||||||
}
|
|
||||||
canvas.addEventListener("mousemove", onMouseMove);
|
|
||||||
|
|
||||||
var canRender = false;
|
var canRender = false;
|
||||||
var frameNumber = 0;
|
var frameNumber = 0;
|
||||||
function render2()
|
function render2()
|
||||||
{
|
{
|
||||||
if (canRender === true) {
|
if (canRender === true) {
|
||||||
//const configuration = updateSliders(flatSliders);
|
const configuration = updateSliders();
|
||||||
const configuration = updateSliders(snakeSliders);
|
|
||||||
|
|
||||||
canRender = false;
|
canRender = false;
|
||||||
recreateDepth(false);
|
recreateDepth(false);
|
||||||
@ -346,12 +342,7 @@ function render2()
|
|||||||
|
|
||||||
//gltfRenderer.render(renderPass);
|
//gltfRenderer.render(renderPass);
|
||||||
//lightRenderer.render(renderPass);
|
//lightRenderer.render(renderPass);
|
||||||
//flatRenderer.render(device, renderPass, frameNumber, configuration);
|
flatRenderer.render(device, renderPass, frameNumber, configuration);
|
||||||
|
|
||||||
//snakeRenderer.update(device, frameNumber, mousePosition, configuration);
|
|
||||||
//snakeRenderer.render(renderPass, frameNumber);
|
|
||||||
|
|
||||||
fontRenderer.render(renderPass, frameNumber);
|
|
||||||
|
|
||||||
renderPass.end();
|
renderPass.end();
|
||||||
|
|
||||||
@ -367,7 +358,7 @@ function setCanRender()
|
|||||||
{
|
{
|
||||||
canRender = true;
|
canRender = true;
|
||||||
}
|
}
|
||||||
setInterval(setCanRender, 33.33); // 15 fps
|
setInterval(setCanRender, 66.67); // 15 fps
|
||||||
|
|
||||||
recreateDepth(true);
|
recreateDepth(true);
|
||||||
requestAnimationFrame(render2);
|
requestAnimationFrame(render2);
|
||||||
|
|||||||
201
snake.js
201
snake.js
@ -1,201 +0,0 @@
|
|||||||
import { getPath } from "./common.js";
|
|
||||||
|
|
||||||
class SnakeRenderer {
|
|
||||||
createBuffers(device)
|
|
||||||
{
|
|
||||||
const boneCount = 100;
|
|
||||||
const boneLength = 0.1;
|
|
||||||
this.boneSystemAddress = this.module.instance.exports.bone_system__create(boneCount, boneLength);
|
|
||||||
const mem = new DataView(this.module.memory.buffer);
|
|
||||||
this.bonesAddress = mem.getUint32(this.boneSystemAddress, true);
|
|
||||||
const configurationOffset = this.module.instance.exports.bone_system__configuration_offset();
|
|
||||||
this.configurationAddress = this.boneSystemAddress + configurationOffset;
|
|
||||||
this.bonesBufferSize = this.module.instance.exports.bone_system__bones_size(boneCount);
|
|
||||||
this.configurationBufferSize = this.module.instance.exports.bone_system__configuration_size();
|
|
||||||
|
|
||||||
this.frames = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
const bonesBuffer = device.createBuffer({
|
|
||||||
label: `bone buffer ${i}`,
|
|
||||||
size: this.bonesBufferSize,
|
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
const configurationBuffer = device.createBuffer({
|
|
||||||
label: `bone configuration buffer ${i}`,
|
|
||||||
size: this.configurationBufferSize,
|
|
||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bindGroup = device.createBindGroup({
|
|
||||||
label: `bone buffer bind group ${i}`,
|
|
||||||
layout: this.bindGroupLayout.bone,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
resource: { buffer: bonesBuffer },
|
|
||||||
}, {
|
|
||||||
binding: 1,
|
|
||||||
resource: { buffer: configurationBuffer },
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
this.frames.push({
|
|
||||||
bonesBuffer: bonesBuffer,
|
|
||||||
configurationBuffer: configurationBuffer,
|
|
||||||
bindGroup: bindGroup,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(device, canvasFormat, viewUniformBuffer, wgsl, module)
|
|
||||||
{
|
|
||||||
const label = "bone";
|
|
||||||
|
|
||||||
this.module = module;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// shader module
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const shaderModule = device.createShaderModule({
|
|
||||||
label: `${label} shader`,
|
|
||||||
code: wgsl,
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// index buffer
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const array = new Uint16Array(6);
|
|
||||||
array[0] = 0;
|
|
||||||
array[1] = 1;
|
|
||||||
array[2] = 2;
|
|
||||||
array[3] = 0;
|
|
||||||
array[4] = 3;
|
|
||||||
array[5] = 1;
|
|
||||||
|
|
||||||
this.buffer = device.createBuffer({
|
|
||||||
label: `${label} renderer buffer`,
|
|
||||||
size: array.byteLength,
|
|
||||||
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
device.queue.writeBuffer(this.buffer, 0, array, 0, array.length);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// pipeline
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.bindGroupLayout = {
|
|
||||||
view: device.createBindGroupLayout({
|
|
||||||
label: `${label} bind group layout view`,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
visibility: GPUShaderStage.VERTEX,
|
|
||||||
buffer: { type: "uniform" }
|
|
||||||
}]
|
|
||||||
}),
|
|
||||||
bone: device.createBindGroupLayout({
|
|
||||||
label: `${label} bind group layout particle`,
|
|
||||||
entries: [{ // bones
|
|
||||||
binding: 0,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
buffer: { type: "read-only-storage" }
|
|
||||||
}, { // configuration
|
|
||||||
binding: 1,
|
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
|
||||||
buffer: { type: "uniform" }
|
|
||||||
}]
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const pipelineLayout = device.createPipelineLayout({
|
|
||||||
label: `${label} pipeline layout`,
|
|
||||||
bindGroupLayouts: [
|
|
||||||
this.bindGroupLayout.view,
|
|
||||||
this.bindGroupLayout.bone
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
this.renderPipeline = device.createRenderPipeline({
|
|
||||||
label: `${label} pipeline`,
|
|
||||||
layout: pipelineLayout,
|
|
||||||
vertex: {
|
|
||||||
module: shaderModule,
|
|
||||||
entryPoint: "vertexMain",
|
|
||||||
},
|
|
||||||
fragment: {
|
|
||||||
module: shaderModule,
|
|
||||||
entryPoint: "fragmentMain",
|
|
||||||
targets: [{
|
|
||||||
format: canvasFormat,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
primitive: {
|
|
||||||
topology: "triangle-list",
|
|
||||||
},
|
|
||||||
depthStencil: {
|
|
||||||
depthWriteEnabled: true,
|
|
||||||
depthCompare: "less",
|
|
||||||
format: "depth24plus",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// bones
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.createBuffers(device);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// bind group
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.viewBindGroup = device.createBindGroup({
|
|
||||||
label: `${label} bind group`,
|
|
||||||
layout: this.bindGroupLayout.view,
|
|
||||||
entries: [{
|
|
||||||
binding: 0,
|
|
||||||
resource: { buffer: viewUniformBuffer },
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update(device, frameNumber, mousePosition, configuration)
|
|
||||||
{
|
|
||||||
this.module.instance.exports.bone_system__update_configuration(this.boneSystemAddress,
|
|
||||||
configuration.boneCount,
|
|
||||||
configuration.boneLength,
|
|
||||||
configuration.velocityDamping,
|
|
||||||
configuration.velocityAcceleration);
|
|
||||||
|
|
||||||
this.module.instance.exports.bone_system__update(this.boneSystemAddress,
|
|
||||||
mousePosition[0], mousePosition[1], mousePosition[2]);
|
|
||||||
|
|
||||||
device.queue.writeBuffer(this.frames[frameNumber].bonesBuffer, 0,
|
|
||||||
this.module.memory.buffer, this.bonesAddress, this.bonesBufferSize);
|
|
||||||
|
|
||||||
device.queue.writeBuffer(this.frames[frameNumber].configurationBuffer, 0,
|
|
||||||
this.module.memory.buffer, this.configurationAddress, this.configurationBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(renderPass, frameNumber)
|
|
||||||
{
|
|
||||||
renderPass.setPipeline(this.renderPipeline);
|
|
||||||
renderPass.setIndexBuffer(this.buffer, "uint16");
|
|
||||||
renderPass.setBindGroup(0, this.viewBindGroup);
|
|
||||||
renderPass.setBindGroup(1, this.frames[frameNumber].bindGroup);
|
|
||||||
renderPass.drawIndexed(6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSnake(device, canvasFormat, viewUniformBuffer, module)
|
|
||||||
{
|
|
||||||
const snakeWgsl = await getPath("snake.wgsl");
|
|
||||||
|
|
||||||
const renderer = new SnakeRenderer(device, canvasFormat, viewUniformBuffer, snakeWgsl, module);
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { loadSnake };
|
|
||||||
90
snake.wgsl
90
snake.wgsl
@ -1,90 +0,0 @@
|
|||||||
struct Configuration {
|
|
||||||
viewProj: mat4x4f,
|
|
||||||
lightViewProj: mat4x4f,
|
|
||||||
lightPosition: vec4f,
|
|
||||||
eyePosition: vec4f,
|
|
||||||
aspect: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Bone {
|
|
||||||
position: vec4f,
|
|
||||||
velocity: vec4f,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BoneConfiguration {
|
|
||||||
a: vec4f,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
|
||||||
@group(1) @binding(0) var<storage> bones: array<Bone>;
|
|
||||||
@group(1) @binding(1) var<uniform> boneConfig: BoneConfiguration;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@builtin(vertex_index) vertex_index: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4f,
|
|
||||||
@location(0) texture: vec2f,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertices = array(
|
|
||||||
vec2f(1.0, 0.0),
|
|
||||||
vec2f(0.0, 1.0),
|
|
||||||
vec2f(0.0, 0.0),
|
|
||||||
vec2f(1.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertexMain(input: VertexInput) -> VertexOutput
|
|
||||||
{
|
|
||||||
let texture = vertices[input.vertex_index];
|
|
||||||
|
|
||||||
let position = texture * 2.0 - 1.0;
|
|
||||||
|
|
||||||
var output: VertexOutput;
|
|
||||||
output.position = vec4f(position, 0.0, 1.0);
|
|
||||||
output.texture = position * vec2f(config.aspect, 1);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sdCircle(center: vec2f, sample: vec2f) -> f32
|
|
||||||
{
|
|
||||||
let distance = length(center - sample);
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sdSegment(p: vec2f, a: vec2f, b: vec2f) -> f32
|
|
||||||
{
|
|
||||||
let ba = b - a;
|
|
||||||
let pa = p - a;
|
|
||||||
let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
||||||
return length(pa - h * ba);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
|
||||||
{
|
|
||||||
var color = vec3f(input.texture.xy, 0);
|
|
||||||
|
|
||||||
for (var i: u32 = 0; i < u32(boneConfig.a.x); i++) {
|
|
||||||
if (sdCircle(input.texture.xy, bones[i].position.xy) < 0.01) {
|
|
||||||
color = vec3f(1, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (sdSegment(input.texture.xy,
|
|
||||||
bones[i].position.xy,
|
|
||||||
bones[i].position.xy + bones[i].velocity.xy * 0.05) < 0.002) {
|
|
||||||
color = vec3(0, 0, 1);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
if (i == 1 && sdCircle(input.texture.xy, bones[i].velocity.xy) < 0.05) {
|
|
||||||
color = vec3(0, 0, 1);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec4f(color, 1);
|
|
||||||
}
|
|
||||||
32
src/Makefile
32
src/Makefile
@ -9,7 +9,6 @@ CFLAGS = \
|
|||||||
-nostdlib \
|
-nostdlib \
|
||||||
-I$(MINIZ) \
|
-I$(MINIZ) \
|
||||||
-I. \
|
-I. \
|
||||||
-I../include \
|
|
||||||
-Werror \
|
-Werror \
|
||||||
-Wfatal-errors \
|
-Wfatal-errors \
|
||||||
-D_XM_NO_INTRINSICS_
|
-D_XM_NO_INTRINSICS_
|
||||||
@ -33,27 +32,6 @@ LDFLAGS = \
|
|||||||
-Wl,--export=camera_move \
|
-Wl,--export=camera_move \
|
||||||
-Wl,--export=camera_init \
|
-Wl,--export=camera_init \
|
||||||
-Wl,--export=camera_view_projection \
|
-Wl,--export=camera_view_projection \
|
||||||
-Wl,--export=particle_system__create \
|
|
||||||
-Wl,--export=particle_system__update \
|
|
||||||
-Wl,--export=particle__stride \
|
|
||||||
-Wl,--export=particle_configuration__stride \
|
|
||||||
-Wl,--export=bone_system__create \
|
|
||||||
-Wl,--export=bone_system__bones_size \
|
|
||||||
-Wl,--export=bone_system__configuration_size \
|
|
||||||
-Wl,--export=bone_system__configuration_offset \
|
|
||||||
-Wl,--export=bone_system__update \
|
|
||||||
-Wl,--export=bone_system__update_configuration \
|
|
||||||
-Wl,--export=font_layout__create \
|
|
||||||
-Wl,--export=font_layout__texture_address \
|
|
||||||
-Wl,--export=font_layout__glyph_buffer_size \
|
|
||||||
-Wl,--export=font_layout__glyph_buffer_address \
|
|
||||||
-Wl,--export=font_layout__texture_width \
|
|
||||||
-Wl,--export=font_layout__texture_height \
|
|
||||||
-Wl,--export=font_layout__texture_address \
|
|
||||||
-Wl,--export=font_layout__layout_buffer_size \
|
|
||||||
-Wl,--export=font_layout__layout_buffer_address \
|
|
||||||
-Wl,--export=font_layout__layout_buffer_index \
|
|
||||||
-Wl,--export=font_layout__draw \
|
|
||||||
-Wl,--import-undefined \
|
-Wl,--import-undefined \
|
||||||
-Wl,--print-map \
|
-Wl,--print-map \
|
||||||
-Wl,--import-memory \
|
-Wl,--import-memory \
|
||||||
@ -71,15 +49,7 @@ PNG_OBJ = \
|
|||||||
$(MINIZ)/miniz_tinfl.o \
|
$(MINIZ)/miniz_tinfl.o \
|
||||||
memory.o \
|
memory.o \
|
||||||
node.o \
|
node.o \
|
||||||
camera.o \
|
camera.o
|
||||||
fluid/particle.o \
|
|
||||||
fluid/particle_system.o \
|
|
||||||
fluid/api.o \
|
|
||||||
snake/bone.o \
|
|
||||||
snake/bone_system.o \
|
|
||||||
snake/api.o \
|
|
||||||
font_layout/font_layout.o \
|
|
||||||
font_layout/api.o
|
|
||||||
|
|
||||||
module.wasm: $(PNG_OBJ)
|
module.wasm: $(PNG_OBJ)
|
||||||
clang++ $(CFLAGS) $(LDFLAGS) -o $@ $^
|
clang++ $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define acosf(x) __builtin_acosf(x)
|
|
||||||
#define asinf(x) __builtin_asinf(x)
|
|
||||||
#define atan2f(x, y) __builtin_atan2f(x, y)
|
|
||||||
#define atanf(x) __builtin_atanf(x)
|
|
||||||
#define ceilf(x) __builtin_ceilf(x)
|
|
||||||
#define cosf(x) __builtin_cosf(x)
|
|
||||||
#define coshf(x) __builtin_coshf(x)
|
|
||||||
#define exp2f(x) __builtin_exp2f(x)
|
|
||||||
#define expf(x) __builtin_expf(x)
|
|
||||||
#define fabsf(x) __builtin_fabsf(x)
|
|
||||||
#define floorf(x) __builtin_floorf(x)
|
|
||||||
#define isinf(x) (false)
|
|
||||||
#define isnan(x) (false)
|
|
||||||
#define log10f(x) __builtin_log10f(x)
|
|
||||||
#define log2f(x) __builtin_log2f(x)
|
|
||||||
#define logf(x) __builtin_logf(x)
|
|
||||||
#define modff(x, y) __builtin_modff(x, y)
|
|
||||||
#define powf(x, y) __builtin_powf(x, y)
|
|
||||||
#define sinf(x) __builtin_sinf(x)
|
|
||||||
#define sinhf(x) __builtin_sinhf(x)
|
|
||||||
#define sqrtf(x) __builtin_sqrtf(x)
|
|
||||||
#define tanf(x) __builtin_tanf(x)
|
|
||||||
#define tanhf(x) __builtin_tanhf(x)
|
|
||||||
#define sign(x) ((x > 0) - (x < 0))
|
|
||||||
@ -18,7 +18,6 @@ struct ViewState {
|
|||||||
XMFLOAT4X4 lightViewProj;
|
XMFLOAT4X4 lightViewProj;
|
||||||
XMFLOAT4 lightPosition;
|
XMFLOAT4 lightPosition;
|
||||||
XMFLOAT4 eyePosition;
|
XMFLOAT4 eyePosition;
|
||||||
float aspect;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline XMMATRIX camera_view(CameraState const * state)
|
static inline XMMATRIX camera_view(CameraState const * state)
|
||||||
@ -151,5 +150,4 @@ void camera_view_projection(CameraState * camera_state,
|
|||||||
XMStoreFloat4(&view_state->lightPosition, light_position);
|
XMStoreFloat4(&view_state->lightPosition, light_position);
|
||||||
XMVECTOR eye = XMLoadFloat3(&camera_state->eye);
|
XMVECTOR eye = XMLoadFloat3(&camera_state->eye);
|
||||||
XMStoreFloat4(&view_state->eyePosition, eye);
|
XMStoreFloat4(&view_state->eyePosition, eye);
|
||||||
view_state->aspect = aspect;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,9 +154,32 @@
|
|||||||
#endif // !_XM_NO_INTRINSICS_
|
#endif // !_XM_NO_INTRINSICS_
|
||||||
|
|
||||||
#include "sal.h"
|
#include "sal.h"
|
||||||
|
//#include <assert.h>
|
||||||
#define assert(x)
|
#define assert(x)
|
||||||
|
#define isnan(x) (false)
|
||||||
|
#define floorf(x) __builtin_floorf(x)
|
||||||
|
#define sqrtf(x) __builtin_sqrtf(x)
|
||||||
|
#define expf(x) __builtin_expf(x)
|
||||||
|
#define exp2f(x) __builtin_exp2f(x)
|
||||||
|
#define logf(x) __builtin_logf(x)
|
||||||
|
#define log2f(x) __builtin_log2f(x)
|
||||||
|
#define log10f(x) __builtin_log10f(x)
|
||||||
|
#define powf(x, y) __builtin_powf(x, y)
|
||||||
|
#define fabsf(x) __builtin_fabsf(x)
|
||||||
|
#define sinf(x) __builtin_sinf(x)
|
||||||
|
#define sinhf(x) __builtin_sinhf(x)
|
||||||
|
#define tanf(x) __builtin_tanf(x)
|
||||||
|
#define tanhf(x) __builtin_tanhf(x)
|
||||||
|
#define cosf(x) __builtin_cosf(x)
|
||||||
|
#define asinf(x) __builtin_asinf(x)
|
||||||
|
#define acosf(x) __builtin_acosf(x)
|
||||||
|
#define coshf(x) __builtin_coshf(x)
|
||||||
|
#define atanf(x) __builtin_atanf(x)
|
||||||
|
#define atan2f(x, y) __builtin_atan2f(x, y)
|
||||||
|
#define isinf(x) (false)
|
||||||
|
#define ceilf(x) __builtin_ceilf(x)
|
||||||
|
#define modff(x, y) __builtin_modff(x, y)
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "builtin_math.h"
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
#include "particle_system.h"
|
|
||||||
#include "new.h"
|
|
||||||
|
|
||||||
using namespace fluid;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
ParticleSystem * particle_system__create(int maxParticles)
|
|
||||||
{
|
|
||||||
auto particleSystem = New<ParticleSystem>();
|
|
||||||
particleSystem->init(maxParticles);
|
|
||||||
return particleSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void particle_system__update(ParticleSystem * particleSystem)
|
|
||||||
{
|
|
||||||
particleSystem->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
int particle__stride()
|
|
||||||
{
|
|
||||||
return (sizeof (Particle));
|
|
||||||
}
|
|
||||||
|
|
||||||
int particle_configuration__stride()
|
|
||||||
{
|
|
||||||
return (sizeof (ParticleConfiguration));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
#include "particle.h"
|
|
||||||
|
|
||||||
namespace fluid
|
|
||||||
{
|
|
||||||
void Particle::update(ParticleConfiguration * configuration)
|
|
||||||
{
|
|
||||||
XMVECTOR nextPosition = XMLoadFloat4(&position);
|
|
||||||
XMVECTOR nextVelocity = XMLoadFloat4(&velocity);
|
|
||||||
|
|
||||||
nextVelocity += XMVectorSet(0, 1, 0, 0) * configuration->gravity;
|
|
||||||
nextPosition += nextVelocity;
|
|
||||||
|
|
||||||
resolveCollision(configuration);
|
|
||||||
|
|
||||||
XMStoreFloat4(&position, nextPosition);
|
|
||||||
XMStoreFloat4(&velocity, nextVelocity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Particle::resolveCollision(ParticleConfiguration * configuration)
|
|
||||||
{
|
|
||||||
XMVECTOR halfExtents = XMLoadFloat2(&configuration->halfExtents) - XMVectorReplicate(configuration->particleRadius);
|
|
||||||
if (fabsf(position.x) > XMVectorGetX(halfExtents)) {
|
|
||||||
position.x = XMVectorGetX(halfExtents) * sign(position.x);
|
|
||||||
velocity.x *= -1;
|
|
||||||
}
|
|
||||||
if (fabsf(position.y) > XMVectorGetX(halfExtents)) {
|
|
||||||
position.y = XMVectorGetX(halfExtents) * sign(position.y);
|
|
||||||
velocity.y *= -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "directxmath/DirectXMath.h"
|
|
||||||
#include "particle_configuration.h"
|
|
||||||
|
|
||||||
namespace fluid {
|
|
||||||
|
|
||||||
struct Particle {
|
|
||||||
XMFLOAT4 position;
|
|
||||||
XMFLOAT4 velocity;
|
|
||||||
XMFLOAT4 value;
|
|
||||||
|
|
||||||
void update(ParticleConfiguration * configuration);
|
|
||||||
void resolveCollision(ParticleConfiguration * configuration);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "directxmath/DirectXMath.h"
|
|
||||||
|
|
||||||
namespace fluid {
|
|
||||||
|
|
||||||
struct ParticleConfiguration {
|
|
||||||
float particleCount;
|
|
||||||
float particleRadius;
|
|
||||||
float particleMass1;
|
|
||||||
float particleMass2;
|
|
||||||
|
|
||||||
float smoothingRadius;
|
|
||||||
float lineThickness;
|
|
||||||
float lineLength;
|
|
||||||
float _padding1;
|
|
||||||
|
|
||||||
float targetDensity;
|
|
||||||
float pressure;
|
|
||||||
float gravity;
|
|
||||||
float _padding2;
|
|
||||||
|
|
||||||
XMFLOAT2 testSamplePosition;
|
|
||||||
XMFLOAT2 halfExtents;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
#include "particle_system.h"
|
|
||||||
#include "new.h"
|
|
||||||
|
|
||||||
namespace fluid {
|
|
||||||
|
|
||||||
void ParticleSystem::init(int maxParticles)
|
|
||||||
{
|
|
||||||
this->maxParticles = maxParticles;
|
|
||||||
configuration = New<ParticleConfiguration>();
|
|
||||||
particles = New<Particle>(maxParticles);
|
|
||||||
|
|
||||||
for (int i = 0; i < maxParticles; i++) {
|
|
||||||
XMStoreFloat4(&particles[i].position, XMVectorZero());
|
|
||||||
XMStoreFloat4(&particles[i].velocity, XMVectorZero());
|
|
||||||
XMStoreFloat4(&particles[i].value, XMVectorZero());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticleSystem::update()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < configuration->particleCount; i++) {
|
|
||||||
particles[i].update(configuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "particle.h"
|
|
||||||
|
|
||||||
namespace fluid {
|
|
||||||
|
|
||||||
struct ParticleSystem {
|
|
||||||
ParticleConfiguration * configuration;
|
|
||||||
Particle * particles;
|
|
||||||
int maxParticles;
|
|
||||||
|
|
||||||
void init(int maxParticles);
|
|
||||||
void update();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
#include "new.h"
|
|
||||||
#include "font_layout.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
FontLayout * font_layout__create(size_t buffer, int layout_buffer_length)
|
|
||||||
{
|
|
||||||
FontLayout * font_layout = New<FontLayout>();
|
|
||||||
font_layout->init(buffer, layout_buffer_length);
|
|
||||||
return font_layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t font_layout__glyph_buffer_size(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->font->glyph_count * (sizeof (GlyphBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
void * font_layout__glyph_buffer_address(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->glyph_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t font_layout__texture_width(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->font->texture_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t font_layout__texture_height(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->font->texture_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * font_layout__texture_address(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t font_layout__layout_buffer_size(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->layout_buffer_length * (sizeof (LayoutBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
void * font_layout__layout_buffer_address(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->layout_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int font_layout__layout_buffer_index(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
return font_layout->layout_buffer_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void font_layout__draw(FontLayout * font_layout)
|
|
||||||
{
|
|
||||||
font_layout->draw_string("@");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
#include "new.h"
|
|
||||||
#include "font_layout.h"
|
|
||||||
|
|
||||||
static inline size_t texture_offset(font * font)
|
|
||||||
{
|
|
||||||
return (sizeof (struct font)) + (sizeof (struct glyph)) * font->glyph_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FontLayout::init(size_t buffer, int layout_buffer_length)
|
|
||||||
{
|
|
||||||
this->font = reinterpret_cast<struct font *>(buffer + 0);
|
|
||||||
this->glyphs = reinterpret_cast<struct glyph *>(buffer + (sizeof (font)));
|
|
||||||
|
|
||||||
this->texture = reinterpret_cast<void *>(buffer + texture_offset(this->font));
|
|
||||||
|
|
||||||
this->layout_buffer_length = layout_buffer_length;
|
|
||||||
this->layout_buffer_index = 0;
|
|
||||||
this->layout_buffer = New<LayoutBuffer>(layout_buffer_length);
|
|
||||||
|
|
||||||
this->glyph_buffer = New<GlyphBuffer>(this->font->glyph_count);
|
|
||||||
for (uint16_t i = 0; i < this->font->glyph_count; i++) {
|
|
||||||
glyph_bitmap const & bitmap = this->glyphs[i].bitmap;
|
|
||||||
this->glyph_buffer[i].position.x = float(bitmap.x) / float(this->font->texture_width);
|
|
||||||
this->glyph_buffer[i].position.y = float(bitmap.y) / float(this->font->texture_height);
|
|
||||||
this->glyph_buffer[i].size.x = float(bitmap.width) / float(this->font->texture_width);
|
|
||||||
this->glyph_buffer[i].size.y = float(bitmap.height) / float(this->font->texture_height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FontLayout::draw_string(char const * string)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
char c = string[i];
|
|
||||||
if (c == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (c < font->first_char_code || c > font->last_char_code) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int char_index = c - font->first_char_code;
|
|
||||||
glyph const & glyph = glyphs[char_index];
|
|
||||||
|
|
||||||
if (layout_buffer_index >= layout_buffer_length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
layout_buffer[layout_buffer_index].position = XMFLOAT3(0, 0, 0);
|
|
||||||
layout_buffer[layout_buffer_index].glyph_index = char_index;
|
|
||||||
layout_buffer_index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "font.h"
|
|
||||||
#include "directxmath/DirectXMath.h"
|
|
||||||
|
|
||||||
struct GlyphBuffer {
|
|
||||||
XMFLOAT2 position; // in uv units
|
|
||||||
XMFLOAT2 size; // in uv units
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LayoutBuffer {
|
|
||||||
XMFLOAT3 position; // in local coordinate space units
|
|
||||||
float glyph_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FontLayout {
|
|
||||||
font * font;
|
|
||||||
glyph * glyphs;
|
|
||||||
void * texture;
|
|
||||||
int layout_buffer_length; // in elements
|
|
||||||
int layout_buffer_index;
|
|
||||||
GlyphBuffer * glyph_buffer;
|
|
||||||
LayoutBuffer * layout_buffer;
|
|
||||||
|
|
||||||
void init(size_t buffer, int layout_buffer_length);
|
|
||||||
void draw_string(char const * string);
|
|
||||||
};
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
size_t mem_size();
|
size_t mem_size();
|
||||||
size_t mem_alloc(size_t length);
|
size_t mem_alloc(size_t length);
|
||||||
|
|||||||
11
src/new.h
11
src/new.h
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T * New(int count = 1)
|
|
||||||
{
|
|
||||||
size_t size = size_t(count) * (sizeof (T));
|
|
||||||
T * ptr = reinterpret_cast<T *>(mem_alloc(size));
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
#include "new.h"
|
|
||||||
#include "bone_system.h"
|
|
||||||
|
|
||||||
using namespace snake;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void logStrInt(char const * s, int a);
|
|
||||||
|
|
||||||
BoneSystem * bone_system__create(int boneCount, float boneLength)
|
|
||||||
{
|
|
||||||
BoneSystem * system = New<BoneSystem>();
|
|
||||||
system->init(boneCount, boneLength);
|
|
||||||
|
|
||||||
return system;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bone_system__bones_size(int boneCount)
|
|
||||||
{
|
|
||||||
return boneCount * (sizeof (Bone));
|
|
||||||
}
|
|
||||||
|
|
||||||
int bone_system__configuration_size()
|
|
||||||
{
|
|
||||||
return (sizeof (BoneConfiguration));
|
|
||||||
}
|
|
||||||
|
|
||||||
int bone_system__configuration_offset()
|
|
||||||
{
|
|
||||||
return (offsetof (BoneSystem, configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
void bone_system__update(BoneSystem * bone_system, float x, float y, float z)
|
|
||||||
{
|
|
||||||
bone_system->update(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bone_system__update_configuration(BoneSystem * bone_system,
|
|
||||||
int boneCount,
|
|
||||||
float boneLength,
|
|
||||||
float velocityDamping,
|
|
||||||
float velocityAcceleration)
|
|
||||||
{
|
|
||||||
bone_system->configuration.boneCount = boneCount;
|
|
||||||
bone_system->configuration.boneLength = boneLength;
|
|
||||||
bone_system->configuration.velocityDamping = velocityDamping;
|
|
||||||
bone_system->configuration.velocityAcceleration = velocityAcceleration;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#include "bone.h"
|
|
||||||
|
|
||||||
namespace snake {
|
|
||||||
extern "C" void logStrInt(char const * s, float a);
|
|
||||||
|
|
||||||
// assumes ray does intersect with sphere
|
|
||||||
static inline XMVECTOR intersectRaySphere(XMVECTOR rayPosition, XMVECTOR rayDirection,
|
|
||||||
XMVECTOR sphereCenter, float sphereRadius)
|
|
||||||
{
|
|
||||||
XMVECTOR m = rayPosition - sphereCenter;
|
|
||||||
float b = XMVectorGetX(XMVector3Dot(m, rayDirection));
|
|
||||||
float c = XMVectorGetX(XMVector3Dot(m, m)) - sphereRadius * sphereRadius;
|
|
||||||
float discriminant = b * b - c;
|
|
||||||
float t = -b - sqrtf(discriminant);
|
|
||||||
return rayPosition + t * rayDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bone::update(Bone const * parent, BoneConfiguration const * configuration)
|
|
||||||
{
|
|
||||||
XMVECTOR parentPosition = XMLoadFloat4(&parent->position);
|
|
||||||
XMVECTOR position = XMLoadFloat4(&this->position);
|
|
||||||
XMVECTOR velocity = XMLoadFloat4(&this->velocity);
|
|
||||||
|
|
||||||
|
|
||||||
position += velocity;
|
|
||||||
velocity *= configuration->velocityDamping;
|
|
||||||
|
|
||||||
|
|
||||||
XMVECTOR direction = parentPosition - position;
|
|
||||||
XMVECTOR normal = XMVector3Normalize(direction);
|
|
||||||
|
|
||||||
XMVECTOR newPosition = intersectRaySphere(position, normal,
|
|
||||||
parentPosition, configuration->boneLength);
|
|
||||||
|
|
||||||
XMVECTOR force = newPosition - position;
|
|
||||||
velocity += force * configuration->velocityAcceleration;
|
|
||||||
|
|
||||||
XMStoreFloat4(&this->position, newPosition);
|
|
||||||
XMStoreFloat4(&this->velocity, velocity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "directxmath/DirectXMath.h"
|
|
||||||
#include "bone_configuration.h"
|
|
||||||
|
|
||||||
namespace snake {
|
|
||||||
struct Bone {
|
|
||||||
XMFLOAT4 position;
|
|
||||||
XMFLOAT4 velocity;
|
|
||||||
|
|
||||||
void update(Bone const * parent, BoneConfiguration const * configuration);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace snake {
|
|
||||||
struct BoneConfiguration {
|
|
||||||
float boneCount;
|
|
||||||
float boneLength;
|
|
||||||
float velocityDamping;
|
|
||||||
float velocityAcceleration;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
#include "new.h"
|
|
||||||
#include "bone_system.h"
|
|
||||||
|
|
||||||
namespace snake {
|
|
||||||
void BoneSystem::init(int maxBones, float boneLength)
|
|
||||||
{
|
|
||||||
this->maxBones = maxBones;
|
|
||||||
|
|
||||||
configuration.boneCount = maxBones;
|
|
||||||
configuration.boneLength = boneLength;
|
|
||||||
configuration.velocityDamping = 0.0;
|
|
||||||
configuration.velocityAcceleration = 0.0;
|
|
||||||
|
|
||||||
bones = New<Bone>(maxBones);
|
|
||||||
|
|
||||||
XMVECTOR direction = XMVectorSet(0, 1, 0, 0);
|
|
||||||
for (int i = 0; i < maxBones; i++) {
|
|
||||||
XMStoreFloat4(&bones[i].position, direction * boneLength * i);
|
|
||||||
XMStoreFloat4(&bones[i].velocity, XMVectorZero());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoneSystem::update(float x, float y, float z)
|
|
||||||
{
|
|
||||||
bones[0].position.x = x;
|
|
||||||
bones[0].position.y = y;
|
|
||||||
bones[0].position.z = z;
|
|
||||||
|
|
||||||
for (int i = 1; i < configuration.boneCount; i++) {
|
|
||||||
Bone const * parent = &bones[i - 1];
|
|
||||||
Bone * bone = &bones[i];
|
|
||||||
bone->update(parent, &configuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "bone.h"
|
|
||||||
#include "bone_configuration.h"
|
|
||||||
|
|
||||||
namespace snake {
|
|
||||||
struct BoneSystem {
|
|
||||||
Bone * bones;
|
|
||||||
int maxBones;
|
|
||||||
BoneConfiguration configuration;
|
|
||||||
|
|
||||||
void init(int maxBones, float boneLength);
|
|
||||||
void update(float x, float y, float z);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
namespace snake {
|
|
||||||
struct BoneConfiguration {
|
|
||||||
float boneLength;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
CFLAGS = -Og -g -gdwarf-4 -Wall -Wextra -Wno-error -Wfatal-errors
|
|
||||||
CFLAGS += -Wno-error=unused-parameter
|
|
||||||
CFLAGS += -Wno-error=unused-variable
|
|
||||||
CFLAGS += -Wno-error=unused-but-set-variable
|
|
||||||
CFLAGS += -I../../include
|
|
||||||
|
|
||||||
CXXFLAGS = -std=c++23
|
|
||||||
|
|
||||||
FREETYPE_CFLAGS = $(shell pkg-config --cflags freetype2)
|
|
||||||
FREETYPE_LDFLAGS = $(shell pkg-config --libs freetype2)
|
|
||||||
|
|
||||||
%.o: %.cpp
|
|
||||||
$(CXX) $(CFLAGS) $(CXXFLAGS) $(FREETYPE_CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
ttf_outline: ttf_outline.o ttf_2d_pack.o
|
|
||||||
$(CXX) $(LDFLAGS) $(FREETYPE_LDFLAGS) $^ -o $@
|
|
||||||
@ -1,159 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "ttf_2d_pack.h"
|
|
||||||
|
|
||||||
struct size {
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr struct size max_texture = {1024, 1024};
|
|
||||||
|
|
||||||
inline bool area_valid(const uint8_t texture[max_texture.height][max_texture.width],
|
|
||||||
const uint32_t x_offset,
|
|
||||||
const uint32_t y_offset,
|
|
||||||
const struct rect& rect,
|
|
||||||
const struct size& window)
|
|
||||||
{
|
|
||||||
for (uint32_t yi = 0; yi < rect.height; yi++) {
|
|
||||||
for (uint32_t xi = 0; xi < rect.width; xi++) {
|
|
||||||
uint32_t x = x_offset + xi;
|
|
||||||
uint32_t y = y_offset + yi;
|
|
||||||
|
|
||||||
if (texture[y][x] != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (x >= window.width || y >= window.height)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline std::array<uint32_t, 2>
|
|
||||||
from_ix(uint32_t curve_ix)
|
|
||||||
{
|
|
||||||
std::array<uint32_t, 2> x_y = {0, 0};
|
|
||||||
uint32_t curve_bit = 0;
|
|
||||||
|
|
||||||
while (curve_ix != 0) {
|
|
||||||
x_y[(curve_bit + 1) % 2] |= (curve_ix & 1) << (curve_bit / 2);
|
|
||||||
curve_ix >>= 1;
|
|
||||||
curve_bit += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return x_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert(from_ix(0) == std::array<uint32_t, 2>{{0b000, 0b000}});
|
|
||||||
static_assert(from_ix(2) == std::array<uint32_t, 2>{{0b001, 0b000}});
|
|
||||||
static_assert(from_ix(8) == std::array<uint32_t, 2>{{0b010, 0b000}});
|
|
||||||
static_assert(from_ix(10) == std::array<uint32_t, 2>{{0b011, 0b000}});
|
|
||||||
static_assert(from_ix(32) == std::array<uint32_t, 2>{{0b100, 0b000}});
|
|
||||||
static_assert(from_ix(34) == std::array<uint32_t, 2>{{0b101, 0b000}});
|
|
||||||
static_assert(from_ix(40) == std::array<uint32_t, 2>{{0b110, 0b000}});
|
|
||||||
static_assert(from_ix(42) == std::array<uint32_t, 2>{{0b111, 0b000}});
|
|
||||||
|
|
||||||
static_assert(from_ix(1) == std::array<uint32_t, 2>{{0b000, 0b001}});
|
|
||||||
static_assert(from_ix(4) == std::array<uint32_t, 2>{{0b000, 0b010}});
|
|
||||||
static_assert(from_ix(5) == std::array<uint32_t, 2>{{0b000, 0b011}});
|
|
||||||
static_assert(from_ix(16) == std::array<uint32_t, 2>{{0b000, 0b100}});
|
|
||||||
static_assert(from_ix(17) == std::array<uint32_t, 2>{{0b000, 0b101}});
|
|
||||||
static_assert(from_ix(20) == std::array<uint32_t, 2>{{0b000, 0b110}});
|
|
||||||
static_assert(from_ix(21) == std::array<uint32_t, 2>{{0b000, 0b111}});
|
|
||||||
|
|
||||||
constexpr inline int log2(uint32_t n)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
default:
|
|
||||||
case 8: return 3;
|
|
||||||
case 16: return 4;
|
|
||||||
case 32: return 5;
|
|
||||||
case 64: return 6;
|
|
||||||
case 128: return 7;
|
|
||||||
case 256: return 8;
|
|
||||||
case 512: return 9;
|
|
||||||
case 1024: return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pack_into(uint8_t texture[max_texture.height][max_texture.width],
|
|
||||||
size & window,
|
|
||||||
rect & rect)
|
|
||||||
{
|
|
||||||
uint32_t z_curve_ix = 0;
|
|
||||||
|
|
||||||
if (rect.width == 0 || rect.height == 0) {
|
|
||||||
rect.x = 0;
|
|
||||||
rect.y = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto [x_offset, y_offset] = from_ix(z_curve_ix);
|
|
||||||
|
|
||||||
if (x_offset >= window.width and y_offset >= window.height) {
|
|
||||||
assert(window.width < max_texture.width || window.height < max_texture.height);
|
|
||||||
if (window.width == window.height) { window.height *= 2; }
|
|
||||||
else { window.width *= 2; }
|
|
||||||
|
|
||||||
// when the window changes; start again from the beginning and
|
|
||||||
// re-check earlier locations that might have been skipped due
|
|
||||||
// to window size
|
|
||||||
z_curve_ix = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (area_valid(texture, x_offset, y_offset, rect, window)) {
|
|
||||||
for (uint32_t yi = 0; yi < rect.height; yi++) {
|
|
||||||
for (uint32_t xi = 0; xi < rect.width; xi++) {
|
|
||||||
uint32_t x = x_offset + xi;
|
|
||||||
uint32_t y = y_offset + yi;
|
|
||||||
|
|
||||||
texture[y][x] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.x = x_offset;
|
|
||||||
rect.y = y_offset;
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
z_curve_ix += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void insertion_sort(T * arr, int len)
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
while (i < len) {
|
|
||||||
int j = i;
|
|
||||||
while (j > 0 && arr[j - 1] < arr[j]) {
|
|
||||||
std::swap(arr[j - 1], arr[j]);
|
|
||||||
j -= 1;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window_curve_ix pack_all(struct rect * rects, const uint32_t num_rects)
|
|
||||||
{
|
|
||||||
uint8_t texture[max_texture.height][max_texture.width] = { 0 };
|
|
||||||
size window = {1, 1};
|
|
||||||
|
|
||||||
// sort all rectangles by size
|
|
||||||
insertion_sort(rects, num_rects);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < num_rects; i++) {
|
|
||||||
pack_into(texture, window, rects[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {window.width, window.height};
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
struct window_curve_ix {
|
|
||||||
struct {
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
} window;
|
|
||||||
};
|
|
||||||
|
|
||||||
window_curve_ix pack_all(struct rect * rects, const uint32_t num_rects);
|
|
||||||
|
|
||||||
struct rect {
|
|
||||||
uint32_t char_code;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
int32_t x;
|
|
||||||
int32_t y;
|
|
||||||
|
|
||||||
std::strong_ordering operator<=>(const rect& b) const
|
|
||||||
{
|
|
||||||
return (width * height) <=> (b.width * b.height);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,299 +0,0 @@
|
|||||||
#include <bit>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <ft2build.h>
|
|
||||||
#include FT_FREETYPE_H
|
|
||||||
|
|
||||||
#include "font.h"
|
|
||||||
#include "ttf_2d_pack.h"
|
|
||||||
|
|
||||||
std::endian _target_endian;
|
|
||||||
|
|
||||||
constexpr uint32_t max_texture_dim = 1024;
|
|
||||||
constexpr uint32_t max_texture_size = max_texture_dim * max_texture_dim;
|
|
||||||
|
|
||||||
template< class T >
|
|
||||||
constexpr T byteswap(const T n)
|
|
||||||
{
|
|
||||||
if (std::endian::native != _target_endian) {
|
|
||||||
return std::byteswap<T>(n);
|
|
||||||
} else {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t
|
|
||||||
load_outline_char_bitmap_rect(const FT_Face face,
|
|
||||||
const FT_Int32 load_flags,
|
|
||||||
const FT_Render_Mode render_mode,
|
|
||||||
const FT_ULong char_code,
|
|
||||||
struct rect& rect)
|
|
||||||
{
|
|
||||||
FT_Error error;
|
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
|
|
||||||
|
|
||||||
error = FT_Load_Glyph(face, glyph_index, load_flags);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = FT_Render_Glyph(face->glyph, render_mode);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Render_Glyph " << FT_Error_String(error) << '\n';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.char_code = char_code;
|
|
||||||
rect.height = face->glyph->bitmap.rows;
|
|
||||||
rect.width = face->glyph->bitmap.width;
|
|
||||||
rect.x = -1;
|
|
||||||
rect.y = -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t
|
|
||||||
load_outline_char(const FT_Face face,
|
|
||||||
const FT_Int32 load_flags,
|
|
||||||
const FT_Render_Mode render_mode,
|
|
||||||
const uint32_t bits_per_pixel,
|
|
||||||
const FT_ULong char_code,
|
|
||||||
glyph * glyph,
|
|
||||||
uint8_t * texture,
|
|
||||||
uint32_t texture_width,
|
|
||||||
struct rect& rect)
|
|
||||||
{
|
|
||||||
FT_Error error;
|
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
|
|
||||||
|
|
||||||
error = FT_Load_Glyph(face, glyph_index, load_flags);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//std::cerr << "size " << face->glyph->bitmap.rows << ' ' << face->glyph->bitmap.width << '\n';
|
|
||||||
|
|
||||||
//assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE);
|
|
||||||
|
|
||||||
error = FT_Render_Glyph(face->glyph, render_mode);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Render_Glyph " << FT_Error_String(error) << '\n';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(face->glyph->bitmap.pitch > 0)) {
|
|
||||||
assert(face->glyph->bitmap.width == 0);
|
|
||||||
assert(face->glyph->bitmap.rows == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(face->glyph->bitmap.width == rect.width);
|
|
||||||
assert(face->glyph->bitmap.rows == rect.height);
|
|
||||||
|
|
||||||
assert(bits_per_pixel == 8 || bits_per_pixel == 4 || bits_per_pixel == 2 || bits_per_pixel == 1);
|
|
||||||
const uint32_t pixels_per_byte = 8 / bits_per_pixel;
|
|
||||||
const uint32_t texture_stride = texture_width / pixels_per_byte;
|
|
||||||
//std::cerr << "pixels per byte: " << pixels_per_byte << '\n';
|
|
||||||
//std::cerr << "texture stride: " << texture_stride << '\n';
|
|
||||||
|
|
||||||
for (uint32_t y = 0; y < rect.height; y++) {
|
|
||||||
for (uint32_t x = 0; x < rect.width; x++) {
|
|
||||||
const uint32_t texture_ix = (rect.y + y) * texture_stride + (rect.x + x) / pixels_per_byte;
|
|
||||||
const uint32_t texture_ix_mod = (rect.x + x) % pixels_per_byte;
|
|
||||||
assert(texture_ix < max_texture_size);
|
|
||||||
|
|
||||||
uint8_t level;
|
|
||||||
|
|
||||||
//std::cerr << "rxy " << rect.x << ' ' << rect.y << '\n';
|
|
||||||
//std::cerr << "rwh " << rect.width << ' ' << rect.height << '\n';
|
|
||||||
|
|
||||||
//std::cerr << "pixel_mode " << (int)face->glyph->bitmap.pixel_mode << '\n';
|
|
||||||
switch (face->glyph->bitmap.pixel_mode) {
|
|
||||||
case FT_PIXEL_MODE_MONO:
|
|
||||||
// [num_grays] is only used with FT_PIXEL_MODE_GRAY; it gives the number
|
|
||||||
// of gray levels used in the bitmap.
|
|
||||||
level = (face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + (x / 8)] >> (7 - (x % 8))) & 1;
|
|
||||||
break;
|
|
||||||
case FT_PIXEL_MODE_GRAY:
|
|
||||||
//std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n';
|
|
||||||
//assert(face->glyph->bitmap.num_grays == 256);
|
|
||||||
level = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x];
|
|
||||||
level >>= (8 - bits_per_pixel);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
texture[texture_ix] |= level << (bits_per_pixel * texture_ix_mod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glyph_bitmap& bitmap = glyph->bitmap;
|
|
||||||
bitmap.x = byteswap<uint16_t>(rect.x);
|
|
||||||
bitmap.y = byteswap<uint16_t>(rect.y);
|
|
||||||
bitmap.width = byteswap<uint16_t>(rect.width);
|
|
||||||
bitmap.height = byteswap<uint16_t>(rect.height);
|
|
||||||
|
|
||||||
glyph_metrics& metrics = glyph->metrics;
|
|
||||||
metrics.horiBearingX = byteswap<int32_t>(face->glyph->metrics.horiBearingX);
|
|
||||||
metrics.horiBearingY = byteswap<int32_t>(face->glyph->metrics.horiBearingY);
|
|
||||||
metrics.horiAdvance = byteswap<int32_t>(face->glyph->metrics.horiAdvance);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum {
|
|
||||||
start_hex = 1,
|
|
||||||
end_hex = 2,
|
|
||||||
pixel_size = 3,
|
|
||||||
target_endian = 4,
|
|
||||||
font_file_path = 5,
|
|
||||||
output_file_path = 6,
|
|
||||||
argv_length = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
struct window_curve_ix
|
|
||||||
load_all_positions(const FT_Face face,
|
|
||||||
const uint32_t start,
|
|
||||||
const uint32_t end,
|
|
||||||
glyph * glyphs,
|
|
||||||
uint8_t * texture
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const uint32_t num_glyphs = (end - start) + 1;
|
|
||||||
struct rect rects[num_glyphs];
|
|
||||||
|
|
||||||
FT_Int32 load_flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_MODE(FT_RENDER_MODE_SDF) | FT_LOAD_RENDER;
|
|
||||||
FT_Render_Mode render_mode = FT_RENDER_MODE_SDF;
|
|
||||||
|
|
||||||
// first, load all rectangles
|
|
||||||
for (uint32_t char_code = start; char_code <= end; char_code++) {
|
|
||||||
load_outline_char_bitmap_rect(face,
|
|
||||||
load_flags,
|
|
||||||
render_mode,
|
|
||||||
char_code,
|
|
||||||
rects[char_code - start]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate a 2-dimensional packing for the rectangles
|
|
||||||
auto window_curve_ix = pack_all(rects, num_glyphs);
|
|
||||||
|
|
||||||
const uint32_t bits_per_pixel = 8;
|
|
||||||
|
|
||||||
// render all of the glyphs to the texture;
|
|
||||||
for (uint32_t i = 0; i < num_glyphs; i++) {
|
|
||||||
const uint32_t char_code = rects[i].char_code;
|
|
||||||
int32_t err = load_outline_char(face,
|
|
||||||
load_flags,
|
|
||||||
render_mode,
|
|
||||||
bits_per_pixel,
|
|
||||||
char_code,
|
|
||||||
&glyphs[char_code - start],
|
|
||||||
texture,
|
|
||||||
window_curve_ix.window.width,
|
|
||||||
rects[i]);
|
|
||||||
if (err < 0) assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return window_curve_ix;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
FT_Library library;
|
|
||||||
FT_Face face;
|
|
||||||
FT_Error error;
|
|
||||||
|
|
||||||
if (argc != argv_length) {
|
|
||||||
std::cerr << "usage: " << argv[0] << " [start-hex] [end-hex] [pixel-size] [target-endian] [font-file-path] [output-file-path]\n\n";
|
|
||||||
std::cerr << "ex. 1: " << argv[0] << " 3000 30ff 30 0 little ipagp.ttf font.bin\n";
|
|
||||||
std::cerr << "ex. 2: " << argv[0] << " 20 7f 30 1 big DejaVuSans.ttf font.bin\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = FT_Init_FreeType(&library);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Init_FreeType\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = FT_New_Face(library, argv[font_file_path], 0, &face);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_New_Face\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss3;
|
|
||||||
int font_size;
|
|
||||||
ss3 << std::dec << argv[pixel_size];
|
|
||||||
ss3 >> font_size;
|
|
||||||
std::cerr << "font_size: " << font_size << '\n';
|
|
||||||
std::stringstream ss4;
|
|
||||||
|
|
||||||
error = FT_Set_Pixel_Sizes(face, 0, font_size);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "FT_Set_Pixel_Sizes: " << FT_Error_String(error) << error << '\n';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::string(argv[target_endian]).compare("little") == 0) {
|
|
||||||
_target_endian = std::endian::little;
|
|
||||||
} else if (std::string(argv[target_endian]).compare("big") == 0) {
|
|
||||||
_target_endian = std::endian::big;
|
|
||||||
} else {
|
|
||||||
std::cerr << "unknown endian: " << argv[target_endian] << '\n';
|
|
||||||
std::cerr << "expected one of: big, little\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t end;
|
|
||||||
|
|
||||||
std::stringstream ss1;
|
|
||||||
ss1 << std::hex << argv[start_hex];
|
|
||||||
ss1 >> start;
|
|
||||||
std::stringstream ss2;
|
|
||||||
ss2 << std::hex << argv[end_hex];
|
|
||||||
ss2 >> end;
|
|
||||||
|
|
||||||
uint32_t num_glyphs = (end - start) + 1;
|
|
||||||
glyph glyphs[num_glyphs];
|
|
||||||
uint8_t texture[max_texture_size];
|
|
||||||
memset(texture, 0x00, max_texture_size);
|
|
||||||
|
|
||||||
auto window_curve_ix = load_all_positions(face, start, end, glyphs, texture);
|
|
||||||
|
|
||||||
uint32_t texture_size;
|
|
||||||
texture_size = window_curve_ix.window.width * window_curve_ix.window.height;
|
|
||||||
|
|
||||||
font font;
|
|
||||||
font.first_char_code = byteswap<uint32_t>(start);
|
|
||||||
font.last_char_code = byteswap<uint32_t>(end);
|
|
||||||
font.face_metrics.height = byteswap<int32_t>(face->size->metrics.height);
|
|
||||||
font.face_metrics.max_advance = byteswap<int32_t>(face->size->metrics.max_advance);
|
|
||||||
font.glyph_count = byteswap<uint16_t>(num_glyphs);
|
|
||||||
font.texture_width = byteswap<uint16_t>(window_curve_ix.window.width);
|
|
||||||
font.texture_height = byteswap<uint16_t>(window_curve_ix.window.height);
|
|
||||||
|
|
||||||
std::cerr << "start: 0x" << std::hex << start << '\n';
|
|
||||||
std::cerr << "end: 0x" << std::hex << end << '\n';
|
|
||||||
std::cerr << "texture_width: " << std::dec << window_curve_ix.window.width << '\n';
|
|
||||||
std::cerr << "texture_height: " << std::dec << window_curve_ix.window.height << '\n';
|
|
||||||
|
|
||||||
FILE * out = fopen(argv[output_file_path], "w");
|
|
||||||
if (out == NULL) {
|
|
||||||
perror("fopen(w)");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite(reinterpret_cast<void*>(&font), (sizeof (font)), 1, out);
|
|
||||||
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out);
|
|
||||||
//fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
|
|
||||||
|
|
||||||
fclose(out);
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user