Javascript THREE.JS-展平测地多面体

Javascript THREE.JS-展平测地多面体,javascript,three.js,3d,geodesic-sphere,Javascript,Three.js,3d,Geodesic Sphere,我正在开发一个基于六边形瓷砖的行星生成器——想想Civ5地形投影到测地球体上 下图为该形状的上半部分: 我希望将这些单独的六边形和五边形展平成一个平面,尽管是一个不规则的平面,以便在反转变换和重组球体之前,在其上应用标准、正常的地形生成技术 我将生成两个平面,北方和南方,考虑到它本质上不可能展平任何真正的球体 然而,考虑到可以在2d空间中显示六角网格,我认为没有理由不能制作这样的网格——尽管沿五边形有一些变形 在我看来,我的步骤如下 生成每个平铺,就像它自己的THREE.Object3D一样,它

我正在开发一个基于六边形瓷砖的行星生成器——想想Civ5地形投影到测地球体上

下图为该形状的上半部分:

我希望将这些单独的六边形和五边形展平成一个平面,尽管是一个不规则的平面,以便在反转变换和重组球体之前,在其上应用标准、正常的地形生成技术

我将生成两个平面,北方和南方,考虑到它本质上不可能展平任何真正的球体

然而,考虑到可以在2d空间中显示六角网格,我认为没有理由不能制作这样的网格——尽管沿五边形有一些变形

在我看来,我的步骤如下

  • 生成每个平铺,就像它自己的THREE.Object3D一样,它本身包含一个具有7个顶点和6个面的网格(对于12个五边形,则为6个和5个)
  • 将每个磁贴(Object3D)的世界坐标中的纬度标准化为零(忽略落在“南半球”上的纬度-这是一个单独但相同的步骤)
  • 旋转每个对象,使其内部网格与世界的地平线对齐-对象和内部网格的顶点是独立的,因此这很棘手。基本上,我需要每个网格顶点的y值为0,而不会扭曲形状本身
  • 沿世界的水平面重新定位每个Object3d,以使平铺具有适当的间距,并类似于六边形栅格
  • 将每个平铺的每个顶点缝合到一个新的几何体中,即单个连续平面
  • 一旦我有了它,我将应用我选择实现的任何世界建筑,“拾取”每个变换的顶点到它的原始平铺上,然后取消变换。瞧,一个妖术世界

    构建hexsphere非常简单。我正在使用。我如何实现tiles的确切代码将在本文的底部给出

    此外,规范化每个瓷砖的纬度位置也相当简单。只要把它的位置和完成

    我现在面临的问题是将六边形旋转到地平线上。我试着使用标准的三角运算。瓷砖的顶点相对于其中心点,而不是世界中心。因此,如果我取离瓷砖中心x距离最大的顶点,我可以计算瓷砖(本质上是一个平面)相对于地平线形成的x平面角度

    # If center-point of tile is ( 0, 0, 0 ) relative to itself
    # and
    # Vertex with maximum x distance from center is ( x, y, z )
    # then
    # the x-distance from the center is x, and the y-distance is y, z-distance is z
    # therefor
    # I will always have two sides, the opposite and the adjacent, of any triangle I'm forming with sed point
    #
    # Using normal trig, I can calculate the angle
    
    let rotationX = Math.atan( vertex.y / vertex.x )
    
    我只是将该旋转反向应用于object3D(或者我想是内部网格?)

    这一直不起作用,我无法形成这样的感觉:我至少遗漏了问题的一个关键部分。如果我在这件事上完全不对劲,我也不会感到惊讶

    下一步是适当地间隔瓷砖,使其xz位置类似于2d坐标,而不是3d坐标。这一点我也不知道

    我意识到这可能是毫无意义的。我想我可以简单地在球体上执行我的世界构建,但是我很难将我的思想弯曲到第三维度

    代码如下

    AppAbstract.js-包含场景、摄影机等

    class AppAbstract {
    
        constructor( positional, parameters ) {
    
            this.setupScene( parameters );
    
        };
    
        setupScene( parameters ) {
    
            this._scene = new THREE.Scene();
    
            this._camera = new THREE.PerspectiveCamera( 50, 2, 1, 100000 );
            this._camera.position.z = 2700;
            this._camera.position.y = 0;
    
            this._renderer = new THREE.WebGLRenderer( {
                canvas: parameters[ 'canvas' ],
                antialias: false, 
                alpha: true,
            } );
    
            this.renderer.setSize( window.innerWidth, window.innerWidth / 2 );
            this.renderer.setPixelRatio( window.devicePixelRatio );
            this.renderer.sortObjects = false;
    
            window.addEventListener( 'resize', () => {
                this.renderer.setSize( window.innerWidth, window.innerWidth / 2 );
            }, false );
    
            this.ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
            this._scene.add( this.ambientLight );
    
            this._controls = new THREE.OrbitControls( this._camera, this._renderer.domElement );
            this.controls.enableDamping = true;
            this.controls.dampingFactor = 0.1;
            this.controls.rotateSpeed = 0.1;
            this.controls.autoRotate = false;
            this.controls.autoRotateSpeed = 0.01;
            this.controls.zoomSpeed = 0.1;
    
        };
    
        get scene() {
    
            return this._scene;
    
        };
    
        get camera() {
    
            return this._camera;
    
        };
    
        get renderer() {
    
            return this._renderer;
    
        };
    
        get controls() {
    
            return this._controls;
    
        };
    
    };
    
    planetap.js-

        class PlanetApp extends AppAbstract {
    
        constructor( positional, parameters ) {
            super( positional, parameters );
    
            this.planet = new Planet( this, positional, parameters );
            this.scene.add( this.planet );
    
        };
    
    };
    
    Planet.js

    class Planet extends THREE.Object3D {
    
        constructor( app, positional, parameters ) {
    
            this.generateMaterials();
            this.generateTiles( positional[ 0 ] );
    
        };
    
        generateMaterials() {
    
            this.material = new THREE.MeshBasicMaterial( {
                color: 0x6495ED,
            } );
    
            this.wireframe = new THREE.MeshBasicMaterial( {
                color: 0x000000,
                wireframe: true,
            } );
    
        };
    
        generateTiles( subdivisions ) {
    
            let hexasphere = new Hexasphere( 1000, subdivisions, 0.99 );
    
            hexasphere.tiles.map( ( tile, index ) => {
    
                this.tiles.push( new Tile( tile, index ) );
                this.add( this.tiles[ index ] );
    
            } );
    
        };
    
    }
    
    Tile.js

    class Tile extends THREE.Object3D {
    
        constructor( tile, index ) { super();
    
            this.index = index;
    
            this.getCenterPoint( tile );
            this.getOrdinalLatitude();
    
            this.generateMaterials();
            this.generateGeometry( tile );
            this.generateObjects();
    
        };
    
        getCenterPoint( tile ) {
    
            let [ xPositions, yPositions, zPositions ] = [ [], [], [] ];
    
            for ( let index = 0; index < tile.boundary.length; index++ ) {
    
                let vertex = tile.boundary[ index ];
    
                xPositions.push( parseFloat( vertex.x ) );
                yPositions.push( parseFloat( vertex.y ) );
                zPositions.push( parseFloat( vertex.z ) );
    
            };
    
            let centerX = xPositions.reduce( ( a, b ) => a + b, 0 );
            centerX = centerX / xPositions.length;
    
            let centerY = yPositions.reduce( ( a, b ) => a + b, 0 );
            centerY = centerY / yPositions.length;
    
            let centerZ = zPositions.reduce( ( a, b ) => a + b, 0 );
            centerZ = centerZ / zPositions.length;
    
            this.position.set( centerX, centerY, centerZ );
    
        };
    
        getOrdinalLatitude() {
    
            let boolean = this.position.y >= -10;
    
            let ordinal = ( boolean ) ? 'northern' : 'southern';
            this.ordinalLatitude = ordinal;
    
        };
    
        generateMaterials() {
    
            this.material = new THREE.MeshStandardMaterial( {
                color: 0x0077be,
            } );
    
            this.wireframe = new THREE.MeshStandardMaterial( {
                color: 0x000000,
                wireframe: true,
            } );
    
        };
    
        generateVertices( tile ) {
    
            this.geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
    
            tile.boundary.map( ( point, index ) => {
    
                let xPosition = point.x - this.position.x;
                let yPosition = point.y - this.position.y;
                let zPosition = point.z - this.position.z;
    
                let vertex = new THREE.Vector3( 
                    xPosition, yPosition, zPosition
                 );
    
                this.geometry.vertices.push( vertex );
    
            } );
    
        };
    
        generateFaces() {
    
            this.geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
            this.geometry.faces.push( new THREE.Face3( 0, 2, 3 ) );
            this.geometry.faces.push( new THREE.Face3( 0, 3, 4 ) );
            this.geometry.faces.push( new THREE.Face3( 0, 4, 5 ) );
    
            if ( this.geometry.vertices.length == 6 ) {
                this.geometry.faces.push( new THREE.Face3( 0, 5, 1 ) );
    
            };
    
            if ( this.geometry.vertices.length == 7 ) {
                this.geometry.faces.push( new THREE.Face3( 0, 5, 6 ) );
                this.geometry.faces.push( new THREE.Face3( 0, 6, 1 ) );
            };
    
        };
    
        generateGeometry( tile ) {
    
            this.geometry = new THREE.Geometry();
            this.generateVertices( tile );
            this.generateFaces();
    
        };
    
        generateObjects( tile ) {
    
            var mesh = new THREE.Mesh( this.geometry, this.material );
            var frame = new THREE.Mesh( this.geometry, this.wireframe );
            this.add( mesh ); this.add( frame );
    
        };
    
        normalizeLatitude() {
    
            this.position.set( this.position.x, 0, this.position.z );
    
        };
    
        normalizeXRotation() {
    
            let furthestVertex = this.geometry.vertices.reduce( 
                ( a, b ) => a.x > b.x ? a : b
            );
    
            let radians = Math.atan( furthestVertex.z / furthestVertex.x );
            this.rotateX( -radians );
    
        };
    
        normalizeYRotation() {
    
            let furthestVertex = this.geometry.vertices.reduce( 
                ( a, b ) => a.y > b.y ? a : b
            );
    
            let radians = Math.atan( furthestVertex.z / furthestVertex.y );
            this.rotateY( -radians );
    
        };
    
        normalizeZRotation() {
    
            let furthestVertex = this.geometry.vertices.reduce( 
                ( a, b ) => a.z > b.z ? a : b
            );
    
            let radians = Math.atan( furthestVertex.z / furthestVertex.x );
            this.rotateZ( -radians );
    
        };
    
        normalizeRotation() {
    
            // this.normalizeXRotation();
            // this.normalizeYRotation();
            // this.normalizeZRotation();
    
        };
    
    };
    
    
    类平铺扩展了3.Object3D{
    构造函数(tile,index){super();
    这个指数=指数;
    此.getCenterPoint(平铺);
    this.getOrdinality();
    这个。生成材料();
    本发明涉及发电计量学(瓷砖);
    这是.generateObject();
    };
    getCenterPoint(平铺){
    设[xPositions,yPositions,zPositions]=[[],[],[];
    for(让index=0;indexa+b,0);
    centerX=centerX/xPositions.length;
    设centerY=yPositions.reduce((a,b)=>a+b,0);
    centerY=centerY/y位置.length;
    让centerZ=zPositions.reduce((a,b)=>a+b,0);
    centerZ=centerZ/zPositions.length;
    此.position.set(centerX、centerY、centerZ);
    };
    GetOrdinality(){
    设布尔值=this.position.y>=-10;
    设序数=(布尔)?'northern':'southern';
    这个。序数=序数;
    };
    生成材料(){
    this.material=新的3.mesh标准材质({
    颜色:0x0077be,
    } );
    this.wireframe=新的三个.MeshStandardMaterial({
    颜色:0x000000,
    线框:对,
    } );
    };
    发电机(瓷砖){
    this.geometry.vertices.push(新的3.Vector3(0,0,0));
    tile.boundary.map((点,索引)=>{
    设xPosition=point.x—此.position.x;
    让yPosition=point.y-这个.position.y;
    设zPosition=point.z-此.position.z;
    设顶点=新的三个。向量3(
    xPosition,yPosition,zPosition
    );
    这个.geometry.vertex.push(顶点);
    } );
    };
    生成(){
    这个.geometry.faces.push(新的三个.Face3(0,1,2));
    这个.geometry.faces.push(新的三个.Face3(0,2,3));
    这个.geometry.faces.push(新的三个.Face3(0,3,4));
    这个.geometry.faces.push(新的三个.Face3(0,4,5));
    if(this.geometry.vertices.length==6){
    这个.geometry.faces.push(新的三个.Face3(0,5,1));
    };
    if(this.geometry.vertices.length==7){
    这个.geometry.faces.push(新的三个.Face3(0,5,6));
    这个.geometry.faces.push(新的三个.Face3(0,6,1));
    };
    };
    发电计量(瓷砖){