Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR #2/5 Astolfo feature/builtin-macro #66

Conversation

RealAstolfo
Copy link

Co-Authored-By: Thomas ten Cate ttencate@gmail.com

These are the macros created by ttencate, and is meant to be merged after #65,

this pr implements "Real" to be closer to upstream godot's C++ internals, also adds a vector macro to construct vector2 through vector4 easier.

Co-Authored-By: Thomas ten Cate <ttencate@gmail.com>
Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for these macros!

I think the macros are suitable to avoid code duplication for cases that are very boiler-platey or trivial. However, this PR goes a bit too far in some places, as it forcibly reuses code even if that code is significantly different. This necessitates extra helpers to make the reuse work, making the logic non-local and harder to read.

The use of paste! for snake_case conversion + concatenation is quite clever! 😎
I hope it doesn't affect compile times too badly.

Comment on lines +13 to +14
/// Currently, this is always `f32`; working with an engine compiled with `precision=double` is
/// not supported yet.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't support double, I would avoid all the indirection through Real and just use f32 and the respective glam types everywhere. This differentiation is relatively easy to add later, if necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to recall that the engine uses float in some places even if compiled with precision=double, so it seemed smart to make people think about this every time they use a floating-point type in gdextension. However, there's no guarantee that adding Real prematurely will do this. Removing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once (if ever) Godot 4 fully supports switching between float and double, we should probably spend some time to design how to integrate that feature. It will affect a lot of places like codegen, FFI etc., so a solution should consider all of those.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't Godot already support double precision?
godotengine/godot-proposals#892
Granted it's a compile-time flag but all the main PRs have been merged and the biggest issues with it (camera jitter) have been fixed already to my knowledge. Seeing as they are taking bug reports regarding double-precision usage I think it's safe to say it's supported. Unless I'm misunderstanding your comment 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, even better! I seem to have missed it 😬

Thanks for the input! Support for double is then something we might consider for godot-rust, although I'd like to get the core features ready first.

Comment on lines +200 to +210
#[inline]
pub fn length(&self) -> $component_type {
self.0.length()
}

/// Returns the vector scaled to unit length. Equivalent to `self / self.length()`. See
/// also `is_normalized()`.
#[inline]
pub fn normalized(&self) -> Self {
self.0.normalize().into()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should take self by value.

Comment on lines +115 to +127
impl From<$inner_type> for $vector {
/// Wraps an inner type in a Godot vector.
fn from(inner: $inner_type) -> Self {
Self(inner)
}
}

impl From<$vector> for $inner_type {
/// Unwraps a Godot vector into its inner type.
fn from(vector: $vector) -> Self {
vector.0
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be public (and thus cannot use From trait). Problem is that it makes the internally used glam version part of the public interface and causes version conflicts. See godot-rust/gdnative#594 (comment) for 3 examples where this happened with euclid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, adding from_glam and as_glam instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you return a copy, maybe use to_glam(). I think the Rust convention for "as_" is to return references.

Comment on lines +129 to +137
impl std::fmt::Display for $vector {
/// Formats this vector in the same way the Godot engine would.
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
format_string!($($components),*),
$(self.$components()),*)
}
}
Copy link
Member

@Bromeon Bromeon Jan 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite hard-to-understand code and needs an external macro format_string! to power its implementation. A reader needs to spend significant time to reconstruct how this string is built. We don't really win anything by having less code.

I'd rather lift the impl Display out of the macro:

impl std::fmt::Display for Vector3 {
	/// Formats this vector in the same way the Godot engine would.
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        	let Self { x, y, z } = self;
		write!(f, "({x}, {y}, {z})")
	}
}

This is much easier to read and makes immediately obvious how the resulting string looks.

Comment on lines +158 to +179
/// Implements `From` that does a component-wise cast to convert one vector type to another.
macro_rules! impl_vector_from {
(
// Name of the vector type.
$vector:ty,
// Name of the original type.
$from:ty,
// Type of target component, for example `Real`.
$component_type:ty,
// Names of the components, for example `(x, y)`.
($($components:ident),*)$(,)?
) => {
paste::paste! {
impl From<$from> for $vector {
#[doc = "Converts a `" $from "` into a `" $vector "`. Note that this might be a lossy operation."]
fn from(from: $from) -> Self {
Self::new($(from.$components() as $component_type),*)
}
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want From for lossy operations. There is no impl From<f32> for i32, either.

And I would not even add From the other way around. Since this is a rarely used conversion and has a lot of potential to be used wrongly (prime example: convert between pixels/world coordinates instead of going through proper screen-space transforms), I'd prefer explicitly named methods, if at all.

Does glam provide this conversion? I haven't found it.

Copy link
Contributor

@ttencate ttencate Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to Rustify the engine API here, which does it with conversion constructors Vector2(Vector2i from). Explicitly named ctors are a better approach indeed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do something like:

Vector2i::from_float_lossy(v: Vector2)
Vector2::from_int(v: Vector2i)

Comment on lines +100 to +113
$(
#[doc = "Returns the `" $components "` component of this vector."]
#[inline]
pub fn $components(&self) -> $component_type {
self.0.$components
}

#[doc = "Sets the `" $components "` component of this vector."]
#[inline]
pub fn [<set_ $components>](&mut self, $components: $component_type) {
self.0.$components = $components;
}
)*
}
Copy link
Member

@Bromeon Bromeon Jan 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned elsewhere, I would just use public x and y.

Copy link
Contributor

@ttencate ttencate Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... which means not wrapping glam. (Or Deref'ing to glam but that still exposes glam in the public API which we don't want.) Let's do it!

@Bromeon Bromeon added feature Adds functionality to the library c: core Core components labels Jan 16, 2023
// Name of the vector type.
$vector:ty,
// Type of each individual component, for example `i32`.
$component_type:ty,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be called scalar instead of component_type.

@ttencate
Copy link
Contributor

Since @RealAstolfo allowed me to take this PR back, I've created a new PR: #71.

There are a lot of changes compared to this one, to address @Bromeon's comments above. Most notably we now have public x/y/z fields, which changes how the rest of the API is implemented. @RealAstolfo This will probably cause you some merge pain; I'm very sorry about that.

bors bot added a commit that referenced this pull request Jan 20, 2023
71: Implement the basics of built-in vector types r=Bromeon a=ttencate

For Vector{2,3,4}{,i} this implements:
- public fields
- constructors
- constants
- operators
- indexing by axis
- (private) conversions to/from glam types
- Display
- a couple of functions like `abs()` and `length()` for demonstration

See also #6.

This PR supersedes #66.

Co-authored-by: Thomas ten Cate <ttencate@gmail.com>
Co-authored-by: Jan Haller <bromeon@gmail.com>
@Bromeon
Copy link
Member

Bromeon commented Jan 20, 2023

Now implemented in #71. Thanks a lot to both of you, the discussion of potential vector designs was really fruitful! ❤️

@Bromeon Bromeon closed this Jan 20, 2023
bors bot added a commit that referenced this pull request Feb 1, 2023
67: PR #3/5 Astolfo feature/builtin-vector r=Bromeon a=RealAstolfo



This PR builds upon ttencates macro and my math functions to create a rust implementation of what godot has in its C++ as close as i think, meant to be merged after #66 

Co-authored-by: RealAstolfo <astolfo.gman@gmail.com>
@RealAstolfo RealAstolfo deleted the astolfo-feature/builtin-macro branch February 5, 2023 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: core Core components feature Adds functionality to the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants