Compare commits
13 Commits
e89a46e146
...
v0.0.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51de3ced13
|
||
| 8d403ba803 | |||
|
|
6bb301153a
|
||
|
|
02eee5f9bf
|
||
|
|
4f48a519a9
|
||
|
|
97199ac20f
|
||
|
|
f36f0cb230
|
||
|
|
ed3d48e07f
|
||
|
|
c610d6c991
|
||
|
8865b9b032
|
|||
|
235ee5d979
|
|||
|
23a48572f3
|
|||
|
|
9271d3a7e4
|
@@ -86,6 +86,4 @@ else
|
||||
git push origin main
|
||||
fi
|
||||
|
||||
rm app/static/CHANGELOG.md
|
||||
cp CHANGELOG.md app/static/CHANGELOG.md
|
||||
echo "✅ Release process for $TAG complete"
|
||||
|
||||
@@ -54,13 +54,13 @@ jobs:
|
||||
pnpm check
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" pnpm test
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: ./.gitea/scripts/build.sh
|
||||
|
||||
- name: 🚀 Create Release Commit
|
||||
if: gitea.ref_type == 'tag'
|
||||
run: ./.gitea/scripts/create-release.sh
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: ./.gitea/scripts/build.sh
|
||||
|
||||
- name: 🏷️ Create Gitea Release
|
||||
if: gitea.ref_type == 'tag'
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -62,6 +62,14 @@ version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "leaf"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nodarium_macros",
|
||||
"nodarium_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "math"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -11,6 +11,7 @@ uniform vec3 camPos;
|
||||
uniform vec2 zoomLimits;
|
||||
uniform vec3 backgroundColor;
|
||||
uniform vec3 lineColor;
|
||||
uniform int gridType; // 0 = grid lines, 1 = dots
|
||||
|
||||
// Anti-aliased step: threshold in the same units as `value`
|
||||
float aaStep(float threshold, float value, float deriv) {
|
||||
@@ -78,35 +79,51 @@ void main(void) {
|
||||
float ux = (vUv.x - 0.5) * width + cx * cz;
|
||||
float uy = (vUv.y - 0.5) * height - cy * cz;
|
||||
|
||||
// extra small grid
|
||||
float m1 = grid(ux, uy, divisions * 4.0, thickness * 4.0) * 0.9;
|
||||
float m2 = grid(ux, uy, divisions * 16.0, thickness * 16.0) * 0.5;
|
||||
float xsmall = max(m1, m2);
|
||||
|
||||
float s3 = circle_grid(ux, uy, cz / 1.6, 1.0) * 0.5;
|
||||
xsmall = max(xsmall, s3);
|
||||
if(gridType == 0) {
|
||||
// extra small grid
|
||||
float m1 = grid(ux, uy, divisions * 4.0, thickness * 4.0) * 0.9;
|
||||
float m2 = grid(ux, uy, divisions * 16.0, thickness * 16.0) * 0.5;
|
||||
float xsmall = max(m1, m2);
|
||||
|
||||
float s3 = circle_grid(ux, uy, cz / 1.6, 1.0) * 0.5;
|
||||
xsmall = max(xsmall, s3);
|
||||
|
||||
// small grid
|
||||
float c1 = grid(ux, uy, divisions, thickness) * 0.6;
|
||||
float c2 = grid(ux, uy, divisions * 2.0, thickness * 2.0) * 0.5;
|
||||
float small = max(c1, c2);
|
||||
// small grid
|
||||
float c1 = grid(ux, uy, divisions, thickness) * 0.6;
|
||||
float c2 = grid(ux, uy, divisions * 2.0, thickness * 2.0) * 0.5;
|
||||
float small = max(c1, c2);
|
||||
|
||||
float s1 = circle_grid(ux, uy, cz * 10.0, 2.0) * 0.5;
|
||||
small = max(small, s1);
|
||||
float s1 = circle_grid(ux, uy, cz * 10.0, 2.0) * 0.5;
|
||||
small = max(small, s1);
|
||||
|
||||
// large grid
|
||||
float c3 = grid(ux, uy, divisions / 8.0, thickness / 8.0) * 0.5;
|
||||
float c4 = grid(ux, uy, divisions / 2.0, thickness / 4.0) * 0.4;
|
||||
float large = max(c3, c4);
|
||||
// large grid
|
||||
float c3 = grid(ux, uy, divisions / 8.0, thickness / 8.0) * 0.5;
|
||||
float c4 = grid(ux, uy, divisions / 2.0, thickness / 4.0) * 0.4;
|
||||
float large = max(c3, c4);
|
||||
|
||||
float s2 = circle_grid(ux, uy, cz * 20.0, 1.0) * 0.4;
|
||||
large = max(large, s2);
|
||||
float s2 = circle_grid(ux, uy, cz * 20.0, 1.0) * 0.4;
|
||||
large = max(large, s2);
|
||||
|
||||
float c = mix(large, small, min(nz * 2.0 + 0.05, 1.0));
|
||||
c = mix(c, xsmall, clamp((nz - 0.3) / 0.7, 0.0, 1.0));
|
||||
float c = mix(large, small, min(nz * 2.0 + 0.05, 1.0));
|
||||
c = mix(c, xsmall, clamp((nz - 0.3) / 0.7, 0.0, 1.0));
|
||||
|
||||
vec3 color = mix(backgroundColor, lineColor, c);
|
||||
vec3 color = mix(backgroundColor, lineColor, c);
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
} else {
|
||||
float large = circle_grid(ux, uy, cz * 20.0, 1.0) * 0.4;
|
||||
|
||||
float medium = circle_grid(ux, uy, cz * 10.0, 1.0) * 0.6;
|
||||
|
||||
float small = circle_grid(ux, uy, cz * 2.5, 1.0) * 0.8;
|
||||
|
||||
float c = mix(large, medium, min(nz * 2.0 + 0.05, 1.0));
|
||||
c = mix(c, small, clamp((nz - 0.3) / 0.7, 0.0, 1.0));
|
||||
|
||||
vec3 color = mix(backgroundColor, lineColor, c);
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
import BackgroundVert from './Background.vert';
|
||||
|
||||
type Props = {
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
cameraPosition: [number, number, number];
|
||||
width: number;
|
||||
height: number;
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
cameraPosition?: [number, number, number];
|
||||
width?: number;
|
||||
height?: number;
|
||||
type?: 'grid' | 'dots' | 'none';
|
||||
};
|
||||
|
||||
let {
|
||||
@@ -18,9 +19,18 @@
|
||||
maxZoom = 150,
|
||||
cameraPosition = [0, 1, 0],
|
||||
width = globalThis?.innerWidth || 100,
|
||||
height = globalThis?.innerHeight || 100
|
||||
height = globalThis?.innerHeight || 100,
|
||||
type = 'grid'
|
||||
}: Props = $props();
|
||||
|
||||
const typeMap = new Map([
|
||||
['grid', 0],
|
||||
['dots', 1],
|
||||
['none', 2]
|
||||
]);
|
||||
|
||||
const gridType = $derived(typeMap.get(type) || 0);
|
||||
|
||||
let bw = $derived(width / cameraPosition[2]);
|
||||
let bh = $derived(height / cameraPosition[2]);
|
||||
</script>
|
||||
@@ -51,6 +61,9 @@
|
||||
},
|
||||
dimensions: {
|
||||
value: [100, 100]
|
||||
},
|
||||
gridType: {
|
||||
value: 0
|
||||
}
|
||||
}}
|
||||
uniforms.camPos.value={cameraPosition}
|
||||
@@ -59,6 +72,7 @@
|
||||
uniforms.lineColor.value={appSettings.value.theme && colors['outline']}
|
||||
uniforms.zoomLimits.value={[minZoom, maxZoom]}
|
||||
uniforms.dimensions.value={[width, height]}
|
||||
uniforms.gridType.value={gridType}
|
||||
/>
|
||||
</T.Mesh>
|
||||
</T.Group>
|
||||
|
||||
@@ -83,7 +83,7 @@ export class GraphState {
|
||||
addMenuPosition = $state<[number, number] | null>(null);
|
||||
|
||||
snapToGrid = $state(false);
|
||||
showGrid = $state(true);
|
||||
backgroundType = $state<'grid' | 'dots' | 'none'>('grid');
|
||||
showHelp = $state(false);
|
||||
|
||||
cameraDown = [0, 0];
|
||||
@@ -194,7 +194,11 @@ export class GraphState {
|
||||
if (node?.inputs?.[key] === undefined) continue;
|
||||
if ('setting' in node.inputs[key]) continue;
|
||||
if (node.inputs[key].hidden) continue;
|
||||
if (node.inputs[key].type === 'shape') {
|
||||
if (
|
||||
node.inputs[key].type === 'shape'
|
||||
&& node.inputs[key].external !== true
|
||||
&& node.inputs[key].internal !== false
|
||||
) {
|
||||
height += 20;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -132,8 +132,9 @@
|
||||
position={graphState.cameraPosition}
|
||||
/>
|
||||
|
||||
{#if graphState.showGrid !== false}
|
||||
{#if graphState.backgroundType !== 'none'}
|
||||
<Background
|
||||
type={graphState.backgroundType}
|
||||
cameraPosition={graphState.cameraPosition}
|
||||
{maxZoom}
|
||||
{minZoom}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
settings?: Record<string, unknown>;
|
||||
|
||||
activeNode?: NodeInstance;
|
||||
showGrid?: boolean;
|
||||
backgroundType?: 'grid' | 'dots' | 'none';
|
||||
snapToGrid?: boolean;
|
||||
showHelp?: boolean;
|
||||
settingTypes?: Record<string, unknown>;
|
||||
@@ -27,7 +27,7 @@
|
||||
registry,
|
||||
settings = $bindable(),
|
||||
activeNode = $bindable(),
|
||||
showGrid = $bindable(true),
|
||||
backgroundType = $bindable('grid'),
|
||||
snapToGrid = $bindable(true),
|
||||
showHelp = $bindable(false),
|
||||
settingTypes = $bindable(),
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
const graphState = new GraphState(manager);
|
||||
$effect(() => {
|
||||
graphState.showGrid = showGrid;
|
||||
graphState.backgroundType = backgroundType;
|
||||
graphState.snapToGrid = snapToGrid;
|
||||
graphState.showHelp = showHelp;
|
||||
});
|
||||
|
||||
@@ -31,11 +31,24 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
let value = $state(getDefaultValue());
|
||||
let value = $state(structuredClone($state.snapshot(getDefaultValue())));
|
||||
|
||||
function diffArray(a: number[], b?: number[] | number) {
|
||||
if (!Array.isArray(b)) return true;
|
||||
if (Array.isArray(a) !== Array.isArray(b)) return true;
|
||||
if (a.length !== b.length) return true;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (value !== undefined && node?.props?.[id] !== value) {
|
||||
node.props = { ...node.props, [id]: value };
|
||||
const a = $state.snapshot(value);
|
||||
const b = $state.snapshot(node?.props?.[id]);
|
||||
const isDiff = Array.isArray(a) ? diffArray(a, b) : a !== b;
|
||||
if (value !== undefined && isDiff) {
|
||||
node.props = { ...node.props, [id]: a };
|
||||
if (graph) {
|
||||
graph.save();
|
||||
graph.execute();
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
const inputType = $derived(node?.state?.type?.inputs?.[id]);
|
||||
|
||||
const socketId = $derived(`${node.id}-${id}`);
|
||||
const height = $derived(input.type === 'shape' ? 200 : 100);
|
||||
const isShape = $derived(input.type === 'shape' && input.external !== true);
|
||||
const height = $derived(isShape ? 200 : 100);
|
||||
|
||||
const graphState = getGraphState();
|
||||
const graphId = graph?.id;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { decodeFloat, splitNestedArray } from '@nodarium/utils';
|
||||
import type { PerformanceStore } from '@nodarium/utils';
|
||||
import { Canvas } from '@threlte/core';
|
||||
import { Vector3 } from 'three';
|
||||
import { DoubleSide, Vector3 } from 'three';
|
||||
import { type Group, MeshMatcapMaterial, TextureLoader } from 'three';
|
||||
import { createGeometryPool, createInstancedGeometryPool } from './geometryPool';
|
||||
import Scene from './Scene.svelte';
|
||||
@@ -14,7 +14,8 @@
|
||||
matcap.colorSpace = 'srgb';
|
||||
const material = new MeshMatcapMaterial({
|
||||
color: 0xffffff,
|
||||
matcap
|
||||
matcap,
|
||||
side: DoubleSide
|
||||
});
|
||||
|
||||
let sceneComponent = $state<ReturnType<typeof Scene>>();
|
||||
|
||||
@@ -28,7 +28,7 @@ function getValue(input: NodeInput, value?: unknown) {
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (input.type === 'vec3') {
|
||||
if (input.type === 'vec3' || input.type === 'shape') {
|
||||
return [
|
||||
0,
|
||||
value.length + 1,
|
||||
|
||||
@@ -30,10 +30,11 @@ export const AppSettingTypes = {
|
||||
},
|
||||
nodeInterface: {
|
||||
title: 'Node Interface',
|
||||
showNodeGrid: {
|
||||
type: 'boolean',
|
||||
label: 'Show Grid',
|
||||
value: true
|
||||
backgroundType: {
|
||||
type: 'select',
|
||||
label: 'Background',
|
||||
options: ['grid', 'dots', 'none'],
|
||||
value: 'grid'
|
||||
},
|
||||
snapToGrid: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
const match = line.match(regex);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const [, sha, link, description] = match;
|
||||
@@ -137,7 +137,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
<style lang="postcss">
|
||||
@reference "tailwindcss";
|
||||
|
||||
#changelog :global(.commits) {
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
graph={pm.graph}
|
||||
bind:this={graphInterface}
|
||||
registry={nodeRegistry}
|
||||
showGrid={appSettings.value.nodeInterface.showNodeGrid}
|
||||
backgroundType={appSettings.value.nodeInterface.backgroundType}
|
||||
snapToGrid={appSettings.value.nodeInterface.snapToGrid}
|
||||
bind:activeNode
|
||||
bind:showHelp={appSettings.value.nodeInterface.showHelp}
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
"value": 1,
|
||||
"hidden": true
|
||||
},
|
||||
"rotation": {
|
||||
"type": "float",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"value": 0.5,
|
||||
"hidden": true
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"min": 1,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use glam::{Mat4, Quat, Vec3};
|
||||
use nodarium_macros::nodarium_execute;
|
||||
use nodarium_macros::nodarium_definition_file;
|
||||
use nodarium_macros::{nodarium_execute, nodarium_definition_file};
|
||||
use nodarium_utils::{
|
||||
concat_args, evaluate_float, evaluate_int,
|
||||
geometry::{
|
||||
create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path,
|
||||
},
|
||||
log, split_args,
|
||||
geometry::{create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path},
|
||||
split_args,
|
||||
};
|
||||
|
||||
nodarium_definition_file!("src/input.json");
|
||||
@@ -15,13 +12,13 @@ nodarium_definition_file!("src/input.json");
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
let args = split_args(input);
|
||||
let mut inputs = split_args(args[0]);
|
||||
log!("WASM(instance): inputs: {:?}", inputs);
|
||||
|
||||
let mut geo_data = args[1].to_vec();
|
||||
let geo = wrap_geometry_data(&mut geo_data);
|
||||
|
||||
let mut transforms: Vec<Mat4> = Vec::new();
|
||||
|
||||
// Find max depth
|
||||
let mut max_depth = 0;
|
||||
for path_data in inputs.iter() {
|
||||
if path_data[2] != 0 {
|
||||
@@ -30,7 +27,8 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
max_depth = max_depth.max(path_data[3]);
|
||||
}
|
||||
|
||||
let depth = evaluate_int(args[5]);
|
||||
let rotation = evaluate_float(args[5]);
|
||||
let depth = evaluate_int(args[6]);
|
||||
|
||||
for path_data in inputs.iter() {
|
||||
if path_data[3] < (max_depth - depth + 1) {
|
||||
@@ -38,24 +36,34 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
}
|
||||
|
||||
let amount = evaluate_int(args[2]);
|
||||
|
||||
let lowest_instance = evaluate_float(args[3]);
|
||||
let highest_instance = evaluate_float(args[4]);
|
||||
|
||||
let path = wrap_path(path_data);
|
||||
|
||||
for i in 0..amount {
|
||||
let alpha =
|
||||
lowest_instance + (i as f32 / amount as f32) * (highest_instance - lowest_instance);
|
||||
let alpha = lowest_instance
|
||||
+ (i as f32 / (amount - 1) as f32) * (highest_instance - lowest_instance);
|
||||
|
||||
let point = path.get_point_at(alpha);
|
||||
let direction = path.get_direction_at(alpha);
|
||||
let tangent = path.get_direction_at(alpha);
|
||||
let size = point[3] + 0.01;
|
||||
|
||||
let axis_rotation = Quat::from_axis_angle(
|
||||
Vec3::from_slice(&tangent).normalize(),
|
||||
i as f32 * rotation,
|
||||
);
|
||||
|
||||
let path_rotation = Quat::from_rotation_arc(Vec3::Y, Vec3::from_slice(&tangent).normalize());
|
||||
|
||||
let rotation = path_rotation * axis_rotation;
|
||||
|
||||
let transform = Mat4::from_scale_rotation_translation(
|
||||
Vec3::new(point[3], point[3], point[3]),
|
||||
Quat::from_xyzw(direction[0], direction[1], direction[2], 1.0).normalize(),
|
||||
Vec3::new(size, size, size),
|
||||
rotation,
|
||||
Vec3::from_slice(&point),
|
||||
);
|
||||
|
||||
transforms.push(transform);
|
||||
}
|
||||
}
|
||||
@@ -67,11 +75,11 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
);
|
||||
let mut instances = wrap_instance_data(&mut instance_data);
|
||||
instances.set_geometry(geo);
|
||||
(0..transforms.len()).for_each(|i| {
|
||||
instances.set_transformation_matrix(i, &transforms[i].to_cols_array());
|
||||
});
|
||||
|
||||
log!("WASM(instance): geo: {:?}", instance_data);
|
||||
for (i, transform) in transforms.iter().enumerate() {
|
||||
instances.set_transformation_matrix(i, &transform.to_cols_array());
|
||||
}
|
||||
|
||||
inputs.push(&instance_data);
|
||||
|
||||
concat_args(inputs)
|
||||
|
||||
6
nodes/max/plantarium/leaf/.gitignore
vendored
Normal file
6
nodes/max/plantarium/leaf/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
12
nodes/max/plantarium/leaf/Cargo.toml
Normal file
12
nodes/max/plantarium/leaf/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "leaf"
|
||||
version = "0.1.0"
|
||||
authors = ["Max Richter <jim-x@web.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
|
||||
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
|
||||
24
nodes/max/plantarium/leaf/src/input.json
Normal file
24
nodes/max/plantarium/leaf/src/input.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"id": "max/plantarium/leaf",
|
||||
"outputs": [
|
||||
"geometry"
|
||||
],
|
||||
"inputs": {
|
||||
"shape": {
|
||||
"type": "shape",
|
||||
"external": true
|
||||
},
|
||||
"size": {
|
||||
"type": "float",
|
||||
"value": 1
|
||||
},
|
||||
"xResolution": {
|
||||
"type": "integer",
|
||||
"description": "The amount of stems to produce",
|
||||
"min": 1,
|
||||
"max": 64,
|
||||
"value": 1,
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
166
nodes/max/plantarium/leaf/src/lib.rs
Normal file
166
nodes/max/plantarium/leaf/src/lib.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
use std::convert::TryInto;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use nodarium_macros::nodarium_definition_file;
|
||||
use nodarium_macros::nodarium_execute;
|
||||
use nodarium_utils::encode_float;
|
||||
use nodarium_utils::evaluate_float;
|
||||
use nodarium_utils::evaluate_int;
|
||||
use nodarium_utils::log;
|
||||
use nodarium_utils::wrap_arg;
|
||||
use nodarium_utils::{split_args, decode_float};
|
||||
|
||||
nodarium_definition_file!("src/input.json");
|
||||
|
||||
fn calculate_y(x: f32) -> f32 {
|
||||
let term1 = (x * PI * 2.0).sin().abs();
|
||||
let term2 = (x * 2.0 * PI + (PI / 2.0)).sin() / 2.0;
|
||||
term1 + term2
|
||||
}
|
||||
|
||||
// Helper vector math functions
|
||||
fn vec_sub(a: &[f32; 3], b: &[f32; 3]) -> [f32; 3] {
|
||||
[a[0] - b[0], a[1] - b[1], a[2] - b[2]]
|
||||
}
|
||||
|
||||
fn vec_cross(a: &[f32; 3], b: &[f32; 3]) -> [f32; 3] {
|
||||
[
|
||||
a[1] * b[2] - a[2] * b[1],
|
||||
a[2] * b[0] - a[0] * b[2],
|
||||
a[0] * b[1] - a[1] * b[0],
|
||||
]
|
||||
}
|
||||
|
||||
fn vec_normalize(v: &[f32; 3]) -> [f32; 3] {
|
||||
let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
|
||||
if len == 0.0 { [0.0, 0.0, 0.0] } else { [v[0]/len, v[1]/len, v[2]/len] }
|
||||
}
|
||||
|
||||
#[nodarium_execute]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
let args = split_args(input);
|
||||
let input_path = split_args(args[0])[0];
|
||||
let size = evaluate_float(args[1]);
|
||||
let width_resolution = evaluate_int(args[2]).max(3) as usize;
|
||||
let path_length = (input_path.len() - 4) / 2;
|
||||
|
||||
let slice_count = path_length;
|
||||
let face_amount = (slice_count - 1) * (width_resolution - 1) * 2;
|
||||
let position_amount = slice_count * width_resolution;
|
||||
|
||||
let out_length =
|
||||
3 // metadata
|
||||
+ face_amount * 3 // indices
|
||||
+ position_amount * 3 // positions
|
||||
+ position_amount * 3; // normals
|
||||
|
||||
let mut out = vec![0 as i32; out_length];
|
||||
|
||||
log!("face_amount={:?} position_amount={:?}", face_amount, position_amount);
|
||||
|
||||
out[0] = 1;
|
||||
out[1] = position_amount.try_into().unwrap();
|
||||
out[2] = face_amount.try_into().unwrap();
|
||||
let mut offset = 3;
|
||||
|
||||
// Writing Indices
|
||||
let mut idx = 0;
|
||||
for i in 0..(slice_count - 1) {
|
||||
let base0 = (i * width_resolution) as i32;
|
||||
let base1 = ((i + 1) * width_resolution) as i32;
|
||||
|
||||
for j in 0..(width_resolution - 1) {
|
||||
let a = base0 + j as i32;
|
||||
let b = base0 + j as i32 + 1;
|
||||
let c = base1 + j as i32;
|
||||
let d = base1 + j as i32 + 1;
|
||||
|
||||
// triangle 1
|
||||
out[offset + idx + 0] = a;
|
||||
out[offset + idx + 1] = b;
|
||||
out[offset + idx + 2] = c;
|
||||
|
||||
// triangle 2
|
||||
out[offset + idx + 3] = b;
|
||||
out[offset + idx + 4] = d;
|
||||
out[offset + idx + 5] = c;
|
||||
|
||||
idx += 6;
|
||||
}
|
||||
}
|
||||
|
||||
offset += face_amount * 3;
|
||||
|
||||
// Writing Positions
|
||||
let width = 50.0;
|
||||
let mut positions = vec![[0.0f32; 3]; position_amount];
|
||||
for i in 0..slice_count {
|
||||
let ax = i as f32 / (slice_count -1) as f32;
|
||||
|
||||
let px = decode_float(input_path[2 + i * 2 + 0]);
|
||||
let pz = decode_float(input_path[2 + i * 2 + 1]);
|
||||
|
||||
|
||||
for j in 0..width_resolution {
|
||||
let alpha = j as f32 / (width_resolution - 1) as f32;
|
||||
let x = 2.0 * (-px * (alpha - 0.5) + alpha * width);
|
||||
let py = calculate_y(alpha-0.5)*5.0*(ax*PI).sin();
|
||||
let pz_val = pz - 100.0;
|
||||
|
||||
let pos_idx = i * width_resolution + j;
|
||||
positions[pos_idx] = [x - width, py, pz_val];
|
||||
|
||||
let flat_idx = offset + pos_idx * 3;
|
||||
out[flat_idx + 0] = encode_float((x - width) * size);
|
||||
out[flat_idx + 1] = encode_float(py * size);
|
||||
out[flat_idx + 2] = encode_float(pz_val * size);
|
||||
}
|
||||
}
|
||||
|
||||
// Writing Normals
|
||||
offset += position_amount * 3;
|
||||
let mut normals = vec![[0.0f32; 3]; position_amount];
|
||||
|
||||
for i in 0..(slice_count - 1) {
|
||||
for j in 0..(width_resolution - 1) {
|
||||
let a = i * width_resolution + j;
|
||||
let b = i * width_resolution + j + 1;
|
||||
let c = (i + 1) * width_resolution + j;
|
||||
let d = (i + 1) * width_resolution + j + 1;
|
||||
|
||||
// triangle 1: a,b,c
|
||||
let u = vec_sub(&positions[b], &positions[a]);
|
||||
let v = vec_sub(&positions[c], &positions[a]);
|
||||
let n1 = vec_cross(&u, &v);
|
||||
|
||||
// triangle 2: b,d,c
|
||||
let u2 = vec_sub(&positions[d], &positions[b]);
|
||||
let v2 = vec_sub(&positions[c], &positions[b]);
|
||||
let n2 = vec_cross(&u2, &v2);
|
||||
|
||||
for &idx in &[a, b, c] {
|
||||
normals[idx][0] += n1[0];
|
||||
normals[idx][1] += n1[1];
|
||||
normals[idx][2] += n1[2];
|
||||
}
|
||||
|
||||
for &idx in &[b, d, c] {
|
||||
normals[idx][0] += n2[0];
|
||||
normals[idx][1] += n2[1];
|
||||
normals[idx][2] += n2[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalize and write to output
|
||||
for i in 0..position_amount {
|
||||
let n = vec_normalize(&normals[i]);
|
||||
let flat_idx = offset + i * 3;
|
||||
out[flat_idx + 0] = encode_float(n[0]);
|
||||
out[flat_idx + 1] = encode_float(n[1]);
|
||||
out[flat_idx + 2] = encode_float(n[2]);
|
||||
}
|
||||
|
||||
wrap_arg(&out)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
use nodarium_macros::nodarium_definition_file;
|
||||
use nodarium_macros::nodarium_execute;
|
||||
use nodarium_utils::{concat_args, log, split_args};
|
||||
use nodarium_utils::{concat_args, split_args};
|
||||
|
||||
nodarium_definition_file!("src/input.json");
|
||||
|
||||
#[nodarium_execute]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
let args = split_args(input);
|
||||
log!("vec3 input: {:?}", input);
|
||||
log!("vec3 args: {:?}", args);
|
||||
concat_args(args)
|
||||
concat_args(split_args(input))
|
||||
}
|
||||
|
||||
@@ -274,6 +274,11 @@
|
||||
stroke-width: 1px;
|
||||
stroke: var(--color-layer-3);
|
||||
fill: var(--color-layer-2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
svg:hover circle {
|
||||
opacity: 1;
|
||||
}
|
||||
circle.active,
|
||||
circle:hover {
|
||||
|
||||
Reference in New Issue
Block a user