Skip to content

Commit

Permalink
feat: implement face_direction and fix: cylinder collider with top/bo…
Browse files Browse the repository at this point in the history
…ttom (#176)
  • Loading branch information
leanmendoza authored Jan 24, 2024
1 parent eef5483 commit 03bf605
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 26 deletions.
12 changes: 2 additions & 10 deletions godot/src/decentraland_components/mesh_renderer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,8 @@ func set_plane(uvs: Array):


func set_cylinder(top_radius: float, bottom_radius: float):
# if current_type != MeshRendererPrimitiveType.MRPT_CYLINDER:
# self.mesh = CylinderMesh.new()
# self.mesh.set_height(1.0)
# current_type = MeshRendererPrimitiveType.MRPT_CYLINDER
#
# self.mesh.set_top_radius(top_radius)
# self.mesh.set_bottom_radius(bottom_radius)

if current_type != MeshRendererPrimitiveType.MRPT_PLANE:
current_type = MeshRendererPrimitiveType.MRPT_PLANE
if current_type != MeshRendererPrimitiveType.MRPT_CYLINDER:
current_type = MeshRendererPrimitiveType.MRPT_CYLINDER
self.mesh = ArrayMesh.new()
else:
self.mesh.clear_surfaces()
Expand Down
229 changes: 215 additions & 14 deletions rust/decentraland-godot-lib/src/scene_runner/components/mesh_collider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,200 @@ use crate::{
scene_runner::scene::Scene,
};
use godot::{
engine::{AnimatableBody3D, BoxShape3D, CollisionShape3D, CylinderShape3D, SphereShape3D},
engine::{
mesh::PrimitiveType, AnimatableBody3D, ArrayMesh, BoxShape3D, CollisionShape3D,
CylinderShape3D, SphereShape3D,
},
prelude::*,
};
use num_traits::Zero;

fn build_cylinder_arrays(radius_top: f32, radius_bottom: f32) -> VariantArray {
let mut uvs_array = PackedVector2Array::new();
let mut vertices_array = PackedVector3Array::new();
let mut normals_array = PackedVector3Array::new();
let mut triangles_array = PackedInt32Array::new();
let num_vertices = 10;
let length = 1.0;
let offset_pos = Vector3::new(0.0, -0.5, 0.0);
let num_vertices_plus_one = num_vertices + 1;

vertices_array.resize(2 * num_vertices_plus_one + (num_vertices + 1) + (num_vertices + 1));
normals_array.resize(2 * num_vertices_plus_one + (num_vertices + 1) + (num_vertices + 1));
uvs_array.resize(2 * num_vertices_plus_one + (num_vertices + 1) + (num_vertices + 1));

let uvs = uvs_array.as_mut_slice();
let vertices = vertices_array.as_mut_slice();
let normals = normals_array.as_mut_slice();

let slope = ((radius_bottom - radius_top) / length).atan();
let slope_sin = -slope.sin();
let slope_cos = slope.sin();

for i in 0..num_vertices {
let angle = 2.0 * std::f32::consts::PI * i as f32 / num_vertices as f32;
let angle_sin = -angle.sin();
let angle_cos = angle.cos();
let angle_half = 2.0 * std::f32::consts::PI * (i as f32 + 0.5) / num_vertices as f32;
let angle_half_sin = -angle_half.sin();
let angle_half_cos = angle_half.cos();

vertices[i] =
Vector3::new(radius_top * angle_cos, length, radius_top * angle_sin) + offset_pos;
vertices[i + num_vertices_plus_one] =
Vector3::new(radius_bottom * angle_cos, 0.0, radius_bottom * angle_sin) + offset_pos;

if radius_top.is_zero() {
normals[i] = Vector3::new(
angle_half_cos * slope_cos,
-slope_sin,
angle_half_sin * slope_cos,
);
} else {
normals[i] = Vector3::new(angle_cos * slope_cos, -slope_sin, angle_sin * slope_cos);
}

if radius_bottom.is_zero() {
normals[i + num_vertices_plus_one] = Vector3::new(
angle_half_cos * slope_cos,
-slope_sin,
angle_half_sin * slope_cos,
);
} else {
normals[i + num_vertices_plus_one] =
Vector3::new(angle_cos * slope_cos, -slope_sin, angle_sin * slope_cos);
}

uvs[i] = Vector2::new(1.0 - 1.0 * i as f32 / num_vertices as f32, 1.0);
uvs[i + num_vertices_plus_one] =
Vector2::new(1.0 - 1.0 * i as f32 / num_vertices as f32, 0.0);
}

vertices[num_vertices] = vertices[0];
vertices[num_vertices + num_vertices_plus_one] = vertices[num_vertices_plus_one];
uvs[num_vertices] = Vector2::new(1.0 - 1.0 * num_vertices as f32 / num_vertices as f32, 1.0);
uvs[num_vertices + num_vertices_plus_one] =
Vector2::new(1.0 - 1.0 * num_vertices as f32 / num_vertices as f32, 0.0);
normals[num_vertices] = normals[0];
normals[num_vertices + num_vertices_plus_one] = normals[num_vertices_plus_one];

let cover_top_index_start = 2 * num_vertices_plus_one;
let cover_top_index_end = 2 * num_vertices_plus_one + num_vertices;
for i in 0..num_vertices {
let angle = 2.0 * std::f32::consts::PI * i as f32 / num_vertices as f32;
let angle_sin = -angle.sin();
let angle_cos = angle.cos();

vertices[cover_top_index_start + i] =
Vector3::new(radius_top * angle_cos, length, radius_top * angle_sin) + offset_pos;
normals[cover_top_index_start + i] = Vector3::new(0.0, 1.0, 0.0);
uvs[cover_top_index_start + i] = Vector2::new(angle_cos / 2.0 + 0.5, angle_sin / 2.0 + 0.5);
}

vertices[cover_top_index_start + num_vertices] = Vector3::new(0.0, length, 0.0) + offset_pos;
normals[cover_top_index_start + num_vertices] = Vector3::new(0.0, 1.0, 0.0);
uvs[cover_top_index_start + num_vertices] = Vector2::new(0.5, 0.5);

let cover_bottom_index_start = cover_top_index_start + num_vertices + 1;
let cover_bottom_index_end = cover_bottom_index_start + num_vertices;
for i in 0..num_vertices {
let angle = 2.0 * std::f32::consts::PI * i as f32 / num_vertices as f32;
let angle_sin = -angle.sin();
let angle_cos = angle.cos();

vertices[cover_bottom_index_start + i] =
Vector3::new(radius_bottom * angle_cos, 0.0, radius_bottom * angle_sin) + offset_pos;
normals[cover_bottom_index_start + i] = Vector3::new(0.0, -1.0, 0.0);
uvs[cover_bottom_index_start + i] =
Vector2::new(angle_cos / 2.0 + 0.5, angle_sin / 2.0 + 0.5);
}

vertices[cover_bottom_index_start + num_vertices] = Vector3::new(0.0, 0.0, 0.0) + offset_pos;
normals[cover_bottom_index_start + num_vertices] = Vector3::new(0.0, -1.0, 0.0);
uvs[cover_bottom_index_start + num_vertices] = Vector2::new(0.5, 0.5);

if radius_top.is_zero() || radius_bottom.is_zero() {
triangles_array.resize(num_vertices_plus_one * 3 + num_vertices * 3 + num_vertices * 3);
} else {
triangles_array.resize(num_vertices_plus_one * 6 + num_vertices * 3 + num_vertices * 3);
}
let triangles = triangles_array.as_mut_slice();

let mut cnt = 0;
if radius_top.is_zero() {
for i in 0..num_vertices {
triangles[cnt] = (i + num_vertices_plus_one) as i32;
cnt += 1;
triangles[cnt] = (i) as i32;
cnt += 1;
triangles[cnt] = (i + 1 + num_vertices_plus_one) as i32;
cnt += 1;
}
} else if radius_bottom.is_zero() {
for i in 0..num_vertices {
triangles[cnt] = (i) as i32;
cnt += 1;
triangles[cnt] = (i + 1) as i32;
cnt += 1;
triangles[cnt] = (i + num_vertices_plus_one) as i32;
cnt += 1;
}
} else {
for i in 0..num_vertices {
let ip1 = i + 1;
triangles[cnt] = (i) as i32;
cnt += 1;
triangles[cnt] = (ip1) as i32;
cnt += 1;
triangles[cnt] = (i + num_vertices_plus_one) as i32;
cnt += 1;

triangles[cnt] = (ip1 + num_vertices_plus_one) as i32;
cnt += 1;
triangles[cnt] = (i + num_vertices_plus_one) as i32;
cnt += 1;
triangles[cnt] = (ip1) as i32;
cnt += 1;
}
}

for i in 0..num_vertices {
let mut next = cover_top_index_start + i + 1;

if next == cover_top_index_end {
next = cover_top_index_start
}

triangles[cnt] = (next) as i32;
cnt += 1;
triangles[cnt] = (cover_top_index_start + i) as i32;
cnt += 1;
triangles[cnt] = (cover_top_index_end) as i32;
cnt += 1;
}

for i in 0..num_vertices {
let mut next = cover_bottom_index_start + i + 1;
if next == cover_bottom_index_end {
next = cover_bottom_index_start;
}

triangles[cnt] = (cover_bottom_index_end) as i32;
cnt += 1;
triangles[cnt] = (cover_bottom_index_start + i) as i32;
cnt += 1;
triangles[cnt] = (next) as i32;
cnt += 1;
}

let mut ret = VariantArray::new();
ret.resize(13);
ret.set(0, vertices_array.to_variant());
ret.set(1, normals_array.to_variant());
ret.set(4, uvs_array.to_variant());
ret.set(12, triangles_array.to_variant());
ret
}

pub fn create_or_update_mesh(
animatable_body_3d: &mut Gd<AnimatableBody3D>,
Expand Down Expand Up @@ -58,19 +249,29 @@ pub fn create_or_update_mesh(
sphere_mesh.upcast()
}
pb_mesh_collider::Mesh::Cylinder(cylinder_mesh_value) => {
let mut cylinder_shape = match current_shape {
Some(current_shape) => current_shape
.try_cast::<CylinderShape3D>()
.unwrap_or(CylinderShape3D::new()),
None => CylinderShape3D::new(),
};
// TODO: top and bottom radius
let radius = (cylinder_mesh_value.radius_top.unwrap_or(0.5)
+ cylinder_mesh_value.radius_bottom.unwrap_or(0.5))
* 0.5;
cylinder_shape.set_radius(radius);
cylinder_shape.set_height(1.0);
cylinder_shape.upcast()
let mut array_mesh = ArrayMesh::new();
let arrays = build_cylinder_arrays(
cylinder_mesh_value.radius_top.unwrap_or(0.5),
cylinder_mesh_value.radius_bottom.unwrap_or(0.5),
);
array_mesh.add_surface_from_arrays(PrimitiveType::PRIMITIVE_TRIANGLES, arrays);
if let Some(new_shape) = array_mesh.create_trimesh_shape() {
new_shape.upcast()
} else {
let mut cylinder_shape = match current_shape {
Some(current_shape) => current_shape
.try_cast::<CylinderShape3D>()
.unwrap_or(CylinderShape3D::new()),
None => CylinderShape3D::new(),
};
// TODO: top and bottom radius
let radius = (cylinder_mesh_value.radius_top.unwrap_or(0.5)
+ cylinder_mesh_value.radius_bottom.unwrap_or(0.5))
* 0.5;
cylinder_shape.set_radius(radius);
cylinder_shape.set_height(1.0);
cylinder_shape.upcast()
}
}
pb_mesh_collider::Mesh::Plane(_plane_mesh) => {
let mut box_shape = match current_shape {
Expand Down
27 changes: 25 additions & 2 deletions rust/decentraland-godot-lib/src/scene_runner/components/tween.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::{collections::HashSet, time::Duration};

use godot::builtin::Basis;

use crate::{
dcl::{
components::{
Expand Down Expand Up @@ -211,8 +213,29 @@ pub fn update_tween(scene: &mut Scene, crdt_state: &mut SceneCrdtState) {
let end = data.end.clone().unwrap().to_godot();

if data.face_direction == Some(true) {
// TODO: This must be calculated one per tween data update, not per frame
// TODO: Implement transform.rotation = start.look_at(end)
let direction = (end - start).normalized();
let basis = if direction.is_zero_approx() {
Basis::IDENTITY
} else {
let v_x = godot::builtin::Vector3::UP.cross(direction);
let v_x = if v_x.is_zero_approx() {
// same workaround as bevy-explorer
// when the direction is colinear to the up vector, we use the forward vector as up+
godot::builtin::Vector3::FORWARD.cross(direction)
} else {
v_x
}
.normalized();
let v_y = direction.cross(v_x);

let mut basis = Basis::IDENTITY;
basis.set_col_a(v_x);
basis.set_col_b(v_y);
basis.set_col_c(direction);
basis
};

transform.rotation = basis.to_quat();
}

transform.translation = start + ((end - start) * ease_value);
Expand Down

0 comments on commit 03bf605

Please sign in to comment.