dylnmc
dylnmc

Reputation: 4010

three.js SphereBufferedGeometry without triangulated mesh

I have been searching the google for this question, but I didn't find much.

Basically, I just want a sphere that has quads -- i.e. no triangulation in the mesh. In fact, if it is possible, I would like it to only have vertical lines, but I assume this is out of the question, but I definitely do not want diagonals.

I saw the EdgesHelper, but I don't think that is what I want regarding the creation of a sphere.

I don't think this is a duplicate, but if it is, please point me to the right place; thanks.

Upvotes: 0

Views: 1040

Answers (2)

dylnmc
dylnmc

Reputation: 4010

see https://jsfiddle.net/dylnmc/3o9gae9r/ to see how I ended up creating my own quad. sphere that has phi- and theta- start and length values so that you can create partial spheres. Kudos to prisoner849, who answered above/below.

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-30, 60, -120);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

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

var r = 20,
    verticalLines = 32, /* must be divisible by 4! */
    horizontalLines = 16; /* must be divisible by 2! */

var quadSphere_threeQuarters = createQuadSphere(r, 3 * verticalLines / 4, horizontalLines, {
    color: 0x0000ff
}, 0, 3 * Math.PI / 2);
scene.add(quadSphere_threeQuarters);

var quadSphere_oneEighth = createQuadSphere(r, verticalLines / 4, horizontalLines / 2, {
    color: 0xff0000
}, 3 * Math.PI / 2, Math.PI / 2, Math.PI / 2, Math.PI / 2);
quadSphere_oneEighth.position.x += 50;
scene.add(quadSphere_oneEighth);

var quadSphere_threeQuarters_right = createQuadSphere(r, 3 * verticalLines / 4, horizontalLines, {
    color: 0xff00ff
}, 0, 3 * Math.PI / 2);
quadSphere_threeQuarters_right.position.x -= 50;
scene.add(quadSphere_threeQuarters_right);

var quadSphere_oneEighth_right = createQuadSphere(r, verticalLines / 4, horizontalLines / 2, {
    color: 0xff00ff
}, 3 * Math.PI / 2, Math.PI / 2, Math.PI / 2, Math.PI / 2);
quadSphere_oneEighth_right.position.x -= 50;
scene.add(quadSphere_oneEighth_right);



function createQuadSphere(r, widthSegments, heightSegments, atts, phiStart, phiLen, thetaStart, thetaLen) {
    var sphere = new THREE.Group(),
        material = new THREE.LineBasicMaterial(atts);

    if (phiStart === undefined) {
        phiStart = Math.PI / 2;
    }
    if (phiLen === undefined) {
        phiLen = 2 * Math.PI;
    }
    if (thetaStart === undefined) {
        thetaStart = 0;
    }
    if (thetaLen === undefined) {
        thetaLen = Math.PI;
    }

    /* width segments (longitude) */
    for (var phiDelta = phiLen / widthSegments, phi = phiStart, arc = createVerticalArc(r, heightSegments, thetaStart + Math.PI / 2, thetaLen); phi <= phiStart + phiLen + phiDelta; phi += phiDelta) {
        var arcTmpGeometry = arc.clone();
        arcTmpGeometry.rotateY(phi);
        var arcLine = new THREE.Line(arcTmpGeometry, material);
        sphere.add(arcLine);
    }

    /* height segments (latitude) */
    for (var thetaDelta = thetaLen / heightSegments, theta = thetaStart; theta < thetaStart + thetaLen - thetaDelta; theta += thetaDelta) {
        if (theta === 0) {
            continue;
        }
        var arcR = r * Math.sin(theta),
            arcH = r * Math.cos(theta),
            arcTmpGeometry = createHorizontalArc(arcR, widthSegments, phiStart, phiLen);
        arcTmpGeometry.rotateX(Math.PI / 2);
        arcTmpGeometry.rotateY(Math.PI / 2);
        arcTmpGeometry.translate(0, arcH, 0);
        var arcLine = new THREE.Line(arcTmpGeometry, material);
        sphere.add(arcLine);
    }

    return sphere;
}

function createVerticalArc(r, segments, thetaStart, thetaLen) {
    var geometry = new THREE.CircleGeometry(r, segments, thetaStart, thetaLen);
    geometry.vertices.shift();
    return geometry;
}

function createHorizontalArc(r, segments, phiStart, phiLen) {
    var geometry = new THREE.CircleGeometry(r, segments, phiStart, phiLen);
    geometry.vertices.shift();
    if (phiLen >= 2 * Math.PI) {
        geometry.vertices.push(geometry.vertices[0].clone());
    }
    return geometry;
}


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>

code:

function createQuadSphere(r, widthSegments, heightSegments, atts, phiStart, phiLen, thetaStart, thetaLen) {
    var sphere = new THREE.Group(),
        material = new THREE.LineBasicMaterial(atts);

    if (phiStart === undefined) {
        phiStart = Math.PI / 2;
    }
    if (phiLen === undefined) {
        phiLen = 2 * Math.PI;
    }
    if (thetaStart === undefined) {
        thetaStart = 0;
    }
    if (thetaLen === undefined) {
        thetaLen = Math.PI;
    }

    /* width segments (longitude) */
    for (var phiDelta = phiLen / widthSegments, phi = phiStart, arc = createVerticalArc(r, heightSegments, thetaStart + Math.PI / 2, thetaLen); phi <= phiStart + phiLen + phiDelta; phi += phiDelta) {
        var arcTmpGeometry = arc.clone();
        arcTmpGeometry.rotateY(phi);
        var arcLine = new THREE.Line(arcTmpGeometry, material);
        sphere.add(arcLine);
    }

    /* height segments (latitude) */
    for (var thetaDelta = thetaLen / heightSegments, theta = thetaStart; theta < thetaStart + thetaLen - thetaDelta; theta += thetaDelta) {
        if (theta === 0) {
            continue;
        }
        var arcR = r * Math.sin(theta),
            arcH = r * Math.cos(theta),
            arcTmpGeometry = createHorizontalArc(arcR, widthSegments, phiStart, phiLen);
        arcTmpGeometry.rotateX(Math.PI / 2);
        arcTmpGeometry.rotateY(Math.PI / 2);
        arcTmpGeometry.translate(0, arcH, 0);
        var arcLine = new THREE.Line(arcTmpGeometry, material);
        sphere.add(arcLine);
    }

    return sphere;
}

function createVerticalArc(r, segments, thetaStart, thetaLen) {
    var geometry = new THREE.CircleGeometry(r, segments, thetaStart, thetaLen);
    geometry.vertices.shift();
    return geometry;
}

function createHorizontalArc(r, segments, phiStart, phiLen) {
    var geometry = new THREE.CircleGeometry(r, segments, phiStart, phiLen);
    geometry.vertices.shift();
    if (phiLen >= 2 * Math.PI) {
        geometry.vertices.push(geometry.vertices[0].clone());
    }
    return geometry;
}

how to use:

var radius = 20, widthSegments = 32, heightSegments = 16,
    atts = {color: "red"},
    phiStart = Math.PI / 2, phiLen = Math.PI,
    thetaStart = 0, thetaLen = Math.PI;
var sphere = createQuadSphere(radius, widthSegments / 2,
             heightSegments, atts, phiStart, phiLen,
             thetaStart, thetaLen);
scene.add(sphere);

I think the sphere might be rotated 180 in x-z plane (phi), but I am tired from long day at work, and it suites my needs :)


terrible place to put this, but I also made arcs with thickness .. because I had to for my project: https://jsfiddle.net/u98wssL2/

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-80, 80, -350);
var renderer = new THREE.WebGLRenderer({
    alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.backgroundColor = "#bbbbbb"
document.body.appendChild(renderer.domElement);

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

var r = 20,
    verticalLines = 32,
    /* must be divisible by 4! */
    horizontalLines = 32; /* must be divisible by 2! */

var tubeArc = createTubeArc(50, horizontalLines, 5, 8, 0, 2 * Math.PI, 'round', {
    'color': 'red'
})
scene.add(tubeArc);

var tubeArc1 = createTubeArc(50, horizontalLines, 5, 8, Math.PI, Math.PI, 'round', {
    'color': 'yellow'
})
tubeArc1.position.x += 50;
scene.add(tubeArc1);

var tubeArc2 = createTubeArc(50, horizontalLines, 5, 8, 0, Math.PI, 'round', {
    'color': 'blue'
})
tubeArc2.position.x -= 50;
scene.add(tubeArc2);

function createTubeArc(radius, segments, cylinderRadius, cylinderSegments, thetaStart, thetaLen, capStyle, atts) {
    var tubeArc = new THREE.Group();
    if (capStyle !== "round" && capStyle !== "open" && capStyle !== "flat") {
        capStyle = "round";
    }
    // TODO: implement round :D (when you think you might need something ...)
    var material = new THREE.MeshBasicMaterial(atts);
    for (var thetaDelta = thetaLen / segments, theta = thetaStart + thetaDelta / 2, cylinderHeight = Math.sqrt(2 * radius * radius - 2 * radius * radius * Math.cos(thetaDelta)) + Math.sqrt(2 * cylinderRadius * cylinderRadius - 2 * cylinderRadius * cylinderRadius * Math.cos(thetaDelta)); theta < thetaStart + thetaLen + thetaDelta / 2; theta += thetaDelta) {
        var ptX = radius * Math.cos(theta),
            ptY = radius * Math.sin(theta),
            cylinderTmpGeometry = new THREE.CylinderGeometry(cylinderRadius, cylinderRadius, cylinderHeight, cylinderSegments, 1, (capStyle === "open"), 0, 2 * Math.PI),
            cylinder = new THREE.Mesh(cylinderTmpGeometry, material);
        cylinder.rotateZ(theta);
        cylinder.position.x = ptX;
        cylinder.position.y = ptY;
        if (capStyle === "open") {
            cylinder.material.side = THREE.DoubleSide;
        }
        tubeArc.add(cylinder);
    }

    return tubeArc;
}


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>

Upvotes: 2

prisoner849
prisoner849

Reputation: 17586

enter image description here I've made such customizable solution from the scratch, using THREE.CircleGeometry() (not buffered, though):

function createSphereOfQuadsWireframe(radius, widthSegments, heightSegments, color, showWidthSegments, showHeightSegments) {
  var sphereObj = new THREE.Group();

  if (showWidthSegments) {
    // width segments
    var arcGeom = createArc(radius, heightSegments, false); // as the amount of width segments may be odd, it's better to use half-circles, that's why the third parameter is `false`
    var widthSector = Math.PI * 2 / widthSegments;
    for (var ws = 0; ws < widthSegments; ws++) {
      var arcGeomTmp = arcGeom.clone();
      arcGeomTmp.rotateY(widthSector * ws);
      var arcLine = new THREE.Line(arcGeomTmp, new THREE.LineBasicMaterial({
        color: color
      }));
      sphereObj.add(arcLine);
    }
  }

  if (showHeightSegments) {
    //height segments
    var heightSector = Math.PI / heightSegments;
    for (var hs = 1; hs < heightSegments; hs++) {
      var hRadius = Math.sin(hs * heightSector) * radius;
      var height = Math.cos(hs * heightSector) * radius;
      var arcHeightGeom = createArc(hRadius, widthSegments, true);
      arcHeightGeom.rotateX(Math.PI / 2);
      arcHeightGeom.translate(0, height, 0);
      var arcHeightLine = new THREE.Line(arcHeightGeom, new THREE.LineBasicMaterial({
        color: color
      }));
      sphereObj.add(arcHeightLine);
    }
  }
  return sphereObj;
}

function createArc(radius, segments, full) {
  var geom = new THREE.CircleGeometry(radius, segments, Math.PI / 2, full ? Math.PI * 2 : Math.PI);
  geom.vertices.shift();
  if (full) geom.vertices.push(geom.vertices[0].clone());
  return geom;
}

and its use is like this:

var fullSphere = createSphereOfQuadsWireframe(20, 32, 16, "yellow", true, true);
scene.add(fullSphere);

var widthOnlySphere = createSphereOfQuadsWireframe(20, 32, 16, "pink", true, false);
widthOnlySphere.position.set(-50, 0, 0);
scene.add(widthOnlySphere);

var heightOnlySphere = createSphereOfQuadsWireframe(20, 32, 16, "aqua", false, true);
heightOnlySphere.position.set(50, 0, 0);
scene.add(heightOnlySphere);

jsfiddle example

Upvotes: 4

Related Questions