Wrapping a vector3 instance in threejs
Often I might be in a situation with a threejs project in which I would like to apply some kind of rules for Vector3 class instances that have to do with boundaries in terms of the possible range of values. There are two general ideas that come to mind with this clamping and wrapping.
In the past I have wrote one blog post on the clamp method of the Vector3 class, and that is one way to go about applying limitations. That is that when a vector goes out of a set range it will be clamped to a value that is within the range, and do so in a box kind of area as it is used by passing two vector3 class instances that define the lowermost and uppermost corners of the box. In that post I also wrote about the clamp length method that works by giving number values that define a min and max vector unit length. This is yet another option that works well, but then both work by clamping values rather than wrapping values. That is that some times when a Vector3 instance goes out of range I might not want to clamp it, but wrap it around to an opposite side of an area.
Sense I have all ready wrote a post on the subject of clamping Vector3 objects I thought that I should also write at least one point on the subject of wrapping them as well. It would seem that there are now such methods in the Vector3 class itself to do this sort of thing. However that is not to say that there are not tools to work with that are relevant to this as there are. Also there is just simply knowing a thing or two about how to go about wrapping values in general with a little vanilla javaScript code as well.
Wrapping Vector3 class instances in threejs, and what to know first
There are a few things that you might want to read about first with threejs as well as core javaScript in general. When it comes to threejs there is of course the very basics of getting started with the library, as well as specifics about the Vector3 class in general. When it comes to core javaScript there is a lot to wrote about when it comes to the built in modulo operator and what not to use in in many situations as well. I am not going to want to get into all of it in detail here in this post as I have done so in older posts on the subjects I am referring to. In this section I will just be briefly write about a few things to be aware of, and link to other content as needed.
The deal with javaScript modulo
A long time ago I wrote a blog post on the built in modulo operator in core javaScript and what it wrong with it. In truth there is not really anything wrong with the operator actually it is just that it works a little differently from what many developers might be used to when it comes to dealing with negative numbers. So then there is knowing how to go about adding what is often called mathematical modulo, or euclidean modulo to a JavaScript environment. There are many quick copy and past methods when it comes to vanilla javaScript, however if threejs is there to work with there is all ready a euclidean modulo method in the math utils object as well that can be used.
The Math utils object in threejs
In these examples on wrapping a vector in threejs I am making use of a method in the math utils object in threejs that is a kind of modulo operation that differs from what is baked into javaScript itself. There are a number of other methods to be aware of in the math utils object also, so it would be a good idea to look into the math utils object more if you have not done so before hand. Many of the methods are usual suspect type methods that I would use, but there is also a few methods that I would like to see there but they are missing, one of which would be a wrap method at least as of r140 backward. That’s okay though as maybe many of the methods should actaully be part of some other library actually that can be used with threejs or any javaScript project for that matter.
Making a custom cut utility library with a wrap method
When I was working on a whole bunch of vanilla javaScript projects I started a general utilities library that just contains a collection of methods that I find myself copying and pasting from one project to another. One of the methods in the library is a wrap method, along with a clamp method as well.
Wrap methods in other libraries
In the Math object of the Game Framework called Phaser there is a wrap method, and I have found that method works okay with a few exceptions with certain sets of arguments. In any cases there is looking into the source code of various projects that have libraries like this to see how they are preforming a certain kind of task such as this.
Source code is on Github
The source code examples that I am writing about here can also be found in my test threejs repo. This is also where I park the source code for my many other blog posts on threejs as well.
Version numbers matter
I was using r140 of threejs when I first wrote this post, and the last time I came around to do some editing I was using r146.
1 - The deal with modulo and wrapping values
First off here is a quick example that helps to show what the deal is with the built in javaScript modulo operator compared to using the euclidean modulo method in the math utils object. I have two sets of mesh objects that I would like to wrap, and I am trying to do so by just using modulo alone. When I use the core javaScript modulo operator the end result is that I get the outcome that I want, but only with positive numbers, negative numbers result in the object going out of the desired range. When I use the euclidean modulo method I do get a desired outcome where the mesh object loops back around within the desired range.
|
|
However even though things are working the way that I want them to with the euclidean modulo method it is still working in a way that is relative to zero forward rather than in a way that can work with a range that might go into negative numbers. Still the general idea of wrapping is there, from here forward I just need to find ways to adjust the range.
2 - A Wrap Values method
The process of making a wrap method from the ground up might prove to be a little involved, at least when it comes to making one from the ground up without looking into what is out there on the open internet at least. In any case there is taking an approach in which I am figuring out that I need to do on a axis by axis bases which just seems like the thing to do. What is good about this is that once I figure out something that works well for one axis, then it is generally just a mater of applying the same logic to all other axis values, at least that would seem to be the case with this anyway with respect to the way that I want to do it.
However I would not just want to use a wrap method with vectors alone mind you. So then this post will be on making a wrap method in general to begin with.
2.1 - Using MathUtils.euclideanModulo to create a wrap method
The basic idea of what I want does work very much so with the MathUtils.euclideanModulo method. However I want to have a wrap method where I pass a value, and then a min and max value rather than just two values. So then a wrap method can be made by just using the Math.max and Math.min methods with the a, and b values that are given after the first value. These min and max values can then be used to get a range which in turn can then be used to figure an alpha value by using THREE.MathUtils.euclideanModulo and then divining that over the range.
|
|
I have not battle tested this mind you, however thus far it seems to work fine.
2.2 - Using the distance to method along with euclidean modulo
The code that I worked out for this solution involves making two instances of the Vector2 class and then calling the distance to method of each to get an idea of what the max distance from 0 is as well as the current distance is. Once I have these two values I can use them with the euclidean modulo method to get how mush I need to add to the min vector or subtract from the max vector for the current axis. Once again I did not battle test this, but also once again it seems to give me a desired result thus far to say the least.
|
|
2.3 - Wrap Axis method based off the Math.Wrap method from Phaser
I have wrote a blog post or two on the game framework called Phaser in the past and I remember that the math object of the framework has a wrap method. This method does seem to work the way that I would want it to, but with a few exceptions one of which has to do with NaN values when given the value 0 for the min or max values of the range that I would like to wrap to. This can be fixed fairly easily though of course by just adding some code that will return 0 for such a case of course.
The end result is a warp method that I like better that the more complex solution involving the distance to method, that still seems to give the same end results for the domain that I given the function thus far. Also sense this is based on code from the popular game framework I am pretty sure this will work well for just about any values that I throw at it. What is also nice about this wrap method is that it is also very much a vanilla javaScript solution then as I am just using javaScript alone in the body of the wrap function.
|
|
3 - A Wrap method to work with vector3 objects
Now that I have a method that seems to work okay for one axis all I need to do to make a wrap method for Vector3 is to just call the method for each axis. What is great about this is that in order to make solutions that will also work for the Vector2 class the only major change is to just call the wrap axis for x and y only when making the wrap vector method. Also if I put more time into researching other solutions for this, and fine a better way of wrapping an axis, I can just recreate the wrap axis method, and leave everything else as is.
|
|
4 - A wrap Vector module and some demos
Having a wrap vector method seems like the kind of tool that I would want to take with me from project to project. So then in this section I will be writing about the start of a wrap vector module that I might turn into a separate full blown project that I will make one of my threejs example posts. There are a few ideas that come to mind for advanced features, but for now I think this module might just need to have one public method that will wrap a vector that I give it. On top of that I might want to also make a wrap number method public as well while I am at it so for now maybe that will be it for this project, as far as this post goes at least anyway.
4.0 - A wrap Vector module
The module that I have together then thus far just makes use of the wrap method based off of the one from Phaser, along with the wrap axis method. I then create a public api that is a function and make the main function of the module the wrap vector method. I am thinking that will be the main feature of interest with this that I will be using over and over again in projects, so maybe that is all that I need to do with this one for the most part. The only other thing that I did is make the wrap method public as I might want to do this sort of thing with a number rather than a vector class instance in some cases.
|
|
4.1 - Basic demo of the module
A basic example of the module will now work by setting something up like this one. Here I just have two mesh objects and I would like to wrap them both to the same set of vectors as I move them around. Thus far the module seems to work just fine with this simple hello world type example, but now I would like to move on to something a little more involved.
|
|
4.2 - Seeded Random example
For this example I made a few functions that will create and update a group of mesh objects. When I create a group I can set some data with the user data objects for the group as well as each mesh object.
|
|
5 - Animation Loop Demos
For this section I will not be going over the source code examples of a few animaiton loop demos that make use of the various wrapping features that I have wrote about this far in this post.
5.1 - Wrapping Vector unit length animation loop example
Here I have an animation loop example that is the basic for one of my videos for this blog post. The first video that I made was based on one of the module examples that I made with the wrap axis methods. For this example I wanted to just make something that has to do with wrapping vector unit length. I also wanted to make something that is a little more interesting than just a single mesh object having the length wrapped so I went a little overboard with expressions and so forth.
|
|
5.2 - Wrap Grid Demo
A while back I made a threejs project example that I just called object grid wrap because I am very bad at names. Anyway the project is a fairly complex way of creating a grid of objects that I then move around by way of two offset values, and then they, well wrap around when the go out of bounds. The project works okay, but there are for sure a few ruff edges with it at least as of this writing anyway. To cut to the chase here I might get around to making a new revision of that project at some point and to help address some of the problems I am seeing with it I thought I would try to make a very simple form of what I want here as an animation loop demo.
|
|
Conclusion
Wrapping Vectors is something that I do find myself doing over and over again as a way to limit vectors to a given range. I have made a whole lot of projects that do something to this effect one of which I find myself using a whole lot when I make my various little video projects that I call my “object grid wrap module”. When it comes to that project I am just using the Euclidean Modulo method to do so by wrapping given values to a range of 0 and 1 and then use those values to set the position of objects in a grid. That kind of system seems to work well also on top of having some kind of function that will wrap a vector directly like I did with my main wrap method example in this post.