mirror of
https://github.com/storytold/spark.git
synced 2026-10-17 00:12:59 +00:00
Initial commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
};
|
||||
@@ -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;`,
|
||||
];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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} `)};`];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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});`];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -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[];
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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});`,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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}`);
|
||||
}
|
||||
@@ -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};`,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user