feat: add gravity node
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m52s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m52s
This commit is contained in:
parent
41ca2123ba
commit
c1e6d141bf
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -78,6 +78,22 @@ version = "0.27.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
|
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gravity"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"glam",
|
||||||
|
"macros",
|
||||||
|
"noise",
|
||||||
|
"serde",
|
||||||
|
"serde-wasm-bindgen",
|
||||||
|
"utils",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-test",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -65,6 +65,14 @@
|
|||||||
"min": 3,
|
"min": 3,
|
||||||
"max": 64,
|
"max": 64,
|
||||||
"setting": "resolution.curve"
|
"setting": "resolution.curve"
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"type": "float",
|
||||||
|
"hidden": true,
|
||||||
|
"min": 0,
|
||||||
|
"max": 360,
|
||||||
|
"step": 0.01,
|
||||||
|
"value": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::f32::consts::PI;
|
|
||||||
|
|
||||||
use glam::Vec3;
|
|
||||||
use macros::include_definition_file;
|
use macros::include_definition_file;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use utils::{
|
use utils::{
|
||||||
concat_arg_vecs, evaluate_float, evaluate_int,
|
concat_arg_vecs, evaluate_float, evaluate_int,
|
||||||
geometry::{create_path, get_direction_at_path, get_point_at_path, wrap_path, wrap_path_mut},
|
geometry::{
|
||||||
|
create_path, interpolate_along_path, rotate_vector_by_angle, wrap_path, wrap_path_mut,
|
||||||
|
},
|
||||||
log, set_panic_hook, split_args,
|
log, set_panic_hook, split_args,
|
||||||
};
|
};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -51,42 +51,35 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
|||||||
|
|
||||||
let length = evaluate_float(args[1]);
|
let length = evaluate_float(args[1]);
|
||||||
let thickness = evaluate_float(args[2]);
|
let thickness = evaluate_float(args[2]);
|
||||||
let offset_single = evaluate_float(args[3]);
|
let offset_single = if i % 2 == 0 {
|
||||||
|
evaluate_float(args[3])
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
// log!("a: {}, length: {}, thickness: {}, offset_single: {}, lowest_branch: {}, highest_branch: {}", a, length, thickness, offset_single, lowest_branch, highest_branch);
|
|
||||||
|
|
||||||
// log!("a: {}, length: {}, thickness: {}, offset_single: {}, lowest_branch: {}, highest_branch: {}", a, length, thickness, offset_single, lowest_branch, highest_branch);
|
|
||||||
let root_alpha = (a * (highest_branch - lowest_branch) + lowest_branch)
|
let root_alpha = (a * (highest_branch - lowest_branch) + lowest_branch)
|
||||||
.min(1.0)
|
.min(1.0)
|
||||||
.max(0.0);
|
.max(0.0);
|
||||||
|
|
||||||
let is_left = i % 2 == 0;
|
let (branch_origin, orthogonal, direction) = interpolate_along_path(
|
||||||
|
path.points,
|
||||||
|
root_alpha + (offset_single - 0.5) * 6.0 / resolution as f32,
|
||||||
|
);
|
||||||
|
|
||||||
let branch_origin = get_point_at_path(path.points, root_alpha);
|
let rotation_angle = (evaluate_float(args[9]) * PI / 180.0) * i as f32;
|
||||||
//const [_vx, , _vz] = interpolateSkeletonVec(stem.skeleton, a);
|
|
||||||
let direction_slice = get_direction_at_path(path.points, root_alpha);
|
|
||||||
let direction = Vec3::from_slice(&direction_slice).normalize();
|
|
||||||
|
|
||||||
let rotation_angle = if is_left { PI } else { -PI };
|
|
||||||
|
|
||||||
// check if diration contains NaN
|
// check if diration contains NaN
|
||||||
if direction[0].is_nan() || direction[1].is_nan() || direction[2].is_nan() {
|
if orthogonal[0].is_nan() || orthogonal[1].is_nan() || orthogonal[2].is_nan() {
|
||||||
log!(
|
log!(
|
||||||
"BRANCH direction contains NaN: {:?}, slice: {:?} branch_origin: {:?}, branch: {}",
|
"BRANCH direction contains NaN: {:?}, branch_origin: {:?}, branch: {}",
|
||||||
direction,
|
direction,
|
||||||
direction_slice,
|
|
||||||
branch_origin,
|
branch_origin,
|
||||||
i
|
i
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let branch_direction = Vec3::from_slice(&[
|
let branch_direction = rotate_vector_by_angle(orthogonal, direction, rotation_angle);
|
||||||
direction[0] * rotation_angle.cos() - direction[2] * rotation_angle.sin(),
|
|
||||||
0.0,
|
|
||||||
direction[0] * rotation_angle.sin() + direction[2] * rotation_angle.cos(),
|
|
||||||
])
|
|
||||||
.normalize();
|
|
||||||
|
|
||||||
log!(
|
log!(
|
||||||
"BRANCH depth: {}, branch_origin: {:?}, direction_at: {:?}, branch_direction: {:?}",
|
"BRANCH depth: {}, branch_origin: {:?}, direction_at: {:?}, branch_direction: {:?}",
|
||||||
|
6
nodes/max/plantarium/gravity/.gitignore
vendored
Normal file
6
nodes/max/plantarium/gravity/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
|
bin/
|
||||||
|
pkg/
|
||||||
|
wasm-pack.log
|
30
nodes/max/plantarium/gravity/Cargo.toml
Normal file
30
nodes/max/plantarium/gravity/Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "gravity"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Max Richter <jim-x@web.de>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["console_error_panic_hook"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.84"
|
||||||
|
|
||||||
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
|
# code size when deploying.
|
||||||
|
utils = { version = "0.1.0", path = "../../../../packages/utils" }
|
||||||
|
macros = { version = "0.1.0", path = "../../../../packages/macros" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde-wasm-bindgen = "0.4"
|
||||||
|
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||||
|
web-sys = { version = "0.3.69", features = ["console"] }
|
||||||
|
noise = "0.9.0"
|
||||||
|
glam = "0.27.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
wasm-bindgen-test = "0.3.34"
|
6
nodes/max/plantarium/gravity/package.json
Normal file
6
nodes/max/plantarium/gravity/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "wasm-pack build --release --out-name index --no-default-features",
|
||||||
|
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
|
||||||
|
}
|
||||||
|
}
|
29
nodes/max/plantarium/gravity/src/input.json
Normal file
29
nodes/max/plantarium/gravity/src/input.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"id": "max/plantarium/gravity",
|
||||||
|
"outputs": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"inputs": {
|
||||||
|
"plant": {
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"strength": {
|
||||||
|
"type": "float",
|
||||||
|
"min": 0.1,
|
||||||
|
"max": 1
|
||||||
|
},
|
||||||
|
"curviness": {
|
||||||
|
"type": "float",
|
||||||
|
"hidden": true,
|
||||||
|
"min": 0.1,
|
||||||
|
"max": 1
|
||||||
|
},
|
||||||
|
"depth": {
|
||||||
|
"type": "integer",
|
||||||
|
"min": 1,
|
||||||
|
"max": 10,
|
||||||
|
"value": 1,
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
nodes/max/plantarium/gravity/src/lib.rs
Normal file
129
nodes/max/plantarium/gravity/src/lib.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use glam::Vec3;
|
||||||
|
use macros::include_definition_file;
|
||||||
|
use utils::{
|
||||||
|
concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, reset_call_count,
|
||||||
|
set_panic_hook, split_args,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
include_definition_file!("src/input.json");
|
||||||
|
|
||||||
|
fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 {
|
||||||
|
a + (b - a) * t
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
|
reset_call_count();
|
||||||
|
|
||||||
|
let args = split_args(input);
|
||||||
|
|
||||||
|
let plants = split_args(args[0]);
|
||||||
|
let depth = evaluate_int(args[3]);
|
||||||
|
|
||||||
|
let mut max_depth = 0;
|
||||||
|
for path_data in plants.iter() {
|
||||||
|
if path_data[2] != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
max_depth = max_depth.max(path_data[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Vec<Vec<i32>> = plants
|
||||||
|
.iter()
|
||||||
|
.map(|_path_data| {
|
||||||
|
let mut path_data = _path_data.to_vec();
|
||||||
|
if path_data[2] != 0 || path_data[3] < (max_depth - depth + 1) {
|
||||||
|
return path_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = wrap_path_mut(&mut path_data);
|
||||||
|
|
||||||
|
let mut offset_vec = Vec3::ZERO;
|
||||||
|
|
||||||
|
for i in 1..path.length {
|
||||||
|
// let alpha = i as f32 / (path.length - 1) as f32;
|
||||||
|
let start_index = (i - 1) * 4;
|
||||||
|
let end_index = start_index + 4;
|
||||||
|
|
||||||
|
let start_point = Vec3::from_slice(&path.points[start_index..start_index + 3]);
|
||||||
|
let end_point = Vec3::from_slice(&path.points[end_index..end_index + 3]);
|
||||||
|
log!("--------------------------------");
|
||||||
|
|
||||||
|
log!(
|
||||||
|
"start_index: {:?} end_index: {:?} length:{}",
|
||||||
|
start_index,
|
||||||
|
end_index,
|
||||||
|
path.points.len()
|
||||||
|
);
|
||||||
|
if start_point[0].is_nan() {
|
||||||
|
log!("start_point is nan {:?}", path.points);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
log!("start_point: {:?}", start_point);
|
||||||
|
log!("end_point: {:?}", end_point);
|
||||||
|
|
||||||
|
let length = (end_point - start_point).length();
|
||||||
|
|
||||||
|
let normalised = (end_point - start_point).normalize();
|
||||||
|
|
||||||
|
if normalised[0].is_nan() {
|
||||||
|
log!("normalised is nan {:?}", normalised);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let strength = evaluate_float(args[1]);
|
||||||
|
let down_point = Vec3::new(0.0, -length * strength, 0.0);
|
||||||
|
|
||||||
|
if down_point[0].is_nan() {
|
||||||
|
log!("down_point is nan {:?}", down_point);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let curviness = evaluate_float(args[2]);
|
||||||
|
|
||||||
|
let mut mid_point = lerp_vec3(
|
||||||
|
normalised,
|
||||||
|
down_point,
|
||||||
|
curviness * (i as f32 / path.length as f32).sqrt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if mid_point[0].is_nan() {
|
||||||
|
log!("mid_point is nan {:?}", mid_point);
|
||||||
|
log!("normalised: {:?}", normalised);
|
||||||
|
log!("curviness: {:?}", curviness);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if mid_point[0] == 0.0 && mid_point[2] == 0.0 {
|
||||||
|
mid_point[0] += 0.0001;
|
||||||
|
mid_point[2] += 0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
mid_point = mid_point.normalize();
|
||||||
|
|
||||||
|
mid_point *= length;
|
||||||
|
|
||||||
|
let final_end_point = start_point + mid_point;
|
||||||
|
let offset_end_point = end_point + offset_vec;
|
||||||
|
|
||||||
|
if offset_end_point[0].is_nan() {
|
||||||
|
log!("offset_end_point is nan {:?}", offset_end_point);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.points[end_index] = offset_end_point[0];
|
||||||
|
path.points[end_index + 1] = offset_end_point[1];
|
||||||
|
path.points[end_index + 2] = offset_end_point[2];
|
||||||
|
|
||||||
|
let offset = final_end_point - end_point;
|
||||||
|
offset_vec += offset;
|
||||||
|
}
|
||||||
|
path_data
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
concat_args(output.iter().map(|x| x.as_slice()).collect())
|
||||||
|
}
|
13
nodes/max/plantarium/gravity/tests/web.rs
Normal file
13
nodes/max/plantarium/gravity/tests/web.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! Test suite for the Web and headless browsers.
|
||||||
|
|
||||||
|
#![cfg(target_arch = "wasm32")]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen_test;
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn pass() {
|
||||||
|
assert_eq!(1 + 1, 2);
|
||||||
|
}
|
@ -21,7 +21,7 @@
|
|||||||
"type": "float",
|
"type": "float",
|
||||||
"label": "Fixate bottom of plant",
|
"label": "Fixate bottom of plant",
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
"value": 1,
|
"value": 1.0,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 1
|
"max": 1
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,7 @@ pub fn extrude_path(input_path: PathData, res_x: usize) -> Vec<i32> {
|
|||||||
let position_offset = i * res_x;
|
let position_offset = i * res_x;
|
||||||
|
|
||||||
let pos = Vec3::new(path[i * 4], path[i * 4 + 1], path[i * 4 + 2]);
|
let pos = Vec3::new(path[i * 4], path[i * 4 + 1], path[i * 4 + 2]);
|
||||||
let thickness = path[i * 4 + 3];
|
let thickness = path[i * 4 + 3].max(0.000001);
|
||||||
|
|
||||||
// Get direction of the current segment
|
// Get direction of the current segment
|
||||||
let segment_dir = (if i == 0 {
|
let segment_dir = (if i == 0 {
|
||||||
|
21
packages/utils/src/geometry/math.rs
Normal file
21
packages/utils/src/geometry/math.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use glam::{Quat, Vec3};
|
||||||
|
|
||||||
|
/// Rotates a vector around a given axis by a specified angle.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `vector` - The vector to rotate.
|
||||||
|
/// * `axis` - The axis to rotate around.
|
||||||
|
/// * `angle_radians` - The angle to rotate by, in radians.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// * The rotated vector.
|
||||||
|
pub fn rotate_vector_by_angle(vector: Vec3, axis: Vec3, angle_radians: f32) -> Vec3 {
|
||||||
|
// Normalize the axis to ensure it's a unit vector
|
||||||
|
let normalized_axis = axis.normalize();
|
||||||
|
|
||||||
|
// Create a quaternion representing the rotation around the axis by the given angle
|
||||||
|
let rotation_quat = Quat::from_axis_angle(normalized_axis, angle_radians);
|
||||||
|
|
||||||
|
// Rotate the vector using the quaternion
|
||||||
|
rotation_quat.mul_vec3(vector)
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
mod calculate_normals;
|
mod calculate_normals;
|
||||||
mod extrude_path;
|
mod extrude_path;
|
||||||
mod geometry_data;
|
mod geometry_data;
|
||||||
|
mod math;
|
||||||
mod path_data;
|
mod path_data;
|
||||||
mod transform;
|
mod transform;
|
||||||
|
|
||||||
pub use calculate_normals::*;
|
pub use calculate_normals::*;
|
||||||
pub use extrude_path::*;
|
pub use extrude_path::*;
|
||||||
pub use geometry_data::*;
|
pub use geometry_data::*;
|
||||||
|
pub use math::*;
|
||||||
pub use path_data::*;
|
pub use path_data::*;
|
||||||
pub use transform::*;
|
pub use transform::*;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use glam::{vec3, vec4, Vec3, Vec4, Vec4Swizzles};
|
||||||
|
|
||||||
// 0: node-type, stem: 0
|
// 0: node-type, stem: 0
|
||||||
// 1: depth
|
// 1: depth
|
||||||
static PATH_HEADER_SIZE: usize = 2;
|
static PATH_HEADER_SIZE: usize = 2;
|
||||||
@ -207,3 +209,112 @@ pub fn get_direction_at_path(path: &[f32], alpha: f32) -> [f32; 3] {
|
|||||||
|
|
||||||
[dx / norm, dy / norm, dz / norm]
|
[dx / norm, dy / norm, dz / norm]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A function that interpolates a position along a path given by `points_data` at a position
|
||||||
|
/// specified by `alpha` (ranging from 0.0 to 1.0), calculates an orthogonal vector to the path,
|
||||||
|
/// and returns the direction of the path at that point.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `points_data` - A slice of `f32` containing x, y, z coordinates and thickness for each point defining the path.
|
||||||
|
/// * `alpha` - A float from 0.0 to 1.0 indicating the relative position along the path.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// * A tuple containing the interpolated position along the path as Vec4 (including thickness),
|
||||||
|
/// a vector orthogonal to the path, and the direction of the path at that position.
|
||||||
|
pub fn interpolate_along_path(points_data: &[f32], _alpha: f32) -> (Vec4, Vec3, Vec3) {
|
||||||
|
let alpha = _alpha.min(0.999999).max(0.000001);
|
||||||
|
assert!(
|
||||||
|
points_data.len() % 4 == 0,
|
||||||
|
"The points data must be a multiple of 4."
|
||||||
|
);
|
||||||
|
|
||||||
|
let num_points = points_data.len() / 4;
|
||||||
|
assert!(
|
||||||
|
num_points > 1,
|
||||||
|
"There must be at least two points to define a path."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate the total length of the path and the lengths of each segment.
|
||||||
|
let mut segment_lengths = Vec::with_capacity(num_points - 1);
|
||||||
|
let mut total_length = 0.0;
|
||||||
|
for i in 0..num_points - 1 {
|
||||||
|
let start_index = i * 4;
|
||||||
|
let end_index = (i + 1) * 4;
|
||||||
|
let start_point = vec3(
|
||||||
|
points_data[start_index],
|
||||||
|
points_data[start_index + 1],
|
||||||
|
points_data[start_index + 2],
|
||||||
|
);
|
||||||
|
let end_point = vec3(
|
||||||
|
points_data[end_index],
|
||||||
|
points_data[end_index + 1],
|
||||||
|
points_data[end_index + 2],
|
||||||
|
);
|
||||||
|
let length = (end_point - start_point).length();
|
||||||
|
segment_lengths.push(length);
|
||||||
|
total_length += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the target length along the path corresponding to `alpha`.
|
||||||
|
let target_length = alpha * total_length;
|
||||||
|
let mut accumulated_length = 0.0;
|
||||||
|
|
||||||
|
// Find the segment that contains the point at `target_length`.
|
||||||
|
for (i, &length) in segment_lengths.iter().enumerate() {
|
||||||
|
if accumulated_length + length >= target_length {
|
||||||
|
// Calculate the position within this segment.
|
||||||
|
let segment_alpha = (target_length - accumulated_length) / length;
|
||||||
|
let start_index = i * 4;
|
||||||
|
let end_index = (i + 1) * 4;
|
||||||
|
let start_point = vec4(
|
||||||
|
points_data[start_index],
|
||||||
|
points_data[start_index + 1],
|
||||||
|
points_data[start_index + 2],
|
||||||
|
points_data[start_index + 3],
|
||||||
|
);
|
||||||
|
let end_point = vec4(
|
||||||
|
points_data[end_index],
|
||||||
|
points_data[end_index + 1],
|
||||||
|
points_data[end_index + 2],
|
||||||
|
points_data[end_index + 3],
|
||||||
|
);
|
||||||
|
let position = start_point + (end_point - start_point) * segment_alpha;
|
||||||
|
|
||||||
|
// Calculate the tangent vector to the path at this segment.
|
||||||
|
let tangent = (end_point.xyz() - start_point.xyz()).normalize();
|
||||||
|
|
||||||
|
// Calculate an orthogonal vector. Assume using the global up vector (0, 1, 0)
|
||||||
|
let global_up = vec3(0.0, 1.0, 0.0);
|
||||||
|
let orthogonal = tangent.cross(global_up).normalize();
|
||||||
|
|
||||||
|
// If the orthogonal vector is zero, choose another axis.
|
||||||
|
let orthogonal = if orthogonal.length_squared() == 0.0 {
|
||||||
|
tangent.cross(vec3(1.0, 0.0, 0.0)).normalize()
|
||||||
|
} else {
|
||||||
|
orthogonal
|
||||||
|
};
|
||||||
|
|
||||||
|
return (position, orthogonal, tangent);
|
||||||
|
}
|
||||||
|
accumulated_length += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As a fallback for numerical precision issues, use the last point and a default orthogonal vector.
|
||||||
|
let last_start_index = (num_points - 2) * 4;
|
||||||
|
let last_end_index = (num_points - 1) * 4;
|
||||||
|
let last_start_point = vec4(
|
||||||
|
points_data[last_start_index],
|
||||||
|
points_data[last_start_index + 1],
|
||||||
|
points_data[last_start_index + 2],
|
||||||
|
points_data[last_start_index + 3],
|
||||||
|
);
|
||||||
|
let last_end_point = vec4(
|
||||||
|
points_data[last_end_index],
|
||||||
|
points_data[last_end_index + 1],
|
||||||
|
points_data[last_end_index + 2],
|
||||||
|
points_data[last_end_index + 3],
|
||||||
|
);
|
||||||
|
let last_tangent = (last_end_point.xyz() - last_start_point.xyz()).normalize();
|
||||||
|
let last_orthogonal = last_tangent.cross(vec3(0.0, 1.0, 0.0)).normalize();
|
||||||
|
(last_end_point, last_orthogonal, last_tangent)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user