openDriver开源插件main.js源码分析

news2025/1/13 11:43:44

、基础要求

阅读本文章需要对以下知识有相关的了解

  • Threejs 3D渲染引擎
  • dat.gui界面控制插件
  • webgl三维绘图协议、着色器相关知识
  • UV坐标、XYZ惯性坐标
  • XODR文件格式

拓展 

自动驾驶场景仿真标准(一)- OpenDRIVE - 知乎

《OpenDRIVE1.6规格文档》3_opendrive1.6 收费站_YMWM_的博客-CSDN博客

WebGL+Three.js 入门与实战、搞定前端前沿技术_threejs从入门到入土_不穿铠甲的穿山甲的博客-CSDN博客

源码分享

链接: https://pan.baidu.com/s/1o9HtGzEuSxsaKVPRp-RENw

提取码: 2k5y

源码划分

jsView

这个项目是java后台资源服务,启动该项目,前端通过请求获取xodr资源

opendriver

开源插件,用于渲染xodr,浏览器访问html即可在本地渲染3D效果

部分源码main.js详细叙述

创建Threejs基础代码

//创建渲染器

const renderer = new THREE.WebGLRenderer(

    {

        antialias: true, //抗锯齿,防失真

        sortObjects: false //定义渲染器是否应对对象进行排序

    });

//开启投影阴影

renderer.shadowMap.enabled = true;

//设置渲染器渲染区域

renderer.setSize(window.innerWidth, window.innerHeight);

//渲染

document.getElementById('ThreeJS').appendChild(renderer.domElement);

//创建场景

const scene = new THREE.Scene();

//创建透视相机

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100000);

//让Z轴朝上+

camera.up.set(0, 0, 1); /* Coordinate system with Z pointing up */

//创建控制器

const controls = new THREE.MapControls(camera, renderer.domElement);

//const controls = new THREE.OrbControls(camera, renderer.domElement);

//dat.gui组件监听物体拖动

controls.addEventListener('start', () => {

    /**

     * 拖动开始停止开启聚集光和停止旋转

     */

    spotlight_paused = true; //聚集光暂停

    controls.autoRotate = false; //是否旋转

});

controls.addEventListener('end', () => {

    //拖动结束聚集光关闭并且开启旋转

    spotlight_paused = false;//聚集光开始

    //将其设为true,以自动围绕目标旋转。请注意,如果它被启用,你必须在你的动画循环里调用

    // controls.autoRotate = true;//开启旋转

});

//物体将会自动旋转

controls.autoRotate = true;//开启旋转

//创建平行光

const light = new THREE.DirectionalLight(0xffffff, 1.0);

scene.add(light);

//平行光方向指向原点(0,0,0)

scene.add(light.target);

//添加坐标轴辅助器

const axesHepler = new THREE.AxesHelper(5)

scene.add(axesHepler)

创建离屏渲染场景

//选择车道线场景

const lane_picking_scene = new THREE.Scene();

//车道线场景背景颜色

lane_picking_scene.background = new THREE.Color(0xffffff);

//路标场景

const roadmark_picking_scene = new THREE.Scene();

//路标场景背景颜色

roadmark_picking_scene.background = new THREE.Color(0xffffff);

//坐标场景

const xyz_scene = new THREE.Scene();

//坐标场景背景颜色

xyz_scene.background = new THREE.Color(0xffffff);

const st_scene = new THREE.Scene();

st_scene.background = new THREE.Color(0xffffff);

创建离屏渲染器

//创建渲染器

const lane_picking_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const roadmark_picking_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const xyz_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const st_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

创建着色器(glsl语法)

该部分实际内容在html中

//获取顶点着色器

const idVertexShader = document.getElementById('idVertexShader').textContent;

const idFragmentShader = document.getElementById('idFragmentShader').textContent;

const xyzVertexShader = document.getElementById('xyzVertexShader').textContent;

const xyzFragmentShader = document.getElementById('xyzFragmentShader').textContent;

const stVertexShader = document.getElementById('stVertexShader').textContent;

const stFragmentShader = document.getElementById('stFragmentShader').textContent;

创建离屏渲染材质

//设置车道中轴线颜色

const refline_material = new THREE.LineBasicMaterial({

    //color: COLORS.ref_line,

    color: 0x0000FF

});

const road_network_material = new THREE.MeshPhongMaterial({

    vertexColors: THREE.VertexColors,

    wireframe: PARAMS.wireframe,//显示现况

    shininess: 20.0,

    transparent: true,

    opacity: 0.4

});


 

//道路线

const lane_outlines_material = new THREE.LineBasicMaterial({

    color: COLORS.lane_outline,

    //color: 0xCD3278,

});

//外侧线

const roadmark_outlines_material = new THREE.LineBasicMaterial({

    color: COLORS.roadmark_outline,

});

const id_material = new THREE.ShaderMaterial({

    vertexShader: idVertexShader,

    fragmentShader: idFragmentShader,

});

const xyz_material = new THREE.ShaderMaterial({

    vertexShader: xyzVertexShader,

    fragmentShader: xyzFragmentShader,

});

const st_material = new THREE.ShaderMaterial({

    vertexShader: stVertexShader,

    fragmentShader: stFragmentShader,

});

const roadmarks_material = new THREE.MeshBasicMaterial({

    vertexColors: THREE.VertexColors,

});

const road_objects_material = new THREE.MeshBasicMaterial({

    vertexColors: THREE.VertexColors,

    side: THREE.DoubleSide,

    wireframe: true,

});

获取xodr文件

/* load WASM + odr map */

libOpenDrive().then(Module => {

    ModuleOpenDrive = Module;

    fetch("http://localhost:8080/upload/factory_v3.1.2.xodr").then((file_data) => {

        file_data.text().then((file_text) => {

            //加载数据文件

            loadFile(file_text, false);

        });

    });

});

加载地图

function loadFile(file_text, clear_map) {

    //清除文件系统中保存的文件节点

    if (clear_map)

        ModuleOpenDrive['FS_unlink']('./data.xodr');

    //将文件数据转为节点保存到文件系统本地指定的文件目录,支持读写

    ModuleOpenDrive['FS_createDataFile'](".", "data.xodr", file_text, true, true);

    //OpenDriveMap不为null,则已经打开过,需要删除地图重新尝试打开

    if (OpenDriveMap)

        OpenDriveMap.delete()

    //打开驱动地图

    OpenDriveMap = new ModuleOpenDrive.OpenDriveMap("./data.xodr", PARAMS.lateralProfile, PARAMS.laneHeight, true);

    loadOdrMap(clear_map);

}

function reloadOdrMap() {

    if (OpenDriveMap)

        OpenDriveMap.delete();

    OpenDriveMap = new ModuleOpenDrive.OpenDriveMap("./data.xodr", PARAMS.lateralProfile, PARAMS.laneHeight, true);

    loadOdrMap(true, false);

}

loadOdrMap 加载地图核心逻辑

清除地图

    //是否清除地图

    if (clear_map) {

        road_network_mesh.userData.odr_road_network_mesh.delete();

        scene.remove(road_network_mesh, roadmarks_mesh, road_objects_mesh, refline_lines, lane_outline_lines, roadmark_outline_lines, ground_grid);

        lane_picking_scene.remove(...lane_picking_scene.children);

        roadmark_picking_scene.remove(...roadmark_picking_scene.children);

        xyz_scene.remove(...xyz_scene.children);

        st_scene.remove(...st_scene.children);

        for (let obj of disposable_objs)

            obj.dispose();

    }

渲染st坐标系s参考线

    /**

     * 道路中间s方向的线

     */

    //获取参考线矩形几何体

    const reflines_geom = new THREE.BufferGeometry();

    //获取驱动中参考线数据信息

    const odr_refline_segments = OpenDriveMap.get_refline_segments(parseFloat(PARAMS.resolution));

    //设置参考线坐标点

    reflines_geom.setAttribute('position', new THREE.Float32BufferAttribute(getStdVecEntries(odr_refline_segments.vertices).flat(), 3));

    //设置参考线坐标点索引

    reflines_geom.setIndex(getStdVecEntries(odr_refline_segments.indices, true));

    //创建参考线物体

    refline_lines = new THREE.LineSegments(reflines_geom, refline_material);

    refline_lines.renderOrder = 10;

    //设置是否可见

    refline_lines.visible = PARAMS.ref_line;

    refline_lines.matrixAutoUpdate = false;

    disposable_objs.push(reflines_geom);

    //将参考线加入场景中

    scene.add(refline_lines);

 道路面

这部分稍微有些不好理解

核心思想:

1.3D中画的是三角形,所以根据三角形三个顶点画三角形进而填充面

2.车道ID是索引

//根据细节级别获取道路网格物体PARAMS.resolution 默认0.3中等级别

    const odr_road_network_mesh = OpenDriveMap.get_mesh(parseFloat(PARAMS.resolution));

    //获取所有路段的车道(车道id为 -2, -1, 0, 1, 2)

    const odr_lanes_mesh = odr_road_network_mesh.lanes_mesh;

    //获取所有路段车道点集构建道路物体

    const road_network_geom = get_geometry(odr_lanes_mesh);

    //设置道路颜色

    road_network_geom.attributes.color.array.fill(COLORS.road);

    //获取所有路段的车道的起始位置索引

    //将索引作为车道开始和结束信息

    for (const [vert_start_idx, _] of getStdMapEntries(odr_lanes_mesh.lane_start_indices)) {

        //根据起始位置索引获取车道信息

        const vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(vert_start_idx);

        //结束索引减去起始索引的长度就是该路段该车道的长度

        const vert_count = vert_idx_interval[1] - vert_idx_interval[0];

        //索引数据压缩

        const vert_start_idx_encoded = encodeUInt32(vert_start_idx)

        const attr_arr = new Float32Array(vert_count * 4);

        for (let i = 0; i < vert_count; i++)

            attr_arr.set(

                vert_start_idx_encoded, //存入的数据集合,批量存入,底层会打散

                i * 4 //数组起始索引位置

            );

        road_network_geom.attributes.id.array.set(attr_arr, vert_idx_interval[0] * 4);

    }

    disposable_objs.push(road_network_geom);

    road_network_mesh = new THREE.Mesh(road_network_geom, road_network_material);

    road_network_mesh.renderOrder = 0;

    road_network_mesh.userData = { odr_road_network_mesh };

    road_network_mesh.matrixAutoUpdate = false;

    road_network_mesh.visible = !(PARAMS.view_mode == 'Outlines');

    scene.add(road_network_mesh);

 选中被选中的车道颜色设置

    const lane_picking_mesh = new THREE.Mesh(road_network_geom, id_material);

    lane_picking_mesh.matrixAutoUpdate = false;

    lane_picking_scene.add(lane_picking_mesh);

使用车道ID转为颜色值作为每个车道的每个点的颜色值(这样在使用拾取点颜色的时候就能拿到该点在哪个车道ID) 

 

 效果图:

  rgb真实的值是以车道ID作为参数转换过来的

xyz惯性坐标系

每个点的颜色存的不是车道ID,而是该点的坐标(在拾取的该点的颜色的时候拿到的就是该点的webgl坐标) ,然后偏移值进行操作得到的就是世界坐标

    /* xyz coords road network mesh */

    const xyz_mesh = new THREE.Mesh(road_network_geom, xyz_material);

    xyz_mesh.matrixAutoUpdate = false;

    xyz_scene.add(xyz_mesh);

完整代码简单叙述:

/* globals */

var ModuleOpenDrive = null;

var OpenDriveMap = null;

var refline_lines = null;

var road_network_mesh = null;

var roadmarks_mesh = null;

var lane_outline_lines = null;

var roadmark_outline_lines = null;

var ground_grid = null;

var disposable_objs = [];

var mouse = new THREE.Vector2();

var spotlight_info = document.getElementById('spotlight_info');

var INTERSECTED_LANE_ID = 0xffffffff;

var INTERSECTED_ROADMARK_ID = 0xffffffff;

//聚集光是否暂停

var spotlight_paused = false;

const COLORS = {

    road: 1.0,

    roadmark: 1.0,

    road_object: 0.9,

    lane_outline: 0xae52d4,

    roadmark_outline: 0xffffff,

    ref_line: 0x69f0ae,

    background: 0x444444,

    lane_highlight: 0xff0000,

    roadmark_highlight: 0xff0000,

};

//自适应大小

window.addEventListener('resize', onWindowResize, false);

//监听鼠标位置改变

window.addEventListener('mousemove', onDocumentMouseMove, false);

window.addEventListener('dblclick', onDocumentMouseDbClick, false);

/* notifactions */

const notyf = new Notyf({

    duration: 3000,

    position: { x: 'left', y: 'bottom' },

    types: [{ type: 'info', background: '#607d8b', icon: false }]

});


 

//创建渲染器

const renderer = new THREE.WebGLRenderer(

    {

        antialias: true, //抗锯齿,防失真

        sortObjects: false //定义渲染器是否应对对象进行排序

    });

//开启投影阴影

renderer.shadowMap.enabled = true;

//设置渲染器渲染区域

renderer.setSize(window.innerWidth, window.innerHeight);

//渲染

document.getElementById('ThreeJS').appendChild(renderer.domElement);

//创建场景

const scene = new THREE.Scene();

//创建透视相机

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100000);

//设置相机的朝向类似 lookAt方法

camera.up.set(0, 0, 1); /* Coordinate system with Z pointing up */

//创建控制器

const controls = new THREE.MapControls(camera, renderer.domElement);

controls.addEventListener('start', () => {

    spotlight_paused = true; //聚集光暂停

    controls.autoRotate = false; //是否旋转

});

controls.addEventListener('end', () => {

    spotlight_paused = false;//聚集光开始

});

//将其设为true,以自动围绕目标旋转。请注意,如果它被启用,你必须在你的动画循环里调用

controls.autoRotate = true;//开启旋转

//创建平行光

const light = new THREE.DirectionalLight(0xffffff, 1.0);

scene.add(light);

//平行光方向指向原点(0,0,0)

scene.add(light.target);

//选择车道线场景

const lane_picking_scene = new THREE.Scene();

//车道线场景背景颜色

lane_picking_scene.background = new THREE.Color(0xffffff);

//路标场景

const roadmark_picking_scene = new THREE.Scene();

//路标场景背景颜色

roadmark_picking_scene.background = new THREE.Color(0xffffff);

//坐标场景

const xyz_scene = new THREE.Scene();

//坐标场景背景颜色

xyz_scene.background = new THREE.Color(0xffffff);

const st_scene = new THREE.Scene();

st_scene.background = new THREE.Color(0xffffff);

//创建渲染器

const lane_picking_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const roadmark_picking_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const xyz_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

const st_texture = new THREE.WebGLRenderTarget(1, 1, { type: THREE.FloatType });

//获取顶点着色器

const idVertexShader = document.getElementById('idVertexShader').textContent;

const idFragmentShader = document.getElementById('idFragmentShader').textContent;

const xyzVertexShader = document.getElementById('xyzVertexShader').textContent;

const xyzFragmentShader = document.getElementById('xyzFragmentShader').textContent;

const stVertexShader = document.getElementById('stVertexShader').textContent;

const stFragmentShader = document.getElementById('stFragmentShader').textContent;

//设置车道中轴线颜色

const refline_material = new THREE.LineBasicMaterial({

    //color: COLORS.ref_line,

    color: 0x0000FF

});

const road_network_material = new THREE.MeshPhongMaterial({

    vertexColors: THREE.VertexColors,

    wireframe: PARAMS.wireframe,//显示现况

    shininess: 20.0,

    transparent: true,

    opacity: 0.4

});


 

//道路线

const lane_outlines_material = new THREE.LineBasicMaterial({

    color: COLORS.lane_outline,

    //color: 0xCD3278,

});

//外侧线

const roadmark_outlines_material = new THREE.LineBasicMaterial({

    color: COLORS.roadmark_outline,

});

const id_material = new THREE.ShaderMaterial({

    vertexShader: idVertexShader,

    fragmentShader: idFragmentShader,

});

const xyz_material = new THREE.ShaderMaterial({

    vertexShader: xyzVertexShader,

    fragmentShader: xyzFragmentShader,

});

const st_material = new THREE.ShaderMaterial({

    vertexShader: stVertexShader,

    fragmentShader: stFragmentShader,

});

const roadmarks_material = new THREE.MeshBasicMaterial({

    vertexColors: THREE.VertexColors,

});

const road_objects_material = new THREE.MeshBasicMaterial({

    vertexColors: THREE.VertexColors,

    side: THREE.DoubleSide,

    wireframe: true,

});

/* load WASM + odr map */

libOpenDrive().then(Module => {

    console.log(Module)

    ModuleOpenDrive = Module;

    fetch("http://localhost:8080/upload/factory_v3.1.2.xodr").then((file_data) => {

        file_data.text().then((file_text) => {

            //加载数据文件

            loadFile(file_text, false);

        });

    });

});

function onFileSelect(file) {

    let file_reader = new FileReader();

    file_reader.onload = () => { loadFile(file_reader.result, true); };

    file_reader.readAsText(file);

}

function loadFile(file_text, clear_map) {

    //清除文件系统中保存的文件节点

    if (clear_map)

        ModuleOpenDrive['FS_unlink']('./data.xodr');

    //将文件数据转为节点保存到文件系统本地指定的文件目录,支持读写

    ModuleOpenDrive['FS_createDataFile'](".", "data.xodr", file_text, true, true);

    //OpenDriveMap不为null,则已经打开过,需要删除地图重新尝试打开

    if (OpenDriveMap)

        OpenDriveMap.delete()

    //打开驱动地图

    OpenDriveMap = new ModuleOpenDrive.OpenDriveMap("./data.xodr", PARAMS.lateralProfile, PARAMS.laneHeight, true);

    loadOdrMap(clear_map);

}

function reloadOdrMap() {

    if (OpenDriveMap)

        OpenDriveMap.delete();

    OpenDriveMap = new ModuleOpenDrive.OpenDriveMap("./data.xodr", PARAMS.lateralProfile, PARAMS.laneHeight, true);

    loadOdrMap(true, false);

}

function loadOdrMap(clear_map = true, fit_view = true) {

    //web浏览器内置api,精度微秒级,performance.now是浏览器(Web API)提供的方法,不同浏览器获取到的精度不同。Date.now是Javascript内置方法,差异主要在于浏览器遵循的ECMAScript规范。

    const t0 = performance.now();

    //是否清除地图

    if (clear_map) {

        road_network_mesh.userData.odr_road_network_mesh.delete();

        scene.remove(road_network_mesh, roadmarks_mesh, road_objects_mesh, refline_lines, lane_outline_lines, roadmark_outline_lines, ground_grid);

        lane_picking_scene.remove(...lane_picking_scene.children);

        roadmark_picking_scene.remove(...roadmark_picking_scene.children);

        xyz_scene.remove(...xyz_scene.children);

        st_scene.remove(...st_scene.children);

        for (let obj of disposable_objs)

            obj.dispose();

    }

    //获取参考线矩形几何体

    const reflines_geom = new THREE.BufferGeometry();

    //获取驱动中参考线数据信息

    const odr_refline_segments = OpenDriveMap.get_refline_segments(parseFloat(PARAMS.resolution));

    //设置参考线坐标点

    reflines_geom.setAttribute('position', new THREE.Float32BufferAttribute(getStdVecEntries(odr_refline_segments.vertices).flat(), 3));

    //设置参考线坐标点索引

    reflines_geom.setIndex(getStdVecEntries(odr_refline_segments.indices, true));

    //创建参考线物体

    refline_lines = new THREE.LineSegments(reflines_geom, refline_material);

    refline_lines.renderOrder = 10;

    //设置是否可见

    refline_lines.visible = PARAMS.ref_line;

    refline_lines.matrixAutoUpdate = false;

    disposable_objs.push(reflines_geom);

    //将参考线加入场景中

    scene.add(refline_lines);


 

    /*

     *道路

     */

    //根据细节级别获取道路网格物体PARAMS.resolution 默认0.3中等级别

    const odr_road_network_mesh = OpenDriveMap.get_mesh(parseFloat(PARAMS.resolution));

    const odr_lanes_mesh = odr_road_network_mesh.lanes_mesh;

    //获取道路物体

    const road_network_geom = get_geometry(odr_lanes_mesh);

    //设置道路颜色

    road_network_geom.attributes.color.array.fill(COLORS.road);

    //odr_lanes_mesh.lane_start_indices线开始标记,

    //扯个车道总共有5条线,分别标记为-2、-1、0、-1、-2

    for (const [vert_start_idx, _] of getStdMapEntries(odr_lanes_mesh.lane_start_indices)) {

      //  console.log(vert_start_idx)

        //根据

        const vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(vert_start_idx);

        //每个路段的长度

        const vert_count = vert_idx_interval[1] - vert_idx_interval[0];

        const vert_start_idx_encoded = encodeUInt32(vert_start_idx);

       // console.log(vert_start_idx, vert_start_idx_encoded,vert_idx_interval)

        const attr_arr = new Float32Array(vert_count * 4);

        for (let i = 0; i < vert_count; i++)

       

            attr_arr.set(

                vert_start_idx_encoded, //存入的数据集合,批量存入,底层会打散

                i * 4 //数组起始索引位置

                );

       road_network_geom.attributes.id.array.set(attr_arr, vert_idx_interval[0] * 4);

    }

    disposable_objs.push(road_network_geom);

    /* road network mesh */

    road_network_mesh = new THREE.Mesh(road_network_geom, road_network_material);

    road_network_mesh.renderOrder = 0;

    road_network_mesh.userData = { odr_road_network_mesh };

    road_network_mesh.matrixAutoUpdate = false;

    road_network_mesh.visible = !(PARAMS.view_mode == 'Outlines');

    scene.add(road_network_mesh);

   

    /* picking road network mesh */

    const lane_picking_mesh = new THREE.Mesh(road_network_geom, id_material);

    lane_picking_mesh.matrixAutoUpdate = false;

    lane_picking_scene.add(lane_picking_mesh);

    /* xyz coords road network mesh */

    const xyz_mesh = new THREE.Mesh(road_network_geom, xyz_material);

    xyz_mesh.matrixAutoUpdate = false;

    xyz_scene.add(xyz_mesh);

    /* st coords road network mesh */

    const st_mesh = new THREE.Mesh(road_network_geom, st_material);

    st_mesh.matrixAutoUpdate = false;

    st_scene.add(st_mesh);

    /* roadmarks geometry */

    const odr_roadmarks_mesh = odr_road_network_mesh.roadmarks_mesh;

    const roadmarks_geom = get_geometry(odr_roadmarks_mesh);

    roadmarks_geom.attributes.color.array.fill(COLORS.roadmark);

    for (const [vert_start_idx, _] of getStdMapEntries(odr_roadmarks_mesh.roadmark_type_start_indices)) {

        const vert_idx_interval = odr_roadmarks_mesh.get_idx_interval_roadmark(vert_start_idx);

        const vert_count = vert_idx_interval[1] - vert_idx_interval[0];

        const vert_start_idx_encoded = encodeUInt32(vert_start_idx);

        const attr_arr = new Float32Array(vert_count * 4);

        for (let i = 0; i < vert_count; i++)

            attr_arr.set(

                vert_start_idx_encoded, //数组

                 i * 4 //偏移量,每次偏移step:数组大小

                 );

        roadmarks_geom.attributes.id.array.set(attr_arr, vert_idx_interval[0] * 4);

    }

    disposable_objs.push(roadmarks_geom);

    /* roadmarks mesh */

    roadmarks_mesh = new THREE.Mesh(roadmarks_geom, roadmarks_material);

    roadmarks_mesh.matrixAutoUpdate = false;

    roadmarks_mesh.visible = !(PARAMS.view_mode == 'Outlines') && PARAMS.roadmarks;

    scene.add(roadmarks_mesh);

    /* picking roadmarks mesh */

    const roadmark_picking_mesh = new THREE.Mesh(roadmarks_geom, id_material);

    roadmark_picking_mesh.matrixAutoUpdate = false;

    roadmark_picking_scene.add(roadmark_picking_mesh);

    /* road objects geometry */

    const odr_road_objects_mesh = odr_road_network_mesh.road_objects_mesh;

    const road_objects_geom = get_geometry(odr_road_objects_mesh);

    road_objects_geom.attributes.color.array.fill(COLORS.road_object);

    for (const [vert_start_idx, _] of getStdMapEntries(odr_road_objects_mesh.road_object_start_indices)) {

        const vert_idx_interval = odr_roadmarks_mesh.get_idx_interval_roadmark(vert_start_idx);

        const vert_count = vert_idx_interval[1] - vert_idx_interval[0];

        const vert_start_idx_encoded = encodeUInt32(vert_start_idx);

        const attr_arr = new Float32Array(vert_count * 4);

        for (let i = 0; i < vert_count; i++)

            attr_arr.set(vert_start_idx_encoded, i * 4);

        roadmarks_geom.attributes.id.array.set(attr_arr, vert_idx_interval[0] * 4);

    }

    disposable_objs.push(road_objects_geom);

    /* road objects mesh */

    road_objects_mesh = new THREE.Mesh(road_objects_geom, road_objects_material);

    road_objects_mesh.matrixAutoUpdate = false;

    scene.add(road_objects_mesh);

    /* lane outline */

    const lane_outlines_geom = new THREE.BufferGeometry();

    lane_outlines_geom.setAttribute('position', road_network_geom.attributes.position);

    lane_outlines_geom.setIndex(getStdVecEntries(odr_lanes_mesh.get_lane_outline_indices(), true));

    lane_outline_lines = new THREE.LineSegments(lane_outlines_geom, lane_outlines_material);

    lane_outline_lines.renderOrder = 9;

    disposable_objs.push(lane_outlines_geom);

    scene.add(lane_outline_lines);

    /* roadmark outline */

    const roadmark_outlines_geom = new THREE.BufferGeometry();

    roadmark_outlines_geom.setAttribute('position', roadmarks_geom.attributes.position);

    roadmark_outlines_geom.setIndex(getStdVecEntries(odr_roadmarks_mesh.get_roadmark_outline_indices(), true));

    roadmark_outline_lines = new THREE.LineSegments(roadmark_outlines_geom, roadmark_outlines_material);

    roadmark_outline_lines.renderOrder = 8;

    roadmark_outline_lines.matrixAutoUpdate = false;

    disposable_objs.push(roadmark_outlines_geom);

    roadmark_outline_lines.visible = PARAMS.roadmarks;

    scene.add(roadmark_outline_lines);

    /* fit view and camera */

    const bbox_reflines = new THREE.Box3().setFromObject(refline_lines);

    const max_diag_dist = bbox_reflines.min.distanceTo(bbox_reflines.max);

    camera.far = max_diag_dist * 1.5;

    controls.autoRotate = fit_view;

    if (fit_view)

        fitViewToBbox(bbox_reflines);

    /* ground grid */

    let bbox_center_pt = new THREE.Vector3();

    bbox_reflines.getCenter(bbox_center_pt);

    ground_grid = new THREE.GridHelper(max_diag_dist, max_diag_dist / 10, 0x2f2f2f, 0x2f2f2f);

    ground_grid.geometry.rotateX(Math.PI / 2);

    ground_grid.position.set(bbox_center_pt.x, bbox_center_pt.y, bbox_reflines.min.z - 0.1);

    disposable_objs.push(ground_grid.geometry);

    scene.add(ground_grid);

    /* fit light */

    light.position.set(bbox_reflines.min.x, bbox_reflines.min.y, bbox_reflines.max.z + max_diag_dist);

    light.target.position.set(bbox_center_pt.x, bbox_center_pt.y, bbox_center_pt.z);

    light.position.needsUpdate = true;

    light.target.position.needsUpdate = true;

    light.target.updateMatrixWorld();

    const t1 = performance.now();

    console.log("Heap size: " + ModuleOpenDrive.HEAP8.length / 1024 / 1024 + " mb");

    const info_msg = `

        <div class=popup_info>

        <h3>加载完成</h3>

        <table>

            <tr><th>加载用时</th><th>${((t1 - t0) / 1e3).toFixed(2)}s</th></tr>

            <tr><th>路段数量</th><th>${OpenDriveMap.roads.size()}</th></tr>

            <tr><th>节点数量</th><th>${renderer.info.render.triangles}</th></tr>

        </table>

        </div>`;

    notyf.open({ type: 'info', message: info_msg });

    odr_roadmarks_mesh.delete();

    odr_lanes_mesh.delete();

    spotlight_info.style.display = "none";

    animate();

}

function animate() {

    setTimeout(function () {

        requestAnimationFrame(animate);

    }, 1000 / 30);

    controls.update();

    if (PARAMS.spotlight && !spotlight_paused) {

        camera.setViewOffset(renderer.domElement.width, renderer.domElement.height, mouse.x * window.devicePixelRatio | 0, mouse.y * window.devicePixelRatio | 0, 1, 1);

        renderer.setRenderTarget(lane_picking_texture);

        renderer.render(lane_picking_scene, camera);

        renderer.setRenderTarget(roadmark_picking_texture);

        renderer.render(roadmark_picking_scene, camera);

        renderer.setRenderTarget(xyz_texture);

        renderer.render(xyz_scene, camera);

        renderer.setRenderTarget(st_texture);

        renderer.render(st_scene, camera);

        const lane_id_pixel_buffer = new Float32Array(4);

        renderer.readRenderTargetPixels(lane_picking_texture, 0, 0, 1, 1, lane_id_pixel_buffer);

        const roadmark_id_pixel_buffer = new Float32Array(4);

        renderer.readRenderTargetPixels(roadmark_picking_texture, 0, 0, 1, 1, roadmark_id_pixel_buffer);

        const xyz_pixel_buffer = new Float32Array(4);

        renderer.readRenderTargetPixels(xyz_texture, 0, 0, 1, 1, xyz_pixel_buffer);

        xyz_pixel_buffer[0] += OpenDriveMap.x_offs;

        xyz_pixel_buffer[1] += OpenDriveMap.y_offs;

        const st_pixel_buffer = new Float32Array(4);

        renderer.readRenderTargetPixels(st_texture, 0, 0, 1, 1, st_pixel_buffer);

        camera.clearViewOffset();

        renderer.setRenderTarget(null);

        if (isValid(lane_id_pixel_buffer)) {

            const decoded_lane_id = decodeUInt32(lane_id_pixel_buffer);

            const odr_lanes_mesh = road_network_mesh.userData.odr_road_network_mesh.lanes_mesh;

            if (INTERSECTED_LANE_ID != decoded_lane_id) {

                if (INTERSECTED_LANE_ID != 0xffffffff) {

                    const prev_lane_vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(INTERSECTED_LANE_ID);

                    road_network_mesh.geometry.attributes.color.array.fill(COLORS.road, prev_lane_vert_idx_interval[0] * 3, prev_lane_vert_idx_interval[1] * 3);

                }

                INTERSECTED_LANE_ID = decoded_lane_id;

                const lane_vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(INTERSECTED_LANE_ID);

                const vert_count = (lane_vert_idx_interval[1] - lane_vert_idx_interval[0]);

                applyVertexColors(road_network_mesh.geometry.attributes.color, new THREE.Color(COLORS.lane_highlight), lane_vert_idx_interval[0], vert_count);

                road_network_mesh.geometry.attributes.color.needsUpdate = true;

                spotlight_info.style.display = "block";

            }

            odr_lanes_mesh.delete();

        } else {

            if (INTERSECTED_LANE_ID != 0xffffffff) {

                const odr_lanes_mesh = road_network_mesh.userData.odr_road_network_mesh.lanes_mesh;

                const lane_vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(INTERSECTED_LANE_ID);

                road_network_mesh.geometry.attributes.color.array.fill(COLORS.road, lane_vert_idx_interval[0] * 3, lane_vert_idx_interval[1] * 3);

                road_network_mesh.geometry.attributes.color.needsUpdate = true;

                odr_lanes_mesh.delete();

            }

            INTERSECTED_LANE_ID = 0xffffffff;

            spotlight_info.style.display = "none";

        }

        if (isValid(roadmark_id_pixel_buffer)) {

            const decoded_roadmark_id = decodeUInt32(roadmark_id_pixel_buffer);

            const odr_roadmarks_mesh = road_network_mesh.userData.odr_road_network_mesh.roadmarks_mesh;

            if (INTERSECTED_ROADMARK_ID != decoded_roadmark_id) {

                if (INTERSECTED_ROADMARK_ID != 0xffffffff) {

                    const prev_roadmark_vert_idx_interval = odr_roadmarks_mesh.get_idx_interval_roadmark(INTERSECTED_ROADMARK_ID);

                    roadmarks_mesh.geometry.attributes.color.array.fill(COLORS.roadmark, prev_roadmark_vert_idx_interval[0] * 3, prev_roadmark_vert_idx_interval[1] * 3);

                }

                INTERSECTED_ROADMARK_ID = decoded_roadmark_id;

                const roadmark_vert_idx_interval = odr_roadmarks_mesh.get_idx_interval_roadmark(INTERSECTED_ROADMARK_ID);

                const vert_count = (roadmark_vert_idx_interval[1] - roadmark_vert_idx_interval[0]);

                applyVertexColors(roadmarks_mesh.geometry.attributes.color, new THREE.Color(COLORS.roadmark_highlight), roadmark_vert_idx_interval[0], vert_count);

                roadmarks_mesh.geometry.attributes.color.needsUpdate = true;

            }

            odr_roadmarks_mesh.delete();

        } else {

            if (INTERSECTED_ROADMARK_ID != 0xffffffff) {

                const odr_roadmarks_mesh = road_network_mesh.userData.odr_road_network_mesh.roadmarks_mesh;

                const roadmark_vert_idx_interval = odr_roadmarks_mesh.get_idx_interval_lane(INTERSECTED_ROADMARK_ID);

                roadmarks_mesh.geometry.attributes.color.array.fill(COLORS.roadmark, roadmark_vert_idx_interval[0] * 3, roadmark_vert_idx_interval[1] * 3);

                roadmarks_mesh.geometry.attributes.color.needsUpdate = true;

                odr_roadmarks_mesh.delete();

            }

            INTERSECTED_ROADMARK_ID = 0xffffffff;

        }

        if (INTERSECTED_LANE_ID != 0xffffffff) {

            const odr_lanes_mesh = road_network_mesh.userData.odr_road_network_mesh.lanes_mesh;

            const road_id = odr_lanes_mesh.get_road_id(INTERSECTED_LANE_ID);

            const lanesec_s0 = odr_lanes_mesh.get_lanesec_s0(INTERSECTED_LANE_ID);

            const lane_id = odr_lanes_mesh.get_lane_id(INTERSECTED_LANE_ID);

            const lane_type = OpenDriveMap.roads.get(road_id).s_to_lanesection.get(lanesec_s0).id_to_lane.get(lane_id).type;

            odr_lanes_mesh.delete();

            spotlight_info.innerHTML = `

                    <table>

                        <tr><th>路段编号</th><th>${road_id}</th></tr>

                        <tr><th>横断面</th><th>${lanesec_s0.toFixed(2)}</th></tr>

                        <tr><th>车道</th><th>${lane_id} <span style="color:gray;">${lane_type}</span></th></tr>

                        <tr><th>s/t</th><th>[${st_pixel_buffer[0].toFixed(2)}, ${st_pixel_buffer[1].toFixed(2)}]</th>

                        <tr><th>世界坐标</th><th>[${xyz_pixel_buffer[0].toFixed(2)}, ${xyz_pixel_buffer[1].toFixed(2)}, ${xyz_pixel_buffer[2].toFixed(2)}]</th></tr>

                    </table>`;

        }

    }

    renderer.render(scene, camera);

}

//集合体

function get_geometry(odr_meshunion) {

    const geom = new THREE.BufferGeometry();

    geom.setAttribute('position', new THREE.Float32BufferAttribute(getStdVecEntries(odr_meshunion.vertices, true).flat(), 3));

    //获取uv坐标

    geom.setAttribute('st', new THREE.Float32BufferAttribute(getStdVecEntries(odr_meshunion.st_coordinates, true).flat(), 2));

    //设置点颜色

    geom.setAttribute('color', new THREE.Float32BufferAttribute(new Float32Array(geom.attributes.position.count * 3), 3));

    //设置几何体唯一编号

    geom.setAttribute('id', new THREE.Float32BufferAttribute(new Float32Array(geom.attributes.position.count * 4), 4));

    //设置几何体索引数据

    geom.setIndex(getStdVecEntries(odr_meshunion.indices, true));

    geom.computeVertexNormals();

    return geom;

}

function fitViewToBbox(bbox, restrict_zoom = true) {

    let center_pt = new THREE.Vector3();

    bbox.getCenter(center_pt);

    const l2xy = 0.5 * Math.sqrt(Math.pow(bbox.max.x - bbox.min.x, 2.0) + Math.pow(bbox.max.y - bbox.min.y, 2));

    const fov2r = (camera.fov * 0.5) * (Math.PI / 180.0);

    const dz = l2xy / Math.tan(fov2r);

    camera.position.set(bbox.min.x, center_pt.y, bbox.max.z + dz);

    controls.target.set(center_pt.x, center_pt.y, center_pt.z);

    if (restrict_zoom)

        controls.maxDistance = center_pt.distanceTo(bbox.max) * 1.2;

    controls.update();

}

function fitViewToObj(obj) {

    const bbox = new THREE.Box3().setFromObject(obj);

    fitViewToBbox(bbox);

}

//改变颜色

function applyVertexColors(buffer_attribute, color, offset, count) {

    console.log("我被改变了", buffer_attribute)

    const colors = new Float32Array(count * buffer_attribute.itemSize);

    for (let i = 0; i < (count * buffer_attribute.itemSize); i += buffer_attribute.itemSize) {

        colors[i] = color.r;

        colors[i + 1] = color.g;

        colors[i + 2] = color.b;

        // colors[i] =0;

        // colors[i + 1] = 0;

        // colors[i + 2] = 255;

    }

    buffer_attribute.array.set(colors, offset * buffer_attribute.itemSize);

}

function getStdMapKeys(std_map, delete_map = false) {

    let map_keys = [];

    const map_keys_vec = std_map.keys();

    for (let idx = 0; idx < map_keys_vec.size(); idx++)

        map_keys.push(map_keys_vec.get(idx));

    map_keys_vec.delete();

    if (delete_map)

        std_map.delete();

    return map_keys;

}

function getStdMapEntries(std_map) {

    let map_entries = [];

     //key路段索引 , std_map.get(key)车道编号

    for (let key of getStdMapKeys(std_map)){

        console.log(key, std_map.get(key))

        map_entries.push([key, std_map.get(key)]);

    }

    console.log("总数量", map_entries.length)

    return map_entries;

}

function getStdVecEntries(std_vec, delete_vec = false, ArrayType = null) {

    //获取道路网格三位坐标点数据

    let entries = ArrayType ? new ArrayType(std_vec.size()) : new Array(std_vec.size());

    for (let idx = 0; idx < std_vec.size(); idx++)

        entries[idx] = std_vec.get(idx);

    if (delete_vec)

        std_vec.delete();

    return entries;

}

function isValid(rgba) {

    return !(rgba[0] == 1 && rgba[1] == 1 && rgba[2] == 1 && rgba[3] == 1);

}

function encodeUInt32(ui32) {

    rgba = new Float32Array(4);

    rgba[0] = (Math.trunc(ui32) % 256) / 255.;

    rgba[1] = (Math.trunc(ui32 / 256) % 256) / 255.;

    rgba[2] = (Math.trunc(ui32 / 256 / 256) % 256) / 255.;

    rgba[3] = (Math.trunc(ui32 / 256 / 256 / 256) % 256) / 255.;

    return rgba;

}

function decodeUInt32(rgba) {

    return Math.round(rgba[0] * 255) + Math.round(rgba[1] * 255) * 256 + Math.round(rgba[2] * 255) * 256 * 256 + Math.round(rgba[3] * 255) * 256 * 256 * 256;

}


 

//监听画面变化,更新渲染画面,(自适应的大小)

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;

    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

    renderer.setPixelRatio(window.devicePixelRatio);

}

function onDocumentMouseMove(event) {

    event.preventDefault();

    mouse.x = event.clientX;

    mouse.y = event.clientY;

}

//双击触发函数

function onDocumentMouseDbClick(e) {

    if (INTERSECTED_LANE_ID != 0xffffffff) {

        const odr_lanes_mesh = road_network_mesh.userData.odr_road_network_mesh.lanes_mesh;

        const lane_vert_idx_interval = odr_lanes_mesh.get_idx_interval_lane(INTERSECTED_LANE_ID);

        const vertA = odr_lanes_mesh.vertices.get(lane_vert_idx_interval[0]);

        const vertB = odr_lanes_mesh.vertices.get(lane_vert_idx_interval[1] - 1);

        odr_lanes_mesh.delete();

        const bbox = new THREE.Box3();

        bbox.setFromArray([vertA, vertB].flat());

        fitViewToBbox(bbox, false);

    }

}

 效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/444152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年SICWGHS两大高含金量商赛组队招募中

想参加商赛&#xff0c;问了周围一圈朋友&#xff0c;不是没时间就是没兴趣&#xff0c;找个靠谱的队友怎么这么难&#xff1f; 相信这是不少商赛热爱者的共同烦恼&#xff0c;别急&#xff01;翰林来承包你的“找队友”任务&#xff0c;各路学霸等你来pick&#xff01; 两大…

屏幕亮度调节工具:Simple Screen Shade Mac

Simple Screen Shade Mac版是mac上一款优秀的屏幕颜色亮度调节工具&#xff0c;能够让我们Mac电脑的显示器背景变暗&#xff0c;这样可以保护你眼睛的健康并保持舒适。Simple Screen Shade 旨在实现最大程度的简单性和易用性。你可以设置灰色背景以减轻明亮鲜艳的色彩&#xff…

上海亚商投顾:沪指跌近2%险守3300点 AI概念股集体重挫

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日放量调整&#xff0c;深成指午后跌超2%&#xff0c;沪指、创业板指均跌近2%&#xff0c;科创50指数跌…

java-word模板转化为pdf

文章目录 一、引入包1.1在pom引入1.2 因为我们的项目是打包成jar,所以以上方法在本地idea运行没有问题&#xff0c;linux系统不行1.2.1解决方法11.2.2解决方法2 二、配置文件--License.xml--去除水印2.1 license.xml直接放到resources的根目录下即可2.2 工具类 三、调用效果 一…

部署架构 因为单体架构痛点 升级到微服务架构

如图为单体部署 痛点 多人协作可能产生很多的回归测试 代码管理复杂度提升 软件包升级会导致增加测试次数 举例 单体电商 1增加功能(增加小程序平台) 2 并发增加 出现 1 代码复用 2 系统间相互调用 3 接口不仅要对外服务&#xff0c;也得对内提供接口 4 数据分析功…

OTG是什么意思?

OTG是什么意思&#xff1f; OTG是怎么样实现的&#xff1f; TYPE-C接口的手机如何实现同时充电OTG功能&#xff1f; OTG是什么意思&#xff1f; OTG是On-The-Go的缩写&#xff0c;是一项新兴技术&#xff0c;主要应用于不同的设备或移动设备间的联接&#xff0c;进行数据交…

腾讯安全助力高校信息安全建设,护航湾区教育高质量发展

2023年4月20日-21日&#xff0c;首届大湾区信息网络安全大会在广州市长隆国际会展中心隆重召开。会议以“同筑安全屏障&#xff0c;共赢湾区未来”为主题&#xff0c;旨在响应国家安全战略&#xff0c;推动粤港澳大湾区信息网络安全的建设和发展&#xff0c;保障经济社会稳定运…

「OceanBase 4.1 体验」|国产分布式数据库不好用?别再打脸了

文章目录 分布式数据库分布式数据库有哪些&#xff1f;OceanBase4.1安装部署Index Skip Scan总结 随着互联网的高速发展和数据量的爆炸式增长&#xff0c;如何能够高效、可靠、安全地存储海量数据成为了每个企业的重要课题。 分布式数据库 分布式数据库通常是由多个独立的数据…

无人机各个类型介绍

为了执行&#xff0c;无人机可能由类似的元件制成&#xff0c;但无论是它们的能力&#xff0c;还是由什么组成的&#xff0c;它们都在某种程度上有所不同。大多数无人机都是为了执行特定任务而制造的&#xff0c;因此以特定的方式建造&#xff0c;以适应它们将要使用的环境。 …

docker-mysql-主从设计

一、docker主从 1.新建主从镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.28 docker run -p 3308:3306 --name mysqlslave -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.282.分别进入两个容器&#xff0c;修改配置文件 #1.进入容器 $ do…

Java8新特性【函数式接口、Lambda表达、Stream流】

一、Lambda表达式 Java8是Java语言自JDK1.5以后的一个重大的里程碑版本&#xff0c;因为它增加了很多新特性&#xff0c;这些新特性会改变编程的风格和解决问题的方式。 例如&#xff1a;日期时间API、Lambda表达式&#xff08;λ&#xff09;、Stream API(操作集合)、方法引用…

用户量达6.33亿即时配送,拼的不止这些

上班忘了带电脑&#xff0c;回去取的话&#xff0c;来回要3个小时&#xff0c;还得损失半天薪资&#xff1b;好友生日&#xff0c;想在聚餐时给对方一个惊喜&#xff0c;但带着蛋糕去又容易提前剧透&#xff1b;老人突然生病在家&#xff0c;医院的号难挂、得排长队&#xff0c…

TSINGSEE视频能力在交通运输可视化管理平台项目中的应用

一、行业背景 为贯彻落实交通强国试点工作要求&#xff0c;提升交通建设工程信息化管理水平&#xff0c;进一步强化交通建设工程管理&#xff0c;各地政府部门决定在全省高速公路、国省道、大型水运、地方铁路、机场工程安装视频监控系统&#xff0c;在建交通建设项目尚未安装…

OldWang带你了解MySQL(九)

文章目录 &#x1f525;MySQL中的索引&#x1f525;MySQL中的索引类型&#x1f525;普通索引&#x1f525;唯一索引&#x1f525;主键索引&#x1f525;组合索引 &#x1f525;MySQL中的索引 索引介绍 索引是对数据库表中的一列或多列值进行排序的一种结构&#xff0c;使用索引…

【C++学习笔记】字符串、向量和数组

字符串类型 1.C语言风格字符串&#xff1a;char 变量名[] "字符串值" 1. char: 字符常量或者单个字符 单引号定义 &#xff1b;字符串常量用 双引号 定义 2. 输出直接用cout char str1[] "hello world"; cout << str1 << endl;2.C语言风格…

Linux环境下 通过V4L2读取视频+UDP发送图片文件

该图为整个项目的流程图 其中左边的流程为总流程&#xff0c;包括通过中断读取摄像头的帧数据&#xff0c;通过内存映射将内核态的数据映射到用户态&#xff0c;读取用户态的数据&#xff0c;采用循环发送图片数据。 右边是发送图片的流程图&#xff0c;将用户态的缓冲区的数…

780E编译底包教程

这里写目录标题 准备1 安装开发环境准备2 拉取编译工程源码 代码编译修改或者增加用户程序说明 准备 1 安装开发环境准备 需要用户自行安装好Xmake\vscode\git 环境教程传送门 2 拉取编译工程源码 注意, 需要两个库 主库 https://gitee.com/openLuat/LuatOS bsp库 https://…

LAMP架构中的网站搭建

前言&#xff1a;本次操作依赖于LAMP的环境已经配置完全&#xff0c;网站也是依赖于开发人员现有的网站包框架&#xff0c;实施在LAMP已搭建好的环境进行安装部署 1. 对mysql进行操作 ——创建数据库&#xff0c;并进行授权 1.创建数据库&#xff0c;并进行授权 mysql -u roo…

Redis6学习

Redis6 1. NoSQL数据库简介 1.1 技术发展 技术的分类 1、解决功能性的问题&#xff1a;Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN。 2、解决扩展性的问题&#xff1a;Struts、Spring、SpringMVC、Hibernate、Mybatis。 3、解决性能的问题&#xff1a;NoSQL、Jav…

国产什么牌子的蓝牙耳机音质好?国产适合听音乐的蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机的性能越来越多&#xff0c;一款蓝牙耳机不可能将各种性能做到极致。大家在选择蓝牙耳机时&#xff0c;无外乎从佩戴、音质、降噪、延迟等因素出发&#xff0c;那么&#xff0c;国产什么牌子的蓝牙耳机音质好&#xff1f;根据这个问题&#xff0c;我…