Creating custom hollow cylinder geometries in Three.js

I am looking to design my own unique geometry for hollow cylinders using three.js. I have attempted to blend elements from the RingGeometry and CylinderGeometry classes, but encountered some visual issues:

  • The cap normalization is incorrect (both caps only display texture on top)
  • The torso normal is incorrect (inner torso visible only from inside)

Here is an example: (image used for caps) https://i.sstatic.net/EHiTc.jpg

Below is the code snippet:

/**
 * 
 * @param {number} radius 
 * @param {number} holeRadius 
 * @param {number} height 
 * @param {number} segments 
 * @param {boolean} openEnded 
 * @param {number} thetaStart 
 * @param {number} thetaLength 
 */
function HollowCylinderGeometry(radius, holeRadius, height, segments, openEnded, thetaStart, thetaLength) {

    if (!(this instanceof HollowCylinderGeometry)) {
        throw new TypeError("HollowCylinderGeometry needs to be called using new");
    }

    THREE.Geometry.call(this);

    this.type = 'HollowCylinderGeometry';

    this.parameters = {
        radius: radius,
        holeRadius: holeRadius,
        height: height,
        segments: segments,
        openEnded: openEnded,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    this.fromBufferGeometry(new HollowCylinderBufferGeometry(radius, holeRadius, height, segments, openEnded, thetaStart, thetaLength));
    this.mergeVertices();

}

HollowCylinderGeometry.prototype = Object.create(THREE.Geometry.prototype);
HollowCylinderGeometry.prototype.constructor = HollowCylinderGeometry;

/**
 * 
 * @param {number} radius 
 * @param {number} holeRadius 
 * @param {number} height 
 * @param {number} segments 
 * @param {boolean} openEnded 
 * @param {number} thetaStart 
 * @param {number} thetaLength 
 */
function HollowCylinderBufferGeometry(radius, holeRadius, height, segments, openEnded, thetaStart, thetaLength) {

    if (!(this instanceof HollowCylinderBufferGeometry)) {
        throw new TypeError("HollowCylinderBufferGeometry needs to be called using new");
    }

    ...
}

Answer №1

Let Three.js handle the heavy lifting for you by utilizing THREE.Shape() and THREE.ExtrudeGeometry().

https://i.sstatic.net/N7nek.png

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 10, 20);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x818181);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
var texture1 = loader.load("https://threejs.org/examples/textures/crate.gif");
texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping;
texture1.repeat.set(0.05, 0.05);
var texture2 = loader.load("https://threejs.org/examples/textures/hardwood2_diffuse.jpg");
texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping;
texture2.repeat.set(0.1, 0.1);

var outerRadius = 10;
var innerRadius = 5;
var height = 2;

var arcShape = new THREE.Shape();
arcShape.moveTo(outerRadius * 2, outerRadius);
arcShape.absarc(outerRadius, outerRadius, outerRadius, 0, Math.PI * 2, false);
var holePath = new THREE.Path();
holePath.moveTo(outerRadius + innerRadius, outerRadius);
holePath.absarc(outerRadius, outerRadius, innerRadius, 0, Math.PI * 2, true);
arcShape.holes.push(holePath);

var geometry = new THREE.ExtrudeGeometry(arcShape, {
  amount: height,
  bevelEnabled: false,
  steps: 1,
  curveSegments: 60
});
geometry.center();
geometry.rotateX(Math.PI * -.5);
var mesh = new THREE.Mesh(geometry, [new THREE.MeshBasicMaterial({
  map: texture1
}), new THREE.MeshBasicMaterial({
  map: texture2
})]);
scene.add(mesh);

render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Answer №2

To determine if a face is visible, its orientation should be consistent, whether drawn clockwise or counterclockwise. Refer to Face Culling.
It's important to draw all polygons in the same counterclockwise orientation.

Modify the function generateTorso:

if ( isOuter ) {
    indices.push(a, b, d);
    indices.push(b, c, d);
} else {
    indices.push(a, d, b);
    indices.push(b, d, c);
}

Update the function generateCap:

if (isTop) {
    indices.push(a, b, d);
    indices.push(b, c, d);
} else {
    indices.push(a, d, b);
    indices.push(b, d, c);
}


Preview:

https://i.sstatic.net/db6ov.png

View the Code Snippet:

// JavaScript code snippet
// Contains functions for creating a Hollow Cylinder Geometry using Three.js

var renderer, scene, camera, controls;

function HollowCylinderGeometry(radius, holeRadius, height, segments, openEnded) {
    // Function to create Hollow Cylinder Geometry
}

// More functions and code here...
// CSS code snippet
// Styles for rendering the Hollow Cylinder Geometry using Three.js

body {
    margin: 0;
    overflow: hidden;
}

canvas {
    width: 100%;
    height: 100%
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

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

Comparing Arrays with jQuery

I am currently working on a tic-tac-toe project that involves a simple 3x3 grid. I have been storing the index of each selected box in an array for each player. However, I am facing difficulty in comparing my player arrays with the winner array to determin ...

Navigate to the specified URL once the Ajax function has completed successfully

I'm having trouble with opening a URL from an Ajax function. It seems like the URL is not being called. This is the code I am using: $(document).on( "click",".btndriver", function() { var id = $(this).attr("id"); var nombre = $(this).att ...

jqGrid display/hide columns options dialogue box

I am looking to implement a feature that allows users to toggle columns in my jqGrid. I came across a module for this purpose, but unfortunately, it is no longer supported in jqGrid 4.x. I have searched for a replacement module without success. Are there ...

Property is not accessible in the componentDidMount lifecycle method

I am having trouble using redux to fire an actionCreator and get my initial data when the app loads. The actionNoteGetLatest function is not recognized as a prop. Can someone assist me with this? In the code snippet provided below, I attempted to call act ...

What is the best way to extract individual objects from several arrays and consolidate them into a single array?

Currently, I have a collection of objects stored in a variable called listOfObjects. They are not separated by commas because I utilized the Object.entries method to extract these values from another array. console.log(listOfObjects) outputs { q: 'L ...

What is the process for making changes to a document in Mongoose?

My goal is to allow users to update existing mongoose documents using a form with method-override package. Despite trying various solutions found on Stackoverflow, I have not been able to resolve my issue. The desired functionality is for the user to view ...

Receiving a void value from the concealed field on the server end

When calling a JavaScript function and passing the value of a hidden field, I intend to use that value on the server-side. However, the hidden field's value is null. Client-Side Function function getDetails() { document.forms[0].HdnNode. ...

Navigate back to the previous page without reloading

Initially, I must clarify that the title of my query may not be entirely representative of what I wish to convey. The situation at hand is as follows: I have developed a jQuery table designed for exhibiting video records. Embedded within this table is a hy ...

How to save HTML content in a variable for future use in Next.js

When working with Next JS, my code resembles something like this: function Test() { return ( <section> <div>Test</div> </section> ); } Now, imagine that I want to have multiple entries here, gen ...

The variable that was posted is not being successfully transferred within the query

There seems to be an issue with retrieving data from the ajax call in ajaxcall.php and assigning it to $place = $_POST['place']; in listplace.php. Despite this problem, the code runs smoothly otherwise. I've tried numerous times to get the ...

Creating a Vue component that leverages the computed property from a mixin

My situation involves a straightforward component that utilizes a mixin shared across various components that have similar functionalities. However, upon running it, an issue arises: The error message "Property or method 'activeClass' is not ...

Issue with ng-hide logic malfunctioning

I am currently developing an Ionic application and encountering some issues with the ng-hide directive. My goal is to display or hide a button based on whether the user has completed registration. The button in question: <button class="button button-c ...

Utilize an array of JSON objects to populate an array of interfaces in Angular/Typescript

I am currently facing a dilemma - my code is functioning without any errors when executed, but my text editor is flagging an issue stating that the property 'categories' does not exist on type 'CategoryInterface[]' (specifically on the ...

Bulma Steps failing to advance to the next step despite clicking submit

I am currently working on implementing Buefy Steps in a Vue project. The Buefy steps are functioning correctly, but I am facing an issue where the 'Submit' button does not progress to the next step (e.g., from "Account" to "Profile"). App.vue: & ...

Property computation being initiated before property initialization

I am encountering an issue in my Vue application where I am trying to filter an array received from map getters based on a type prop within a computed property. Despite verifying that both the array elements and the prop are strings, the filtering process ...

What is the best way to locate a DOM element using jQuery after I have repositioned it on the page?

I designed a page that consists of two sections, with boxes in each. The functionality I implemented allows users to click on a box in either section and move it to the other section. Initially, this feature works smoothly for the first movement. Theoretic ...

JavaScript jQuery Asynchronous Function调用

I am looking for a way to call a function from the "on success" area without having to embed my code within that section. I need to use this code multiple times and would like to find a way to place it outside of the jQuery.ajax() function. Below is the c ...

Updating Firebase without altering the syntax can be achieved by following specific steps to

I'm feeling a bit lost on how to tackle this, as the project I've been handling is currently using firebase 7.14.0. Being a new developer, I've been struggling with this version due to the lack of documentation available. Is there a way for ...

Troublesome issue with custom select feature in foundation when used within a button

I'm currently incorporating Zurb Foundation 4 into my website and using the "custom forms" feature for special styling on form elements. The issue I'm facing is that HTML select elements aren't functioning properly when placed inside a butto ...

Add a color gradient to the material of a mesh using Three.js

Currently, I have successfully loaded an STL file into my scene with a single color applied to a phong material. However, I am eager to find a way to apply two colors to this mesh's material and create a gradient effect along the Z-axis similar to th ...