What methods can I use to construct a 3D bezier spline using pre-defined 2D (xz) segments while maintaining a consistent "descent rate"?

Currently, I am in the process of developing a small 3D game that incorporates a spline for generating randomized tracks. To better explain this concept visually, think of games like Impossible Road and Audiosurf. I started this project about a week ago using Godot but switched to Three JS recently because I find TypeScript easier to work with compared to GDScript.

Both Godot and Three provide bezier spline classes which I found helpful. I began building my spline with cubic beziers by defining various "prefab segments" such as turns and loops. By randomizing the sequence of these segments, I aim to create dynamically generated tracks similar to Impossible road.

Initially, working on a flat plane (XZ), where the track is one-dimensional, went smoothly. However, when I introduced height variance into the equation, complications arose. My primary objective is to ensure a consistent descent speed along the spline, maintaining uniform displacement over the y-axis per distance covered on the XZ plane.

While exploring methods to implement descending slopes on the spline, traditional rotation techniques proved ineffective due to the curve's structure. This led me to contemplate how to add 'y' values for descent while ensuring continuity between curve segments.

I have encountered challenges in calculating the rate of descent elegantly for curves since simple linear adjustments do not apply directly to curved surfaces. It seems traditional approaches geared towards straight line segments are insufficient for managing dynamic curves efficiently.

My current focus lies in unraveling the intricacies of implementing continuous descents within bezier splines effectively. Despite researching extensively, I remain unable to find conclusive answers or resources addressing this specific issue.

Moreover, I am contemplating whether utilizing B-Splines or Catmull-Rom Splines might offer more straightforward solutions for creating seamless paths. While aware that other types of splines could potentially simplify the process, adapting my segment definitions to fit their requirements presents another obstacle.

If you're interested in delving deeper into the technicalities, feel free to explore my complete code repository on GitHub: https://github.com/thomasboyt/rascal

Answer №1

After considering @Spektre's suggestion, I came up with a solution that worked for me:

Instead of struggling to find the perfect control points for constant slope on a Bezier spline, I opted to create my spline as a "2D" spline on the XZ plane. Then, I simply added height linearly to the generated points when rendering/calculating the spline.

In hindsight, this approach seems obvious, but I was fixated on generating the spline the "correct way" with Bezier control points. While it is possible to achieve my desired result using Bezier curves - a friend shared an article on drawing helices with me, which likely covers this concept (although the math is beyond my expertise).

Implementing non-linear height displacement - specifically, randomly generated heights for each segment - was also straightforward using this method. I started by generating random heights, then used a 2D Catmull-Rom Spline with x=t and y=height at each point to interpolate over them. This technique effectively addressed any issues with discontinuity.

The outcome can be viewed here, and it's quite visually appealing to me:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for applying a texture to only one side of a cube without using THREE.MultiMaterial to lower draw calls

Imagine a scenario where there is a cube featuring 2 different materials. While I am utilizing MultiMaterial, I have noticed that it results in 6 draw calls instead of just 2. This concerns me in terms of performance, especially if the project is scaled up ...

Retrieve the value of [routerLinkActive] in the component's class

Recently, I've been working on a tab component called TabComponent and it includes the following HTML template: <a [routerLink]='link' [routerLinkActive]="[is-active]">link label</a> <button>Close tab</button> The c ...

Can a type alias be created for more than one parameter of a class or function with multiple type parameters?

When using Vue, there are situations where a generic function may require 3, 4, or even 5 type parameters. Is it possible to create a type alias for these parameters in order to avoid typing them out repeatedly? Something like this perhaps: // Example of ...

When utilizing a combination of generics in a Typescript mapped type, the resulting property type is not computed

In an attempt to create a versatile method that can handle a Mapped Type named QueryParamObject and partially read its properties: QueryParamObject can handle any type and returns a type where all properties are either string[] or string: export type Quer ...

Having trouble setting up mongodb-memory-server 8 to work with jest

I am currently working on integrating the latest version of mongodb-memory-server with jest on a node express server. While following the guide provided in the mongodb-memory-server documentation (), I encountered some gaps that I am struggling to fill in. ...

Always deemed non-assignable but still recognized as a universal type?

I'm curious about why the never type is allowed as input in generic's extended types. For example: type Pluralize<A extends string> = `${A}s` type Working = Pluralize<'language'> // 'languages' -> Works as e ...

Sending a POST request with parameters using HttpClient

My current challenge involves making a POST request to an endpoint that requires query string parameters instead of passing them in the body of the request. const params = new HttpParams() .set('param1', '1') .set('param2' ...

Determining the spatial capacity of a mesh using ThreeJS surpasses the volume of its bounding box

Issue at Hand: The challenge lies in the fact that the bounding box volume is turning out to be smaller than the volume calculated from the mesh. Attempts So Far: To begin with, I computed the volume of a bounding box using the following code: //loaded ...

In THREE's PerspectiveCamera, achieve a distortion-free 90-degree field of view

I am currently working on a website that utilizes THREE.js to produce a 3D environment. When it comes to video games, the standard camera field of view angle is typically around 90 degrees. However, when I set the PerspectiveCamera in THREE.js to this valu ...

Positioning Objects Next to Each Other in three.js Using Global World Coordinates

After loading one object using objloader onto my scene, I later load a second object. However, I would like to change the absolute position of the second object to another side of the first object. Both objects' positions are relative to local (0,0,0 ...

Guide on incorporating a movie onto a three.js canvas

I am looking to incorporate short semi-transparent videos that will sporadically appear on top of a three.js canvas, creating an effect similar to THREE.GlitchShaderPass but with video content instead. Would it be best to render a plane in front of the ca ...

Incorporating responsive design with React and Typescript

Trying to utilize React with TypeScript, I aim to dynamically generate components based on a field name // Storing all available components const components = { ComponentA, ComponentB, }; // Dynamically render the component based on fieldName const di ...

Error: Couldn't locate Next.js - TypeScript module

I encountered an error with the image, but I am unsure of the reason behind it. Additionally, the directory is included in the second image. https://i.sstatic.net/knUzH.png import Link from 'next/link'; import { useState } from 'react' ...

Ways to prevent Deno Workers from using cached source code

Good day, I am currently working on building a custom javascript code execution platform using Deno Workers. Additionally, I have implemented an Oak web server to manage requests for script modifications and their compilation and execution. An issue arise ...

Running complex Firestore query within Cloud Functions

Currently, I am developing triggers that interact with a Firestore movie and user database. The main goal of one trigger is to present a new user with a list of top-rated movies in genres they have selected as their favorites. To achieve this, I store the ...

Tips for incorporating a conditional background color in a styled component with react and typescript

Is there a way to dynamically change the background color of a styled component based on a condition using React and TypeScript? What I am attempting to achieve: I have a MainComponent that displays ListContent within a DragAndDropComponent. When a user ...

Clearing Out a Shopping Cart in Angular

Hey there, I have a little dilemma with my shopping cart system. I can easily add and delete products using an API. However, when it comes to deleting an item from the cart, I have to do it one by one by clicking on a button for each item, which is not ver ...

Determine parameter types and return values by analyzing the generic interface

I am currently working on a feature where I need to create a function that takes an interface as input and automatically determines the return types based on the 'key' provided in the options object passed to the function. Here is an example of ...

Tips for compressing a node.js typescript backend project?

Here's a glimpse of my webpack.config.js file: const path = require('path'); var fs = require('fs') var nodeModules = {}; fs.readdirSync('node_modules').filter(function (x) {return ['.bin'].indexOf(x) === -1;}) ...

Error: The argument provided for user.token is of type 'string | undefined' which cannot be assigned to a parameter of type 'string'

I'm currently engaged in a project that involves ASP.NET Core Web API and Angular 13. Here is the login post request from the endpoint: > https://localhost:44396/api/v1/auth/login { "status_code": 200, "message&qu ...