web 3d场景构建+three.js+室内围墙,仓库,楼梯,货架模型等,第一人称进入场景案例

2023-10-29

 

翻到了之前的一个案例,基于three.js做的仓库布局模拟,地图元素除了大模型外,其他都是通过JSON数据解析动态生成的,例如墙体,柱子门口,地标等,集成了第一人称的插件可以第一人称进入场景有需要的可以下载看看,对想入门的朋友应该有一些参考价值。

/**

   *创建自定义几何体

   *输入参数几何体底面逆时针坐标组、几何体高度

   * 目前只支持凸多边形 逆时针则连线,顺时针不连线

   */

function createCustomBufferGeometry(planeArr, height, color) {

    let planes = planeArr;

    let planes2 = [];

    //组装顶面坐标

    for (let i = 0; i < planes.length; i++) {

        planes2.push(new THREE.Vector3(planes[i].x, planes[i].y + height, planes[i].z));

    }

    planes = planes.concat(planes2);

    let arr = [];

    //循环组成三角面

    for (let i = 0; i < planes.length; i++) {

        let j = i + 1, k2 = j + planes2.length;

        let xLength = planes2.length;

        if (j >= planes2.length && i < planes2.length) {

            j = 0; k2 = j + planes2.length;

        }

        if (i >= planes2.length) {

            if (j >= planes.length) {

                j = planes2.length;

            }

            k2 = i - planes2.length;

            xLength = planes.length;

        }

        for (let x = i + 2; x < xLength; x++) {

            arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);

            arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);

            arr = arr.concat([planes[x].x, planes[x].y, planes[x].z]);

            // if (((planes[j].x - planes[i].x) * (planes[x].z - planes[i].z) - (planes[x].x - planes[i].x) * ( planes[j].z - planes[i].z)) < 0) {

            //     arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);

            //     arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);

            //     arr = arr.concat([planes[x].x, planes[x].y, planes[x].z]);

            // }

        }

        arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);

        if (i < planes2.length) {

            arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);

            arr = arr.concat([planes[k2].x, planes[k2].y, planes[k2].z]);

        } else {

            arr = arr.concat([planes[k2].x, planes[k2].y, planes[k2].z]);

            arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);

        }

    }

    let bufferGeometry = new THREE.BufferGeometry();

    let vertices = new Float32Array(arr);

    // itemSize = 3 因为每个顶点都是一个三元组。

    bufferGeometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));

    bufferGeometry.computeFaceNormals();//计算法向量,会对光照产生影响

    bufferGeometry.computeVertexNormals();//自动设置法向量

    let material = new THREE.MeshLambertMaterial({ color: color });

    let mesh = new THREE.Mesh(bufferGeometry, material);

    _bufferGeometry = bufferGeometry;

    //worldScene.add(mesh);

    return mesh;

}

var _bufferGeometry;

/**

    *地图数据坐标是左上角为原点开始的二维坐标系x,y,绘制以左上角开始

    * web 3d坐标原点是屏幕中心点,绘制的时候也是以中心为相对位置

    * MapXLength:地图最长距离,MapZLength 地图最宽距离

    * 转换规则 3d.position.x =  2d.width/2 -  maxWidth/2 +2d.position.x

    * 3d.position.z = 2d.height/2 - maxHeight/2 +2d.position.z;

    * 3d.position.y y轴高度 2D地图无需设置,默认为0,如果有高度, 3d.position.y =  2d.高度.y/2+2d.高度位置+MapYLength/2

    */

function handCoordinate(data) {

    data.x = data.x / 10;

    data.y = data.y / 10;

    data.z = data.z / 10;

    if (data.positionY)

        data.positionY = data.positionY / 10;

    else

        data.positionY = 0;

    data.width = data.width / 10;

    data.height = data.height / 10;

    data.x = data.width / 2 - MapXLength / 2 + data.x;

    data.z = data.height / 2 - MapZLength / 2 + data.z;

    if (data.groupOption) {

        data.groupOption.offSetX = data.groupOption.offSetX / 10;

        data.groupOption.offSetY = data.groupOption.offSetY / 10;

        data.groupOption.offSetZ = data.groupOption.offSetZ / 10;

    }

}

var _baseBox;

var  _floorType;

//最底层的box

function createBaseBox(data,floorType) {

    data.width = data.width / 10;

    data.height = data.height / 10;

    MapXLength = data.width;

    MapZLength = data.height;

    sizeRatio = MapXLength / MapXLength;

    if(data.floorType){

        _floorType = data.floorType;

        if(!floorModels[floorType])

        {

            floorModels[floorType] = [];

        }

    }

    var geometry = new THREE.BoxBufferGeometry(data.width, 1, data.height);

    var material = new THREE.MeshLambertMaterial({ color: data.color });

    var cube = new THREE.Mesh(geometry, material);

    cube.position.set(0, 0, 0);

    //cube.castShadow = true;//开启投影

    cube.receiveShadow = true;//接收阴影

    cube.geometry.computeBoundingBox();

    _baseBox = cube.geometry.boundingBox;

    clickObjects.push(cube);//加入点击对象组

    worldScene.add(cube);

    floorModels[floorType].push(cube);

    //console.log(cube);

    //地图标注

    //  worldScene.add(createTextTextureBySprite(data))

}

//创建几何体

function createBox(data,_floorType) {

   

    handCoordinate(data);

    var geometry = new THREE.BoxGeometry(data.width, data.y, data.height);

    var material = new THREE.MeshLambertMaterial({ color: data.color, vertexColors: THREE.FaceColors });

    var cube = new THREE.Mesh(geometry, material);

    cube.castShadow = true;//开启投影

    //cube.receiveShadow = true;//接收阴影

    cube.position.set(data.x, 0.55 + data.positionY / 2 + data.y / 2, data.z);

    var newMesh;

    //几何体组合处理

    if (data.bspMesh) {

        newMesh = cube;

        data.bspMesh.forEach(x => {

            handCoordinate(x);

            let tempMesh;

            if (x.geometryType == 'box') {

                tempGeometry = new THREE.BoxGeometry(x.width, x.y, x.height);

                tempMesh = new THREE.Mesh(tempGeometry, new THREE.MeshLambertMaterial({ color: x.color }));

            }

            tempMesh.position.set(x.x, 0.55 + x.positionY / 2 + x.y / 2, x.z);

            newMesh = bspMesh(x.type, newMesh, tempMesh);

        })


 

    } else {

    }

    let finalMesh;

    if (newMesh) {

        newMesh.castShadow = true;//开启投影

        // worldScene.add(newMesh);

        finalMesh = newMesh;

    } else {

        finalMesh = cube;

        // worldScene.add(cube);

    }

    //多个相同模型组合

    if (data.type && data.type == 'group') {

        for (let i = 0; i < data.groupOption.total; i++) {

            let tempMesh = finalMesh.clone();

            if (data.groupOption.offSetX != 0) {

                tempMesh.position.x = finalMesh.position.x + (data.width + data.groupOption.offSetX) * i;

            }

            if (data.groupOption.offSetY != 0) {

                tempMesh.position.y = finalMesh.position.y + (data.y + data.groupOption.offSety) * i;

            }

            if (data.groupOption.offSetZ != 0) {

                tempMesh.position.z = finalMesh.position.z + (data.height + data.groupOption.offSetZ) * i;

            }

            // tempMesh.position.set(

            //    ( data.width+tempMesh.position.x+data.groupOption.offSetX)*i,

            //    ( data.y+tempMesh.position.y+data.groupOption.offSetY)*i,

            //     (data.height+tempMesh.position.z+data.groupOption.offSetZ)*i);

            worldScene.add(tempMesh);

            floorModels[_floorType].push(tempMesh);

        }

    } else {

        worldScene.add(finalMesh);

        floorModels[_floorType].push(finalMesh);

    }

    //地图标注

    let sprite = createTextureBySprite(data);

    if (sprite != null)

    worldScene.add(sprite);

}




 

//创建圆柱体

function createCylinder(data) {

    handCoordinate(data);

    var geometry = new THREE.CylinderGeometry(data.width / 2, data.width / 2, data.y, 32);

    var material = new THREE.MeshLambertMaterial({ color: data.color, vertexColors: THREE.FaceColors });

    var cylinder = new THREE.Mesh(geometry, material);

    cylinder.position.set(data.x, 0.55 + data.positionY / 2 + data.y / 2, data.z);

    for (let i = 0; i < 64; i++) {

        geometry.faces[i].color = new THREE.Color('#004892');

    }

    worldScene.add(cylinder);

}

/**

 * 创建网格

 * @param {几何体对象} geometry

 */

function createMesh(geometry, color) {

    if (!color) {

        color = '#4685C6';

    }

    return new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: color }));

}

/**

 * A,B模型type:'intersect' 交集  'union' 并集  subtract 差集

 * @param {A模型} geometryA

 * @param {B模型} geometryB

 */

function bspGeometry(type, geometryA, geometryB) {

    //生成ThreeBSP对象

    var a = new ThreeBSP(geometryA);

    var b = new ThreeBSP(geometryB);

    //进行算

    var resultBSP;

    if (type == 'intersect')

        resultBSP = a.intersect(b);

    else if (type == 'union')

        resultBSP = a.union(b);

    else

        resultBSP = a.subtract(b);

    //从BSP对象内获取到处理完后的mesh模型数据

    var result = resultBSP.toGeometry();

    //更新模型的面和顶点的数据

    result.computeFaceNormals();

    result.computeVertexNormals();

    return cresult;

}

/**

 * A,B模型type:'intersect' 交集  'union' 并集  subtract 差集

 * @param {A模型} meshA

 * @param {B模型} meshB

 */

function bspMesh(type, meshA, meshB) {

    //生成ThreeBSP对象

    var a = new ThreeBSP(meshA);

    var b = new ThreeBSP(meshB);

    //进行算

    var resultBSP;

    if (type == 'intersect')

        resultBSP = a.intersect(b);

    else if (type == 'union')

        resultBSP = a.union(b);

    else

        resultBSP = a.subtract(b);

    //从BSP对象内获取到处理完后的mesh模型数据

    var result = resultBSP.toMesh();

    result.material = meshA.material;

    //更新模型的面和顶点的数据

    result.geometry.computeFaceNormals();

    result.geometry.computeVertexNormals();

    testResult = result

    return result;

}

var testResult;



 

/**

 *创建地图标注

 *canvas地图标注的内容很小需要放大,放大会失真,后期调整其缩放大小,或者不采用canvas渲染

 */

function createTextureBySprite(data) {

    if ((data.title == '' && data.imageurl == '') || (!data.title && !data.imageurl)) {

        return null;

    }

    let canvas = document.createElement('canvas');

    canvas.width=3000;

    canvas.height=2000;

   

    let ctx = canvas.getContext('2d');

   

    ctx.lineWidth = 1;

    ctx.textAlign = "center";

    ctx.textBaseline = "middle";

    ctx.textAlign = 'center';

    if (data.font) {

        ctx.font = data.font;

    } else {

        ctx.font = "Normal 180px Arial"

    }

    if (data.textcolor) {

        ctx.fillStyle = data.textcolor;

    }

    //ctx.lineWidth = 4;

    if (data.imageurl) {

        let img = new Image();

        img.src = data.imageurl;

        img.onload = function () {

            ctx.drawImage(img, 30, 90);

            texture.needsUpdate = true;

        }

    }

    /*

    把整个 canvas 作为纹理,所以字尽量大一些,撑满整个 canvas 画布。

    但也要小心文字溢出画布。

    */

    ctx.fillText(data.title, 400, 200);

    let texture = new THREE.CanvasTexture(canvas);

    let material = new THREE.SpriteMaterial({

        map: texture,

        transparent: true, // 避免遮挡其他图形

       // sizeAttenuation:false

    });

    let textMesh = new THREE.Sprite(material);

    /*

    精灵很小,要放大

    */

   textMesh.scale.set(10, 10, 10);

    /*

    WebGL 3D 世界中的位置

    */

    textMesh.position.set(data.x,data.y + 1.5, data.z);//data.y + 3

    return textMesh;

}


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

web 3d场景构建+three.js+室内围墙,仓库,楼梯,货架模型等,第一人称进入场景案例 的相关文章

随机推荐

  • MacOS13+系统运行Stable Diffusion出现的问题及解决方法汇总

    目录 先睹为快 开发环境 问题一 点 生成 按钮就退出程序 问题二 生成的图片是马赛克 第一步 解决环境问题 第二步 更新指定的torch版本包 步骤一 更新指定的torch包 步骤二 重新安装pytorch nightly版本的包 问题三
  • 解决虚拟机VM和WSL2切换问题

    操作环境 windows11 虚拟机 wsl2用的都是Ubuntu 18 04 问题描述 因为之前一直使用wsl2 很久没有使用VM虚拟机 今天打开的时候发现VM不能用了 于是查资料发现用VM和wsl2会冲突 解决办法 首先说一下怎么从之前
  • C和C++中字符串说明与记录

    文章目录 目的 C语言 字符基本说明 字符串声明与操作 字符串常用函数说明 属性 复制 合并 替换 查询 比较 类型检查 类型转换 C C 字符串基本说明 C string和C语言字符串转换 C string常用方法说明 属性 类型转换 修
  • flutter滚动Text文本

    GlobalKey scrollTextKey new GlobalKey Timer scrollTextTimer ScrollController controller int index 0 override void initSt
  • js实现首页图片的轮播效果

    在大多数的网站里面首页都会有一个图片轮播的效果 而我们开发者要实现这种效果要么是使用第三方插件 要么是使用js自己写函数来实现这种效果 而我自己就是自己写js来实现这种效果 首先 我们来看一下最终完成的效果 这个就是最终实现的效果 下面我们
  • Qt5.0+msvc2010:解決中文乱码的问题

    1 在Qt Creator的工具列 选择 工具 gt 选项 进入设置界面 2 在弹出的设置界面里面 选择 文本编辑器 选择 行为 选项卡 把 行为 选项卡下面的 默认编码 置成 UTF 8 同时 UTF 8 BOM 置成 总是删除 最后点
  • MySQL(69)MySQL查看视图

    MySQL查看视图 创建好视图后 可以通过查看视图的语句来查看视图的字段信息以及详细信息 本节主要讲解如何使用 SQL 语句来查看视图的字段信息以及详细信息 查看视图的字段信息 查看视图的字段信息与查看数据表的字段信息一样 都是使用 DES
  • docker安装fastdfs

    1 搜索fastdfs docker search fastdfs 2 拉取镜像 docker pull morunchang fastdfs 3 运行tracker docker run d name tracker net host m
  • Gerrit 使用git常见问题

    目录 git push 失败报错 remote rejected master gt master prohibited by Gerrit git push报错 ERROR missing Change Id in message foo
  • Android Logcat&debug实用技巧

    logcat 作为我们最常用的调试手段 相信大家都不会陌生 这里总结一下 在使用Logcat过程中 常用的一些debug技巧 目的是为了快速有效的帮助找到并解决问题 先来看一看Logcat打印结构 常用 logcat v threadtim
  • 网上投稿地址大全

    网上投稿地址大全 上海科技报 kgbshen online sh cn 厂长经理日报 cjb490 sina com 四川日报 sichuandaily scol com cn 四川农村日报 country scol com cn 华西都市
  • MATLAB线性规划相关函数用法

    一 线性规划的Matlab标准形式及软件求解 1 MATLAB中规定线性规划的标准形式为 其中c和 x为n 维列向量 A Aeq 为适当维数的矩阵 b beq为适当维数的列向量 Aeq 对应约束条件中等式约束的系数矩阵 A为约不等式约束的系
  • 保姆级Mycat操作详解

    Mycat环境搭建 下载 http dl mycat org cn 2 0 上传到虚拟机并解压 tar zxvf mycat tar gz cd mycat ll 授权 设置mycat权限 chmod R 777 mycat 环境变量 配置
  • ubuntu 下 linux 2.6.18.tar.gz 内核编译,Ubuntu安装Linux2.6.35内核编译

    Ubuntu12 04 参考 百度 在ubuntu安装Linux2 6 35内核 https www linuxidc com Linux 2011 08 40124 htm http www mamicode com info detai
  • 教资高中信息技术 倒数第四天

    第一天 过教材 因为 时间紧张 大部分简写 用于复习 总结 1信息的基本特征 普传功夫真驾驶 2信息技术发展阶段 语文印电计 3人类获取信息途径 直接 间接 4搜索引擎 全文 目录 元 5信息管理过程 信息采集 加工 储存 编码 6人工智能
  • upload-labs通关记录

    upload labs源码地址 https github com c0ny1 upload labs upload labs Pass 1 js检查 Pass 2 只验证Content type Pass 3 黑名单绕过 Pass 4 ht
  • 算法的改进让刷脸核验在极短时间内完成

    刷脸支付系统是一款基于人脸识别系统的支付平台 该系统无需钱包 卡或手机 会自动将消费者面部信息与个人账户相关联 支付时只需要面对设备屏幕上的摄像头即可 整个交易过程 便捷 传统的支付方式 需要把卡 现金 手机等作为介质 但一旦忘记带现金 手
  • STM32与HC-SR04超声波测距

    首先 先来看一下这个模块的基本功能和原理 HC SR04超声波测距模块可提供2cm 400cm的非接触式距离感测功能 测距精度可达高到3mm 模块包括超声波发射器 接收器与控制电路 像智能小车的测距以及转向 或是一些项目中 常常会用到 智能
  • MacOSX上的NFS文件共享

    在MacOSX上接触过rsync方式文件共享和NFS方式文件共享 两种方式的基本使用方式如下 1 rsync方式 这种方式和scp用法很像 rsync file1 user host xx file2 如果传输目录 需要加一个 a参数 如果
  • web 3d场景构建+three.js+室内围墙,仓库,楼梯,货架模型等,第一人称进入场景案例

    翻到了之前的一个案例 基于three js做的仓库布局模拟 地图元素除了大模型外 其他都是通过JSON数据解析动态生成的 例如墙体 柱子门口 地标等 集成了第一人称的插件可以第一人称进入场景有需要的可以下载看看 对想入门的朋友应该有一些参考