Initial commit

This commit is contained in:
Diego Marcos
2025-05-23 08:52:14 -07:00
committed by Diego Marcos Segura
commit 3332f609a9
129 changed files with 64575 additions and 0 deletions
+575
View File
@@ -0,0 +1,575 @@
import type { IUniform } from "three";
import type { DynoType } from "./types";
import {
DynoLiteral,
DynoOutput,
type DynoVal,
DynoValue,
type HasDynoOut,
valType,
} from "./value";
const DEFAULT_INDENT = " ";
export class Compilation {
globals: Set<string> = new Set();
statements: string[] = [];
uniforms: Record<string, IUniform> = {};
declares: Set<string> = new Set();
updaters: (() => void)[] = [];
sequence = 0;
indent: string = DEFAULT_INDENT;
constructor({ indent }: { indent?: string } = {}) {
this.indent = indent ?? DEFAULT_INDENT;
}
nextSequence() {
return this.sequence++;
}
}
export type IOTypes = Record<string, DynoType>;
type GenerateContext<InTypes extends IOTypes, OutTypes extends IOTypes> = {
inputs: { [K in keyof InTypes]?: string };
outputs: { [K in keyof OutTypes]?: string };
compile: Compilation;
};
export class Dyno<InTypes extends IOTypes, OutTypes extends IOTypes> {
inTypes: InTypes;
outTypes: OutTypes;
inputs: { [K in keyof InTypes]?: DynoVal<InTypes[K]> };
update?: () => void;
globals?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
statements?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
generate: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => {
globals?: string[];
statements?: string[];
uniforms?: Record<string, IUniform>;
};
constructor({
inTypes,
outTypes,
inputs,
update,
globals,
statements,
generate,
}: {
inTypes?: InTypes;
outTypes?: OutTypes;
inputs?: { [K in keyof InTypes]?: DynoVal<InTypes[K]> };
update?: () => void;
globals?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
statements?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
generate?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => {
globals?: string[];
statements?: string[];
uniforms?: Record<string, IUniform>;
};
}) {
this.inTypes = inTypes ?? ({} as InTypes);
this.outTypes = outTypes ?? ({} as OutTypes);
this.inputs = inputs ?? {};
this.update = update;
this.globals = globals;
this.statements = statements;
this.generate =
generate ??
(({ inputs, outputs, compile }) => {
return {
globals: this.globals?.({ inputs, outputs, compile }),
statements: this.statements?.({ inputs, outputs, compile }),
};
});
}
get outputs(): { [K in keyof OutTypes]: DynoVal<OutTypes[K]> } {
const outputs = {} as { [K in keyof OutTypes]: DynoVal<OutTypes[K]> };
for (const key in this.outTypes) {
outputs[key] = new DynoOutput(this, key);
}
return outputs;
}
apply(inputs: { [K in keyof InTypes]?: DynoVal<InTypes[K]> }): {
[K in keyof OutTypes]: DynoVal<OutTypes[K]>;
} {
Object.assign(this.inputs, inputs);
return this.outputs;
}
compile({
inputs,
outputs,
compile,
}: {
inputs: { [K in keyof InTypes]?: string };
outputs: { [K in keyof OutTypes]?: string };
compile: Compilation;
}): string[] {
const result = [
`// ${this.constructor.name}(${Object.values(inputs).join(", ")}) => (${Object.values(outputs).join(", ")})`,
];
const declares: (keyof OutTypes)[] = [];
for (const key in outputs) {
const name = outputs[key];
if (name && !compile.declares.has(name)) {
compile.declares.add(name);
declares.push(key);
}
}
const { globals, statements, uniforms } = this.generate({
inputs,
outputs,
compile,
});
for (const global of globals ?? []) {
compile.globals.add(global);
}
for (const key in uniforms) {
compile.uniforms[key] = uniforms[key];
}
if (this.update) {
compile.updaters.push(this.update);
}
for (const key of declares) {
const name = outputs[key];
if (name) {
if (!compile.uniforms[name]) {
result.push(`${dynoDeclare(name, this.outTypes[key])};`);
}
}
}
if (statements?.length) {
result.push("{");
result.push(...statements.map((line) => compile.indent + line));
result.push("}");
}
return result;
}
}
export type DynoBlockType<InTypes extends IOTypes, OutTypes extends IOTypes> = (
inputs: { [K in keyof InTypes]?: DynoVal<InTypes[K]> },
outputs: { [K in keyof OutTypes]?: DynoVal<OutTypes[K]> },
{ roots }: { roots: Dyno<InTypes, OutTypes>[] },
) => { [K in keyof OutTypes]?: DynoVal<OutTypes[K]> } | undefined;
export class DynoBlock<
InTypes extends IOTypes,
OutTypes extends IOTypes,
> extends Dyno<InTypes, OutTypes> {
construct: DynoBlockType<InTypes, OutTypes>;
constructor({
inTypes,
outTypes,
inputs,
update,
globals,
construct,
}: {
inTypes?: InTypes;
outTypes?: OutTypes;
inputs?: { [K in keyof InTypes]?: DynoVal<InTypes[K]> };
update?: () => void;
globals?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
construct: DynoBlockType<InTypes, OutTypes>;
}) {
super({
inTypes,
outTypes,
inputs,
update,
globals,
generate: (args) => this.generateBlock(args),
});
this.construct = construct;
}
generateBlock({
inputs,
outputs,
compile,
}: {
inputs: { [K in keyof InTypes]?: string };
outputs: { [K in keyof OutTypes]?: string };
compile: Compilation;
}) {
const blockInputs: { [K in keyof InTypes]?: DynoVal<InTypes[K]> } = {};
const blockOutputs: { [K in keyof OutTypes]?: DynoVal<OutTypes[K]> } = {};
for (const key in inputs) {
if (inputs[key] != null) {
blockInputs[key] = new DynoLiteral(this.inTypes[key], inputs[key]);
}
}
for (const key in outputs) {
if (outputs[key] != null) {
blockOutputs[key] = new DynoValue(this.outTypes[key]);
}
}
const options = { roots: [] };
const returned = this.construct(blockInputs, blockOutputs, options);
for (const global of this.globals?.({ inputs, outputs, compile }) ?? []) {
compile.globals.add(global);
}
const ordering: Dyno<IOTypes, IOTypes>[] = [];
const nodeOuts = new Map<
Dyno<IOTypes, IOTypes>,
{ sequence: number; outNames: Map<string, string>; newOuts: Set<string> }
>();
function visit(
node: Dyno<IOTypes, IOTypes>,
outKey?: string,
outName?: string,
) {
let outs = nodeOuts.get(node);
if (!outs) {
// First time visiting this node
outs = {
sequence: compile.nextSequence(),
outNames: new Map(),
newOuts: new Set(),
};
nodeOuts.set(node, outs);
for (const key in node.inputs) {
let input = node.inputs[key];
while (input) {
if (input instanceof DynoValue) {
if (input instanceof DynoOutput) {
visit(input.dyno, input.key);
}
break;
}
// Must be as HasDynoOut<T>
input = input.dynoOut();
}
}
ordering.push(node);
}
if (outKey) {
if (!outName) {
outs.newOuts.add(outKey);
}
outs.outNames.set(outKey, outName ?? `${outKey}_${outs.sequence}`);
}
}
for (const root of options.roots) {
visit(root);
}
for (const key in blockOutputs) {
let value = returned?.[key] ?? blockOutputs[key];
while (value) {
if (value instanceof DynoValue) {
if (value instanceof DynoOutput) {
visit(value.dyno, value.key, outputs[key]);
}
break;
}
// Must be as HasDynoOut<T>
value = value.dynoOut();
}
blockOutputs[key] = value;
}
const steps = [];
for (const dyno of ordering) {
// compile.statements.push(`// ${dyno.constructor.name}(${Object.values(inputs).join(", ")}) => (${Object.values(outputs).join(", ")})`);
const inputs: Record<string, string> = {};
const outputs: Record<string, string> = {};
for (const key in dyno.inputs) {
let value = dyno.inputs[key];
while (value) {
if (value instanceof DynoValue) {
if (value instanceof DynoLiteral) {
inputs[key] = value.getLiteral();
} else if (value instanceof DynoOutput) {
const source = nodeOuts.get(value.dyno)?.outNames.get(value.key);
if (!source) {
throw new Error(
`Source not found for ${value.dyno.constructor.name}.${value.key}`,
);
}
inputs[key] = source;
}
break;
}
// Must be as HasDynOut<T>
value = value.dynoOut();
}
}
const outs = nodeOuts.get(dyno) ?? { outNames: new Map() };
for (const [key, name] of outs.outNames.entries()) {
outputs[key] = name;
}
const newSteps = dyno.compile({ inputs, outputs, compile });
steps.push(newSteps);
}
const literalOutputs = [];
for (const key in outputs) {
if (blockOutputs[key] instanceof DynoLiteral) {
literalOutputs.push(
`${outputs[key]} = ${blockOutputs[key].getLiteral()};`,
);
}
}
if (literalOutputs.length > 0) {
steps.push(literalOutputs);
}
const statements = steps.flatMap((step, index) => {
// Add a blank line between steps
return index === 0 ? step : ["", ...step];
});
return { statements };
}
}
export function dynoBlock<
InTypes extends Record<string, DynoType>,
OutTypes extends Record<string, DynoType>,
>(
inTypes: InTypes,
outTypes: OutTypes,
construct: DynoBlockType<InTypes, OutTypes>,
{ update, globals }: { update?: () => void; globals?: () => string[] } = {},
) {
return new DynoBlock({ inTypes, outTypes, construct, update, globals });
}
export function dyno<
InTypes extends Record<string, DynoType>,
OutTypes extends Record<string, DynoType>,
>({
inTypes,
outTypes,
inputs,
update,
globals,
statements,
generate,
}: {
inTypes: InTypes;
outTypes: OutTypes;
inputs?: { [K in keyof InTypes]?: DynoVal<InTypes[K]> };
update?: () => void;
globals?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
statements?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => string[];
generate?: ({
inputs,
outputs,
compile,
}: GenerateContext<InTypes, OutTypes>) => {
globals?: string[];
statements?: string[];
uniforms?: Record<string, IUniform>;
};
}) {
return new Dyno({
inTypes,
outTypes,
inputs,
update,
globals,
statements,
generate,
});
}
export function dynoDeclare(name: string, type: DynoType, count?: number) {
const typeStr = typeof type === "string" ? type : type.type;
if (!typeStr) {
throw new Error(`Invalid DynoType: ${String(type)}`);
}
return `${typeStr} ${name}${count != null ? `[${count}]` : ""}`;
}
export function unindentLines(s: string): string[] {
let seenNonEmpty = false;
const lines = s
.split("\n")
.map((line) => {
const trimmedLine = line.trimEnd();
if (seenNonEmpty) {
return trimmedLine;
}
if (trimmedLine.length > 0) {
seenNonEmpty = true;
return trimmedLine;
}
return null;
})
.filter((line) => line != null);
while (lines.length > 0 && lines[lines.length - 1].length === 0) {
lines.pop();
}
if (lines.length === 0) {
return [];
}
const indent = lines[0].match(/^\s*/)?.[0];
if (!indent) {
return lines; // No indent, return as is
}
// Remove indent from the beginning of each line
const regex = new RegExp(`^${indent}`);
return lines.map((line) => line.replace(regex, ""));
}
export function unindent(s: string): string {
return unindentLines(s).join("\n");
}
export class UnaryOp<
A extends DynoType,
OutType extends DynoType,
OutKey extends string,
>
extends Dyno<{ a: A }, { [key in OutKey]: OutType }>
implements HasDynoOut<OutType>
{
constructor({
a,
outKey,
outTypeFunc,
}: { a: DynoVal<A>; outKey: OutKey; outTypeFunc: (aType: A) => OutType }) {
const inTypes = { a: valType(a) };
const outType = outTypeFunc(valType(a));
const outTypes = { [outKey]: outType } as { [key in OutKey]: OutType };
super({ inTypes, outTypes, inputs: { a } });
this.outKey = outKey;
}
outKey: OutKey;
dynoOut(): DynoValue<OutType> {
return new DynoOutput(this, this.outKey);
}
}
export class BinaryOp<
A extends DynoType,
B extends DynoType,
OutType extends DynoType,
OutKey extends string,
>
extends Dyno<{ a: A; b: B }, { [key in OutKey]: OutType }>
implements HasDynoOut<OutType>
{
constructor({
a,
b,
outKey,
outTypeFunc,
}: {
a: DynoVal<A>;
b: DynoVal<B>;
outKey: OutKey;
outTypeFunc: (aType: A, bType: B) => OutType;
}) {
const inTypes = { a: valType(a), b: valType(b) };
const outType = outTypeFunc(valType(a), valType(b));
const outTypes = { [outKey]: outType } as { [key in OutKey]: OutType };
super({ inTypes, outTypes, inputs: { a, b } });
this.outKey = outKey;
}
outKey: OutKey;
dynoOut(): DynoValue<OutType> {
return new DynoOutput(this, this.outKey);
}
}
export class TrinaryOp<
A extends DynoType,
B extends DynoType,
C extends DynoType,
OutType extends DynoType,
OutKey extends string,
>
extends Dyno<{ a: A; b: B; c: C }, { [key in OutKey]: OutType }>
implements HasDynoOut<OutType>
{
constructor({
a,
b,
c,
outKey,
outTypeFunc,
}: {
a: DynoVal<A>;
b: DynoVal<B>;
c: DynoVal<C>;
outKey: OutKey;
outTypeFunc: (aType: A, bType: B, cType: C) => OutType;
}) {
const inTypes = { a: valType(a), b: valType(b), c: valType(c) };
const outType = outTypeFunc(valType(a), valType(b), valType(c));
const outTypes = { [outKey]: outType } as { [key in OutKey]: OutType };
super({ inTypes, outTypes, inputs: { a, b, c } });
this.outKey = outKey;
}
outKey: OutKey;
dynoOut(): DynoValue<OutType> {
return new DynoOutput(this, this.outKey);
}
}
+22
View File
@@ -0,0 +1,22 @@
// TODO:
// if, switch, for, comment,
// arrayIndex, arrayLength,
export const dynoIf = () => {
throw new Error("Not implemented");
};
export const dynoSwitch = () => {
throw new Error("Not implemented");
};
export const dynoFor = () => {
throw new Error("Not implemented");
};
export const comment = () => {
throw new Error("Not implemented");
};
export const arrayIndex = () => {
throw new Error("Not implemented");
};
export const arrayLength = () => {
throw new Error("Not implemented");
};
+451
View File
@@ -0,0 +1,451 @@
import { UnaryOp } from "./base";
import { type SimpleTypes, typeLiteral } from "./types";
import type { DynoVal } from "./value";
export const bool = <T extends "bool" | "int" | "uint" | "float">(
value: DynoVal<T>,
): DynoVal<"bool"> => new Bool({ value });
export const int = <T extends "bool" | "int" | "uint" | "float">(
value: DynoVal<T>,
): DynoVal<"int"> => new Int({ value });
export const uint = <T extends "bool" | "int" | "uint" | "float">(
value: DynoVal<T>,
): DynoVal<"uint"> => new Uint({ value });
export const float = <T extends "bool" | "int" | "uint" | "float">(
value: DynoVal<T>,
): DynoVal<"float"> => new Float({ value });
export const bvec2 = <T extends "bool" | "bvec2" | "ivec2" | "uvec2" | "vec2">(
value: DynoVal<T>,
): DynoVal<"bvec2"> => new BVec2({ value });
export const bvec3 = <T extends "bool" | "bvec3" | "ivec3" | "uvec3" | "vec3">(
value: DynoVal<T>,
): DynoVal<"bvec3"> => new BVec3({ value });
export const bvec4 = <T extends "bool" | "bvec4" | "ivec4" | "uvec4" | "vec4">(
value: DynoVal<T>,
): DynoVal<"bvec4"> => new BVec4({ value });
export const ivec2 = <T extends "int" | "bvec2" | "ivec2" | "uvec2" | "vec2">(
value: DynoVal<T>,
): DynoVal<"ivec2"> => new IVec2({ value });
export const ivec3 = <T extends "int" | "bvec3" | "ivec3" | "uvec3" | "vec3">(
value: DynoVal<T>,
): DynoVal<"ivec3"> => new IVec3({ value });
export const ivec4 = <T extends "int" | "bvec4" | "ivec4" | "uvec4" | "vec4">(
value: DynoVal<T>,
): DynoVal<"ivec4"> => new IVec4({ value });
export const uvec2 = <T extends "uint" | "bvec2" | "ivec2" | "uvec2" | "vec2">(
value: DynoVal<T>,
): DynoVal<"uvec2"> => new UVec2({ value });
export const uvec3 = <T extends "uint" | "bvec3" | "ivec3" | "uvec3" | "vec3">(
value: DynoVal<T>,
): DynoVal<"uvec3"> => new UVec3({ value });
export const uvec4 = <T extends "uint" | "bvec4" | "ivec4" | "uvec4" | "vec4">(
value: DynoVal<T>,
): DynoVal<"uvec4"> => new UVec4({ value });
export const vec2 = <
T extends "float" | "bvec2" | "ivec2" | "uvec2" | "vec2" | "vec3" | "vec4",
>(
value: DynoVal<T>,
): DynoVal<"vec2"> => new Vec2({ value });
export const vec3 = <
T extends "float" | "bvec3" | "ivec3" | "uvec3" | "vec3" | "vec4",
>(
value: DynoVal<T>,
): DynoVal<"vec3"> => new Vec3({ value });
export const vec4 = <T extends "float" | "bvec4" | "ivec4" | "uvec4" | "vec4">(
value: DynoVal<T>,
): DynoVal<"vec4"> => new Vec4({ value });
export const mat2 = <T extends "float" | "mat2" | "mat3" | "mat4">(
value: DynoVal<T>,
): DynoVal<"mat2"> => new Mat2({ value });
export const mat3 = <T extends "float" | "mat2" | "mat3" | "mat4">(
value: DynoVal<T>,
): DynoVal<"mat3"> => new Mat3({ value });
export const mat4 = <T extends "float" | "mat2" | "mat3" | "mat4">(
value: DynoVal<T>,
): DynoVal<"mat4"> => new Mat4({ value });
export const floatBitsToInt = (value: DynoVal<"float">): DynoVal<"int"> =>
new FloatBitsToInt({ value });
export const floatBitsToUint = (value: DynoVal<"float">): DynoVal<"uint"> =>
new FloatBitsToUint({ value });
export const intBitsToFloat = (value: DynoVal<"int">): DynoVal<"float"> =>
new IntBitsToFloat({ value });
export const uintBitsToFloat = (value: DynoVal<"uint">): DynoVal<"float"> =>
new UintBitsToFloat({ value });
export const packSnorm2x16 = (value: DynoVal<"vec2">): DynoVal<"uint"> =>
new PackSnorm2x16({ value });
export const unpackSnorm2x16 = (value: DynoVal<"uint">): DynoVal<"vec2"> =>
new UnpackSnorm2x16({ value });
export const packUnorm2x16 = (value: DynoVal<"vec2">): DynoVal<"uint"> =>
new PackUnorm2x16({ value });
export const unpackUnorm2x16 = (value: DynoVal<"uint">): DynoVal<"vec2"> =>
new UnpackUnorm2x16({ value });
export const packHalf2x16 = (value: DynoVal<"vec2">): DynoVal<"uint"> =>
new PackHalf2x16({ value });
export const unpackHalf2x16 = (value: DynoVal<"uint">): DynoVal<"vec2"> =>
new UnpackHalf2x16({ value });
export const uintToRgba8 = (value: DynoVal<"uint">): DynoVal<"vec4"> =>
new UintToRgba8({ value });
export class SimpleCast<
Allowed extends SimpleTypes,
OutType extends SimpleTypes,
OutKey extends string,
> extends UnaryOp<Allowed, OutType, OutKey> {
constructor({
value,
outType,
outKey,
}: { value: DynoVal<Allowed>; outType: OutType; outKey: OutKey }) {
super({ a: value, outTypeFunc: () => outType, outKey });
this.statements = ({ inputs, outputs }) => [
`${outputs[outKey]} = ${typeLiteral(outType)}(${inputs.a});`,
];
}
}
export class Bool extends SimpleCast<
"bool" | "int" | "uint" | "float",
"bool",
"bool"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "int" | "uint" | "float"> }) {
super({ value, outType: "bool", outKey: "bool" });
}
}
export class Int extends SimpleCast<
"bool" | "int" | "uint" | "float",
"int",
"int"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "int" | "uint" | "float"> }) {
super({ value, outType: "int", outKey: "int" });
}
}
export class Uint extends SimpleCast<
"bool" | "int" | "uint" | "float",
"uint",
"uint"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "int" | "uint" | "float"> }) {
super({ value, outType: "uint", outKey: "uint" });
}
}
export class Float extends SimpleCast<
"bool" | "int" | "uint" | "float",
"float",
"float"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "int" | "uint" | "float"> }) {
super({ value, outType: "float", outKey: "float" });
}
}
export class BVec2 extends SimpleCast<
"bool" | "bvec2" | "ivec2" | "uvec2" | "vec2",
"bvec2",
"bvec2"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "bvec2" | "ivec2" | "uvec2" | "vec2"> }) {
super({ value, outType: "bvec2", outKey: "bvec2" });
}
}
export class BVec3 extends SimpleCast<
"bool" | "bvec3" | "ivec3" | "uvec3" | "vec3",
"bvec3",
"bvec3"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "bvec3" | "ivec3" | "uvec3" | "vec3"> }) {
super({ value, outType: "bvec3", outKey: "bvec3" });
}
}
export class BVec4 extends SimpleCast<
"bool" | "bvec4" | "ivec4" | "uvec4" | "vec4",
"bvec4",
"bvec4"
> {
constructor({
value,
}: { value: DynoVal<"bool" | "bvec4" | "ivec4" | "uvec4" | "vec4"> }) {
super({ value, outType: "bvec4", outKey: "bvec4" });
}
}
export class IVec2 extends SimpleCast<
"int" | "bvec2" | "ivec2" | "uvec2" | "vec2",
"ivec2",
"ivec2"
> {
constructor({
value,
}: { value: DynoVal<"int" | "bvec2" | "ivec2" | "uvec2" | "vec2"> }) {
super({ value, outType: "ivec2", outKey: "ivec2" });
}
}
export class IVec3 extends SimpleCast<
"int" | "bvec3" | "ivec3" | "uvec3" | "vec3",
"ivec3",
"ivec3"
> {
constructor({
value,
}: { value: DynoVal<"int" | "bvec3" | "ivec3" | "uvec3" | "vec3"> }) {
super({ value, outType: "ivec3", outKey: "ivec3" });
}
}
export class IVec4 extends SimpleCast<
"int" | "bvec4" | "ivec4" | "uvec4" | "vec4",
"ivec4",
"ivec4"
> {
constructor({
value,
}: { value: DynoVal<"int" | "bvec4" | "ivec4" | "uvec4" | "vec4"> }) {
super({ value, outType: "ivec4", outKey: "ivec4" });
}
}
export class UVec2 extends SimpleCast<
"uint" | "ivec2" | "bvec2" | "uvec2" | "vec2",
"uvec2",
"uvec2"
> {
constructor({
value,
}: { value: DynoVal<"uint" | "ivec2" | "bvec2" | "uvec2" | "vec2"> }) {
super({ value, outType: "uvec2", outKey: "uvec2" });
}
}
export class UVec3 extends SimpleCast<
"uint" | "ivec3" | "bvec3" | "uvec3" | "vec3",
"uvec3",
"uvec3"
> {
constructor({
value,
}: { value: DynoVal<"uint" | "ivec3" | "bvec3" | "uvec3" | "vec3"> }) {
super({ value, outType: "uvec3", outKey: "uvec3" });
}
}
export class UVec4 extends SimpleCast<
"uint" | "ivec4" | "bvec4" | "uvec4" | "vec4",
"uvec4",
"uvec4"
> {
constructor({
value,
}: { value: DynoVal<"uint" | "ivec4" | "bvec4" | "uvec4" | "vec4"> }) {
super({ value, outType: "uvec4", outKey: "uvec4" });
}
}
export class Vec2 extends SimpleCast<
"float" | "bvec2" | "ivec2" | "uvec2" | "vec2" | "vec3" | "vec4",
"vec2",
"vec2"
> {
constructor({
value,
}: {
value: DynoVal<
"float" | "bvec2" | "ivec2" | "uvec2" | "vec2" | "vec3" | "vec4"
>;
}) {
super({ value, outType: "vec2", outKey: "vec2" });
}
}
export class Vec3 extends SimpleCast<
"float" | "bvec3" | "ivec3" | "uvec3" | "vec3" | "vec2" | "vec4",
"vec3",
"vec3"
> {
constructor({
value,
}: {
value: DynoVal<
"float" | "bvec3" | "ivec3" | "uvec3" | "vec3" | "vec2" | "vec4"
>;
}) {
super({ value, outType: "vec3", outKey: "vec3" });
}
}
export class Vec4 extends SimpleCast<
"float" | "bvec4" | "ivec4" | "uvec4" | "vec4",
"vec4",
"vec4"
> {
constructor({
value,
}: { value: DynoVal<"float" | "bvec4" | "ivec4" | "uvec4" | "vec4"> }) {
super({ value, outType: "vec4", outKey: "vec4" });
}
}
export class Mat2 extends SimpleCast<
"float" | "mat2" | "mat3" | "mat4",
"mat2",
"mat2"
> {
constructor({
value,
}: { value: DynoVal<"float" | "mat2" | "mat3" | "mat4"> }) {
super({ value, outType: "mat2", outKey: "mat2" });
}
}
export class Mat3 extends SimpleCast<
"float" | "mat2" | "mat3" | "mat4",
"mat3",
"mat3"
> {
constructor({
value,
}: { value: DynoVal<"float" | "mat2" | "mat3" | "mat4"> }) {
super({ value, outType: "mat3", outKey: "mat3" });
}
}
export class Mat4 extends SimpleCast<
"float" | "mat2" | "mat3" | "mat4",
"mat4",
"mat4"
> {
constructor({
value,
}: { value: DynoVal<"float" | "mat2" | "mat3" | "mat4"> }) {
super({ value, outType: "mat4", outKey: "mat4" });
}
}
export class FloatBitsToInt extends UnaryOp<"float", "int", "int"> {
constructor({ value }: { value: DynoVal<"float"> }) {
super({ a: value, outKey: "int", outTypeFunc: () => "int" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.int} = floatBitsToInt(${inputs.a});`];
};
}
}
export class FloatBitsToUint extends UnaryOp<"float", "uint", "uint"> {
constructor({ value }: { value: DynoVal<"float"> }) {
super({ a: value, outKey: "uint", outTypeFunc: () => "uint" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.uint} = floatBitsToUint(${inputs.a});`];
};
}
}
export class IntBitsToFloat extends UnaryOp<"int", "float", "float"> {
constructor({ value }: { value: DynoVal<"int"> }) {
super({ a: value, outKey: "float", outTypeFunc: () => "float" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.float} = intBitsToFloat(${inputs.a});`];
};
}
}
export class UintBitsToFloat extends UnaryOp<"uint", "float", "float"> {
constructor({ value }: { value: DynoVal<"uint"> }) {
super({ a: value, outKey: "float", outTypeFunc: () => "float" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.float} = uintBitsToFloat(${inputs.a});`];
};
}
}
export class PackSnorm2x16 extends UnaryOp<"vec2", "uint", "uint"> {
constructor({ value }: { value: DynoVal<"vec2"> }) {
super({ a: value, outKey: "uint", outTypeFunc: () => "uint" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.uint} = packSnorm2x16(${inputs.a});`];
};
}
}
export class UnpackSnorm2x16 extends UnaryOp<"uint", "vec2", "vec2"> {
constructor({ value }: { value: DynoVal<"uint"> }) {
super({ a: value, outKey: "vec2", outTypeFunc: () => "vec2" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.vec2} = unpackSnorm2x16(${inputs.a});`];
};
}
}
export class PackUnorm2x16 extends UnaryOp<"vec2", "uint", "uint"> {
constructor({ value }: { value: DynoVal<"vec2"> }) {
super({ a: value, outKey: "uint", outTypeFunc: () => "uint" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.uint} = packUnorm2x16(${inputs.a});`];
};
}
}
export class UnpackUnorm2x16 extends UnaryOp<"uint", "vec2", "vec2"> {
constructor({ value }: { value: DynoVal<"uint"> }) {
super({ a: value, outKey: "vec2", outTypeFunc: () => "vec2" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.vec2} = unpackUnorm2x16(${inputs.a});`];
};
}
}
export class PackHalf2x16 extends UnaryOp<"vec2", "uint", "uint"> {
constructor({ value }: { value: DynoVal<"vec2"> }) {
super({ a: value, outKey: "uint", outTypeFunc: () => "uint" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.uint} = packHalf2x16(${inputs.a});`];
};
}
}
export class UnpackHalf2x16 extends UnaryOp<"uint", "vec2", "vec2"> {
constructor({ value }: { value: DynoVal<"uint"> }) {
super({ a: value, outKey: "vec2", outTypeFunc: () => "vec2" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.vec2} = unpackHalf2x16(${inputs.a});`];
};
}
}
export class UintToRgba8 extends UnaryOp<"uint", "vec4", "rgba8"> {
constructor({ value }: { value: DynoVal<"uint"> }) {
super({ a: value, outKey: "rgba8", outTypeFunc: () => "vec4" });
this.statements = ({ inputs, outputs }) => {
return [
`uvec4 uRgba = uvec4(${inputs.a} & 0xffu, (${inputs.a} >> 8u) & 0xffu, (${inputs.a} >> 16u) & 0xffu, (${inputs.a} >> 24u) & 0xffu);`,
`${outputs.rgba8} = vec4(uRgba) / 255.0;`,
];
};
}
}
+434
View File
@@ -0,0 +1,434 @@
import { BinaryOp, TrinaryOp, UnaryOp } from "./base";
import {
type AllIntTypes,
type BoolTypes,
type IntTypes,
type ScalarTypes,
type SimpleTypes,
type UintTypes,
type ValueTypes,
isBoolType,
isIntType,
isScalarType,
isUintType,
isVector2Type,
isVector3Type,
} from "./types";
import { type DynoVal, valType } from "./value";
export const and = <T extends "bool" | AllIntTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<T> => new And({ a, b });
export const or = <T extends "bool" | AllIntTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<T> => new Or({ a, b });
export const xor = <T extends "bool" | AllIntTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<T> => new Xor({ a, b });
export const not = <T extends BoolTypes | AllIntTypes>(
a: DynoVal<T>,
): DynoVal<T> => new Not({ a });
export const lessThan = <T extends ValueTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<CompareOutput<T>> => new LessThan({ a, b });
export const lessThanEqual = <T extends ValueTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<CompareOutput<T>> => new LessThanEqual({ a, b });
export const greaterThan = <T extends ValueTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<CompareOutput<T>> => new GreaterThan({ a, b });
export const greaterThanEqual = <T extends ValueTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<CompareOutput<T>> => new GreaterThanEqual({ a, b });
export const equal = <T extends ValueTypes | BoolTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<EqualOutput<T>> => new Equal({ a, b });
export const notEqual = <T extends ValueTypes | BoolTypes>(
a: DynoVal<T>,
b: DynoVal<T>,
): DynoVal<NotEqualOutput<T>> => new NotEqual({ a, b });
export const any = <T extends "bvec2" | "bvec3" | "bvec4">(
a: DynoVal<T>,
): DynoVal<"bool"> => new Any({ a });
export const all = <T extends "bvec2" | "bvec3" | "bvec4">(
a: DynoVal<T>,
): DynoVal<"bool"> => new All({ a });
export const select = <T extends SimpleTypes>(
cond: DynoVal<"bool">,
t: DynoVal<T>,
f: DynoVal<T>,
): DynoVal<T> => new Select({ cond, t, f });
export const compXor = <T extends BoolTypes | AllIntTypes>(
a: DynoVal<T>,
): DynoVal<CompXorOutput<T>> => new CompXor({ a });
export class And<T extends "bool" | AllIntTypes> extends BinaryOp<
T,
T,
T,
"and"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({ a, b, outTypeFunc: (aType: T, bType: T) => aType, outKey: "and" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.and === "bool") {
return [`${outputs.and} = ${inputs.a} && ${inputs.b};`];
}
return [`${outputs.and} = ${inputs.a} & ${inputs.b};`];
};
}
}
export class Or<T extends "bool" | AllIntTypes> extends BinaryOp<
T,
T,
T,
"or"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({ a, b, outTypeFunc: (aType: T, bType: T) => aType, outKey: "or" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.or === "bool") {
return [`${outputs.or} = ${inputs.a} || ${inputs.b};`];
}
return [`${outputs.or} = ${inputs.a} | ${inputs.b};`];
};
}
}
export class Xor<T extends "bool" | AllIntTypes> extends BinaryOp<
T,
T,
T,
"xor"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({ a, b, outTypeFunc: (aType: T, bType: T) => aType, outKey: "xor" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.xor === "bool") {
return [`${outputs.xor} = ${inputs.a} ^^ ${inputs.b};`];
}
return [`${outputs.xor} = ${inputs.a} ^ ${inputs.b};`];
};
}
}
export class Not<T extends BoolTypes | AllIntTypes> extends UnaryOp<
T,
T,
"not"
> {
constructor({ a }: { a: DynoVal<T> }) {
super({ a, outTypeFunc: (aType: T) => aType, outKey: "not" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.not === "bool") {
return [`${outputs.not} = !${inputs.a};`];
}
return [`${outputs.not} = not(${inputs.a});`];
};
}
}
export class LessThan<T extends ValueTypes> extends BinaryOp<
T,
T,
CompareOutput<T>,
"lessThan"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({
a,
b,
outTypeFunc: (aType: T, bType: T) => compareOutputType(aType, "lessThan"),
outKey: "lessThan",
});
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.lessThan === "bool") {
return [`${outputs.lessThan} = ${inputs.a} < ${inputs.b};`];
}
return [`${outputs.lessThan} = lessThan(${inputs.a}, ${inputs.b});`];
};
}
}
export class LessThanEqual<T extends ValueTypes> extends BinaryOp<
T,
T,
CompareOutput<T>,
"lessThanEqual"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({
a,
b,
outTypeFunc: (aType: T, bType: T) =>
compareOutputType(aType, "lessThanEqual"),
outKey: "lessThanEqual",
});
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.lessThanEqual === "bool") {
return [`${outputs.lessThanEqual} = ${inputs.a} <= ${inputs.b};`];
}
return [
`${outputs.lessThanEqual} = lessThanEqual(${inputs.a}, ${inputs.b});`,
];
};
}
}
export class GreaterThan<T extends ValueTypes> extends BinaryOp<
T,
T,
CompareOutput<T>,
"greaterThan"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({
a,
b,
outTypeFunc: (aType: T, bType: T) =>
compareOutputType(aType, "greaterThan"),
outKey: "greaterThan",
});
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.greaterThan === "bool") {
return [`${outputs.greaterThan} = ${inputs.a} > ${inputs.b};`];
}
return [
`${outputs.greaterThan} = greaterThan(${inputs.a}, ${inputs.b});`,
];
};
}
}
export class GreaterThanEqual<T extends ValueTypes> extends BinaryOp<
T,
T,
CompareOutput<T>,
"greaterThanEqual"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({
a,
b,
outTypeFunc: (aType: T, bType: T) =>
compareOutputType(aType, "greaterThanEqual"),
outKey: "greaterThanEqual",
});
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.greaterThanEqual === "bool") {
return [`${outputs.greaterThanEqual} = ${inputs.a} >= ${inputs.b};`];
}
return [
`${outputs.greaterThanEqual} = greaterThanEqual(${inputs.a}, ${inputs.b});`,
];
};
}
}
export class Equal<T extends ValueTypes | BoolTypes> extends BinaryOp<
T,
T,
EqualOutput<T>,
"equal"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({ a, b, outTypeFunc: equalOutputType<T>, outKey: "equal" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.equal === "bool") {
return [`${outputs.equal} = ${inputs.a} == ${inputs.b};`];
}
return [`${outputs.equal} = equal(${inputs.a}, ${inputs.b});`];
};
}
}
export class NotEqual<T extends ValueTypes | BoolTypes> extends BinaryOp<
T,
T,
NotEqualOutput<T>,
"notEqual"
> {
constructor({ a, b }: { a: DynoVal<T>; b: DynoVal<T> }) {
super({ a, b, outTypeFunc: notEqualOutputType<T>, outKey: "notEqual" });
this.statements = ({ inputs, outputs }) => {
if (this.outTypes.notEqual === "bool") {
return [`${outputs.notEqual} = ${inputs.a} != ${inputs.b};`];
}
return [`${outputs.notEqual} = notEqual(${inputs.a}, ${inputs.b});`];
};
}
}
export class Any<T extends BoolTypes> extends UnaryOp<T, "bool", "any"> {
constructor({ a }: { a: DynoVal<T> }) {
super({ a, outTypeFunc: (aType: T) => "bool", outKey: "any" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.any} = any(${inputs.a});`];
};
}
}
export class All<T extends BoolTypes> extends UnaryOp<T, "bool", "all"> {
constructor({ a }: { a: DynoVal<T> }) {
super({ a, outTypeFunc: (aType: T) => "bool", outKey: "all" });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.all} = all(${inputs.a});`];
};
}
}
export class Select<T extends SimpleTypes> extends TrinaryOp<
"bool",
T,
T,
T,
"select"
> {
constructor({
cond,
t,
f,
}: { cond: DynoVal<"bool">; t: DynoVal<T>; f: DynoVal<T> }) {
super({
a: cond,
b: t,
c: f,
outKey: "select",
outTypeFunc: (aType: "bool", bType: T, cType: T) => bType,
});
this.statements = ({ inputs, outputs }) => {
const { a: cond, b: t, c: f } = inputs;
return [`${outputs.select} = (${cond}) ? (${t}) : (${f});`];
};
}
}
type CompareOutput<T extends ValueTypes> = T extends ScalarTypes
? "bool"
: T extends "ivec2" | "uvec2" | "vec2"
? "bvec2"
: T extends "ivec3" | "uvec3" | "vec3"
? "bvec3"
: T extends "ivec4" | "uvec4" | "vec4"
? "bvec4"
: never;
function compareOutputType<T extends ValueTypes>(
type: T,
operator: string,
): CompareOutput<T> {
if (isScalarType(type)) {
return "bool" as CompareOutput<T>;
}
if (type === "ivec2" || type === "uvec2" || type === "vec2") {
return "bvec2" as CompareOutput<T>;
}
if (type === "ivec3" || type === "uvec3" || type === "vec3") {
return "bvec3" as CompareOutput<T>;
}
if (type === "ivec4" || type === "uvec4" || type === "vec4") {
return "bvec4" as CompareOutput<T>;
}
throw new Error(`Invalid ${operator} type: ${type}`);
}
type EqualOutput<A extends ValueTypes | BoolTypes> = A extends ScalarTypes
? "bool"
: A extends BoolTypes
? A
: A extends "ivec2" | "uvec2" | "vec2"
? "bvec2"
: A extends "ivec3" | "uvec3" | "vec3"
? "bvec3"
: A extends "ivec4" | "uvec4" | "vec4"
? "bvec4"
: never;
function equalOutputType<A extends ValueTypes | BoolTypes>(
type: A,
operator = "equal",
): EqualOutput<A> {
if (isScalarType(type)) {
return "bool" as EqualOutput<A>;
}
if (isBoolType(type)) {
return type as EqualOutput<A>;
}
if (type === "ivec2" || type === "uvec2" || type === "vec2") {
return "bvec2" as EqualOutput<A>;
}
if (type === "ivec3" || type === "uvec3" || type === "vec3") {
return "bvec3" as EqualOutput<A>;
}
if (type === "ivec4" || type === "uvec4" || type === "vec4") {
return "bvec4" as EqualOutput<A>;
}
throw new Error(`Invalid ${operator} type: ${type}`);
}
type NotEqualOutput<A extends ValueTypes | BoolTypes> = EqualOutput<A>;
function notEqualOutputType<A extends ValueTypes | BoolTypes>(
type: A,
): NotEqualOutput<A> {
return equalOutputType(type, "notEqual");
}
type CompXorOutput<A extends BoolTypes | AllIntTypes> = A extends BoolTypes
? "bool"
: A extends IntTypes
? "int"
: A extends UintTypes
? "uint"
: never;
function compXorOutputType<A extends BoolTypes | AllIntTypes>(
type: A,
): CompXorOutput<A> {
if (isBoolType(type)) {
return "bool" as CompXorOutput<A>;
}
if (isIntType(type)) {
return "int" as CompXorOutput<A>;
}
if (isUintType(type)) {
return "uint" as CompXorOutput<A>;
}
throw new Error(`Invalid compXor type: ${type}`);
}
export class CompXor<T extends BoolTypes | AllIntTypes> extends UnaryOp<
T,
CompXorOutput<T>,
"compXor"
> {
constructor({ a }: { a: DynoVal<T> }) {
const outType = compXorOutputType(valType(a));
super({ a, outTypeFunc: (aType: T) => outType, outKey: "compXor" });
this.statements = ({ inputs, outputs }) => {
if (isScalarType(this.outTypes.compXor)) {
return [`${outputs.compXor} = ${inputs.a};`];
}
const components = isVector2Type(outType)
? ["x", "y"]
: isVector3Type(outType)
? ["x", "y", "z"]
: ["x", "y", "z", "w"];
const operands = components.map((c) => `${inputs.a}.${c}`);
const operator = isBoolType(outType) ? "^^" : "^";
return [`${outputs.compXor} = ${operands.join(` ${operator} `)};`];
};
}
}
+534
View File
@@ -0,0 +1,534 @@
import { BinaryOp, Dyno, TrinaryOp, UnaryOp } from "./base";
import {
type AddOutput,
type ClampOutput,
type DivOutput,
type IModOutput,
type IsInfOutput,
type IsNanOutput,
type MaxOutput,
type MinOutput,
type MixOutput,
type ModOutput,
type MulOutput,
type SmoothstepOutput,
type StepOutput,
type SubOutput,
absOutputType,
addOutputType,
ceilOutputType,
clampOutputType,
divOutputType,
exp2OutputType,
expOutputType,
floorOutputType,
fractOutputType,
imodOutputType,
inversesqrtOutputType,
isInfOutputType,
isNanOutputType,
log2OutputType,
logOutputType,
maxOutputType,
minOutputType,
mixOutputType,
modOutputType,
modfOutputType,
mulOutputType,
negOutputType,
powOutputType,
roundOutputType,
signOutputType,
smoothstepOutputType,
sqrOutputType,
sqrtOutputType,
stepOutputType,
subOutputType,
truncOutputType,
} from "./mathTypes";
import type {
AllIntTypes,
AllSignedTypes,
AllValueTypes,
BoolTypes,
FloatTypes,
SignedTypes,
ValueTypes,
} from "./types";
import { type DynoVal, valType } from "./value";
export const add = <A extends AllValueTypes, B extends AllValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<AddOutput<A, B>> => new Add({ a, b });
export const sub = <A extends AllValueTypes, B extends AllValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<SubOutput<A, B>> => new Sub({ a, b });
export const mul = <A extends AllValueTypes, B extends AllValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<MulOutput<A, B>> => new Mul({ a, b });
export const div = <A extends AllValueTypes, B extends AllValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<DivOutput<A, B>> => new Div({ a, b });
export const imod = <A extends AllIntTypes, B extends AllIntTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<IModOutput<A, B>> => new IMod({ a, b });
export const mod = <A extends FloatTypes, B extends FloatTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<ModOutput<A, B>> => new Mod({ a, b });
export const modf = <A extends FloatTypes>(a: DynoVal<A>) =>
new Modf({ a }).outputs;
export const neg = <A extends AllSignedTypes>(a: DynoVal<A>): DynoVal<A> =>
new Neg({ a });
export const abs = <A extends SignedTypes>(a: DynoVal<A>): DynoVal<A> =>
new Abs({ a });
export const sign = <A extends SignedTypes>(a: DynoVal<A>): DynoVal<A> =>
new Sign({ a });
export const floor = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Floor({ a });
export const ceil = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Ceil({ a });
export const trunc = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Trunc({ a });
export const round = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Round({ a });
export const fract = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Fract({ a });
export const pow = <A extends FloatTypes>(
a: DynoVal<A>,
b: DynoVal<A>,
): DynoVal<A> => new Pow({ a, b });
export const exp = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Exp({ a });
export const exp2 = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Exp2({ a });
export const log = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Log({ a });
export const log2 = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Log2({ a });
export const sqr = <A extends ValueTypes>(a: DynoVal<A>): DynoVal<A> =>
new Sqr({ a });
export const sqrt = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Sqrt({ a });
export const inversesqrt = <A extends FloatTypes>(a: DynoVal<A>): DynoVal<A> =>
new InverseSqrt({ a });
export const min = <A extends ValueTypes, B extends ValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<MinOutput<A, B>> => new Min({ a, b });
export const max = <A extends ValueTypes, B extends ValueTypes>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<MaxOutput<A, B>> => new Max({ a, b });
export const clamp = <A extends ValueTypes, MinMax extends ValueTypes>(
a: DynoVal<A>,
min: DynoVal<MinMax>,
max: DynoVal<MinMax>,
): DynoVal<ClampOutput<A, MinMax>> => new Clamp({ a, min, max });
export const mix = <A extends FloatTypes, T extends FloatTypes | BoolTypes>(
a: DynoVal<A>,
b: DynoVal<A>,
t: DynoVal<T>,
): DynoVal<MixOutput<A, T>> => new Mix({ a, b, t });
export const step = <A extends FloatTypes, B extends FloatTypes>(
edge: DynoVal<A>,
x: DynoVal<B>,
): DynoVal<StepOutput<A, B>> => new Step({ edge, x });
export const smoothstep = <X extends FloatTypes, Edge extends X | "float">(
edge0: DynoVal<Edge>,
edge1: DynoVal<Edge>,
x: DynoVal<X>,
): DynoVal<SmoothstepOutput<Edge, Edge, X>> =>
new Smoothstep({ edge0, edge1, x });
export const isNan = <A extends FloatTypes>(
a: DynoVal<A>,
): DynoVal<IsNanOutput<A>> => new IsNan({ a });
export const isInf = <A extends FloatTypes>(
a: DynoVal<A>,
): DynoVal<IsInfOutput<A>> => new IsInf({ a });
export class Add<
A extends AllValueTypes,
B extends AllValueTypes,
> extends BinaryOp<A, B, AddOutput<A, B>, "sum"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "sum", outTypeFunc: addOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.sum} = ${inputs.a} + ${inputs.b};`];
};
}
}
export class Sub<
A extends AllValueTypes,
B extends AllValueTypes,
> extends BinaryOp<A, B, SubOutput<A, B>, "difference"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "difference", outTypeFunc: subOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.difference} = ${inputs.a} - ${inputs.b};`];
};
}
}
export class Mul<
A extends AllValueTypes,
B extends AllValueTypes,
> extends BinaryOp<A, B, MulOutput<A, B>, "product"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "product", outTypeFunc: mulOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.product} = ${inputs.a} * ${inputs.b};`];
};
}
}
export class Div<
A extends AllValueTypes,
B extends AllValueTypes,
> extends BinaryOp<A, B, DivOutput<A, B>, "quotient"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "quotient", outTypeFunc: divOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.quotient} = ${inputs.a} / ${inputs.b};`];
};
}
}
export class IMod<
A extends AllIntTypes,
B extends AllIntTypes,
> extends BinaryOp<A, B, IModOutput<A, B>, "remainder"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "remainder", outTypeFunc: imodOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.remainder} = ${inputs.a} % ${inputs.b};`];
};
}
}
export class Mod<A extends FloatTypes, B extends FloatTypes> extends BinaryOp<
A,
B,
ModOutput<A, B>,
"remainder"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "remainder", outTypeFunc: modOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.remainder} = mod(${inputs.a}, ${inputs.b});`];
};
}
}
export class Modf<A extends FloatTypes> extends Dyno<
{ a: A },
{ fract: A; integer: A }
> {
constructor({ a }: { a: DynoVal<A> }) {
const inTypes = { a: valType(a) };
const outType = modfOutputType<A>(inTypes.a);
const outTypes = {
fract: outType,
integer: outType,
};
super({ inTypes, outTypes, inputs: { a } });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.fract} = modf(${inputs.a}, ${outputs.integer});`];
};
}
}
export class Neg<A extends AllSignedTypes> extends UnaryOp<A, A, "neg"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "neg", outTypeFunc: negOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.neg} = -${inputs.a};`];
};
}
}
export class Abs<A extends SignedTypes> extends UnaryOp<A, A, "abs"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "abs", outTypeFunc: absOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.abs} = abs(${inputs.a});`];
};
}
}
export class Sign<A extends SignedTypes> extends UnaryOp<A, A, "sign"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "sign", outTypeFunc: signOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.sign} = sign(${inputs.a});`];
};
}
}
export class Floor<A extends FloatTypes> extends UnaryOp<A, A, "floor"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "floor", outTypeFunc: floorOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.floor} = floor(${inputs.a});`];
};
}
}
export class Ceil<A extends FloatTypes> extends UnaryOp<A, A, "ceil"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "ceil", outTypeFunc: ceilOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.ceil} = ceil(${inputs.a});`];
};
}
}
export class Trunc<A extends FloatTypes> extends UnaryOp<A, A, "trunc"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "trunc", outTypeFunc: truncOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.trunc} = trunc(${inputs.a});`];
};
}
}
export class Round<A extends FloatTypes> extends UnaryOp<A, A, "round"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "round", outTypeFunc: roundOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.round} = round(${inputs.a});`];
};
}
}
export class Fract<A extends FloatTypes> extends UnaryOp<A, A, "fract"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "fract", outTypeFunc: fractOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.fract} = fract(${inputs.a});`];
};
}
}
export class Pow<A extends FloatTypes> extends BinaryOp<A, A, A, "power"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<A> }) {
super({ a, b, outKey: "power", outTypeFunc: powOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.power} = pow(${inputs.a}, ${inputs.b});`];
};
}
}
export class Exp<A extends FloatTypes> extends UnaryOp<A, A, "exp"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "exp", outTypeFunc: expOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.exp} = exp(${inputs.a});`];
};
}
}
export class Exp2<A extends FloatTypes> extends UnaryOp<A, A, "exp2"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "exp2", outTypeFunc: exp2OutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.exp2} = exp2(${inputs.a});`];
};
}
}
export class Log<A extends FloatTypes> extends UnaryOp<A, A, "log"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "log", outTypeFunc: logOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.log} = log(${inputs.a});`];
};
}
}
export class Log2<A extends FloatTypes> extends UnaryOp<A, A, "log2"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "log2", outTypeFunc: log2OutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.log2} = log2(${inputs.a});`];
};
}
}
export class Sqr<A extends ValueTypes> extends UnaryOp<A, A, "sqr"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "sqr", outTypeFunc: sqrOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.sqr} = ${inputs.a} * ${inputs.a};`];
};
}
}
export class Sqrt<A extends FloatTypes> extends UnaryOp<A, A, "sqrt"> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "sqrt", outTypeFunc: sqrtOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.sqrt} = sqrt(${inputs.a});`];
};
}
}
export class InverseSqrt<A extends FloatTypes> extends UnaryOp<
A,
A,
"inversesqrt"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "inversesqrt", outTypeFunc: inversesqrtOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.inversesqrt} = inversesqrt(${inputs.a});`];
};
}
}
export class Min<A extends ValueTypes, B extends ValueTypes> extends BinaryOp<
A,
B,
MinOutput<A, B>,
"min"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "min", outTypeFunc: minOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.min} = min(${inputs.a}, ${inputs.b});`];
};
}
}
export class Max<A extends ValueTypes, B extends ValueTypes> extends BinaryOp<
A,
B,
MaxOutput<A, B>,
"max"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "max", outTypeFunc: maxOutputType<A, B> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.max} = max(${inputs.a}, ${inputs.b});`];
};
}
}
export class Clamp<
A extends ValueTypes,
MinMax extends ValueTypes,
> extends TrinaryOp<A, MinMax, MinMax, ClampOutput<A, MinMax>, "clamp"> {
constructor({
a,
min,
max,
}: { a: DynoVal<A>; min: DynoVal<MinMax>; max: DynoVal<MinMax> }) {
super({
a,
b: min,
c: max,
outKey: "clamp",
outTypeFunc: clampOutputType<A, MinMax>,
});
this.statements = ({ inputs, outputs }) => {
const { a, b: min, c: max } = inputs;
return [`${outputs.clamp} = clamp(${a}, ${min}, ${max});`];
};
}
}
export class Mix<
A extends FloatTypes,
T extends FloatTypes | BoolTypes,
> extends TrinaryOp<A, A, T, MixOutput<A, T>, "mix"> {
constructor({ a, b, t }: { a: DynoVal<A>; b: DynoVal<A>; t: DynoVal<T> }) {
super({ a, b, c: t, outKey: "mix", outTypeFunc: mixOutputType<A, T> });
this.statements = ({ inputs, outputs }) => {
const { a, b, c: t } = inputs;
return [`${outputs.mix} = mix(${a}, ${b}, ${t});`];
};
}
}
export class Step<
Edge extends FloatTypes,
X extends FloatTypes,
> extends BinaryOp<Edge, X, StepOutput<Edge, X>, "step"> {
constructor({ edge, x }: { edge: DynoVal<Edge>; x: DynoVal<X> }) {
super({
a: edge,
b: x,
outKey: "step",
outTypeFunc: stepOutputType<Edge, X>,
});
this.statements = ({ inputs, outputs }) => {
const { a: edge, b: x } = inputs;
return [`${outputs.step} = step(${edge}, ${x});`];
};
}
}
export class Smoothstep<
X extends FloatTypes,
Edge extends X | "float",
> extends TrinaryOp<
Edge,
Edge,
X,
SmoothstepOutput<Edge, Edge, X>,
"smoothstep"
> {
constructor({
edge0,
edge1,
x,
}: { edge0: DynoVal<Edge>; edge1: DynoVal<Edge>; x: DynoVal<X> }) {
super({
a: edge0,
b: edge1,
c: x,
outKey: "smoothstep",
outTypeFunc: smoothstepOutputType<Edge, Edge, X>,
});
this.statements = ({ inputs, outputs }) => {
const { a: edge0, b: edge1, c: x } = inputs;
return [`${outputs.smoothstep} = smoothstep(${edge0}, ${edge1}, ${x});`];
};
}
}
export class IsNan<A extends FloatTypes> extends UnaryOp<
A,
IsNanOutput<A>,
"isNan"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "isNan", outTypeFunc: isNanOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.isNan} = isNan(${inputs.a});`];
};
}
}
export class IsInf<A extends FloatTypes> extends UnaryOp<
A,
IsInfOutput<A>,
"isInf"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "isInf", outTypeFunc: isInfOutputType<A> });
this.statements = ({ inputs, outputs }) => {
return [`${outputs.isInf} = isInf(${inputs.a});`];
};
}
}
+717
View File
@@ -0,0 +1,717 @@
import {
type AllFloatTypes,
type AllIntTypes,
type AllSignedTypes,
type AllValueTypes,
type BaseType,
type BoolTypes,
type FloatTypes,
type IntTypes,
type SignedTypes,
type UintTypes,
type ValueTypes,
isAllFloatType,
isFloatType,
isIntType,
isMat2,
isMat3,
isMat4,
isUintType,
} from "./types";
export type AddOutput<
A extends AllValueTypes,
B extends AllValueTypes,
> = BaseType &
(A extends B
? A
: A extends "int"
? B extends IntTypes
? B
: never
: B extends "int"
? A extends IntTypes
? A
: never
: A extends "uint"
? B extends UintTypes
? B
: never
: B extends "uint"
? A extends UintTypes
? A
: never
: A extends "float"
? B extends AllFloatTypes
? B
: never
: B extends "float"
? A extends AllFloatTypes
? A
: never
: never);
export type SubOutput<
A extends AllValueTypes,
B extends AllValueTypes,
> = AddOutput<A, B>;
export type MulOutput<
A extends AllValueTypes,
B extends AllValueTypes,
> = BaseType &
(A extends "int"
? B extends IntTypes
? B
: never
: B extends "int"
? A extends IntTypes
? A
: never
: A extends "uint"
? B extends UintTypes
? B
: never
: B extends "uint"
? A extends UintTypes
? A
: never
: A extends "float"
? B extends AllFloatTypes
? B
: never
: B extends "float"
? A extends AllFloatTypes
? A
: never
: A extends IntTypes
? B extends A
? A
: never
: B extends IntTypes
? A extends B
? A
: never
: A extends UintTypes
? B extends A
? A
: never
: B extends UintTypes
? A extends B
? A
: never
: // Vector * Matrix/Vector
A extends "vec2"
? B extends "vec2" | "mat2" | "mat2x2"
? "vec2"
: B extends "mat3x2"
? "vec3"
: B extends "mat4x2"
? "vec4"
: never
: A extends "vec3"
? B extends "mat2x3"
? "vec2"
: B extends "vec3" | "mat3" | "mat3x3"
? "vec3"
: B extends "mat4x3"
? "vec4"
: never
: A extends "vec4"
? B extends "mat2x4"
? "vec2"
: B extends "mat3x4"
? "vec3"
: B extends "vec4" | "mat4" | "mat4x4"
? "vec4"
: never
: // Matrix * Vector
B extends "vec2"
? A extends "mat2" | "mat2x2"
? "vec2"
: A extends "mat2x3"
? "vec3"
: A extends "mat2x4"
? "vec4"
: never
: B extends "vec3"
? A extends "mat3x2"
? "vec2"
: A extends "mat3" | "mat3x3"
? "vec3"
: A extends "mat3x4"
? "vec4"
: never
: B extends "vec4"
? A extends "mat4x2"
? "vec2"
: A extends "mat4x3"
? "vec3"
: A extends "mat4" | "mat4x4"
? "vec4"
: never
: // Matrix * Matrix: mat{Acols}x{Arows} * mat{Bcols}x{Brows} => mat{Bcols}x{Arows}
A extends "mat2" | "mat2x2" // Acols = 2 => Brows = 2
? B extends "mat2" | "mat2x2"
? "mat2"
: B extends "mat3x2"
? "mat3x2"
: B extends "mat4x2"
? "mat4x2"
: never
: A extends "mat2x3" // Acols = 2 => Brows = 2
? B extends "mat2" | "mat2x2"
? "mat2x3"
: B extends "mat3x2"
? "mat3"
: B extends "mat4x2"
? "mat4x3"
: never
: A extends "mat2x4" // Acols = 2 => Brows = 2
? B extends "mat2" | "mat2x2"
? "mat2x4"
: B extends "mat3x2"
? "mat3x4"
: B extends "mat4x2"
? "mat4"
: never
: A extends "mat3x2" // Acols = 3 => Brows = 3
? B extends "mat2x3"
? "mat2"
: B extends "mat3" | "mat3x3"
? "mat3x2"
: B extends "mat4x3"
? "mat4x2"
: never
: A extends "mat3" | "mat3x3" // Acols = 3 => Brows = 3
? B extends "mat2x3"
? "mat2x3"
: B extends "mat3" | "mat3x3"
? "mat3"
: B extends "mat4x3"
? "mat4x3"
: never
: A extends "mat3x4" // Acols = 3 => Brows = 3
? B extends "mat2x3"
? "mat2x4"
: B extends "mat3" | "mat3x3"
? "mat3x4"
: B extends "mat4x3"
? "mat4"
: never
: A extends "mat4x2" // Acols = 4 => Brows = 4
? B extends "mat2x4"
? "mat2"
: B extends "mat3x4"
? "mat3x2"
: B extends
| "mat4"
| "mat4x4"
? "mat4x2"
: never
: A extends "mat4x3" // Acols = 4 => Brows = 4
? B extends "mat2x4"
? "mat2x3"
: B extends "mat3x4"
? "mat3"
: B extends
| "mat4"
| "mat4x4"
? "mat4x3"
: never
: A extends "mat4" | "mat4x4" // Acols = 4 => Brows = 4
? B extends "mat2x4"
? "mat2x4"
: B extends "mat3x4"
? "mat3x4"
: B extends
| "mat4"
| "mat4x4"
? "mat4"
: never
: never);
export type DivOutput<
A extends AllValueTypes,
B extends AllValueTypes,
> = AddOutput<A, B>;
export type IModOutput<
A extends AllIntTypes,
B extends AllIntTypes,
> = BaseType &
(A extends B
? A
: A extends "int"
? B extends IntTypes
? B
: never
: B extends "int"
? A extends IntTypes
? A
: never
: A extends "uint"
? B extends UintTypes
? B
: never
: B extends "uint"
? A extends UintTypes
? A
: never
: never);
export type ModOutput<A extends FloatTypes, B extends FloatTypes> = BaseType &
(A extends B ? A : B extends "float" ? A : never);
export type PowOutput<A extends FloatTypes, B extends FloatTypes> = BaseType &
(A extends B ? A : never);
export type MinOutput<A extends ValueTypes, B extends ValueTypes> = BaseType &
(A extends B
? A
: B extends "float"
? A extends FloatTypes
? A
: never
: B extends "int"
? A extends IntTypes
? A
: never
: B extends "uint"
? A extends UintTypes
? A
: never
: never);
export type MaxOutput<A extends ValueTypes, B extends ValueTypes> = MinOutput<
A,
B
>;
export type ClampOutput<A extends ValueTypes, B extends ValueTypes> = BaseType &
(B extends "float"
? A extends FloatTypes
? A
: never
: B extends "int"
? A extends IntTypes
? A
: never
: B extends "uint"
? A extends UintTypes
? A
: never
: never);
export type MixOutput<
A extends FloatTypes,
T extends FloatTypes | BoolTypes,
> = BaseType &
(T extends A
? A
: T extends "float"
? A
: T extends "bool"
? A extends "float"
? A
: never
: T extends "bvec2"
? A extends "vec2"
? A
: never
: T extends "bvec3"
? A extends "vec3"
? A
: never
: T extends "bvec4"
? A extends "vec4"
? A
: never
: never);
export type StepOutput<A extends FloatTypes, B extends FloatTypes> = BaseType &
(A extends B ? B : A extends "float" ? B : never);
export type SmoothstepOutput<
A extends FloatTypes,
B extends FloatTypes,
C extends FloatTypes,
> = BaseType &
(A extends B ? (A extends C ? C : A extends "float" ? C : never) : never);
export type IsNanOutput<A extends FloatTypes> = BaseType &
(A extends "float"
? "bool"
: A extends "vec2"
? "bvec2"
: A extends "vec3"
? "bvec3"
: A extends "vec4"
? "bvec4"
: never);
export type IsInfOutput<A extends FloatTypes> = IsNanOutput<A>;
// // Run-time type helper functions
export function addOutputType<A extends AllValueTypes, B extends AllValueTypes>(
a: A,
b: B,
operation = "add",
): AddOutput<A, B> {
const error = () => {
throw new Error(`Invalid ${operation} types: ${a}, ${b}`);
};
// @ts-ignore
if (a === b) return a as AddOutput<A, B>;
if (a === "int") {
if (isIntType(b)) return b as AddOutput<A, B>;
error();
}
if (b === "int") {
if (isIntType(a)) return a as AddOutput<A, B>;
error();
}
if (a === "uint") {
if (isUintType(b)) return b as AddOutput<A, B>;
error();
}
if (b === "uint") {
if (isUintType(a)) return a as AddOutput<A, B>;
error();
}
if (a === "float") {
if (isAllFloatType(b)) return b as AddOutput<A, B>;
error();
}
if (b === "float") {
if (isAllFloatType(a)) return a as AddOutput<A, B>;
error();
}
throw new Error(`Invalid ${operation} types: ${a}, ${b}`);
}
export function subOutputType<A extends AllValueTypes, B extends AllValueTypes>(
a: A,
b: B,
): SubOutput<A, B> {
return addOutputType(a, b, "sub");
}
export function mulOutputType<A extends AllValueTypes, B extends AllValueTypes>(
a: A,
b: B,
): MulOutput<A, B> {
const error = () => {
throw new Error(`Invalid mul types: ${a}, ${b}`);
};
const result = (value: unknown) => value as MulOutput<A, B>;
if (a === "int") {
if (isIntType(b)) return result(b);
error();
}
if (b === "int") {
if (isIntType(a)) return result(a);
error();
}
if (a === "uint") {
if (isUintType(b)) return result(b);
error();
}
if (b === "uint") {
if (isUintType(a)) return result(a);
error();
}
if (a === "float") {
if (isAllFloatType(b)) return result(b);
error();
}
if (b === "float") {
if (isAllFloatType(a)) return result(a);
error();
}
if (isIntType(a) || isUintType(a) || isIntType(b) || isUintType(b)) {
// @ts-ignore
if (a === b) return result(a);
error();
}
// Vector * Matrix/Vector
if (a === "vec2") {
if (b === "vec2" || isMat2(b)) return result("vec2");
if (b === "mat3x2") return result("vec3");
if (b === "mat4x2") return result("vec4");
error();
}
if (a === "vec3") {
if (b === "mat2x3") return result("vec2");
if (b === "vec3" || isMat3(b)) return result("vec3");
if (b === "mat4x3") return result("vec4");
error();
}
if (a === "vec4") {
if (b === "mat2x4") return result("vec2");
if (b === "mat3x4") return result("vec3");
if (b === "vec4" || isMat4(b)) return result("vec4");
error();
}
// Matrix * Vector
if (b === "vec2") {
if (isMat2(a)) return result("vec2");
if (a === "mat2x3") return result("vec3");
if (a === "mat2x4") return result("vec4");
error();
}
if (b === "vec3") {
if (a === "mat3x2") return result("vec2");
if (isMat3(a)) return result("vec3");
if (a === "mat3x4") return result("vec4");
error();
}
if (b === "vec4") {
if (a === "mat4x2") return result("vec2");
if (a === "mat4x3") return result("vec3");
if (isMat4(a)) return result("vec4");
error();
}
// Matrix * Matrix: mat{Acols}x{Arows} * mat{Bcols}x{Brows} => mat{Bcols}x{Arows}
if (isMat2(a)) {
if (isMat2(b)) return result("mat2");
if (b === "mat3x2") return result("mat3x2");
if (b === "mat4x2") return result("mat4x2");
error();
}
if (a === "mat2x3") {
if (isMat2(b)) return result("mat2x3");
if (b === "mat3x2") return result("mat3");
if (b === "mat4x2") return result("mat4x3");
error();
}
if (a === "mat2x4") {
if (isMat2(b)) return result("mat2x4");
if (b === "mat3x2") return result("mat3x4");
if (b === "mat4x2") return result("mat4");
error();
}
if (a === "mat3x2") {
if (b === "mat2x3") return result("mat2");
if (isMat3(b)) return result("mat3x2");
if (b === "mat4x3") return result("mat4x2");
error();
}
if (isMat3(a)) {
if (b === "mat2x3") return result("mat2x3");
if (isMat3(b)) return result("mat3");
if (b === "mat4x3") return result("mat4x3");
error();
}
if (a === "mat3x4") {
if (b === "mat2x3") return result("mat2x4");
if (isMat3(b)) return result("mat3x4");
if (b === "mat4x3") return result("mat4");
error();
}
if (a === "mat4x2") {
if (b === "mat2x4") return result("mat2");
if (b === "mat3x4") return result("mat3x2");
if (isMat4(b)) return result("mat4x2");
error();
}
if (a === "mat4x3") {
if (b === "mat2x4") return result("mat2x3");
if (b === "mat3x4") return result("mat3");
if (isMat4(b)) return result("mat4x3");
error();
}
if (isMat4(a)) {
if (b === "mat2x4") return result("mat2x4");
if (b === "mat3x4") return result("mat3x4");
if (isMat4(b)) return result("mat4");
error();
}
throw new Error(`Invalid mul types: ${a}, ${b}`);
}
export function divOutputType<A extends AllValueTypes, B extends AllValueTypes>(
a: A,
b: B,
): DivOutput<A, B> {
return addOutputType(a, b, "div");
}
export function imodOutputType<A extends AllIntTypes, B extends AllIntTypes>(
a: A,
b: B,
): IModOutput<A, B> {
// @ts-ignore
if (a === b) return a as IModOutput<A, B>;
if (a === "int") {
if (isIntType(b)) return b as IModOutput<A, B>;
} else if (b === "int") {
if (isIntType(a)) return a as IModOutput<A, B>;
} else if (a === "uint") {
if (isUintType(b)) return b as IModOutput<A, B>;
} else if (b === "uint") {
if (isUintType(a)) return a as IModOutput<A, B>;
}
throw new Error(`Invalid imod types: ${a}, ${b}`);
}
export function modOutputType<A extends FloatTypes, B extends FloatTypes>(
a: A,
b: B,
): ModOutput<A, B> {
// @ts-ignore
if (a === b || b === "float") return a as ModOutput<A, B>;
throw new Error(`Invalid mod types: ${a}, ${b}`);
}
export function modfOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function negOutputType<A extends AllSignedTypes>(a: A): A {
return a;
}
export function absOutputType<A extends SignedTypes>(a: A): A {
return a;
}
export function signOutputType<A extends SignedTypes>(a: A): A {
return a;
}
export function floorOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function ceilOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function truncOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function roundOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function fractOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function powOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function expOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function exp2OutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function logOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function log2OutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function sqrOutputType<A extends ValueTypes>(a: A): A {
return a;
}
export function sqrtOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function inversesqrtOutputType<A extends FloatTypes>(a: A): A {
return a;
}
export function minOutputType<A extends ValueTypes, B extends ValueTypes>(
a: A,
b: B,
operation = "min",
): MinOutput<A, B> {
// @ts-ignore
if (a === b) return a as MinOutput<A, B>;
if (b === "float") {
if (isFloatType(a)) return a as MinOutput<A, B>;
} else if (b === "int") {
if (isIntType(a)) return a as MinOutput<A, B>;
} else if (b === "uint") {
if (isUintType(a)) return a as MinOutput<A, B>;
}
throw new Error(`Invalid ${operation} types: ${a}, ${b}`);
}
export function maxOutputType<A extends ValueTypes, B extends ValueTypes>(
a: A,
b: B,
): MaxOutput<A, B> {
return minOutputType(a, b, "max");
}
export function clampOutputType<A extends ValueTypes, B extends ValueTypes>(
a: A,
b: B,
_c: B,
): ClampOutput<A, B> {
if (b === "float") {
if (isFloatType(a)) return a as ClampOutput<A, B>;
} else if (b === "int") {
if (isIntType(a)) return a as ClampOutput<A, B>;
} else if (b === "uint") {
if (isUintType(a)) return a as ClampOutput<A, B>;
}
throw new Error(`Invalid clamp types: ${a}, ${b}`);
}
export function mixOutputType<
A extends FloatTypes,
C extends FloatTypes | BoolTypes,
>(a: A, b: A, c: C): MixOutput<A, C> {
// @ts-ignore
if (c === a) return a as MixOutput<A, C>;
if (c === "float") return a as MixOutput<A, C>;
if (c === "bool" && a === "float") return a as MixOutput<A, C>;
if (c === "bvec2" && a === "vec2") return a as MixOutput<A, C>;
if (c === "bvec3" && a === "vec3") return a as MixOutput<A, C>;
if (c === "bvec4" && a === "vec4") return a as MixOutput<A, C>;
throw new Error(`Invalid mix types: ${a}, ${b}, ${c}`);
}
export function stepOutputType<A extends FloatTypes, B extends FloatTypes>(
a: A,
b: B,
): StepOutput<A, B> {
// @ts-ignore
if (a === b || b === "float") return b as StepOutput<A, B>;
throw new Error(`Invalid step types: ${a}, ${b}`);
}
export function smoothstepOutputType<
A extends FloatTypes,
B extends FloatTypes,
C extends FloatTypes,
>(a: A, b: B, c: C): SmoothstepOutput<A, B, C> {
// @ts-ignore
if (a === b) {
if (a === c || a === "float") return c as SmoothstepOutput<A, B, C>;
}
throw new Error(`Invalid smoothstep types: ${a}, ${b}, ${c}`);
}
export function isNanOutputType<A extends FloatTypes>(
a: A,
operation = "isNan",
): IsNanOutput<A> {
if (a === "float") return "bool" as IsNanOutput<A>;
if (a === "vec2") return "bvec2" as IsNanOutput<A>;
if (a === "vec3") return "bvec3" as IsNanOutput<A>;
if (a === "vec4") return "bvec4" as IsNanOutput<A>;
throw new Error(`Invalid ${operation} types: ${a}`);
}
export function isInfOutputType<A extends FloatTypes>(a: A): IsInfOutput<A> {
return isNanOutputType(a, "isInf");
}
+66
View File
@@ -0,0 +1,66 @@
import { Dyno, unindentLines } from "./base";
import { Gsplat, defineGsplat } from "./splats";
import {
DynoOutput,
type DynoVal,
type DynoValue,
type HasDynoOut,
} from "./value";
export const outputPackedSplat = (gsplat: DynoVal<typeof Gsplat>) =>
new OutputPackedSplat({ gsplat });
export const outputRgba8 = (rgba8: DynoVal<"vec4">) =>
new OutputRgba8({ rgba8 });
export class OutputPackedSplat
extends Dyno<{ gsplat: typeof Gsplat }, { output: "uvec4" }>
implements HasDynoOut<"uvec4">
{
constructor({ gsplat }: { gsplat?: DynoVal<typeof Gsplat> }) {
super({
inTypes: { gsplat: Gsplat },
inputs: { gsplat },
globals: () => [defineGsplat],
statements: ({ inputs, outputs }) => {
const { output } = outputs;
if (!output) {
return [];
}
const { gsplat } = inputs;
if (gsplat) {
return unindentLines(`
if (isGsplatActive(${gsplat}.flags)) {
${output} = packSplat(${gsplat}.center, ${gsplat}.scales, ${gsplat}.quaternion, ${gsplat}.rgba);
} else {
${output} = uvec4(0u, 0u, 0u, 0u);
}
`);
}
return [`${output} = uvec4(0u, 0u, 0u, 0u);`];
},
});
}
dynoOut(): DynoValue<"uvec4"> {
return new DynoOutput(this, "output");
}
}
export class OutputRgba8
extends Dyno<{ rgba8: "vec4" }, { rgba8: "vec4" }>
implements HasDynoOut<"vec4">
{
constructor({ rgba8 }: { rgba8?: DynoVal<"vec4"> }) {
super({
inTypes: { rgba8: "vec4" },
inputs: { rgba8 },
statements: ({ inputs, outputs }) => [
`target = ${inputs.rgba8 ?? "vec4(0.0, 0.0, 0.0, 0.0)"};`,
],
});
}
dynoOut(): DynoValue<"vec4"> {
return new DynoOutput(this, "rgba8");
}
}
+117
View File
@@ -0,0 +1,117 @@
import * as THREE from "three";
import { IDENT_VERTEX_SHADER } from "../utils";
import { Compilation, type Dyno, type IOTypes } from "./base";
export class DynoProgram {
graph: Dyno<IOTypes, IOTypes>;
template: DynoProgramTemplate;
inputs: Record<string, string>;
outputs: Record<string, string>;
shader: string;
uniforms: Record<string, THREE.IUniform>;
updaters: (() => void)[];
constructor({
graph,
inputs,
outputs,
template,
}: {
graph: Dyno<IOTypes, IOTypes>;
inputs?: Record<string, string>;
outputs?: Record<string, string>;
template: DynoProgramTemplate;
}) {
this.graph = graph;
this.template = template;
this.inputs = inputs ?? {};
this.outputs = outputs ?? {};
const compile = new Compilation({ indent: this.template.indent });
for (const key in this.outputs) {
if (this.outputs[key]) {
compile.declares.add(this.outputs[key]);
}
}
const statements = graph.compile({
inputs: this.inputs,
outputs: this.outputs,
compile,
});
this.shader = template.generate({ globals: compile.globals, statements });
this.uniforms = compile.uniforms;
this.updaters = compile.updaters;
// console.log("*** COMPILED SHADER", this.shader);
// console.log("*** UNIFORMS", this.uniforms);
}
prepareMaterial(): THREE.RawShaderMaterial {
return getMaterial(this);
}
update() {
for (const updater of this.updaters) {
updater();
}
}
}
export class DynoProgramTemplate {
before: string;
between: string;
after: string;
indent: string;
constructor(template: string) {
const globals = template.match(/^([ \t]*)\{\{\s*GLOBALS\s*\}\}/m);
const statements = template.match(/^([ \t]*)\{\{\s*STATEMENTS\s*\}\}/m);
if (!globals || !statements) {
throw new Error(
"Template must contain {{ GLOBALS }} and {{ STATEMENTS }}",
);
}
this.before = template.substring(0, globals.index);
this.between = template.substring(
(globals.index as number) + globals[0].length,
statements.index,
);
this.after = template.substring(
(statements.index as number) + statements[0].length,
);
this.indent = statements[1];
}
generate({
globals,
statements,
}: { globals: Set<string>; statements: string[] }): string {
return (
this.before +
Array.from(globals).join("\n\n") +
this.between +
statements.map((s) => this.indent + s).join("\n") +
this.after
);
}
}
const programMaterial = new Map<DynoProgram, THREE.RawShaderMaterial>();
function getMaterial(program: DynoProgram): THREE.RawShaderMaterial {
let material = programMaterial.get(program);
if (material) {
return material;
}
material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3,
vertexShader: IDENT_VERTEX_SHADER,
fragmentShader: program.shader,
uniforms: program.uniforms,
});
programMaterial.set(program, material);
return material;
}
+593
View File
@@ -0,0 +1,593 @@
import { Dyno, UnaryOp, unindent, unindentLines } from "./base";
import {
DynoOutput,
type DynoVal,
type DynoValue,
type HasDynoOut,
} from "./value";
export const Gsplat = { type: "Gsplat" } as { type: "Gsplat" };
export const TPackedSplats = { type: "PackedSplats" } as {
type: "PackedSplats";
};
export const numPackedSplats = (
packedSplats: DynoVal<typeof TPackedSplats>,
): DynoVal<"int"> => new NumPackedSplats({ packedSplats });
export const readPackedSplat = (
packedSplats: DynoVal<typeof TPackedSplats>,
index: DynoVal<"int">,
): DynoVal<typeof Gsplat> => new ReadPackedSplat({ packedSplats, index });
export const readPackedSplatRange = (
packedSplats: DynoVal<typeof TPackedSplats>,
index: DynoVal<"int">,
base: DynoVal<"int">,
count: DynoVal<"int">,
): DynoVal<typeof Gsplat> =>
new ReadPackedSplatRange({ packedSplats, index, base, count });
export const splitGsplat = (gsplat: DynoVal<typeof Gsplat>) =>
new SplitGsplat({ gsplat });
export const combineGsplat = ({
gsplat,
flags,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
}: {
gsplat?: DynoVal<typeof Gsplat>;
flags?: DynoVal<"uint">;
index?: DynoVal<"int">;
center?: DynoVal<"vec3">;
scales?: DynoVal<"vec3">;
quaternion?: DynoVal<"vec4">;
rgba?: DynoVal<"vec4">;
rgb?: DynoVal<"vec3">;
opacity?: DynoVal<"float">;
x?: DynoVal<"float">;
y?: DynoVal<"float">;
z?: DynoVal<"float">;
r?: DynoVal<"float">;
g?: DynoVal<"float">;
b?: DynoVal<"float">;
}): DynoVal<typeof Gsplat> => {
return new CombineGsplat({
gsplat,
flags,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
});
};
export const gsplatNormal = (gsplat: DynoVal<typeof Gsplat>): DynoVal<"vec3"> =>
new GsplatNormal({ gsplat });
export const transformGsplat = (
gsplat: DynoVal<typeof Gsplat>,
{
scale,
rotate,
translate,
recolor,
}: {
scale?: DynoVal<"float">;
rotate?: DynoVal<"vec4">;
translate?: DynoVal<"vec3">;
recolor?: DynoVal<"vec4">;
},
): DynoVal<typeof Gsplat> => {
return new TransformGsplat({ gsplat, scale, rotate, translate, recolor });
};
export const defineGsplat = unindent(`
struct Gsplat {
vec3 center;
uint flags;
vec3 scales;
int index;
vec4 quaternion;
vec4 rgba;
};
const uint GSPLAT_FLAG_ACTIVE = 1u << 0u;
bool isGsplatActive(uint flags) {
return (flags & GSPLAT_FLAG_ACTIVE) != 0u;
}
`);
export const definePackedSplats = unindent(`
struct PackedSplats {
usampler2DArray texture;
int numSplats;
};
`);
export class NumPackedSplats extends UnaryOp<
typeof TPackedSplats,
"int",
"numSplats"
> {
constructor({
packedSplats,
}: { packedSplats: DynoVal<typeof TPackedSplats> }) {
super({ a: packedSplats, outKey: "numSplats", outTypeFunc: () => "int" });
this.statements = ({ inputs, outputs }) => [
`${outputs.numSplats} = ${inputs.a}.numSplats;`,
];
}
}
const defineReadPackedSplat = unindent(`
bool readPackedSplat(usampler2DArray texture, int numSplats, int index, out Gsplat gsplat) {
if ((index >= 0) && (index < numSplats)) {
uvec4 packed = texelFetch(texture, splatTexCoord(index), 0);
unpackSplat(packed, gsplat.center, gsplat.scales, gsplat.quaternion, gsplat.rgba);
return true;
} else {
return false;
}
}
`);
export class ReadPackedSplat
extends Dyno<
{ packedSplats: typeof TPackedSplats; index: "int" },
{ gsplat: typeof Gsplat }
>
implements HasDynoOut<typeof Gsplat>
{
constructor({
packedSplats,
index,
}: { packedSplats?: DynoVal<typeof TPackedSplats>; index?: DynoVal<"int"> }) {
super({
inTypes: { packedSplats: TPackedSplats, index: "int" },
outTypes: { gsplat: Gsplat },
inputs: { packedSplats, index },
globals: () => [defineGsplat, definePackedSplats, defineReadPackedSplat],
statements: ({ inputs, outputs }) => {
const { gsplat } = outputs;
if (!gsplat) {
return [];
}
const { packedSplats, index } = inputs;
let statements: string[];
if (packedSplats && index) {
statements = unindentLines(`
if (readPackedSplat(${packedSplats}.texture, ${packedSplats}.numSplats, ${index}, ${gsplat})) {
bool zeroSize = all(equal(${gsplat}.scales, vec3(0.0, 0.0, 0.0)));
${gsplat}.flags = zeroSize ? 0u : GSPLAT_FLAG_ACTIVE;
} else {
${gsplat}.flags = 0u;
}
`);
} else {
statements = [`${gsplat}.flags = 0u;`];
}
statements.push(`${gsplat}.index = ${index ?? "0"};`);
return statements;
},
});
}
dynoOut(): DynoValue<typeof Gsplat> {
return new DynoOutput(this, "gsplat");
}
}
export class ReadPackedSplatRange
extends Dyno<
{
packedSplats: typeof TPackedSplats;
index: "int";
base: "int";
count: "int";
},
{ gsplat: typeof Gsplat }
>
implements HasDynoOut<typeof Gsplat>
{
constructor({
packedSplats,
index,
base,
count,
}: {
packedSplats?: DynoVal<typeof TPackedSplats>;
index?: DynoVal<"int">;
base?: DynoVal<"int">;
count?: DynoVal<"int">;
}) {
super({
inTypes: {
packedSplats: TPackedSplats,
index: "int",
base: "int",
count: "int",
},
outTypes: { gsplat: Gsplat },
inputs: { packedSplats, index, base, count },
globals: () => [defineGsplat, definePackedSplats, defineReadPackedSplat],
statements: ({ inputs, outputs }) => {
const { gsplat } = outputs;
if (!gsplat) {
return [];
}
const { packedSplats, index, base, count } = inputs;
let statements: string[];
if (packedSplats && index && base && count) {
statements = unindentLines(`
${gsplat}.flags = 0u;
if ((${index} >= ${base}) && (${index} < (${base} + ${count}))) {
if (readPackedSplat(${packedSplats}.texture, ${packedSplats}.numSplats, ${index}, ${gsplat})) {
bool zeroSize = all(equal(${gsplat}.scales, vec3(0.0, 0.0, 0.0)));
${gsplat}.flags = zeroSize ? 0u : GSPLAT_FLAG_ACTIVE;
}
}
`);
} else {
statements = [`${gsplat}.flags = 0u;`];
}
statements.push(`${gsplat}.index = ${index ?? "0"};`);
return statements;
},
});
}
dynoOut(): DynoValue<typeof Gsplat> {
return new DynoOutput(this, "gsplat");
}
}
export class SplitGsplat extends Dyno<
{ gsplat: typeof Gsplat },
{
flags: "uint";
active: "bool";
index: "int";
center: "vec3";
scales: "vec3";
quaternion: "vec4";
rgba: "vec4";
rgb: "vec3";
opacity: "float";
x: "float";
y: "float";
z: "float";
r: "float";
g: "float";
b: "float";
}
> {
constructor({ gsplat }: { gsplat?: DynoVal<typeof Gsplat> }) {
super({
inTypes: { gsplat: Gsplat },
outTypes: {
flags: "uint",
active: "bool",
index: "int",
center: "vec3",
scales: "vec3",
quaternion: "vec4",
rgba: "vec4",
rgb: "vec3",
opacity: "float",
x: "float",
y: "float",
z: "float",
r: "float",
g: "float",
b: "float",
},
inputs: { gsplat },
globals: () => [defineGsplat],
statements: ({ inputs, outputs }) => {
const { gsplat } = inputs;
const {
flags,
active,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
} = outputs;
return [
!flags ? null : `${flags} = ${gsplat ? `${gsplat}.flags` : "0u"};`,
!active
? null
: `${active} = isGsplatActive(${gsplat ? `${gsplat}.flags` : "0u"});`,
!index ? null : `${index} = ${gsplat ? `${gsplat}.index` : "0"};`,
!center
? null
: `${center} = ${gsplat ? `${gsplat}.center` : "vec3(0.0, 0.0, 0.0)"};`,
!scales
? null
: `${scales} = ${gsplat ? `${gsplat}.scales` : "vec3(0.0, 0.0, 0.0)"};`,
!quaternion
? null
: `${quaternion} = ${gsplat ? `${gsplat}.quaternion` : "vec4(0.0, 0.0, 0.0, 1.0)"};`,
!rgba
? null
: `${rgba} = ${gsplat ? `${gsplat}.rgba` : "vec4(0.0, 0.0, 0.0, 0.0)"};`,
!rgb
? null
: `${rgb} = ${gsplat ? `${gsplat}.rgba.rgb` : "vec3(0.0, 0.0, 0.0)"};`,
!opacity
? null
: `${opacity} = ${gsplat ? `${gsplat}.rgba.a` : "0.0"};`,
!x ? null : `${x} = ${gsplat ? `${gsplat}.center.x` : "0.0"};`,
!y ? null : `${y} = ${gsplat ? `${gsplat}.center.y` : "0.0"};`,
!z ? null : `${z} = ${gsplat ? `${gsplat}.center.z` : "0.0"};`,
!r ? null : `${r} = ${gsplat ? `${gsplat}.rgba.r` : "0.0"};`,
!g ? null : `${g} = ${gsplat ? `${gsplat}.rgba.g` : "0.0"};`,
!b ? null : `${b} = ${gsplat ? `${gsplat}.rgba.b` : "0.0"};`,
].filter(Boolean) as string[];
},
});
}
}
export class CombineGsplat
extends Dyno<
{
gsplat: typeof Gsplat;
flags: "uint";
index: "int";
center: "vec3";
scales: "vec3";
quaternion: "vec4";
rgba: "vec4";
rgb: "vec3";
opacity: "float";
x: "float";
y: "float";
z: "float";
r: "float";
g: "float";
b: "float";
},
{ gsplat: typeof Gsplat }
>
implements HasDynoOut<typeof Gsplat>
{
constructor({
gsplat,
flags,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
}: {
gsplat?: DynoVal<typeof Gsplat>;
flags?: DynoVal<"uint">;
index?: DynoVal<"int">;
center?: DynoVal<"vec3">;
scales?: DynoVal<"vec3">;
quaternion?: DynoVal<"vec4">;
rgba?: DynoVal<"vec4">;
rgb?: DynoVal<"vec3">;
opacity?: DynoVal<"float">;
x?: DynoVal<"float">;
y?: DynoVal<"float">;
z?: DynoVal<"float">;
r?: DynoVal<"float">;
g?: DynoVal<"float">;
b?: DynoVal<"float">;
}) {
super({
inTypes: {
gsplat: Gsplat,
flags: "uint",
index: "int",
center: "vec3",
scales: "vec3",
quaternion: "vec4",
rgba: "vec4",
rgb: "vec3",
opacity: "float",
x: "float",
y: "float",
z: "float",
r: "float",
g: "float",
b: "float",
},
outTypes: { gsplat: Gsplat },
inputs: {
gsplat,
flags,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
},
globals: () => [defineGsplat],
statements: ({ inputs, outputs }) => {
const { gsplat: outGsplat } = outputs;
if (!outGsplat) {
return [];
}
const {
gsplat,
flags,
index,
center,
scales,
quaternion,
rgba,
rgb,
opacity,
x,
y,
z,
r,
g,
b,
} = inputs;
return [
`${outGsplat}.flags = ${flags ?? (gsplat ? `${gsplat}.flags` : "0u")};`,
`${outGsplat}.index = ${index ?? (gsplat ? `${gsplat}.index` : "0")};`,
`${outGsplat}.center = ${center ?? (gsplat ? `${gsplat}.center` : "vec3(0.0, 0.0, 0.0)")};`,
`${outGsplat}.scales = ${scales ?? (gsplat ? `${gsplat}.scales` : "vec3(0.0, 0.0, 0.0)")};`,
`${outGsplat}.quaternion = ${quaternion ?? (gsplat ? `${gsplat}.quaternion` : "vec4(0.0, 0.0, 0.0, 1.0)")};`,
`${outGsplat}.rgba = ${rgba ?? (gsplat ? `${gsplat}.rgba` : "vec4(0.0, 0.0, 0.0, 0.0)")};`,
!rgb ? null : `${outGsplat}.rgba.rgb = ${rgb};`,
!opacity ? null : `${outGsplat}.rgba.a = ${opacity};`,
!x ? null : `${outGsplat}.center.x = ${x};`,
!y ? null : `${outGsplat}.center.y = ${y};`,
!z ? null : `${outGsplat}.center.z = ${z};`,
!r ? null : `${outGsplat}.rgba.r = ${r};`,
!g ? null : `${outGsplat}.rgba.g = ${g};`,
!b ? null : `${outGsplat}.rgba.b = ${b};`,
].filter(Boolean) as string[];
},
});
}
dynoOut(): DynoValue<typeof Gsplat> {
return new DynoOutput(this, "gsplat");
}
}
export const defineGsplatNormal = unindent(`
vec3 gsplatNormal(vec3 scales, vec4 quaternion) {
float minScale = min(scales.x, min(scales.y, scales.z));
vec3 normal;
if (scales.z == minScale) {
normal = vec3(0.0, 0.0, 1.0);
} else if (scales.y == minScale) {
normal = vec3(0.0, 1.0, 0.0);
} else {
normal = vec3(1.0, 0.0, 0.0);
}
return quatVec(quaternion, normal);
}
`);
export class GsplatNormal extends UnaryOp<typeof Gsplat, "vec3", "normal"> {
constructor({ gsplat }: { gsplat: DynoVal<typeof Gsplat> }) {
super({ a: gsplat, outKey: "normal", outTypeFunc: () => "vec3" });
this.globals = () => [defineGsplat, defineGsplatNormal];
this.statements = ({ inputs, outputs }) => [
`${outputs.normal} = gsplatNormal(${inputs.a}.scales, ${inputs.a}.quaternion);`,
];
}
}
export class TransformGsplat
extends Dyno<
{
gsplat: typeof Gsplat;
scale: "float";
rotate: "vec4";
translate: "vec3";
recolor: "vec4";
},
{ gsplat: typeof Gsplat }
>
implements HasDynoOut<typeof Gsplat>
{
constructor({
gsplat,
scale,
rotate,
translate,
recolor,
}: {
gsplat?: DynoVal<typeof Gsplat>;
scale?: DynoVal<"float">;
rotate?: DynoVal<"vec4">;
translate?: DynoVal<"vec3">;
recolor?: DynoVal<"vec4">;
}) {
super({
inTypes: {
gsplat: Gsplat,
scale: "float",
rotate: "vec4",
translate: "vec3",
recolor: "vec4",
},
outTypes: { gsplat: Gsplat },
inputs: { gsplat, scale, rotate, translate, recolor },
globals: () => [defineGsplat],
statements: ({ inputs, outputs, compile }) => {
const { gsplat } = outputs;
if (!gsplat || !inputs.gsplat) {
return [];
}
const { scale, rotate, translate, recolor } = inputs;
const indent = compile.indent;
const statements = [
`${gsplat} = ${inputs.gsplat};`,
`if (isGsplatActive(${gsplat}.flags)) {`,
scale ? `${indent}${gsplat}.center *= ${scale};` : null,
rotate
? `${indent}${gsplat}.center = quatVec(${rotate}, ${gsplat}.center);`
: null,
translate ? `${indent}${gsplat}.center += ${translate};` : null,
scale ? `${indent}${gsplat}.scales *= ${scale};` : null,
rotate
? `${indent}${gsplat}.quaternion = quatQuat(${rotate}, ${gsplat}.quaternion);`
: null,
recolor ? `${indent}${gsplat}.rgba *= ${recolor};` : null,
"}",
].filter(Boolean) as string[];
return statements;
},
});
}
dynoOut(): DynoValue<typeof Gsplat> {
return new DynoOutput(this, "gsplat");
}
}
+239
View File
@@ -0,0 +1,239 @@
import { Dyno } from "./base";
import type {
AllSamplerTypes,
IsamplerTypes,
NormalSamplerTypes,
Sampler2DArrayTypes,
Sampler2DTypes,
Sampler3DTypes,
SamplerCubeTypes,
SamplerShadowTypes,
SamplerTypes,
UsamplerTypes,
} from "./types";
import {
DynoOutput,
type DynoVal,
type DynoValue,
type HasDynoOut,
valType,
} from "./value";
export const textureSize = <T extends AllSamplerTypes>(
texture: DynoVal<T>,
lod?: DynoVal<"int">,
): DynoVal<TextureSizeType<T>> => new TextureSize<T>({ texture, lod });
export const texture = <T extends AllSamplerTypes>(
texture: DynoVal<T>,
coord: DynoVal<TextureCoordType<T>>,
bias?: DynoVal<"float">,
): DynoVal<TextureReturnType<T>> => new Texture<T>({ texture, coord, bias });
export const texelFetch = <T extends NormalSamplerTypes>(
texture: DynoVal<T>,
coord: DynoVal<TextureSizeType<T>>,
lod?: DynoVal<"int">,
): DynoVal<TextureReturnType<T>> => new TexelFetch<T>({ texture, coord, lod });
export class TextureSize<T extends AllSamplerTypes>
extends Dyno<{ texture: T; lod: "int" }, { size: TextureSizeType<T> }>
implements HasDynoOut<TextureSizeType<T>>
{
constructor({ texture, lod }: { texture: DynoVal<T>; lod?: DynoVal<"int"> }) {
const textureType = valType(texture);
super({
inTypes: { texture: textureType, lod: "int" },
outTypes: { size: textureSizeType(textureType) },
inputs: { texture, lod },
statements: ({ inputs, outputs }) => [
`${outputs.size} = textureSize(${inputs.texture}, ${inputs.lod ?? "0"});`,
],
});
}
dynoOut(): DynoValue<TextureSizeType<T>> {
return new DynoOutput(this, "size");
}
}
export class Texture<T extends AllSamplerTypes>
extends Dyno<
{ texture: T; coord: TextureCoordType<T>; bias: "float" },
{ sample: TextureReturnType<T> }
>
implements HasDynoOut<TextureReturnType<T>>
{
constructor({
texture,
coord,
bias,
}: {
texture: DynoVal<T>;
coord: DynoVal<TextureCoordType<T>>;
bias?: DynoVal<"float">;
}) {
const textureType = valType(texture);
super({
inTypes: {
texture: textureType,
coord: textureCoordType(textureType),
bias: "float",
},
outTypes: { sample: textureReturnType(textureType) },
inputs: { texture, coord, bias },
statements: ({ inputs, outputs }) => [
`${outputs.sample} = texture(${inputs.texture}, ${inputs.coord}${inputs.bias ? `, ${inputs.bias}` : ""});`,
],
});
}
dynoOut(): DynoValue<TextureReturnType<T>> {
return new DynoOutput(this, "sample");
}
}
export class TexelFetch<T extends NormalSamplerTypes>
extends Dyno<
{ texture: T; coord: TextureSizeType<T>; lod: "int" },
{ texel: TextureReturnType<T> }
>
implements HasDynoOut<TextureReturnType<T>>
{
constructor({
texture,
coord,
lod,
}: {
texture: DynoVal<T>;
coord: DynoVal<TextureSizeType<T>>;
lod?: DynoVal<"int">;
}) {
const textureType = valType(texture);
super({
inTypes: {
texture: textureType,
coord: textureSizeType(textureType),
lod: "int",
},
outTypes: { texel: textureReturnType(textureType) },
inputs: { texture, coord, lod },
statements: ({ inputs, outputs }) => [
`${outputs.texel} = texelFetch(${inputs.texture}, ${inputs.coord}, ${inputs.lod ?? "0"});`,
],
});
}
dynoOut(): DynoValue<TextureReturnType<T>> {
return new DynoOutput(this, "texel");
}
}
type TextureSizeType<T extends AllSamplerTypes> = T extends
| Sampler2DTypes
| SamplerCubeTypes
? "ivec2"
: T extends Sampler3DTypes | Sampler2DArrayTypes
? "ivec3"
: never;
function textureSizeType<T extends AllSamplerTypes>(
textureType: T,
): TextureSizeType<T> {
switch (textureType) {
case "sampler2D":
case "usampler2D":
case "isampler2D":
case "samplerCube":
case "usamplerCube":
case "isamplerCube":
case "sampler2DShadow":
case "samplerCubeShadow":
return "ivec2" as TextureSizeType<T>;
case "sampler3D":
case "usampler3D":
case "isampler3D":
case "sampler2DArray":
case "usampler2DArray":
case "isampler2DArray":
case "sampler2DArrayShadow":
return "ivec3" as TextureSizeType<T>;
default:
throw new Error(`Invalid texture type: ${textureType}`);
}
}
type TextureCoordType<T extends AllSamplerTypes> = T extends Sampler2DTypes
? "vec2"
: T extends
| Sampler3DTypes
| Sampler2DArrayTypes
| SamplerCubeTypes
| Sampler2DArrayTypes
? "vec3"
: T extends "samperCubeShadow" | "sampler2DArrayShadow"
? "vec4"
: never;
function textureCoordType<T extends AllSamplerTypes>(
textureType: T,
): TextureCoordType<T> {
switch (textureType) {
case "sampler2D":
case "usampler2D":
case "isampler2D":
return "vec2" as TextureCoordType<T>;
case "sampler3D":
case "usampler3D":
case "isampler3D":
case "samplerCube":
case "usamplerCube":
case "isamplerCube":
case "sampler2DArray":
case "usampler2DArray":
case "isampler2DArray":
case "sampler2DShadow":
return "vec3" as TextureCoordType<T>;
case "samplerCubeShadow":
case "sampler2DArrayShadow":
return "vec4" as TextureCoordType<T>;
default:
throw new Error(`Invalid texture type: ${textureType}`);
}
}
type TextureReturnType<T extends AllSamplerTypes> = T extends SamplerTypes
? "vec4"
: T extends UsamplerTypes
? "uvec4"
: T extends IsamplerTypes
? "ivec4"
: T extends SamplerShadowTypes
? "float"
: never;
function textureReturnType<T extends AllSamplerTypes>(
textureType: T,
): TextureReturnType<T> {
switch (textureType) {
case "sampler2D":
case "sampler2DArray":
case "sampler3D":
case "samplerCube":
case "sampler2DShadow":
return "vec4" as TextureReturnType<T>;
case "usampler2D":
case "usampler2DArray":
case "usampler3D":
case "usamplerCube":
return "uvec4" as TextureReturnType<T>;
case "isampler2D":
case "isampler2DArray":
case "isampler3D":
case "isamplerCube":
return "ivec4" as TextureReturnType<T>;
case "samplerCubeShadow":
case "sampler2DArrayShadow":
return "float" as TextureReturnType<T>;
default:
throw new Error(`Invalid texture type: ${textureType}`);
}
}
+155
View File
@@ -0,0 +1,155 @@
import { Dyno } from "./base";
import type { DynoVal } from "./value";
export const transformPos = (
position: DynoVal<"vec3">,
{
scale,
scales,
rotate,
translate,
}: {
scale?: DynoVal<"float">;
scales?: DynoVal<"vec3">;
rotate?: DynoVal<"vec4">;
translate?: DynoVal<"vec3">;
},
): DynoVal<"vec3"> => {
return new TransformPosition({ position, scale, scales, rotate, translate })
.outputs.position;
};
export const transformRay = (
ray: DynoVal<"vec3">,
{
scale,
scales,
rotate,
}: {
scale?: DynoVal<"float">;
scales?: DynoVal<"vec3">;
rotate?: DynoVal<"vec4">;
},
): DynoVal<"vec3"> => {
return new TransformRay({ ray, scale, scales, rotate }).outputs.ray;
};
export const transformQuat = (
quaternion: DynoVal<"vec4">,
{ rotate }: { rotate?: DynoVal<"vec4"> },
): DynoVal<"vec4"> => {
return new TransformQuaternion({ quaternion, rotate }).outputs.quaternion;
};
export class TransformPosition extends Dyno<
{
position: "vec3";
scale: "float";
scales: "vec3";
rotate: "vec4";
translate: "vec3";
},
{ position: "vec3" }
> {
constructor({
position,
scale,
scales,
rotate,
translate,
}: {
position?: DynoVal<"vec3">;
scale?: DynoVal<"float">;
scales?: DynoVal<"vec3">;
rotate?: DynoVal<"vec4">;
translate?: DynoVal<"vec3">;
}) {
super({
inTypes: {
position: "vec3",
scale: "float",
scales: "vec3",
rotate: "vec4",
translate: "vec3",
},
outTypes: { position: "vec3" },
inputs: { position, scale, scales, rotate, translate },
statements: ({ inputs, outputs }) => {
const { position } = outputs;
if (!position) {
return [];
}
const { scale, scales, rotate, translate } = inputs;
return [
`${position} = ${inputs.position ?? "vec3(0.0, 0.0, 0.0)"};`,
!scale ? null : `${position} *= ${scale};`,
!scales ? null : `${position} *= ${scales};`,
!rotate ? null : `${position} = quatVec(${rotate}, ${position});`,
!translate ? null : `${position} += ${translate};`,
].filter(Boolean) as string[];
},
});
}
}
export class TransformRay extends Dyno<
{ ray: "vec3"; scale: "float"; scales: "vec3"; rotate: "vec4" },
{ ray: "vec3" }
> {
constructor({
ray,
scale,
scales,
rotate,
}: {
ray?: DynoVal<"vec3">;
scale?: DynoVal<"float">;
scales?: DynoVal<"vec3">;
rotate?: DynoVal<"vec4">;
}) {
super({
inTypes: { ray: "vec3", scale: "float", scales: "vec3", rotate: "vec4" },
outTypes: { ray: "vec3" },
inputs: { ray, scale, scales, rotate },
statements: ({ inputs, outputs }) => {
const { ray } = outputs;
if (!ray) {
return [];
}
const { scale, scales, rotate } = inputs;
return [
`${ray} = ${inputs.ray ?? "vec3(0.0, 0.0, 0.0)"};`,
!scale ? null : `${ray} *= ${scale};`,
!scales ? null : `${ray} *= ${scales};`,
!rotate ? null : `${ray} = quatVec(${rotate}, ${ray});`,
].filter(Boolean) as string[];
},
});
}
}
export class TransformQuaternion extends Dyno<
{ quaternion: "vec4"; rotate: "vec4" },
{ quaternion: "vec4" }
> {
constructor({
quaternion,
rotate,
}: { quaternion?: DynoVal<"vec4">; rotate?: DynoVal<"vec4"> }) {
super({
inTypes: { quaternion: "vec4", rotate: "vec4" },
outTypes: { quaternion: "vec4" },
inputs: { quaternion, rotate },
statements: ({ inputs, outputs }) => {
const { quaternion } = outputs;
if (!quaternion) {
return [];
}
return [
`${quaternion} = ${inputs.quaternion ?? "vec4(0.0, 0.0, 0.0, 1.0)"};`,
!rotate
? null
: `${quaternion} = quatQuat(${inputs.rotate}, ${quaternion});`,
].filter(Boolean) as string[];
},
});
}
}
+182
View File
@@ -0,0 +1,182 @@
import { BinaryOp, UnaryOp } from "./base";
import type { FloatTypes } from "./types";
import type { DynoVal } from "./value";
export const radians = <A extends FloatTypes>(
degrees: DynoVal<A>,
): DynoVal<A> => new Radians({ degrees });
export const degrees = <A extends FloatTypes>(
radians: DynoVal<A>,
): DynoVal<A> => new Degrees({ radians });
export const sin = <A extends FloatTypes>(radians: DynoVal<A>): DynoVal<A> =>
new Sin({ radians });
export const cos = <A extends FloatTypes>(radians: DynoVal<A>): DynoVal<A> =>
new Cos({ radians });
export const tan = <A extends FloatTypes>(radians: DynoVal<A>): DynoVal<A> =>
new Tan({ radians });
export const asin = <A extends FloatTypes>(sin: DynoVal<A>): DynoVal<A> =>
new Asin({ sin });
export const acos = <A extends FloatTypes>(cos: DynoVal<A>): DynoVal<A> =>
new Acos({ cos });
export const atan = <A extends FloatTypes>(tan: DynoVal<A>): DynoVal<A> =>
new Atan({ tan });
export const atan2 = <A extends FloatTypes>(
y: DynoVal<A>,
x: DynoVal<A>,
): DynoVal<A> => new Atan2({ y, x });
export const sinh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Sinh({ x });
export const cosh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Cosh({ x });
export const tanh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Tanh({ x });
export const asinh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Asinh({ x });
export const acosh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Acosh({ x });
export const atanh = <A extends FloatTypes>(x: DynoVal<A>): DynoVal<A> =>
new Atanh({ x });
export class Radians<A extends FloatTypes> extends UnaryOp<A, A, "radians"> {
constructor({ degrees }: { degrees: DynoVal<A> }) {
super({ a: degrees, outTypeFunc: (aType) => aType, outKey: "radians" });
this.statements = ({ inputs, outputs }) => [
`${outputs.radians} = radians(${inputs.a});`,
];
}
}
export class Degrees<A extends FloatTypes> extends UnaryOp<A, A, "degrees"> {
constructor({ radians }: { radians: DynoVal<A> }) {
super({ a: radians, outTypeFunc: (aType) => aType, outKey: "degrees" });
this.statements = ({ inputs, outputs }) => [
`${outputs.degrees} = degrees(${inputs.a});`,
];
}
}
export class Sin<A extends FloatTypes> extends UnaryOp<A, A, "sin"> {
constructor({ radians }: { radians: DynoVal<A> }) {
super({ a: radians, outTypeFunc: (aType) => aType, outKey: "sin" });
this.statements = ({ inputs, outputs }) => [
`${outputs.sin} = sin(${inputs.a});`,
];
}
}
export class Cos<A extends FloatTypes> extends UnaryOp<A, A, "cos"> {
constructor({ radians }: { radians: DynoVal<A> }) {
super({ a: radians, outTypeFunc: (aType) => aType, outKey: "cos" });
this.statements = ({ inputs, outputs }) => [
`${outputs.cos} = cos(${inputs.a});`,
];
}
}
export class Tan<A extends FloatTypes> extends UnaryOp<A, A, "tan"> {
constructor({ radians }: { radians: DynoVal<A> }) {
super({ a: radians, outTypeFunc: (aType) => aType, outKey: "tan" });
this.statements = ({ inputs, outputs }) => [
`${outputs.tan} = tan(${inputs.a});`,
];
}
}
export class Asin<A extends FloatTypes> extends UnaryOp<A, A, "asin"> {
constructor({ sin }: { sin: DynoVal<A> }) {
super({ a: sin, outTypeFunc: (aType) => aType, outKey: "asin" });
this.statements = ({ inputs, outputs }) => [
`${outputs.asin} = asin(${inputs.a});`,
];
}
}
export class Acos<A extends FloatTypes> extends UnaryOp<A, A, "acos"> {
constructor({ cos }: { cos: DynoVal<A> }) {
super({ a: cos, outTypeFunc: (aType) => aType, outKey: "acos" });
this.statements = ({ inputs, outputs }) => [
`${outputs.acos} = acos(${inputs.a});`,
];
}
}
export class Atan<A extends FloatTypes> extends UnaryOp<A, A, "atan"> {
constructor({ tan }: { tan: DynoVal<A> }) {
super({ a: tan, outTypeFunc: (aType) => aType, outKey: "atan" });
this.statements = ({ inputs, outputs }) => [
`${outputs.atan} = atan(${inputs.a});`,
];
}
}
export class Atan2<A extends FloatTypes> extends BinaryOp<A, A, A, "atan2"> {
constructor({ y, x }: { y: DynoVal<A>; x: DynoVal<A> }) {
super({
a: y,
b: x,
outTypeFunc: (aType, bType) => aType,
outKey: "atan2",
});
this.statements = ({ inputs, outputs }) => [
`${outputs.atan2} = atan2(${inputs.a}, ${inputs.b});`,
];
}
}
export class Sinh<A extends FloatTypes> extends UnaryOp<A, A, "sinh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "sinh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.sinh} = sinh(${inputs.a});`,
];
}
}
export class Cosh<A extends FloatTypes> extends UnaryOp<A, A, "cosh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "cosh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.cosh} = cosh(${inputs.a});`,
];
}
}
export class Tanh<A extends FloatTypes> extends UnaryOp<A, A, "tanh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "tanh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.tanh} = tanh(${inputs.a});`,
];
}
}
export class Asinh<A extends FloatTypes> extends UnaryOp<A, A, "asinh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "asinh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.asinh} = asinh(${inputs.a});`,
];
}
}
export class Acosh<A extends FloatTypes> extends UnaryOp<A, A, "acosh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "acosh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.acosh} = acosh(${inputs.a});`,
];
}
}
export class Atanh<A extends FloatTypes> extends UnaryOp<A, A, "atanh"> {
constructor({ x }: { x: DynoVal<A> }) {
super({ a: x, outTypeFunc: (aType) => aType, outKey: "atanh" });
this.statements = ({ inputs, outputs }) => [
`${outputs.atanh} = atanh(${inputs.a});`,
];
}
}
+420
View File
@@ -0,0 +1,420 @@
import type * as THREE from "three";
export type BoolTypes = "bool" | "bvec2" | "bvec3" | "bvec4";
export type IntTypes = "int" | "ivec2" | "ivec3" | "ivec4";
export type UintTypes = "uint" | "uvec2" | "uvec3" | "uvec4";
export type AllIntTypes = IntTypes | UintTypes;
export type FloatTypes = "float" | "vec2" | "vec3" | "vec4";
export type ScalarTypes = "uint" | "int" | "float";
export type Vector2Types = "vec2" | "ivec2" | "uvec2";
export type Vector3Types = "vec3" | "ivec3" | "uvec3";
export type Vector4Types = "vec4" | "ivec4" | "uvec4";
export type VectorTypes = Vector2Types | Vector3Types | Vector4Types;
export type MatFloatTypes =
| "mat2"
| "mat2x2"
| "mat2x3"
| "mat2x4"
| "mat3"
| "mat3x2"
| "mat3x3"
| "mat3x4"
| "mat4"
| "mat4x2"
| "mat4x3"
| "mat4x4";
export type SquareMatTypes =
| "mat2"
| "mat3"
| "mat4"
| "mat2x2"
| "mat3x3"
| "mat4x4";
export type AllFloatTypes = FloatTypes | MatFloatTypes;
export type SignedTypes = IntTypes | FloatTypes;
export type AllSignedTypes = SignedTypes | MatFloatTypes;
export type ValueTypes = FloatTypes | IntTypes | UintTypes;
export type AllValueTypes = AllFloatTypes | IntTypes | UintTypes;
export type SimpleTypes = BoolTypes | AllValueTypes;
export type VectorElementType<A extends VectorTypes> = A extends FloatTypes
? "float"
: A extends IntTypes
? "int"
: A extends UintTypes
? "uint"
: never;
export type SameSizeVec<T extends ValueTypes> = T extends ScalarTypes
? "float"
: T extends "vec2" | "ivec2" | "uvec2"
? "vec2"
: T extends "vec3" | "ivec3" | "uvec3"
? "vec3"
: T extends "vec4" | "ivec4" | "uvec4"
? "vec4"
: never;
export type SameSizeUvec<T extends ValueTypes> = T extends ScalarTypes
? "uint"
: T extends "vec2" | "ivec2" | "uvec2"
? "uvec2"
: T extends "vec3" | "ivec3" | "uvec3"
? "uvec3"
: T extends "vec4" | "ivec4" | "uvec4"
? "uvec4"
: never;
export type SameSizeIvec<T extends ValueTypes> = T extends ScalarTypes
? "int"
: T extends "vec2" | "ivec2" | "uvec2"
? "ivec2"
: T extends "vec3" | "ivec3" | "uvec3"
? "ivec3"
: T extends "vec4" | "ivec4" | "uvec4"
? "ivec4"
: never;
export type SamplerTypes =
| "sampler2D"
| "sampler2DArray"
| "sampler3D"
| "samplerCube";
export type UsamplerTypes =
| "usampler2D"
| "usampler2DArray"
| "usampler3D"
| "usamplerCube";
export type IsamplerTypes =
| "isampler2D"
| "isampler2DArray"
| "isampler3D"
| "isamplerCube";
export type NormalSamplerTypes = SamplerTypes | UsamplerTypes | IsamplerTypes;
export type SamplerShadowTypes =
| "sampler2DShadow"
| "sampler2DArrayShadow"
| "samplerCubeShadow";
export type AllSamplerTypes = NormalSamplerTypes | SamplerShadowTypes;
export type Sampler2DTypes =
| "sampler2D"
| "usampler2D"
| "isampler2D"
| "sampler2DShadow";
export type Sampler2DArrayTypes =
| "sampler2DArray"
| "usampler2DArray"
| "isampler2DArray"
| "sampler2DArrayShadow";
export type Sampler3DTypes = "sampler3D" | "usampler3D" | "isampler3D";
export type SamplerCubeTypes =
| "samplerCube"
| "usamplerCube"
| "isamplerCube"
| "samplerCubeShadow";
export function isBoolType(type: DynoType): boolean {
return (
type === "bool" || type === "bvec2" || type === "bvec3" || type === "bvec4"
);
}
export function isScalarType(type: DynoType): boolean {
return type === "int" || type === "uint" || type === "float";
}
export function isIntType(type: DynoType): boolean {
return (
type === "int" || type === "ivec2" || type === "ivec3" || type === "ivec4"
);
}
export function isUintType(type: DynoType): boolean {
return (
type === "uint" || type === "uvec2" || type === "uvec3" || type === "uvec4"
);
}
export function isFloatType(type: DynoType): boolean {
return (
type === "float" || type === "vec2" || type === "vec3" || type === "vec4"
);
}
export function isMatFloatType(type: DynoType): boolean {
return (
type === "mat2" ||
type === "mat2x2" ||
type === "mat2x3" ||
type === "mat2x4" ||
type === "mat3" ||
type === "mat3x2" ||
type === "mat3x3" ||
type === "mat3x4" ||
type === "mat4" ||
type === "mat4x2" ||
type === "mat4x3" ||
type === "mat4x4"
);
}
export function isAllFloatType(type: DynoType): boolean {
return isFloatType(type) || isMatFloatType(type);
}
export function isVector2Type(type: DynoType): boolean {
return type === "vec2" || type === "ivec2" || type === "uvec2";
}
export function isVector3Type(type: DynoType): boolean {
return type === "vec3" || type === "ivec3" || type === "uvec3";
}
export function isVector4Type(type: DynoType): boolean {
return type === "vec4" || type === "ivec4" || type === "uvec4";
}
export function isVectorType(type: DynoType): boolean {
return isVector2Type(type) || isVector3Type(type) || isVector4Type(type);
}
export function isMat2(type: DynoType): boolean {
return type === "mat2" || type === "mat2x2";
}
export function isMat3(type: DynoType): boolean {
return type === "mat3" || type === "mat3x3";
}
export function isMat4(type: DynoType): boolean {
return type === "mat4" || type === "mat4x4";
}
export function vectorElementType<A extends VectorTypes>(
type: A,
): VectorElementType<A> {
switch (type) {
case "vec2":
return "float" as VectorElementType<A>;
case "vec3":
return "float" as VectorElementType<A>;
case "vec4":
return "float" as VectorElementType<A>;
case "ivec2":
return "int" as VectorElementType<A>;
case "ivec3":
return "int" as VectorElementType<A>;
case "ivec4":
return "int" as VectorElementType<A>;
case "uvec2":
return "uint" as VectorElementType<A>;
case "uvec3":
return "uint" as VectorElementType<A>;
case "uvec4":
return "uint" as VectorElementType<A>;
default:
throw new Error(`Invalid vector type: ${type}`);
}
}
export function vectorDim<A extends VectorTypes>(type: A): number {
switch (type) {
case "vec2":
case "ivec2":
case "uvec2":
return 2;
case "vec3":
case "ivec3":
case "uvec3":
return 3;
case "vec4":
case "ivec4":
case "uvec4":
return 4;
default:
throw new Error(`Invalid vector type: ${type}`);
}
}
export function sameSizeVec<T extends ValueTypes>(type: T): SameSizeVec<T> {
if (isScalarType(type)) {
return "float" as SameSizeVec<T>;
}
if (isVector2Type(type)) {
return "vec2" as SameSizeVec<T>;
}
if (isVector3Type(type)) {
return "vec3" as SameSizeVec<T>;
}
if (isVector4Type(type)) {
return "vec4" as SameSizeVec<T>;
}
throw new Error(`Invalid vector type: ${type}`);
}
export function sameSizeUvec<T extends ValueTypes>(type: T): SameSizeUvec<T> {
if (isScalarType(type)) {
return "uint" as SameSizeUvec<T>;
}
if (isVector2Type(type)) {
return "uvec2" as SameSizeUvec<T>;
}
if (isVector3Type(type)) {
return "uvec3" as SameSizeUvec<T>;
}
if (isVector4Type(type)) {
return "uvec4" as SameSizeUvec<T>;
}
throw new Error(`Invalid vector type: ${type}`);
}
export function sameSizeIvec<T extends ValueTypes>(type: T): SameSizeIvec<T> {
if (isScalarType(type)) {
return "int" as SameSizeIvec<T>;
}
if (isVector2Type(type)) {
return "ivec2" as SameSizeIvec<T>;
}
if (isVector3Type(type)) {
return "ivec3" as SameSizeIvec<T>;
}
if (isVector4Type(type)) {
return "ivec4" as SameSizeIvec<T>;
}
throw new Error(`Invalid vector type: ${type}`);
}
export type BaseType = SimpleTypes | AllSamplerTypes;
export type UserType = { type: string };
export type DynoType = BaseType | UserType;
export type DynoJsType<T extends DynoType> = T extends "bool"
? boolean
: T extends "uint"
? number
: T extends "int"
? number
: T extends "float"
? number
: T extends "bvec2"
? [boolean, boolean]
: T extends "uvec2"
? THREE.Vector2 | [number, number] | Uint32Array
: T extends "ivec2"
? THREE.Vector2 | [number, number] | Int32Array
: T extends "vec2"
? THREE.Vector2 | [number, number] | Float32Array
: T extends "bvec3"
? [boolean, boolean, boolean]
: T extends "uvec3"
? THREE.Vector3 | [number, number, number] | Uint32Array
: T extends "ivec3"
? THREE.Vector3 | [number, number, number] | Int32Array
: T extends "vec3"
?
| THREE.Vector3
| THREE.Color
| [number, number, number]
| Float32Array
: T extends "bvec4"
? [boolean, boolean, boolean, boolean]
: T extends "uvec4"
?
| THREE.Vector4
| [number, number, number, number]
| Uint32Array
: T extends "ivec4"
?
| THREE.Vector4
| [number, number, number, number]
| Int32Array
: T extends "vec4"
?
| THREE.Vector4
| THREE.Quaternion
| [number, number, number, number]
| Float32Array
: T extends "mat2"
? THREE.Matrix2 | Float32Array
: T extends "mat2x2"
? THREE.Matrix2 | Float32Array
: T extends "mat2x3"
? Float32Array
: T extends "mat2x4"
? Float32Array
: T extends "mat3"
? THREE.Matrix3 | Float32Array
: T extends "mat3x2"
? Float32Array
: T extends "mat3x3"
? THREE.Matrix3 | Float32Array
: T extends "mat3x4"
? Float32Array
: T extends "mat4"
? THREE.Matrix4 | Float32Array
: T extends "mat4x2"
? Float32Array
: T extends "mat4x3"
? Float32Array
: T extends "mat4x4"
?
| THREE.Matrix4
| Float32Array
: T extends "usampler2D"
? THREE.Texture
: T extends "isampler2D"
? THREE.Texture
: T extends "sampler2D"
? THREE.Texture
: T extends "sampler2DShadow"
? THREE.Texture
: T extends "usampler2DArray"
? THREE.DataArrayTexture
: T extends "isampler2DArray"
? THREE.DataArrayTexture
: T extends "sampler2DArray"
? THREE.DataArrayTexture
: T extends "sampler2DArrayShadow"
? THREE.Texture
: T extends "usampler3D"
? THREE.DataArrayTexture
: T extends "isampler3D"
? THREE.DataArrayTexture
: T extends "sampler3D"
? THREE.DataArrayTexture
: T extends "usamplerCube"
? THREE.DataArrayTexture
: T extends "isamplerCube"
? THREE.DataArrayTexture
: T extends "samplerCube"
? THREE.DataArrayTexture
: T extends "samplerCubeShadow"
? THREE.Texture
: unknown;
export function typeLiteral(type: DynoType): string {
if (typeof type === "string") {
return type;
}
if (typeof type === "object" && type.type) {
return type.type;
}
throw new Error(`Invalid DynoType: ${String(type)}`);
}
export function numberAsInt(value: number): string {
return Math.trunc(value).toString();
}
export function numberAsUint(value: number): string {
const v = Math.max(0, Math.trunc(value));
return `${v.toString()}u`;
}
export function numberAsFloat(value: number): string {
return value === Number.POSITIVE_INFINITY
? "INFINITY"
: value === Number.NEGATIVE_INFINITY
? "-INFINITY"
: Number.isInteger(value)
? value.toFixed(1)
: value.toString();
}
+826
View File
@@ -0,0 +1,826 @@
import type { IUniform } from "three";
import { Dyno, dynoDeclare } from "./base";
import type { DynoJsType, DynoType } from "./types";
import { DynoOutput, type DynoValue, type HasDynoOut } from "./value";
export const uniform = <V extends DynoJsType<DynoType>>(
key: string,
type: DynoType,
value: V,
) => new DynoUniform({ key, type, value });
export const dynoBool = (value = false, key?: string) =>
new DynoBool({ key, value });
export const dynoUint = (value = 0, key?: string) =>
new DynoUint({ key, value });
export const dynoInt = (value = 0, key?: string) => new DynoInt({ key, value });
export const dynoFloat = (value = 0.0, key?: string) =>
new DynoFloat({ key, value });
export const dynoBvec2 = <V extends DynoJsType<"bvec2">>(
value: V,
key?: string,
) => new DynoBvec2({ key, value });
export const dynoUvec2 = <V extends DynoJsType<"uvec2">>(
value: V,
key?: string,
) => new DynoUvec2({ key, value });
export const dynoIvec2 = <V extends DynoJsType<"ivec2">>(
value: V,
key?: string,
) => new DynoIvec2({ key, value });
export const dynoVec2 = <V extends DynoJsType<"vec2">>(
value: V,
key?: string,
) => new DynoVec2({ key, value });
export const dynoBvec3 = <V extends DynoJsType<"bvec3">>(
value: V,
key?: string,
) => new DynoBvec3({ key, value });
export const dynoUvec3 = <V extends DynoJsType<"uvec3">>(
value: V,
key?: string,
) => new DynoUvec3({ key, value });
export const dynoIvec3 = <V extends DynoJsType<"ivec3">>(
value: V,
key?: string,
) => new DynoIvec3({ key, value });
export const dynoVec3 = <V extends DynoJsType<"vec3">>(
value: V,
key?: string,
) => new DynoVec3({ key, value });
export const dynoBvec4 = <V extends DynoJsType<"bvec4">>(
value: V,
key?: string,
) => new DynoBvec4({ key, value });
export const dynoUvec4 = <V extends DynoJsType<"uvec4">>(
value: V,
key?: string,
) => new DynoUvec4({ key, value });
export const dynoIvec4 = <V extends DynoJsType<"ivec4">>(
value: V,
key?: string,
) => new DynoIvec4({ key, value });
export const dynoVec4 = <V extends DynoJsType<"vec4">>(
value: V,
key?: string,
) => new DynoVec4({ key, value });
export const dynoMat2 = <V extends DynoJsType<"mat2">>(
value: V,
key?: string,
) => new DynoMat2({ key, value });
export const dynoMat2x2 = <V extends DynoJsType<"mat2x2">>(
value: V,
key?: string,
) => new DynoMat2x2({ key, value });
export const dynoMat2x3 = <V extends DynoJsType<"mat2x3">>(
value: V,
key?: string,
) => new DynoMat2x3({ key, value });
export const dynoMat2x4 = <V extends DynoJsType<"mat2x4">>(
value: V,
key?: string,
) => new DynoMat2x4({ key, value });
export const dynoMat3 = <V extends DynoJsType<"mat3">>(
value: V,
key?: string,
) => new DynoMat3({ key, value });
export const dynoMat3x2 = <V extends DynoJsType<"mat3x2">>(
value: V,
key?: string,
) => new DynoMat3x2({ key, value });
export const dynoMat3x3 = <V extends DynoJsType<"mat3x3">>(
value: V,
key?: string,
) => new DynoMat3x3({ key, value });
export const dynoMat3x4 = <V extends DynoJsType<"mat3x4">>(
value: V,
key?: string,
) => new DynoMat3x4({ key, value });
export const dynoMat4 = <V extends DynoJsType<"mat4">>(
value: V,
key?: string,
) => new DynoMat4({ key, value });
export const dynoMat4x2 = <V extends DynoJsType<"mat4x2">>(
value: V,
key?: string,
) => new DynoMat4x2({ key, value });
export const dynoMat4x3 = <V extends DynoJsType<"mat4x3">>(
value: V,
key?: string,
) => new DynoMat4x3({ key, value });
export const dynoMat4x4 = <V extends DynoJsType<"mat4x4">>(
value: V,
key?: string,
) => new DynoMat4x4({ key, value });
export const dynoUsampler2D = <V extends DynoJsType<"usampler2D">>(
value: V,
key?: string,
) => new DynoUsampler2D({ key, value });
export const dynoIsampler2D = <V extends DynoJsType<"isampler2D">>(
value: V,
key?: string,
) => new DynoIsampler2D({ key, value });
export const dynoSampler2D = <V extends DynoJsType<"sampler2D">>(
value: V,
key?: string,
) => new DynoSampler2D({ key, value });
export const dynoUsampler2DArray = <V extends DynoJsType<"usampler2DArray">>(
value: V,
key?: string,
) => new DynoUsampler2DArray({ key, value });
export const dynoIsampler2DArray = <V extends DynoJsType<"isampler2DArray">>(
key: string,
value: V,
) => new DynoIsampler2DArray({ key, value });
export const dynoSampler2DArray = <V extends DynoJsType<"sampler2DArray">>(
value: V,
key?: string,
) => new DynoSampler2DArray({ key, value });
export const dynoUsampler3D = <V extends DynoJsType<"usampler3D">>(
value: V,
key?: string,
) => new DynoUsampler3D({ key, value });
export const dynoIsampler3D = <V extends DynoJsType<"isampler3D">>(
value: V,
key?: string,
) => new DynoIsampler3D({ key, value });
export const dynoSampler3D = <V extends DynoJsType<"sampler3D">>(
value: V,
key?: string,
) => new DynoSampler3D({ key, value });
export const dynoUsamplerCube = <V extends DynoJsType<"usamplerCube">>(
value: V,
key?: string,
) => new DynoUsamplerCube({ key, value });
export const dynoIsamplerCube = <V extends DynoJsType<"isamplerCube">>(
value: V,
key?: string,
) => new DynoIsamplerCube({ key, value });
export const dynoSamplerCube = <V extends DynoJsType<"samplerCube">>(
value: V,
key?: string,
) => new DynoSamplerCube({ key, value });
export const dynoSampler2DShadow = <V extends DynoJsType<"sampler2DShadow">>(
value: V,
key?: string,
) => new DynoSampler2DShadow({ key, value });
export const dynoSampler2DArrayShadow = <
V extends DynoJsType<"sampler2DArrayShadow">,
>(
value: V,
key?: string,
) => new DynoSampler2DArrayShadow({ key, value });
export const dynoSamplerCubeShadow = <
V extends DynoJsType<"samplerCubeShadow">,
>(
value: V,
key?: string,
) => new DynoSamplerCubeShadow({ key, value });
export class DynoUniform<
T extends DynoType,
K extends string = "value",
V extends DynoJsType<T> = DynoJsType<T>,
>
extends Dyno<Record<string, never>, { [key in K]: T }>
implements HasDynoOut<T>
{
public type: T;
public count?: number;
public outKey: K;
public value: V;
public uniform: { value: V; type?: string };
constructor({
key,
type,
count,
value,
update,
globals,
}: {
key?: K;
type: T;
count?: number;
value: V;
update?: (value: V) => V | undefined;
globals?: ({
inputs,
outputs,
}: { inputs: unknown; outputs: { [key in K]?: string } }) => string[];
}) {
key = (key ?? "value") as K;
super({
outTypes: { [key]: type } as { [key in K]: T },
update: () => {
if (update) {
const value = update(this.value);
if (value !== undefined) {
this.value = value;
}
}
this.uniform.value = this.value;
},
generate: ({ inputs, outputs }) => {
const allGlobals = globals?.({ inputs, outputs }) ?? [];
const uniforms: Record<string, IUniform> = {};
const name = outputs[key];
if (name) {
allGlobals.push(`uniform ${dynoDeclare(name, type, count)};`);
uniforms[name] = this.uniform;
}
return { globals: allGlobals, uniforms };
},
});
this.type = type;
this.count = count;
this.value = value;
this.uniform = { value };
this.outKey = key;
}
dynoOut(): DynoValue<T> {
return new DynoOutput(this, this.outKey);
}
}
export class DynoBool<K extends string> extends DynoUniform<
"bool",
K,
boolean
> {
constructor({
key,
value,
update,
}: {
key?: K;
value: boolean;
update?: (value: boolean) => boolean | undefined;
}) {
super({ key, type: "bool", value, update });
}
}
export class DynoUint<K extends string> extends DynoUniform<"uint", K, number> {
constructor({
key,
value,
update,
}: {
key?: K;
value: number;
update?: (value: number) => number | undefined;
}) {
super({ key, type: "uint", value, update });
}
}
export class DynoInt<K extends string> extends DynoUniform<"int", K, number> {
constructor({
key,
value,
update,
}: {
key?: K;
value: number;
update?: (value: number) => number | undefined;
}) {
super({ key, type: "int", value, update });
}
}
export class DynoFloat<K extends string = "value"> extends DynoUniform<
"float",
K,
number
> {
constructor({
key,
value,
update,
}: {
key?: K;
value: number;
update?: (value: number) => number | undefined;
}) {
super({ key, type: "float", value, update });
}
}
export class DynoBvec2<
K extends string,
V extends DynoJsType<"bvec2">,
> extends DynoUniform<"bvec2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "bvec2", value, update });
}
}
export class DynoUvec2<
K extends string,
V extends DynoJsType<"uvec2">,
> extends DynoUniform<"uvec2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "uvec2", value, update });
}
}
export class DynoIvec2<
K extends string,
V extends DynoJsType<"ivec2">,
> extends DynoUniform<"ivec2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "ivec2", value, update });
}
}
export class DynoVec2<
K extends string,
V extends DynoJsType<"vec2">,
> extends DynoUniform<"vec2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "vec2", value, update });
}
}
export class DynoBvec3<
K extends string,
V extends DynoJsType<"bvec3">,
> extends DynoUniform<"bvec3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "bvec3", value, update });
}
}
export class DynoUvec3<
V extends DynoJsType<"uvec3">,
K extends string = "value",
> extends DynoUniform<"uvec3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "uvec3", value, update });
}
}
export class DynoIvec3<
V extends DynoJsType<"ivec3">,
K extends string = "value",
> extends DynoUniform<"ivec3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "ivec3", value, update });
}
}
export class DynoVec3<
V extends DynoJsType<"vec3">,
K extends string = "value",
> extends DynoUniform<"vec3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "vec3", value, update });
}
}
export class DynoBvec4<
K extends string,
V extends DynoJsType<"bvec4">,
> extends DynoUniform<"bvec4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "bvec4", value, update });
}
}
export class DynoUvec4<
K extends string,
V extends DynoJsType<"uvec4">,
> extends DynoUniform<"uvec4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "uvec4", value, update });
}
}
export class DynoIvec4<
K extends string,
V extends DynoJsType<"ivec4">,
> extends DynoUniform<"ivec4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "ivec4", value, update });
}
}
export class DynoVec4<
V extends DynoJsType<"vec4">,
K extends string = "value",
> extends DynoUniform<"vec4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "vec4", value, update });
}
}
export class DynoMat2<
K extends string,
V extends DynoJsType<"mat2">,
> extends DynoUniform<"mat2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat2", value, update });
}
}
export class DynoMat2x2<
K extends string,
V extends DynoJsType<"mat2x2">,
> extends DynoUniform<"mat2x2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat2x2", value, update });
}
}
export class DynoMat2x3<
K extends string,
V extends DynoJsType<"mat2x3">,
> extends DynoUniform<"mat2x3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat2x3", value, update });
}
}
export class DynoMat2x4<
K extends string,
V extends DynoJsType<"mat2x4">,
> extends DynoUniform<"mat2x4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat2x4", value, update });
}
}
export class DynoMat3<
K extends string,
V extends DynoJsType<"mat3">,
> extends DynoUniform<"mat3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat3", value, update });
}
}
export class DynoMat3x2<
K extends string,
V extends DynoJsType<"mat3x2">,
> extends DynoUniform<"mat3x2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat3x2", value, update });
}
}
export class DynoMat3x3<
K extends string,
V extends DynoJsType<"mat3x3">,
> extends DynoUniform<"mat3x3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat3x3", value, update });
}
}
export class DynoMat3x4<
K extends string,
V extends DynoJsType<"mat3x4">,
> extends DynoUniform<"mat3x4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat3x4", value, update });
}
}
export class DynoMat4<
K extends string,
V extends DynoJsType<"mat4">,
> extends DynoUniform<"mat4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat4", value, update });
}
}
export class DynoMat4x2<
K extends string,
V extends DynoJsType<"mat4x2">,
> extends DynoUniform<"mat4x2", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat4x2", value, update });
}
}
export class DynoMat4x3<
K extends string,
V extends DynoJsType<"mat4x3">,
> extends DynoUniform<"mat4x3", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat4x3", value, update });
}
}
export class DynoMat4x4<
K extends string,
V extends DynoJsType<"mat4x4">,
> extends DynoUniform<"mat4x4", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "mat4x4", value, update });
}
}
export class DynoUsampler2D<
K extends string,
V extends DynoJsType<"usampler2D">,
> extends DynoUniform<"usampler2D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "usampler2D", value, update });
}
}
export class DynoIsampler2D<
K extends string,
V extends DynoJsType<"isampler2D">,
> extends DynoUniform<"isampler2D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "isampler2D", value, update });
}
}
export class DynoSampler2D<
K extends string,
V extends DynoJsType<"sampler2D">,
> extends DynoUniform<"sampler2D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "sampler2D", value, update });
}
}
export class DynoUsampler2DArray<
K extends string,
V extends DynoJsType<"usampler2DArray">,
> extends DynoUniform<"usampler2DArray", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "usampler2DArray", value, update });
}
}
export class DynoIsampler2DArray<
K extends string,
V extends DynoJsType<"isampler2DArray">,
> extends DynoUniform<"isampler2DArray", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "isampler2DArray", value, update });
}
}
export class DynoSampler2DArray<
K extends string,
V extends DynoJsType<"sampler2DArray">,
> extends DynoUniform<"sampler2DArray", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "sampler2DArray", value, update });
}
}
export class DynoUsampler3D<
K extends string,
V extends DynoJsType<"usampler3D">,
> extends DynoUniform<"usampler3D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "usampler3D", value, update });
}
}
export class DynoIsampler3D<
K extends string,
V extends DynoJsType<"isampler3D">,
> extends DynoUniform<"isampler3D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "isampler3D", value, update });
}
}
export class DynoSampler3D<
K extends string,
V extends DynoJsType<"sampler3D">,
> extends DynoUniform<"sampler3D", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "sampler3D", value, update });
}
}
export class DynoUsamplerCube<
K extends string,
V extends DynoJsType<"usamplerCube">,
> extends DynoUniform<"usamplerCube", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "usamplerCube", value, update });
}
}
export class DynoIsamplerCube<
K extends string,
V extends DynoJsType<"isamplerCube">,
> extends DynoUniform<"isamplerCube", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "isamplerCube", value, update });
}
}
export class DynoSamplerCube<
K extends string,
V extends DynoJsType<"samplerCube">,
> extends DynoUniform<"samplerCube", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "samplerCube", value, update });
}
}
export class DynoSampler2DShadow<
K extends string,
V extends DynoJsType<"sampler2DShadow">,
> extends DynoUniform<"sampler2DShadow", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "sampler2DShadow", value, update });
}
}
export class DynoSampler2DArrayShadow<
K extends string,
V extends DynoJsType<"sampler2DArrayShadow">,
> extends DynoUniform<"sampler2DArrayShadow", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "sampler2DArrayShadow", value, update });
}
}
export class DynoSamplerCubeShadow<
K extends string,
V extends DynoJsType<"samplerCubeShadow">,
> extends DynoUniform<"samplerCubeShadow", K, V> {
constructor({
key,
value,
update,
}: { key?: K; value: V; update?: (value: V) => V | undefined }) {
super({ key, type: "samplerCubeShadow", value, update });
}
}
+419
View File
@@ -0,0 +1,419 @@
import { Dyno, DynoBlock, unindent } from "./base";
import { float, vec2, vec3, vec4 } from "./convert";
import { mul } from "./math";
import {
ScalarTypes,
type ValueTypes,
Vector2Types,
Vector3Types,
Vector4Types,
isIntType,
isScalarType,
isUintType,
isVector2Type,
isVector3Type,
isVector4Type,
sameSizeUvec,
} from "./types";
import {
DynoOutput,
type DynoVal,
type DynoValue,
type HasDynoOut,
dynoConst,
valType,
} from "./value";
import { combine } from "./vecmat";
export const remapIndex = (
index: DynoVal<"int">,
from: DynoVal<"int">,
to: DynoVal<"int">,
): DynoVal<"int"> => {
return new DynoRemapIndex({ index, from, to });
};
export const pcgMix = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"uint"> => {
return new PcgMix({ value });
};
export const pcgNext = (state: DynoVal<"uint">): DynoVal<"uint"> => {
return new PcgNext({ state });
};
export const pcgHash = (state: DynoVal<"uint">): DynoVal<"uint"> => {
return new PcgHash({ state });
};
export const hash = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"uint"> => {
return new Hash({ value });
};
export const hash2 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"uvec2"> => {
return new Hash2({ value });
};
export const hash3 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"uvec3"> => {
return new Hash3({ value });
};
export const hash4 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"uvec4"> => {
return new Hash4({ value });
};
export const hashFloat = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"float"> => {
return new HashFloat({ value });
};
export const hashVec2 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"vec2"> => {
return new HashVec2({ value });
};
export const hashVec3 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"vec3"> => {
return new HashVec3({ value });
};
export const hashVec4 = <T extends ValueTypes>(
value: DynoVal<T>,
): DynoVal<"vec4"> => {
return new HashVec4({ value });
};
export class DynoRemapIndex
extends Dyno<{ from: "int"; to: "int"; index: "int" }, { index: "int" }>
implements HasDynoOut<"int">
{
constructor({
from,
to,
index,
}: { from: DynoVal<"int">; to: DynoVal<"int">; index: DynoVal<"int"> }) {
super({
inTypes: { from: "int", to: "int", index: "int" },
outTypes: { index: "int" },
inputs: { from, to, index },
statements: ({ inputs, outputs }) => {
return [
`${outputs.index} = ${inputs.index} - ${inputs.from} + ${inputs.to};`,
];
},
});
}
dynoOut(): DynoValue<"int"> {
return new DynoOutput(this, "index");
}
}
export class PcgNext<T extends "uint" | "int" | "float">
extends Dyno<{ state: T }, { state: "uint" }>
implements HasDynoOut<"uint">
{
constructor({ state }: { state: DynoVal<T> }) {
const type = valType(state);
super({
inTypes: { state: type },
outTypes: { state: "uint" },
inputs: { state },
globals: () => [
unindent(`
uint pcg_next(uint state) {
return state * 747796405u + 2891336453u;
}
`),
],
statements: ({ inputs, outputs }) => {
const toUint =
type === "uint"
? `${inputs.state}`
: type === "int"
? `uint(${inputs.state})`
: `floatBitsToUint(${inputs.state})`;
return [`${outputs.state} = pcg_next(${toUint});`];
},
});
}
dynoOut(): DynoValue<"uint"> {
return new DynoOutput(this, "state");
}
}
export class PcgHash
extends Dyno<{ state: "uint" }, { hash: "uint" }>
implements HasDynoOut<"uint">
{
constructor({ state }: { state: DynoVal<"uint"> }) {
super({
inTypes: { state: "uint" },
outTypes: { hash: "uint" },
inputs: { state },
globals: () => [
unindent(`
uint pcg_hash(uint state) {
uint hash = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (hash >> 22u) ^ hash;
}
`),
],
statements: ({ inputs, outputs }) => [
`${outputs.hash} = pcg_hash(${inputs.state});`,
],
});
}
dynoOut(): DynoValue<"uint"> {
return new DynoOutput(this, "hash");
}
}
export class PcgMix<T extends ValueTypes>
extends Dyno<{ value: T }, { state: "uint" }>
implements HasDynoOut<"uint">
{
constructor({ value }: { value: DynoVal<T> }) {
const type = valType(value);
const tempType = sameSizeUvec(type);
super({
inTypes: { value: type },
outTypes: { state: "uint" },
inputs: { value },
globals: () => [
unindent(`
uint pcg_mix(uint value) {
return value;
}
uint pcg_mix(uvec2 value) {
return value.x + 0x9e3779b9u * value.y;
}
uint pcg_mix(uvec3 value) {
return value.x + 0x9e3779b9u * value.y + 0x85ebca6bu * value.z;
}
uint pcg_mix(uvec4 value) {
return value.x + 0x9e3779b9u * value.y + 0x85ebca6bu * value.z + 0xc2b2ae35u * value.w;
}
`),
],
statements: ({ inputs, outputs }) => {
const toUvec = isUintType(type)
? `${inputs.value}`
: isIntType(type)
? `${tempType}(${inputs.value})`
: `floatBitsToUint(${inputs.value})`;
return [
`${tempType} bits = ${toUvec};`,
`${outputs.state} = pcg_mix(bits);`,
];
},
});
}
dynoOut(): DynoValue<"uint"> {
return new DynoOutput(this, "state");
}
}
export class Hash<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "uint" }>
implements HasDynoOut<"uint">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "uint" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
let state = new PcgMix({ value: value }).outputs.state;
state = new PcgNext({ state }).outputs.state;
return new PcgHash({ state }).outputs;
},
});
}
dynoOut(): DynoValue<"uint"> {
return new DynoOutput(this, "hash");
}
}
export class Hash2<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "uvec2" }>
implements HasDynoOut<"uvec2">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "uvec2" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
let state = new PcgMix({ value: value }).outputs.state;
state = new PcgNext({ state }).outputs.state;
const x = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const y = new PcgHash({ state }).outputs.hash;
return { hash: combine({ vectorType: "uvec2", x, y }) };
},
});
}
dynoOut(): DynoValue<"uvec2"> {
return new DynoOutput(this, "hash");
}
}
export class Hash3<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "uvec3" }>
implements HasDynoOut<"uvec3">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "uvec3" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
let state = new PcgMix({ value: value }).outputs.state;
state = new PcgNext({ state }).outputs.state;
const x = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const y = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const z = new PcgHash({ state }).outputs.hash;
return { hash: combine({ vectorType: "uvec3", x, y, z }) };
},
});
}
dynoOut(): DynoValue<"uvec3"> {
return new DynoOutput(this, "hash");
}
}
export class Hash4<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "uvec4" }>
implements HasDynoOut<"uvec4">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "uvec4" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
let state = new PcgMix({ value: value }).outputs.state;
state = new PcgNext({ state }).outputs.state;
const x = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const y = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const z = new PcgHash({ state }).outputs.hash;
state = new PcgNext({ state }).outputs.state;
const w = new PcgHash({ state }).outputs.hash;
return { hash: combine({ vectorType: "uvec4", x, y, z, w }) };
},
});
}
dynoOut(): DynoValue<"uvec4"> {
return new DynoOutput(this, "hash");
}
}
export class HashFloat<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "float" }>
implements HasDynoOut<"float">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "float" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
const word = hash(value);
return { hash: mul(float(word), dynoConst("float", 1 / 2 ** 32)) };
},
});
}
dynoOut(): DynoValue<"float"> {
return new DynoOutput(this, "hash");
}
}
export class HashVec2<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "vec2" }>
implements HasDynoOut<"vec2">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "vec2" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
const words = hash2(value);
return { hash: mul(vec2(words), dynoConst("float", 1 / 2 ** 32)) };
},
});
}
dynoOut(): DynoValue<"vec2"> {
return new DynoOutput(this, "hash");
}
}
export class HashVec3<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "vec3" }>
implements HasDynoOut<"vec3">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "vec3" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
const words = hash3(value);
return { hash: mul(vec3(words), dynoConst("float", 1 / 2 ** 32)) };
},
});
}
dynoOut(): DynoValue<"vec3"> {
return new DynoOutput(this, "hash");
}
}
export class HashVec4<T extends ValueTypes>
extends DynoBlock<{ value: T }, { hash: "vec4" }>
implements HasDynoOut<"vec4">
{
constructor({ value }: { value: DynoVal<T> }) {
super({
inTypes: { value: valType(value) },
outTypes: { hash: "vec4" },
inputs: { value },
construct: ({ value }) => {
if (!value) {
throw new Error("value is required");
}
const words = hash4(value);
return { hash: mul(vec4(words), dynoConst("float", 1 / 2 ** 32)) };
},
});
}
dynoOut(): DynoValue<"vec4"> {
return new DynoOutput(this, "hash");
}
}
+289
View File
@@ -0,0 +1,289 @@
import * as THREE from "three";
import type { Dyno, IOTypes } from "./base";
import {
type DynoJsType,
type DynoType,
type SimpleTypes,
isAllFloatType,
isBoolType,
isIntType,
isUintType,
numberAsFloat,
numberAsInt,
numberAsUint,
} from "./types";
export type DynoVal<T extends DynoType> = DynoValue<T> | HasDynoOut<T>;
export function valType<T extends DynoType>(val: DynoVal<T>): T {
if (val instanceof DynoValue) {
return val.type;
}
const value = val.dynoOut();
return value.type;
}
export interface HasDynoOut<T extends DynoType> {
dynoOut(): DynoValue<T>;
}
export class DynoValue<T extends DynoType> {
type: T;
// This field prevents TypeScript structural matching on objects with a "type" field
private __isDynoValue = true;
constructor(type: T) {
this.type = type;
}
}
export class DynoOutput<
T extends DynoType,
InTypes extends IOTypes,
OutTypes extends IOTypes,
> extends DynoValue<T> {
dyno: Dyno<InTypes, OutTypes>;
key: string;
constructor(dyno: Dyno<InTypes, OutTypes>, key: string) {
super(dyno.outTypes[key] as T);
this.dyno = dyno;
this.key = key;
}
}
export class DynoLiteral<T extends DynoType> extends DynoValue<T> {
literal: string;
constructor(type: T, literal: string) {
super(type);
this.literal = literal;
}
getLiteral(): string {
return this.literal;
}
}
export function dynoLiteral<T extends DynoType>(
type: T,
literal: string,
): DynoLiteral<T> {
return new DynoLiteral(type, literal);
}
export class DynoConst<T extends DynoType> extends DynoLiteral<T> {
value: DynoJsType<T>;
constructor(type: T, value: DynoJsType<T>) {
super(type, "");
this.value = value;
}
getLiteral(): string {
const { type, value } = this;
switch (type) {
case "bool":
return value ? "true" : "false";
case "uint":
return numberAsUint(value as number);
case "int":
return numberAsInt(value as number);
case "float":
return numberAsFloat(value as number);
case "bvec2": {
const v = value as [boolean, boolean];
return `bvec2(${v[0]}, ${v[1]})`;
}
case "uvec2": {
if (value instanceof THREE.Vector2) {
return `uvec2(${numberAsUint(value.x)}, ${numberAsUint(value.y)})`;
}
const v = value as [number, number] | Uint32Array;
return `uvec2(${numberAsUint(v[0])}, ${numberAsUint(v[1])})`;
}
case "ivec2": {
if (value instanceof THREE.Vector2) {
return `ivec2(${numberAsInt(value.x)}, ${numberAsInt(value.y)})`;
}
const v = value as [number, number] | Int32Array;
return `ivec2(${numberAsInt(v[0])}, ${numberAsInt(v[1])})`;
}
case "vec2": {
if (value instanceof THREE.Vector2) {
return `vec2(${numberAsFloat(value.x)}, ${numberAsFloat(value.y)})`;
}
const v = value as [number, number] | Float32Array;
return `vec2(${numberAsFloat(v[0])}, ${numberAsFloat(v[1])})`;
}
case "bvec3": {
const v = value as [boolean, boolean, boolean];
return `bvec3(${v[0]}, ${v[1]}, ${v[2]})`;
}
case "uvec3": {
if (value instanceof THREE.Vector3) {
return `uvec3(${numberAsUint(value.x)}, ${numberAsUint(value.y)}, ${numberAsUint(value.z)})`;
}
const v = value as [number, number, number] | Uint32Array;
return `uvec3(${numberAsUint(v[0])}, ${numberAsUint(v[1])}, ${numberAsUint(v[2])})`;
}
case "ivec3": {
if (value instanceof THREE.Vector3) {
return `ivec3(${numberAsInt(value.x)}, ${numberAsInt(value.y)}, ${numberAsInt(value.z)})`;
}
const v = value as [number, number, number] | Int32Array;
return `ivec3(${numberAsInt(v[0])}, ${numberAsInt(v[1])}, ${numberAsInt(v[2])})`;
}
case "vec3": {
if (value instanceof THREE.Vector3) {
return `vec3(${numberAsFloat(value.x)}, ${numberAsFloat(value.y)}, ${numberAsFloat(value.z)})`;
}
const v = value as [number, number, number] | Float32Array;
return `vec3(${numberAsFloat(v[0])}, ${numberAsFloat(v[1])}, ${numberAsFloat(v[2])})`;
}
case "bvec4": {
const v = value as [boolean, boolean, boolean, boolean];
return `bvec4(${v[0]}, ${v[1]}, ${v[2]}, ${v[3]})`;
}
case "uvec4": {
if (value instanceof THREE.Vector4) {
return `uvec4(${numberAsUint(value.x)}, ${numberAsUint(value.y)}, ${numberAsUint(value.z)}, ${numberAsUint(value.w)})`;
}
const v = value as [number, number, number, number] | Uint32Array;
return `uvec4(${numberAsUint(v[0])}, ${numberAsUint(v[1])}, ${numberAsUint(v[2])}, ${numberAsUint(v[3])})`;
}
case "ivec4": {
if (value instanceof THREE.Vector4) {
return `ivec4(${numberAsInt(value.x)}, ${numberAsInt(value.y)}, ${numberAsInt(value.z)}, ${numberAsInt(value.w)})`;
}
const v = value as [number, number, number, number] | Int32Array;
return `ivec4(${numberAsInt(v[0])}, ${numberAsInt(v[1])}, ${numberAsInt(v[2])}, ${numberAsInt(v[3])})`;
}
case "vec4": {
if (value instanceof THREE.Vector4) {
return `vec4(${numberAsFloat(value.x)}, ${numberAsFloat(value.y)}, ${numberAsFloat(value.z)}, ${numberAsFloat(value.w)})`;
}
if (value instanceof THREE.Quaternion) {
return `vec4(${numberAsFloat(value.x)}, ${numberAsFloat(value.y)}, ${numberAsFloat(value.z)}, ${numberAsFloat(value.w)})`;
}
const v = value as [number, number, number, number] | Float32Array;
return `vec4(${numberAsFloat(v[0])}, ${numberAsFloat(v[1])}, ${numberAsFloat(v[2])}, ${numberAsFloat(v[3])})`;
}
case "mat2":
case "mat2x2": {
const m = value as DynoJsType<"mat2">;
const e =
m instanceof THREE.Matrix2 ? m.elements : (value as Float32Array);
const arg = new Array(4).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat2x3": {
const e = value as DynoJsType<"mat2x3">;
const arg = new Array(6).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat2x4": {
const e = value as DynoJsType<"mat2x4">;
const arg = new Array(8).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat3":
case "mat3x3": {
const m = value as DynoJsType<"mat3">;
const e =
m instanceof THREE.Matrix3 ? m.elements : (value as Float32Array);
const arg = new Array(9).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat3x2": {
const e = value as DynoJsType<"mat3x2">;
const arg = new Array(6).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat3x4": {
const e = value as DynoJsType<"mat3x4">;
const arg = new Array(12).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat4":
case "mat4x4": {
const m = value as DynoJsType<"mat4">;
const e =
m instanceof THREE.Matrix4 ? m.elements : (value as Float32Array);
const arg = new Array(16).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat4x2": {
const e = value as DynoJsType<"mat4x2">;
const arg = new Array(8).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
case "mat4x3": {
const e = value as DynoJsType<"mat4x3">;
const arg = new Array(12).fill(0).map((_, i) => numberAsFloat(e[i]));
return `${type as string}(${arg.join(", ")})`;
}
default:
throw new Error(`Type not implemented: ${String(type)}`);
}
}
}
export function dynoConst<T extends DynoType>(
type: T,
value: DynoJsType<T>,
): DynoConst<T> {
return new DynoConst(type, value);
}
export function literalZero(type: SimpleTypes): string {
const typeString = String(type);
if (isBoolType(type)) {
return `${typeString}(false)`;
}
if (isAllFloatType(type)) {
return `${typeString}(0.0)`;
}
if (isIntType(type)) {
return `${typeString}(0)`;
}
if (isUintType(type)) {
return `${typeString}(0u)`;
}
throw new Error(`Type not implemented: ${typeString}`);
}
export function literalOne(type: SimpleTypes): string {
const typeString = String(type);
if (isBoolType(type)) {
return `${typeString}(true)`;
}
if (isAllFloatType(type)) {
return `${typeString}(1.0)`;
}
if (isIntType(type)) {
return `${typeString}(1)`;
}
if (isUintType(type)) {
return `${typeString}(1u)`;
}
throw new Error(`Type not implemented: ${typeString}`);
}
export function literalNegOne(type: SimpleTypes): string {
const typeString = String(type);
if (isBoolType(type)) {
return `${typeString}(true)`;
}
if (isAllFloatType(type)) {
return `${typeString}(-1.0)`;
}
if (isIntType(type)) {
return `${typeString}(-1)`;
}
if (isUintType(type)) {
return `${typeString}(0xFFFFFFFFu)`;
}
throw new Error(`Type not implemented: ${typeString}`);
}
+835
View File
@@ -0,0 +1,835 @@
import { BinaryOp, Dyno, TrinaryOp, UnaryOp } from "./base";
import {
type FloatTypes,
type IntTypes,
type MatFloatTypes,
type SquareMatTypes,
type UintTypes,
type VectorElementType,
type VectorTypes,
isFloatType,
isIntType,
isUintType,
vectorDim,
vectorElementType,
} from "./types";
import {
DynoOutput,
type DynoVal,
type DynoValue,
type HasDynoOut,
literalZero,
valType,
} from "./value";
export const length = <A extends "vec2" | "vec3" | "vec4">(
a: DynoVal<A>,
): DynoVal<"float"> => new Length({ a });
export const distance = <A extends "vec2" | "vec3" | "vec4">(
a: DynoVal<A>,
b: DynoVal<A>,
): DynoVal<"float"> => new Distance({ a, b });
export const dot = <A extends "vec2" | "vec3" | "vec4">(
a: DynoVal<A>,
b: DynoVal<A>,
): DynoVal<"float"> => new Dot({ a, b });
export const cross = (
a: DynoVal<"vec3">,
b: DynoVal<"vec3">,
): DynoVal<"vec3"> => new Cross({ a, b });
export const normalize = <A extends "vec2" | "vec3" | "vec4">(
a: DynoVal<A>,
): DynoVal<A> => new Normalize({ a });
export const faceforward = <A extends "vec2" | "vec3" | "vec4">(
a: DynoVal<A>,
b: DynoVal<A>,
c: DynoVal<A>,
): DynoVal<A> => new FaceForward({ a, b, c });
export const reflectVec = <A extends "vec2" | "vec3" | "vec4">(
incident: DynoVal<A>,
normal: DynoVal<A>,
): DynoVal<A> => new ReflectVec({ incident, normal });
export const refractVec = <A extends "vec2" | "vec3" | "vec4">(
incident: DynoVal<A>,
normal: DynoVal<A>,
eta: DynoVal<"float">,
): DynoVal<A> => new RefractVec({ incident, normal, eta });
export const split = <V extends VectorTypes>(vector: DynoVal<V>): Split<V> =>
new Split({ vector });
export const combine = <V extends VectorTypes, T extends VectorElementType<V>>({
vector,
vectorType,
x,
y,
z,
w,
r,
g,
b,
a,
}: {
vector?: DynoVal<V>;
vectorType?: V;
x?: DynoVal<T>;
y?: DynoVal<T>;
z?: DynoVal<T>;
w?: DynoVal<T>;
r?: DynoVal<T>;
g?: DynoVal<T>;
b?: DynoVal<T>;
a?: DynoVal<T>;
}): DynoVal<V> => new Combine({ vector, vectorType, x, y, z, w, r, g, b, a });
export const projectH = <A extends "vec3" | "vec4">(
a: DynoVal<A>,
): DynoVal<ProjectHOutput<A>> => new ProjectH({ a });
export const extendVec = <A extends "float" | "vec2" | "vec3">(
a: DynoVal<A>,
b: DynoVal<"float">,
): DynoVal<ExtendVecOutput<A>> => new ExtendVec({ a, b });
export const swizzle = <A extends VectorTypes, S extends SwizzleSelect>(
a: DynoVal<A>,
select: S,
): DynoVal<SwizzleOutput<A, SwizzleSelectLen<S>>> =>
new Swizzle({ vector: a, select });
export const compMult = <A extends MatFloatTypes>(
a: DynoVal<A>,
b: DynoVal<A>,
): DynoVal<A> => new CompMult({ a, b });
export const outer = <
A extends "vec2" | "vec3" | "vec4",
B extends "vec2" | "vec3" | "vec4",
>(
a: DynoVal<A>,
b: DynoVal<B>,
): DynoVal<OuterOutput<A, B>> => new Outer({ a, b });
export const transpose = <A extends MatFloatTypes>(
a: DynoVal<A>,
): DynoVal<TransposeOutput<A>> => new Transpose({ a });
export const determinant = <A extends SquareMatTypes>(
a: DynoVal<A>,
): DynoVal<"float"> => new Determinant({ a });
export const inverse = <A extends SquareMatTypes>(a: DynoVal<A>): DynoVal<A> =>
new Inverse({ a });
export class Length<A extends "vec2" | "vec3" | "vec4"> extends UnaryOp<
A,
"float",
"length"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outTypeFunc: (aType) => "float", outKey: "length" });
this.statements = ({ inputs, outputs }) => [
`${outputs.length} = length(${inputs.a});`,
];
}
}
export class Distance<A extends "vec2" | "vec3" | "vec4"> extends BinaryOp<
A,
A,
"float",
"distance"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<A> }) {
super({ a, b, outKey: "distance", outTypeFunc: (aType, bType) => "float" });
this.statements = ({ inputs, outputs }) => [
`${outputs.distance} = distance(${inputs.a}, ${inputs.b});`,
];
}
}
export class Dot<A extends "vec2" | "vec3" | "vec4"> extends BinaryOp<
A,
A,
"float",
"dot"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<A> }) {
super({ a, b, outKey: "dot", outTypeFunc: (aType, bType) => "float" });
this.statements = ({ inputs, outputs }) => [
`${outputs.dot} = dot(${inputs.a}, ${inputs.b});`,
];
}
}
export class Cross extends BinaryOp<"vec3", "vec3", "vec3", "cross"> {
constructor({ a, b }: { a: DynoVal<"vec3">; b: DynoVal<"vec3"> }) {
super({ a, b, outKey: "cross", outTypeFunc: (aType, bType) => "vec3" });
this.statements = ({ inputs, outputs }) => [
`${outputs.cross} = cross(${inputs.a}, ${inputs.b});`,
];
}
}
export class Normalize<A extends "vec2" | "vec3" | "vec4"> extends UnaryOp<
A,
A,
"normalize"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outTypeFunc: (aType) => aType, outKey: "normalize" });
this.statements = ({ inputs, outputs }) => [
`${outputs.normalize} = normalize(${inputs.a});`,
];
}
}
type ProjectHOutput<A extends "vec3" | "vec4"> = A extends "vec3"
? "vec2"
: A extends "vec4"
? "vec3"
: never;
function projectHOutputType<A extends "vec3" | "vec4">(
type: A,
): ProjectHOutput<A> {
if (type === "vec3") {
return "vec2" as ProjectHOutput<A>;
}
if (type === "vec4") {
return "vec3" as ProjectHOutput<A>;
}
throw new Error("Invalid type");
}
export class ProjectH<A extends "vec3" | "vec4"> extends UnaryOp<
A,
ProjectHOutput<A>,
"projected"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({
a,
outTypeFunc: (aType) => projectHOutputType(aType),
outKey: "projected",
});
this.statements = ({ inputs, outputs }) => {
if (this.inTypes.a === "vec3") {
return [`${outputs.projected} = ${inputs.a}.xy / ${inputs.a}.z;`];
}
if (this.inTypes.a === "vec4") {
return [`${outputs.projected} = ${inputs.a}.xyz / ${inputs.a}.w;`];
}
throw new Error("Invalid type");
};
}
}
type ExtendVecOutput<A extends "float" | "vec2" | "vec3"> = A extends "float"
? "vec2"
: A extends "vec2"
? "vec3"
: A extends "vec3"
? "vec4"
: never;
function extendVecOutputType<A extends "float" | "vec2" | "vec3">(
type: A,
): ExtendVecOutput<A> {
if (type === "float") return "vec2" as ExtendVecOutput<A>;
if (type === "vec2") return "vec3" as ExtendVecOutput<A>;
if (type === "vec3") return "vec4" as ExtendVecOutput<A>;
throw new Error("Invalid type");
}
export class ExtendVec<A extends "float" | "vec2" | "vec3"> extends BinaryOp<
A,
"float",
ExtendVecOutput<A>,
"extend"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<"float"> }) {
const type = valType(a);
const outType = extendVecOutputType(type);
super({ a, b, outKey: "extend", outTypeFunc: () => outType });
this.statements = ({ inputs, outputs }) => [
`${outputs.extend} = ${outType}(${inputs.a}, ${inputs.b});`,
];
}
}
export class FaceForward<A extends "vec2" | "vec3" | "vec4"> extends TrinaryOp<
A,
A,
A,
A,
"forward"
> {
constructor({ a, b, c }: { a: DynoVal<A>; b: DynoVal<A>; c: DynoVal<A> }) {
super({
a,
b,
c,
outKey: "forward",
outTypeFunc: (aType, bType, cType) => aType,
});
this.statements = ({ inputs, outputs }) => [
`${outputs.forward} = faceforward(${inputs.a}, ${inputs.b}, ${inputs.c});`,
];
}
}
export class ReflectVec<A extends "vec2" | "vec3" | "vec4"> extends BinaryOp<
A,
A,
A,
"reflection"
> {
constructor({
incident,
normal,
}: { incident: DynoVal<A>; normal: DynoVal<A> }) {
super({
a: incident,
b: normal,
outKey: "reflection",
outTypeFunc: (aType, bType) => aType,
});
this.statements = ({ inputs, outputs }) => [
`${outputs.reflection} = reflect(${inputs.a}, ${inputs.b});`,
];
}
}
export class RefractVec<A extends "vec2" | "vec3" | "vec4"> extends TrinaryOp<
A,
A,
"float",
A,
"refraction"
> {
constructor({
incident,
normal,
eta,
}: { incident: DynoVal<A>; normal: DynoVal<A>; eta: DynoVal<"float"> }) {
super({
a: incident,
b: normal,
c: eta,
outKey: "refraction",
outTypeFunc: (aType, bType, cType) => aType,
});
this.statements = ({ inputs, outputs }) => [
`${outputs.refraction} = refract(${inputs.a}, ${inputs.b}, ${inputs.c});`,
];
}
}
export class CompMult<A extends MatFloatTypes> extends BinaryOp<
A,
A,
A,
"product"
> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<A> }) {
super({ a, b, outKey: "product", outTypeFunc: (aType, bType) => aType });
this.statements = ({ inputs, outputs }) => [
`${outputs.product} = matrixCompMult(${a}, ${b});`,
];
}
}
type OuterOutput<
A extends "vec2" | "vec3" | "vec4",
B extends "vec2" | "vec3" | "vec4",
> = A extends "vec2"
? B extends "vec2"
? "mat2"
: B extends "vec3"
? "mat3x2"
: B extends "vec4"
? "mat4x2"
: never
: A extends "vec3"
? B extends "vec2"
? "mat2x3"
: B extends "vec3"
? "mat3"
: B extends "vec4"
? "mat4x3"
: never
: A extends "vec4"
? B extends "vec2"
? "mat2x4"
: B extends "vec3"
? "mat3x4"
: B extends "vec4"
? "mat4"
: never
: never;
function outerOutputType<
A extends "vec2" | "vec3" | "vec4",
B extends "vec2" | "vec3" | "vec4",
>(aType: A, bType: B): OuterOutput<A, B> {
if (aType === "vec2") {
if (bType === "vec2") return "mat2" as OuterOutput<A, B>;
if (bType === "vec3") return "mat3x2" as OuterOutput<A, B>;
if (bType === "vec4") return "mat4x2" as OuterOutput<A, B>;
}
if (aType === "vec3") {
if (bType === "vec2") return "mat2x3" as OuterOutput<A, B>;
if (bType === "vec3") return "mat3" as OuterOutput<A, B>;
if (bType === "vec4") return "mat4x3" as OuterOutput<A, B>;
}
if (aType === "vec4") {
if (bType === "vec2") return "mat2x4" as OuterOutput<A, B>;
if (bType === "vec3") return "mat3x4" as OuterOutput<A, B>;
if (bType === "vec4") return "mat4" as OuterOutput<A, B>;
}
throw new Error(`Invalid outer type: ${aType}, ${bType}`);
}
export class Outer<
A extends "vec2" | "vec3" | "vec4",
B extends "vec2" | "vec3" | "vec4",
> extends BinaryOp<A, B, OuterOutput<A, B>, "outer"> {
constructor({ a, b }: { a: DynoVal<A>; b: DynoVal<B> }) {
super({ a, b, outKey: "outer", outTypeFunc: outerOutputType });
this.statements = ({ inputs, outputs }) => [
`${outputs.outer} = outerProduct(${inputs.a}, ${inputs.b});`,
];
}
}
type TransposeOutput<A extends MatFloatTypes> = A extends SquareMatTypes
? A
: A extends "mat2x3"
? "mat3x2"
: A extends "mat2x4"
? "mat4x2"
: A extends "mat3x2"
? "mat2x3"
: A extends "mat3x4"
? "mat4x3"
: A extends "mat4x2"
? "mat2x4"
: A extends "mat4x3"
? "mat3x4"
: never;
function transposeOutputType<A extends MatFloatTypes>(
type: A,
): TransposeOutput<A> {
if (type === "mat2") return "mat2" as TransposeOutput<A>;
if (type === "mat3") return "mat3" as TransposeOutput<A>;
if (type === "mat4") return "mat4" as TransposeOutput<A>;
if (type === "mat2x2") return "mat2x2" as TransposeOutput<A>;
if (type === "mat2x3") return "mat3x2" as TransposeOutput<A>;
if (type === "mat2x4") return "mat4x2" as TransposeOutput<A>;
if (type === "mat3x2") return "mat2x3" as TransposeOutput<A>;
if (type === "mat3x3") return "mat3x3" as TransposeOutput<A>;
if (type === "mat3x4") return "mat4x3" as TransposeOutput<A>;
if (type === "mat4x2") return "mat2x4" as TransposeOutput<A>;
if (type === "mat4x3") return "mat3x4" as TransposeOutput<A>;
if (type === "mat4x4") return "mat4x4" as TransposeOutput<A>;
throw new Error(`Invalid transpose type: ${type}`);
}
export class Transpose<A extends MatFloatTypes> extends UnaryOp<
A,
TransposeOutput<A>,
"transpose"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "transpose", outTypeFunc: transposeOutputType });
this.statements = ({ inputs, outputs }) => [
`${outputs.transpose} = transpose(${inputs.a});`,
];
}
}
export class Determinant<A extends SquareMatTypes> extends UnaryOp<
A,
"float",
"det"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "det", outTypeFunc: (aType) => "float" });
this.statements = ({ inputs, outputs }) => [
`${outputs.det} = determinant(${inputs.a});`,
];
}
}
export class Inverse<A extends SquareMatTypes> extends UnaryOp<
A,
A,
"inverse"
> {
constructor({ a }: { a: DynoVal<A> }) {
super({ a, outKey: "inverse", outTypeFunc: (aType) => aType });
this.statements = ({ inputs, outputs }) => [
`${outputs.inverse} = inverse(${a});`,
];
}
}
type SplitOutTypes<A extends VectorTypes> = A extends "vec2"
? { x: "float"; y: "float"; r: "float"; g: "float" }
: A extends "vec3"
? { x: "float"; y: "float"; z: "float"; r: "float"; g: "float"; b: "float" }
: A extends "vec4"
? {
x: "float";
y: "float";
z: "float";
w: "float";
r: "float";
g: "float";
b: "float";
a: "float";
}
: A extends "ivec2"
? { x: "int"; y: "int"; r: "int"; g: "int" }
: A extends "ivec3"
? { x: "int"; y: "int"; z: "int"; r: "int"; g: "int"; b: "int" }
: A extends "ivec4"
? {
x: "int";
y: "int";
z: "int";
w: "int";
r: "int";
g: "int";
b: "int";
a: "int";
}
: A extends "uvec2"
? { x: "uint"; y: "uint"; r: "uint"; g: "uint" }
: A extends "uvec3"
? {
x: "uint";
y: "uint";
z: "uint";
r: "uint";
g: "uint";
b: "uint";
}
: A extends "uvec4"
? {
x: "uint";
y: "uint";
z: "uint";
w: "uint";
r: "uint";
g: "uint";
b: "uint";
a: "uint";
}
: never;
function splitOutTypes<A extends VectorTypes>(type: A): SplitOutTypes<A> {
const result = (value: unknown) => value as SplitOutTypes<A>;
switch (type) {
case "vec2":
return result({ x: "float", y: "float", r: "float", g: "float" });
case "vec3":
return result({
x: "float",
y: "float",
z: "float",
r: "float",
g: "float",
b: "float",
});
case "vec4":
return result({
x: "float",
y: "float",
z: "float",
w: "float",
r: "float",
g: "float",
b: "float",
a: "float",
});
case "ivec2":
return result({ x: "int", y: "int", r: "int", g: "int" });
case "ivec3":
return result({
x: "int",
y: "int",
z: "int",
r: "int",
g: "int",
b: "int",
});
case "ivec4":
return result({
x: "int",
y: "int",
z: "int",
w: "int",
r: "int",
g: "int",
b: "int",
a: "int",
});
case "uvec2":
return result({ x: "uint", y: "uint", r: "uint", g: "uint" });
case "uvec3":
return result({
x: "uint",
y: "uint",
z: "uint",
r: "uint",
g: "uint",
b: "uint",
});
case "uvec4":
return result({
x: "uint",
y: "uint",
z: "uint",
w: "uint",
r: "uint",
g: "uint",
b: "uint",
a: "uint",
});
default:
throw new Error(`Invalid vector type: ${type}`);
}
}
export class Split<V extends VectorTypes> extends Dyno<
{ vector: V },
SplitOutTypes<V>
> {
constructor({ vector }: { vector: DynoVal<V> }) {
const type = valType(vector);
const inTypes = { vector: type };
const outTypes = splitOutTypes(inTypes.vector);
super({ inTypes, outTypes, inputs: { vector } });
this.statements = ({ inputs, outputs }) => {
const { x, y, z, w, r, g, b, a } = outputs as unknown as Record<
string,
string
>;
const { vector } = inputs;
return [
x ? `${x} = ${vector}.x;` : null,
y ? `${y} = ${vector}.y;` : null,
z ? `${z} = ${vector}.z;` : null,
w ? `${w} = ${vector}.w;` : null,
r ? `${r} = ${vector}.r;` : null,
g ? `${g} = ${vector}.g;` : null,
b ? `${b} = ${vector}.b;` : null,
a ? `${a} = ${vector}.a;` : null,
].filter(Boolean) as string[];
};
}
}
export class Combine<V extends VectorTypes, T extends VectorElementType<V>>
extends Dyno<SplitOutTypes<V> & { vector: V }, { vector: V }>
implements HasDynoOut<V>
{
constructor({
vector,
vectorType,
x,
y,
z,
w,
r,
g,
b,
a,
}: {
vector?: DynoVal<V>;
vectorType?: V;
x?: DynoVal<T>;
y?: DynoVal<T>;
z?: DynoVal<T>;
w?: DynoVal<T>;
r?: DynoVal<T>;
g?: DynoVal<T>;
b?: DynoVal<T>;
a?: DynoVal<T>;
}) {
if (!vector && !vectorType) {
throw new Error("Either vector or vectorType must be provided");
}
const vType = vectorType ?? valType(vector as DynoVal<V>);
const elType = vectorElementType(vType);
const dim = vectorDim(vType);
const inTypes = {
vector: vType,
x: elType,
y: elType,
r: elType,
g: elType,
} as unknown as SplitOutTypes<V> & { vector: V };
const inputs = { vector, x, y, r, g };
if (dim >= 3) {
Object.assign(inTypes, { z: elType, b: elType });
Object.assign(inputs, { z, b });
}
if (dim >= 4) {
Object.assign(inTypes, { w: elType, a: elType });
Object.assign(inputs, { w, a });
}
// @ts-ignore
super({ inTypes, outTypes: { vector: vType }, inputs });
this.statements = ({ inputs, outputs }) => {
const { vector } = outputs;
const {
vector: input,
x,
y,
z,
w,
r,
g,
b,
a,
} = inputs as Record<string, string>;
const statements = [
`${vector}.x = ${x ?? r ?? (input ? `${input}.x` : literalZero(elType))};`,
`${vector}.y = ${y ?? g ?? (input ? `${input}.y` : literalZero(elType))};`,
];
if (dim >= 3)
statements.push(
`${vector}.z = ${z ?? b ?? (input ? `${input}.z` : literalZero(elType))};`,
);
if (dim >= 4)
statements.push(
`${vector}.w = ${w ?? a ?? (input ? `${input}.w` : literalZero(elType))};`,
);
return statements;
};
}
dynoOut(): DynoValue<V> {
return new DynoOutput<V, SplitOutTypes<V> & { vector: V }, { vector: V }>(
this,
"vector",
);
}
}
type SwizzleOutput<
A extends VectorTypes,
Len extends number,
> = A extends FloatTypes
? Len extends 1
? "float"
: Len extends 2
? "vec2"
: Len extends 3
? "vec3"
: Len extends 4
? "vec4"
: never
: A extends IntTypes
? Len extends 1
? "int"
: Len extends 2
? "ivec2"
: Len extends 3
? "ivec3"
: Len extends 4
? "ivec4"
: never
: A extends UintTypes
? Len extends 1
? "uint"
: Len extends 2
? "uvec2"
: Len extends 3
? "uvec3"
: Len extends 4
? "uvec4"
: never
: never;
type SwizzleSelectLen<S extends SwizzleSelect> = S extends Swizzle1Select
? 1
: S extends Swizzle2Select
? 2
: S extends Swizzle3Select
? 3
: S extends Swizzle4Select
? 4
: never;
function swizzleOutputType<A extends VectorTypes, S extends SwizzleSelect>(
type: A,
swizzle: S,
): SwizzleOutput<A, SwizzleSelectLen<S>> {
let result = null;
if (isFloatType(type)) {
result =
swizzle.length === 1
? "float"
: swizzle.length === 2
? "vec2"
: swizzle.length === 3
? "vec3"
: swizzle.length === 4
? "vec4"
: null;
} else if (isIntType(type)) {
result =
swizzle.length === 1
? "int"
: swizzle.length === 2
? "ivec2"
: swizzle.length === 3
? "ivec3"
: swizzle.length === 4
? "ivec4"
: null;
} else if (isUintType(type)) {
result =
swizzle.length === 1
? "uint"
: swizzle.length === 2
? "uvec2"
: swizzle.length === 3
? "uvec3"
: swizzle.length === 4
? "uvec4"
: null;
}
if (result == null) {
throw new Error(`Invalid swizzle: ${swizzle}`);
}
return result as SwizzleOutput<A, SwizzleSelectLen<S>>;
}
type Swizzle1Select = `${"x" | "y" | "z" | "w"}|${"r" | "g" | "b" | "a"}`;
type Swizzle2Select =
| `${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}`
| `${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}`;
type Swizzle3Select =
| `${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}`
| `${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}`;
type Swizzle4Select =
| `${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}${"x" | "y" | "z" | "w"}`
| `${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}${"r" | "g" | "b" | "a"}`;
type SwizzleSelect =
| Swizzle1Select
| Swizzle2Select
| Swizzle3Select
| Swizzle4Select;
export class Swizzle<
A extends VectorTypes,
S extends SwizzleSelect,
> extends UnaryOp<A, SwizzleOutput<A, SwizzleSelectLen<S>>, "swizzle"> {
constructor({ vector, select }: { vector: DynoVal<A>; select: S }) {
super({
a: vector,
outKey: "swizzle",
outTypeFunc: (aType) => swizzleOutputType<A, S>(aType, select),
});
this.statements = ({ inputs, outputs }) => [
`${outputs.swizzle} = ${inputs.a}.${select};`,
];
}
}