【rust/bevy】使用points构造ConvexMesh

news2025/1/16 14:49:42

目录

  • 说在前面
  • 问题提出
  • Rapier
  • 具体实现
  • 参考

说在前面

  • 操作系统:win11
  • rust版本:rustc 1.77.0-nightly
  • bevy版本:0.12
  • github:这里

问题提出

  • three.js中,可以通过使用ConvexGeometry从给定的三维点集合生成凸包(Convex Hull)

    import { ConvexGeometry } from 'three/addons/geometries/ConvexGeometry.js';
    

    例如:

    const geometry = new ConvexGeometry( points );
    const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    const mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
    

    在这里插入图片描述

  • 但是在bevy中如何实现呢?

Rapier

  • 在当前bevy的版本中,并没有类似的实现,目前支持的shape也比three.js少:
    • Box
    • Capsule
    • Circle
    • Cube
    • Cylinder
    • Icosphere
    • Plane
    • Quad
    • RegularPolygon
    • Torus
    • UVSphere
  • 所以,如果要构造ConvexHull,只能借助第三方库(或者自己实现,可以参考three.js实现)
  • 这里我们借助一个物理库rapier来实现
  • 关于rapier
    这里

具体实现

  • 首先我们通过点集构造ConvexHullrapier可以直接完成这一步:

    let collider = Collider::convex_hull(&points);
    

    但是该方法构造的是一个Collider,实际上是无法直接当成Mesh使用的

  • 然后我们将其转换成ConvexPolyhedron

    let convex = c.as_convex_polyhedron().unwrap();
    
  • 该结构中包含一个凸包的所有属性,例如点、边、面等,事实上,它提供了一个方法,能够直接转换成TriangleMesh所需要的数据:

    impl ConvexPolyhedron {
        /// Discretize the boundary of this convex polyhedron as a triangle-mesh.
        pub fn to_trimesh(&self) -> (Vec<Point3<Real>>, Vec<[u32; 3]>) {
            let mut indices = Vec::new();
    
            for face in self.faces() {
                let i1 = face.first_vertex_or_edge;
                let i2 = i1 + face.num_vertices_or_edges;
                let first_id = self.vertices_adj_to_face()[i1 as usize] as u32;
    
                for idx in self.vertices_adj_to_face()[i1 as usize + 1..i2 as usize].windows(2) {
                    indices.push([first_id, idx[0] as u32, idx[1] as u32]);
                }
            }
    
            (self.points().to_vec(), indices)
        }
    }
    

    但是不包含normal数据,导致光照对其没有作用
    在这里插入图片描述

  • 所以我们需要自己实现一个相似的方法,首先我们来看看测试数据,实际上它有20个点,共12个面,每个面是一个正五边形,所以使用TriangleMesh来表达的话,一共是36个三角形

  • 再让我们来看看bevy中构造一个TriangleMesh需要哪些数据:

    fn create_simple_parallelogram() -> Mesh {
        // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
        Mesh::new(PrimitiveTopology::TriangleList)
            // 顶点数据
            .with_inserted_attribute(
                Mesh::ATTRIBUTE_POSITION,
                vec![[0.0, 0.0, 0.0], [1.0, 2.0, 0.0], [2.0, 2.0, 0.0], [1.0, 0.0, 0.0]]
            )
            // uv数据 在本文中我们并不需要
            .with_inserted_attribute(
                Mesh::ATTRIBUTE_UV_0,
                vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
            )
            // 法线
            .with_inserted_attribute(
                Mesh::ATTRIBUTE_NORMAL,
                vec![[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]
            )
            // 三角形的顶点索引
            .with_indices(Some(Indices::U32(vec![
                0, 3, 1,
                1, 3, 2
            ])))
    }
    
  • 对比我们需要的数据,以及to_trimesh方法,发现我们少了normal数据,不过这个数据在face中就有,那么所有条件就具备了

  • 完整代码

    let plist: Vec<f32> = vec![0., 3.568220853805542, 9.341723442077637, 5.773502826690674, 5.773502826690674, 5.773502826690674, -5.773502826690674, 5.773502826690674, 5.773502826690674, 3.568220853805542, 9.341723442077637, 0., -3.568220853805542, 9.341723442077637, 0., 9.341723442077637, 0., 3.568220853805542, 9.341723442077637, 0., -3.568220853805542, 5.773502826690674, 5.773502826690674, -5.773502826690674, 5.773502826690674, -5.773502826690674, -5.773502826690674, 0., -3.568220853805542, -9.341723442077637, 0., 3.568220853805542, -9.341723442077637, -5.773502826690674, -5.773502826690674, -5.773502826690674, -9.341723442077637, 0., -3.568220853805542, -5.773502826690674, 5.773502826690674, -5.773502826690674, -3.568220853805542, -9.341723442077637, 0., -5.773502826690674, -5.773502826690674, 5.773502826690674, -9.341723442077637, 0., 3.568220853805542, 0., -3.568220853805542, 9.341723442077637, 3.568220853805542, -9.341723442077637, 0., 5.773502826690674, -5.773502826690674, 5.773502826690674];
    let points: Vec<Vec3> = plist
        .array_chunks()
        .into_iter()
        .map(|&v: &[f32; 3]| Vec3::from_array(v))
        .collect();
    // 通过点集构造convex hull
    let collider = Collider::convex_hull(&points);
    if let Some(c) = collider {
        let convex = c.as_convex_polyhedron().unwrap();
        // 取convex hull的所有面
        let faces = convex.raw.faces();
        // 取点集
        let points = convex.raw.points();
        // 取映射关系
        let face_to_vertices = convex.raw.vertices_adj_to_face();
    	
        let mut positions = Vec::new();
        // 法向量 用于处理光照
        let mut normals = Vec::new();
        let mut indices = Vec::new();
        // 遍历所有的面
        for face in faces {
            let i1 = face.first_vertex_or_edge;
            let i2 = i1 + face.num_vertices_or_edges;
    
            for idx in i1..i2 {
                let point = points[face_to_vertices[idx as usize] as usize];
                // 重新构造点集 points是原始点集
                positions.push([point.x, point.y, point.z]);
                // 面上的所有点的朝向与面相同
                normals.push([face.normal.x, face.normal.y, face.normal.z]);
            }
    
            for idx in i1 + 1..i2 - 1 {
            	// 构造顶点索引
                indices.push([i1, idx as u32, idx + 1 as u32]);
            }
        }
    	// 构造Mesh
        let mesh = Mesh::new(PrimitiveTopology::TriangleList)
            .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
            .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
            .with_indices(Some(Indices::U32(indices.concat())));
    
        commands.spawn(PbrBundle {
            mesh: meshes.add(mesh),
            material: materials.add(Color::rgb_u8(124, 144, 255).into()),
            transform: Transform::from_xyz(0.0, 1., 0.0).with_scale(Vec3::new(0.1, 0.1, 0.1)),
            ..default()
        });
    }
    
  • 结果(gif)
    在这里插入图片描述

参考

  • ConvexGeometry

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

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

相关文章

【软考中级-软件设计师】day9:进程资源图、信号量PV操作、死锁和银行家算法

大纲 概述 进程的组成和状态 前趋图 进程资源图 真题 同步与互斥 信号量 生产者和消费者 真题 死锁 线程

【C++第二课 - 类和对象上 - 入门知识】struct类、class类、访问限定符、this指针

目录 面向过程与面向对象初步认识类的定义struct定义类class定义类 类的访问限定符及封装访问限定符 声明与定义分离this指针类成员的命名问题this 类的实例化类的对象大小的计算成员函数为何不在对象里面类对象大小计算 面向过程与面向对象初步认识 C语言是面向过程的&#x…

基于Elasticsearch+Logstash+Kibana+Filebeat的日志收集分析及可视化

sudo rm /var/lib/dpkg/lock* sudo dpkg --configure -a apt update tail -f /var/log/car.log 1.1、项目概述 海量的业务应用&#xff0c;也带来了海量的日志数据&#xff0c;给业务应用的运维带来了新的挑战。例如&#xff0c;我们常用的网约车应用&#xff0c;单个平台…

六、数组(1)二维数组

一、二维数组定义方式 1、数据类型 数组名[行数][列数]; 2、数据类型 数组名[行数][列数]{{数据1,数据2},{数据3,数据4}}; 3、数据类型 数组名[行数][列数]{数据1,数据2,数据3,数据4}; 4、数据类型 数组名[][列数]{数据1,数据2,数据3,数据4}; 注&#xff1a;第二种定义方式更…

S32K3系列 --- 硬件I2C Mcal配置

前言 网上看到很多I2C的教程,基本都是模拟I2C,现在S32K3的芯片支持硬件I2C,我想着就配一个硬件的出来吧,这边记录一下,供大家学习。 一、I2C原理 这里主要教大家如何去配置,去使用。 原理的话可以参考这篇文章: 一文搞懂I2C通信总线_i2c通信的详细讲解-CSDN博客 I2…

生信人 RNA-seq

RNA-seq&#xff1a;6-qc-2 #先跑一个综合的 Multiqc 报告 #将fastqc生成的多个报告整合成一个报告&#xff0c;方便查看所有测序数据的质量。 conda install multiqc -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda cd /home/yinwen/RNA-seq_report/ mult…

【算法与数据结构】474、LeetCode一和零

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题要找strs数组的最大子集&#xff0c;这个子集最多含有 m m m个0和 n n n个1。本题也可以抽象成一个…

【LeetCode每日一题】2809. 使数组和小于等于 x 的最少时间

2024-1-19 文章目录 [2809. 使数组和小于等于 x 的最少时间](https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/)思路&#xff1a; 2809. 使数组和小于等于 x 的最少时间 思路&#xff1a; 获取两个列表的长度n&#xff0c;并初始化一个二维数组f&…

Spring Boot整合Redis的高效数据缓存实践

引言 在现代Web应用开发中&#xff0c;数据缓存是提高系统性能和响应速度的关键。Redis作为一种高性能的缓存和数据存储解决方案&#xff0c;被广泛应用于各种场景。本文将研究如何使用Spring Boot整合Redis&#xff0c;通过这个强大的缓存工具提高应用的性能和可伸缩性。 整合…

从零开始c++精讲:第四篇——模板初阶

文章目录 一、泛型编程二、函数模板2.1函数模板概念2.2函数模板格式2.3函数模板原理2.4函数模板实例化2.5函数模板匹配原则 三、类模板3.1类模板的定义格式3.2类模板的实例化 一、泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left, int& righ…

TCP/IP协议及配置、IP地址、子网掩码、网关地址、DNS与DHCP介绍

一、什么是服务器 能够为其他计算机提供服务的更高级的电脑 尺寸:Unit 1u1.75英寸44.45mm4.445cm IDC&#xff08;机房&#xff09; C/S结构 Client/Server客户端和服务端 二、TCP/IP协议 计算机与计算机之间通信的协议 三要素&#xff1a; IP地址 子网掩码 IP路由 I…

C++设计模式之 模板方法模式

【声明】本题目来源于卡码网&#xff08;题目页面 (kamacoder.com)&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【设计模式大纲】 【简介】 --什么是模板方法模式&#xff08;第18种设计模式&#xff09; 模板方法模式&#xff0…

大数据安全 | 期末复习(上)| 补档

文章目录 &#x1f4da;概述⭐️&#x1f407;大数据的定义、来源、特点&#x1f407;大数据安全的含义&#x1f407;大数据安全威胁&#x1f407;保障大数据安全&#x1f407;采集、存储、挖掘环节的安全技术&#x1f407;大数据用于安全&#x1f407;隐私的定义、属性、分类、…

Transformer 可解释性论文整理(超级详细)

Transformer 可解释性论文整理 前段时间想进一步的了解transformer的工作原理&#xff0c;于是找到了几篇可解释性的文章进行阅读&#xff0c;发现了许多比较有趣的现象和结论&#xff0c;对每篇文章都有自己的深度思考和理解&#xff0c;在此记录&#xff0c;欢迎交流。 1. …

「优选算法刷题」:二分查找

一、题目 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 …

【JavaEE进阶】实现验证码

文章目录 &#x1f332;实现说明&#x1f343;Kaptcha插件介绍&#x1f6a9;插件原理&#x1f6a9;引入依赖&#x1f6a9;⽣成验证码&#x1f6a9;Kaptcha详细配置 &#x1f340;准备⼯作&#x1f334;约定前后端交互接⼝&#x1f6a9;需求分析&#x1f6a9;接⼝定义&#x1f6…

力扣 第 122 场双周赛 解题报告 | 珂学家 | 脑筋急转弯 + 滑窗反悔堆

前言 整体评价 倒开差点崩盘&#xff0c;T4这个反悔堆写吐了&#xff0c;T3往众数上去猜了&#xff0c;幸好case良心。 T1. 将数组分成最小总代价的子数组 I 思路: 取 nums[1:] 的最小2个值 可以部分排序&#xff0c;这样更快捷 class Solution {public int minimumCost(in…

Github操作网络异常笔记

Github操作网络异常笔记 1. 源由2. 解决2.1 方案一2.2 方案二 3. 总结 1. 源由 开源技术在国内永远是“蛋疼”&#xff0c;这些"政治"问题对于追求技术的我们&#xff0c;形成无法回避的障碍。 $ git pull ssh: connect to host github.com port 22: Connection ti…

Qt QCustomPlot 绘制子轴

抄大神杰作&#xff1a;QCustomplot&#xff08;五&#xff09;QCPAxisRect进行子绘图-CSDN博客文章浏览阅读5.9k次&#xff0c;点赞7次&#xff0c;收藏60次。文中介绍了QCustomPlot 子绘图需要掌握的类&#xff0c;也就是Matlab中的subplot&#xff0c;最后给出了一个完整的例…

EtherNet/IP开发:C++搭建基础模块,EtherNet/IP源代码

这里是CIP资料的协议层级图&#xff0c;讲解协议构造。 ODVA&#xff08;www.ODVA.org&#xff09;成立于1995年&#xff0c;是一个全球性协会&#xff0c;其成员包括世界领先的自动化公司。结合其成员的支持&#xff0c;ODVA的使命是在工业自动化中推进开放、可互操作的信息和…