Hightopo 使用心得(4)- 3D 场景 Graph3dView 与 Obj 模型

在前一篇文章《Hightopo 使用心得(3)- 吸附与锚点》中,我们在结尾处提到过 HT3D 场景。这里我们通过代码建立一个 3D 场景并添加一个 Obj 模型来介绍一下 HT for Web3D 场景和模型加载方面的使用。

这是我们最终实现的效果:
在这里插入图片描述

3D坐标系

在搭建 3D 场景之前,先介绍一下基本的 3D 概念。

HT for Web3D 场景中采用的是右手坐标系,遵循右手螺旋法则。也就是:x轴正方向朝右,y轴正方向朝上,z轴正方向朝向屏幕外。
在这里插入图片描述
2D 坐标系(x, y)相比,这里多了一条坐标轴,也就是高度轴。2/3D 坐标系具体对应关系如下:

2D3D
坐标轴xx
yz
y

从图片和表格中可以看到,在右手坐标系下,2D坐标系中的 x,y 平面,在 3D 中对应的是 x,z 平面,也就是地平面。而在 3D 中多出来的一条坐标轴是高度坐标轴,也就是 y 轴。

有了三条坐标轴后,显而易见,我们在配置节点(ht.Node)属性时就不能使用原来的方法。为此,HTht.Node 扩展了一些新的方法。其中比较常用的有:
3D位置函数

  • 设置位置(坐标):setPosition3d(x, y, z)|setPosition3d([x, y, z]),可传入x, y, z三个参数,或传入[x, y, z]的数组
  • 获取位置(坐标):getPosition3d()的新函数,返回[x, y, z]数组值,即[getPosition().x, getElevation(), getPosition().y]
  • 设置大小(尺寸):setSize3d(x, y, z)|setSize3d([x, y, z]),可传入x, y, z三个参数,或传入[x, y, z]的数组
  • 获取大小(尺寸):getSize3d()的新函数,返回[x, y, z]数组值,即[getWidth(), getTall(), getHeight()]

3D锚点

3D中节点同样有锚点的概念,同样HTht.Node图元增加了以下新函数:

-设置 3D 锚点: setAnchor3d(x, y, z)|setAnchor3d([x, y, z]),可传入x, y, z三个参数,或传入[x, y, z]的数组
-设置获取y轴方向锚点: getAnchorElevation()|setAnchorElevation(elevation)
-获取 3D 锚点 getAnchor3d()的新函数,返回[x, y, z]数组值,即[getAnchor().x, getAnchorElevation(), getAnchor().y]

3D旋转函数

ht.Node2D坐标系下由getRotation()setRotation(rotation)函数控制旋转,该参数对应于3D坐标系下沿y轴的负旋转值。 同时3D坐标系下增加了rotationXrotationZ两个分别沿着x轴和z轴的新旋转变量,同时增加以下新函数:

  • setRotationY(y)设置沿y轴旋转弧度,相当于setRotation(-y)
  • getRotationY()获取沿y轴旋转弧度,相当于getRotation()
  • 设置围绕三个坐标轴的旋转角度:setRotation3d(x, y, z)|setRotation3d([x, y, z]),可传入x, y, z三个参数,或传入[x, y, z]的数组
  • 获取当前在三个坐标轴的旋转角度:getRotation3d()的新函数,返回[x, y, z]数组值,即[getRotationX(), -getRotation(), getRotationZ()]

3D场景搭建 - ht.graph3d.Graph3dView

在前面文章的例子中,创建一张 2D 图纸,使用的是 new ht.graph.GraphView(); 而在这里,创建一个 3D 场景,我们需要使用 new ht.graph3d.Graph3dView();

HT其他视图组件一样, ht.graph3d.Graph3dView也是基于于统一的ht.DataModel数据模型来驱动图形显示。熟悉了2D图纸的同学可能会发现,其在场景配置,节点配置上与 2D 相似。

使用下面的代码,我们创建和配置了一个 3D 场景,并获取了其对应的数据模型(dataModel):

/*************** 创建一个3D场景,添加到body下,并配置各种属性 ******************/
const g3d = new ht.graph3d.Graph3dView();
g3d.addToDOM(); // 添加到DOM
g3d.setGridVisible(true); // 显示网格
g3d.setEye(2000, 1000, 0); // 设置相机位置
g3d.setCenter(0, 0, 0); // 设置中心点
g3d.setUp(0, 1, 0); // 设置相机角度;这里默认值就是 [0, 1, 0]g3d.setRotatable(true); // 允许旋转,默认值:true
g3d.setZoomable(true); // 允许滚轮缩放,默认值:true
g3d.setPannable(true); // 允许平移,默认值:trueg3d.setEditable(true); // 允许在场景中对节点进行编辑const dm = g3d.getDataModel(); // 获取场景的 DataModel,简写形式:g3d.dm()
dm.setBackground('white'); // 同 dm.setBackground('rgba(255, 255 255, 1)'); 默认为黑色

在这里插入图片描述
创建场景后,我们又让它显示了辅助网格。我们可以将这些网格理解成地平面。模型在网格上方就相当于在地面之上。反之就是在地面下方。

相机的up坐标

在上例中,比较特殊的一个操作是 g3d.setUp(0, 1, 0)。这里是指设置相机的 up 坐标。在计算机图形学中,相机的 up 坐标通常是指相机坐标系中的一个向量,用于定义相机的上方向。

例如在一个场景中,在水平面上有一栋房子,如果相机的 up 坐标是 (0,1,0),则相机将看到一个朝上的房子。如果相机的 up 坐标是(1,0,0),则相机将看到一个朝右的房子和竖直的地面(整个视角都会旋转)。在 HT for Web 中,默认的相机 up 坐标是 [0, 1, 0],也就是我们会看到一个正常的朝上的房子。

OBJ模型

HT3D 场景支持 FBX,OBJ,GLTF 等多种模型格式。这里我们选择比较通用的 OBJ 模型来进行举例。

要使用 OBJ 模型,首先需要在 index.html 中引入 ht-obj.js 插件:

<script src="../../lib/plugin/ht-obj.js"></script>

加载OBJ模型

通过使用 ht.Default.loadObj() 方法可以将 OBJ 模型加载到内存中。在执行loadObj() 时,需要配置 OBJ 路径,材质路径以及相关参数。其中参数 params 的详细说明可以从以下连接获取:

Global | HT for Web (hightopo.com)

它里面比较常用的几个参数有:

centerboolean模型是否居中,默认为false,设置为true则会移动模型位置使其内容居中
prefixstring图片路径前缀,即在map_kd值之前增加的前缀,如果是相对路径则以加载obj的html页面的路径为参考
shape3dstring如果指定了shape3d名称,则HT将自动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册
finishFuncfunction用于加载后的回调处理
/*** 加载 obj 模型** @param {*} modelName* @return {*} */
function loadObj(objPath, mtlPath, modelName) {return new Promise((resolve, reject) => {/*** 模型参数,具体参数参考:https://www.hightopo.com/guide/doc/global.html#LoadObjParams*/const params = {center: true,prefix: 'obj/',shape3d: modelName,finishFunc: (modelMap, array, rawS3) => {resolve({modelMap, array, rawS3});},};// 加载模型ht.Default.loadObj(objPath, mtlPath, params);});
}

其中的 shape3d 参数是一个自定义字符串,可以将该字符串理解为我们为模型配置了一个名字。HT 在加载完 OBJ 模型后,它会把该模型存储到内存中。存储的方式就是通过 ht.Default.setShape3dModel(name, model) 方法。

在上面的代码中,我们为要加载的模型起了一个名字:modelName。在想使用该模型的时候,再通过ht.Default.getShape3dModel(name) 方法便可把模型从内存中取出来。

将Obj模型添加到3D场景

在上面的 loadObj() 只是将 OBJ 模型添加到了内存中。我们还需要在之后将 OBJ 模型添加到场景中。

由于 loadObj() 方法为异步执行,因此其参数里面需要携带一个 finishFunc 作为回调参数。为了减少代码层级,我们将上面的方法封装成了Promise 性质。后面我们可以等待这个 Promise 完成后再执行添加动作。
在这里插入图片描述

const MODELS = {// 直升机HELICOPTER: {name: 'helicopter',obj: 'obj/helicopterhspt_1002_01.obj',mtl: 'obj/helicopterhspt_1002_01.mtl',},// 螺旋桨PROPELLER: {name: 'propeller',obj: 'obj/helicopterhspt_1002_02.obj',mtl: 'obj/helicopterhspt_1002_02.mtl',},
};/*** 加载模型;模型初始化;创建模型Node; 添加模型到3D场景中** @return {*} */
async function createObj(name, obj, mtl) {const objInfo = await loadObj(obj, mtl, name); // 加载计量表模型,此处为异步// * @param {*} modelMap 调用ht.Default.parseObj解析后的返回值,若加载或解析失败则返回值为空// * @param {*} array 所有材质模型组成的数组// * @param {*} rawS3 包含所有模型的原始尺寸const {modelMap, array, rawS3} = objInfo;console.log('createObj: ', objInfo);if (!modelMap) {return;}// 创建 Node 用来存放该模型,后续对模型的操作通过该 Node 进行const node = new ht.Node();node.s({'shape3d': name, // 对应ht.Default.getShape3dModel(name)注册的模型'shape3d.scaleable': false});node.setSize3d(rawS3); // 存放模型在三个坐标轴方向上的大小。简写:node.s3()node.setPosition3d(0, 0, 0); // 此处可以将其放到水平面上。简写:node.p3()dm.add(node);return node;
}const helicopterNode = await createObj(MODELS.HELICOPTER.name, MODELS.HELICOPTER.obj, MODELS.HELICOPTER.mtl);
const propellerNode = await createObj(MODELS.PROPELLER.name, MODELS.PROPELLER.obj, MODELS.PROPELLER.mtl);

直升机模型分为两部分,分别是机体和螺旋桨。由于他们是两个模型,因此需要分别添加。

loadObj 结束后,HT 会将模型通过 ht.Default.setShape3dModel(name, model) 注册到内存中,之后会给 finishFunc 传递三个参数:modelMap, array, rawS3。其解释参考上面代码注释。目前我们用到的只有 rawS3 参数,也就是模型尺寸(大小)。

有了模型和尺寸(大小),我们便可以创建 ht.Node 用来对模型进行管理。将模型添加到 3D 场景中进行管理的主要逻辑如下:

模型 -(绑定到)→ ht.Node -(添加到)→ dataModel -(绑定到)→ Graph3dView

这里面的一个关键步骤是设置 ht.Nodeshape3d 属性。由于在 loadObj 的时候系统已经对模型进行注册,因此这里我们只需要通过将注册的模型名称赋值给 ht.Nodeshape3d 属性,HT 便可自动匹配到内存中对应的 OBJ 模型。

需要注意的是:在加载了模型并将模型绑定到 ht.Node 后并不能使其在 3D 场景中显示。只有通过 dataModel.add(node) 将节点添加到 3D 场景对应的数据模型中时,HT 才会在场景中将模型渲染出来。

模型位置

在上图中我们可以发现,直升机和螺旋桨重合了,并且二者也不在地面上。这里我们详细解释一下。

仔细查看代码,在创建 ht.Node 时,我们执行了下面的操作:

node.setSize3d(rawS3); // 存放模型在三个坐标轴方向上的大小。简写:node.s3()
node.setPosition3d(0, 0, 0); // 此处可以将其放到水平面上。简写:node.p3()

这两行命令分别是设置节点的大小和位置。这里的节点尺寸采用的是模型尺寸。而位置默认放到的坐标系中心点。

由于在 3D 场景中,ht.Node 的默认锚点是 [0.5, 0.5, 0.5],也就是在模型的三维中心点。因此其位置坐标也要对应到其中心点。这样,模型就会有一半在网格上方,另一半在网格下方。

该如何将直升机放到地平面上呢?我们可以通过模型的高度来计算出对应的位置从而将模型放到地平面上。具体代码如下:

// 由于默认创建 Node 的时候,其锚点是在 [0.5, 0.5, 0.5],位置是在 [0, 0, 0]。导致模型并不在水平面以上。let size3d = helicopterNode.getSize3d(); // 获取直升机模型的 [长,宽,高]let height = size3d[1]; // 获取模型高度helicopterNode.setPosition3d([0, height/2, 0]); // 将直升机放到地面上

在这里插入图片描述
而对于螺旋桨,情况又有些复杂。这里需要一些技巧才能将其配置到合适的位置。

我们通过手动调整螺旋桨来获取其应该摆放的位置和角度。这里就用到了g3d.setEditable(true)功能。打开编辑功能后,选中模型,场景中会显示坐标轴,通过拖动不同的坐标轴我们可以对模型进行移动,旋转和缩放。
在这里插入图片描述
将螺旋桨移动到机体合适的位置后,在console中通过 node.getPosition3d()node.getRotation3d() 来获取螺旋桨当前的位置和角度:
在这里插入图片描述
然后配置到代码中。与此同时,我们通过 setHost() 将螺旋桨吸附到了直升机上。这样,后面直升机移动时会带着螺旋桨移动。使二者不会脱离。

propellerNode.setRotation3d([0.10506443461595279, 4.550746858974086, -0.007825951889059535]); // 让螺旋桨水平
propellerNode.setPosition3d([0, 215, -99.00152946490829]); // 将螺旋桨放到直升机上
propellerNode.setHost(helicopterNode); // 螺旋桨吸附到直升机上

在这里插入图片描述

直升机动画

在直升机和螺旋桨都加载完成后,我们现在就可以为其增加相应的动画。
在这里插入图片描述

这里的动画分为两部分:

1. 螺旋桨旋转
2. 直升机移动

/*** 循环前进与后退** @param {*} node*/
function startAnim(node) {const p1 = node.p3(); // 原始位置const p2 = [p1[0], p1[1], p1[2] - 400]; // 目标位置,const forwardParams = {duration: 3 * 1000, // 动画帧数easing: (t) => { return t; }, // 动画缓动函数,默认采用`ht.Default.animEasing`finishFunc: () => {ht.Default.startAnim(backwardParams);// 循环播放该动画}, // 动画结束后调用的函数。action: (v, t) => { // action函数必须提供,实现动画过程中的属性变化。node.setPosition3d( // 此例子展示将节点`node`从位置`p1`动画到位置`p2`。p1[0] + (p2[0] - p1[0]) * v,p1[1] + (p2[1] - p1[1]) * v,p1[2] + (p2[2] - p1[2]) * v,);}};const backwardParams = {duration: 3 * 1000, // 动画帧数easing: (t) => { return t; }, // 动画缓动函数,默认采用`ht.Default.animEasing`finishFunc: () => {ht.Default.startAnim(forwardParams);// 循环播放该动画}, // 动画结束后调用的函数。action: (v, t) => { // action函数必须提供,实现动画过程中的属性变化。node.setPosition3d( // 此例子展示将节点`node`从位置`p1`动画到位置`p2`。p2[0] + (p1[0] - p2[0]) * v,p2[1] + (p1[1] - p2[1]) * v,p2[2] + (p1[2] - p2[2]) * v,);}};ht.Default.startAnim(forwardParams);
}/*** 螺旋桨旋转动画**/
function startPropellerAnim(node) {setInterval(() => {const r3 = node.getRotation3d();node.setRotation3d([r3[0], r3[1] + 0.4, r3[2]]); // 绕 Y 轴旋转。单位:弧度}, 20);
}

螺旋桨旋转动画比较简单。我们只需要让其绕着 y 轴转动就可以了。这里我们利用 setInterval() 起一个定时器,每隔 20 毫秒让其沿着 y 轴旋转 0.4°

关于直升机动画,我们为其找了两个点,让它在这两点之间来回移动。在动画的实现上,我们依然采用前几篇文章提到的 ht.Default.startAnim() 方法。具体实现见上面代码部分。

总结

这篇文章介绍了如何使用 HT for WebGraph3dViewOBJ 模型来创建 3D 场景。里面介绍了 3D 的一些基本概念以及 3D 场景的基本搭建与配置。另外,除了 3D 场景,我这里还重点描述了如何加载 OBJ 文件,如何添加模型节点到 3D 场景中,以及如何为节点添加动画。希望这些基本知识能对大家有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

按头安利 好看又实用的虚幻UE场景模型素材看这里

含泪整理作为游戏设计专业小伙伴们来说&#xff0c;最免费实用高质量的素材和网站&#xff01;收藏后就不用再大海捞针找素材&#xff01;赶紧码住看过来&#xff01; 爱给网 今天小编先给大家介绍爱给网-游戏-虚幻ue-场景类的模型&#xff0c;先给大家整体介绍一下爱给网~ …

Unity3d场景渲染出图

制作了精美的场景&#xff0c;需要出图给同事使用&#xff0c;还在用QQ截图&#xff1f;那渣渣的画质&#xff0c;根本满足不了需求&#xff0c;现在好了&#xff0c;有了我们的ScreenshotTaker工具&#xff0c;帮你轻松解决这个问题。 请复制代码内容到脚本中&#xff0c;并置…

【图形学】30 前向渲染多光照场景代码理解

来源&#xff1a;《UNITY SHADER入门精要》 文章目录 1、代码理解 1、代码理解 我们现在要注意光源的 5 个属性&#xff1a;位置、方向、颜色、强度、衰减。   在理解代码之前&#xff0c;我们依然需要熟悉我们的理论&#xff0c;主要我们要设置两个 Pass&#xff0c;注意它…

OpenGL的3D场景模型大作业

【题目】 opengl 3D环境开发 本文标题&#xff1a;OpenGL的3D场景模型大作业 本文链接&#xff1a;https://xygeng.cn/post/84.html 作者授权&#xff1a;除特别说明外&#xff0c;本文由 庚哥哥 原创编译并授权 木芽博客 刊载发布。 版权声明&#xff1a;本文不使用任何协议…

RealityCapture场景建模笔记

Unity Photogrammetry Workflow 5.2.8 着色和贴纹理&#xff08;Colorize or Texture&#xff09;5.2.10. 网格输出&#xff08;Mesh export&#xff09;输出附有颜色信息的Mesh/点云 5.2.8 着色和贴纹理&#xff08;Colorize or Texture&#xff09; 选择哪个选项将取决于重建…

美国藤校Top30大学对IB成绩的要求

众所周知&#xff0c;IB体系是目前全球认可度比较高的国际课程。那么&#xff0c;用IBDP成绩申请美国TOP30大学需要什么样的成绩&#xff1f; 小智今天来带大家好好研究下美国TOP30大学对IB成绩的要求。 普林斯顿大学 Princeton University 普林斯顿大学对于IB成绩没有具体要…

2023 chatgptAPI查询 原生PHP+html+js+css代码

资源介绍 单文件不压缩6KB不到,总150行原生PHP html js css代码实现查询chatgptAPI&#xff0c;并打字效果展现回复内容(sse流式消息) 使用注意填写自己的APIKEY&#xff0c;推荐美国服务器或者主机部署。适合个人自用。单文件源码&#xff01; 使用源码请先到官方申请apiKey…

推荐自媒体和文案相关神器(短视频与公众号)

一、新片场 介绍&#xff1a;新片场汇聚全球原创优质视频及创作人&#xff0c;提供4K、无广告、无水印视频观看&#xff0c;专业的视频艺术学习教程&#xff0c;正版视觉素材交易等&#xff0c;与百万创作人一起成长 网址&#xff1a;https://www.xinpianchang.com/ 图片&am…

自媒体人必看的几个文案网站,让文案创作更有色

作为自媒体人&#xff0c;无论是图文类还是视频类的&#xff0c;文案都是必不可少的&#xff0c;写出火爆的文案能为我们的内容增添色彩&#xff0c;当然这不可能一蹴而就&#xff0c;需要我们平时多看优质文案&#xff0c;尝试自己进行创作。 文案狗 文案狗是一个文案创意小工…

自媒体人绝对要知道的6款软件工具!免费文案、配音不在话下

NO.1丨喵盐配音&#xff08;小程序&#xff09; 喵盐配音&#xff0c;它是我近期使用次数较多的配音小程序。这是一款专注于文字转语音的智能语音合成小程序&#xff0c;不需要下载&#xff0c;v小橙序搜索在线使用。其拥有200多个抖音热门发音人&#xff0c;支持普通话、英语、…

chatgpt赋能python:Python代码报错?别慌!这些处理技巧教你轻松解决

Python代码报错&#xff1f;别慌&#xff01;这些处理技巧教你轻松解决 作为一名有10年python编程经验的工程师&#xff0c;我深知遇到代码报错时的无助感。代码中报错似乎总会出现在最需要正常运行的时候。但是&#xff0c;不要慌&#xff01;在这篇文章中&#xff0c;我将分…

商汤科技2020笔试题

题型分为20道选择题和3道编程大题。 1.选择题 struct1与struct2的区别 一个是Stuts1 &#xff0c;一个是Stuts2&#xff0c;这是最大的区别&#xff0c;技术方面&#xff0c;Stuts1有个核心控制器&#xff0c;但是只提供了一个接口&#xff0c;也就是execute&#xff0c;还要配…

四面楚歌,商汤科技该如何在AI领域破局

https://www.toutiao.com/a6663198506368369159/ 文/于斌 在经历了多轮业内数额第一的融资之后&#xff0c;国内人工智能创业公司商汤科技再次传出了融资消息。这次据悉准备融资20亿美元。此前在经过软银中国投资10亿美元之后&#xff0c;商汤科技的估值已经达到60亿美元&…

商汤科技面试准备

商汤科技面试准备 秋招收到了东软医疗算法&#xff0c;明略科技算法&#xff0c;云从算法的Offer 但是在我沉淀了整个求职季之后 心心念念的商汤&#xff0c;终终终终终终终终终于给我发起面试邀请了 商汤一直是我最想进的企业&#xff0c;没有之一 一些长久以来&#xff0c;秋…

要做中国OpenAI的很多,但智源要打造大模型领域的Linux

衡宇 发自 凹非寺量子位 | 公众号 QbitAI “要做中国的OpenAI&#xff01;”“要打造中国版ChatGPT&#xff01;”ChatGPT的火以燎原之势蔓延到每一个角落。 人们目光的焦点聚集到这个具体的现象级应用&#xff0c;或者其它大模型支撑的类ChatGPT产品上。大模型似乎成为了人人都…

通过配置不当的微软app劫持Bing 搜索结果,获得4万美元漏洞奖励

聚焦源代码安全&#xff0c;网罗国内外最新资讯&#xff01; 编译&#xff1a;代码卫士 一款配置不当的微软应用可使任何人登录并实时修改 Bing.com 搜索结果以及注入 XSS 攻击&#xff0c;攻陷 Office 365 用户的账号。 Wiz公司的研究人员发现了该问题并将其描述为“BingBang”…

解读ChatGPT背后的技术重点:RLHF、IFT、CoT、红蓝对抗

近段时间&#xff0c;ChatGPT 横空出世并获得巨大成功&#xff0c;使得 RLHF、SFT、IFT、CoT 等这些晦涩的缩写开始出现在普罗大众的讨论中。这些晦涩的首字母缩略词究竟是什么意思&#xff1f;为什么它们如此重要&#xff1f;我们调查了相关的所有重要论文&#xff0c;以对这些…

谷歌优化扩散模型!在三星手机上运行Stable Diffusion,12秒内出图!

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【扩散模型】微信技术交流群 转载自&#xff1a;机器之心 | 编辑&#xff1a;陈萍、小舟 Speed Is All You Need&#xff1a;谷歌提出针对 Stable Diffusion 一些优化建议&am…

再次加入OpenAI,特斯拉前AI总监Andrej Karpathy刚刚官宣!

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>CV微信技术交流群 转载自&#xff1a;机器之心 官宣回归OpenAI&#xff0c;特斯拉前AI高级总监Andrej Karpathy的下家定了。 刚刚&#xff0c;特斯拉前 AI 高级总监 Andrej K…

行业前沿 | 畅想AIGC技术研究与应用实践(一)

AIGC的火热之势已经蔓延到各行各业&#xff0c;技术浪潮冲击下&#xff0c;关于AIGC创业、AIGC未来发展方向以及AIGC的应用实践等&#xff0c;企业、投资者以及技术产品专家们有着不一样的思考。 近日&#xff0c;数美科技联合AMD举办的AIGC沙龙活动&#xff0c;邀请了经纬创投…