版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、osg开源教程整理:荣明、王伟北 京 2008年4月序第一次接触osg是在2001年,当时开源社区刚刚兴起,还没有现在这么火。下载了osg源码,但是在看了几个demo之后,感觉没有什么特别之处。时隔七年之后,我再次将目光投向osg,发现osg确实有其独到之处,很多3d效果已经不弱于甚至超过商业软件,有感于开源力量的巨大。但是,与当前主流3d商业软件如vega、vegaprime、vtree、performer等相比,开源软件的缺点也很明显,其中文档缺乏可谓其致命弱点之一。开发者只能从浩瀚的源码中,进行编程的学习,效率不高。osg组织也意识到这一点,不断推出一些官方教程,网络上有很多osg爱好者
2、发布的心得和教程。我收集、整理了许多学习资料,其中美国海军研究生院发布的osg教程非常好,可作为osg的官方教程的一个很好补充。它共有十一个专题,结合例子程序,一步步教你如何进行osg的开发。我将其编辑成一个较为完善的教材,供大家学习。在教材整理过程中,王伟调试了源程序,对该书编辑和修订做了大量的工作;实验室的学生杨宇蒙、冯锐、章仕锋、李永川和李益强阅读了本书,并提出了宝贵的修改意见,在此一并表示感谢。希望这本小册子能对大家学习osg编程起到一定的帮助作用。谢谢大家阅读本书! 荣 明二oo八年四月于崔春园email:rong_(本书的资料全部来自互联网,仅供个人学习交流
3、使用。感谢竹林小舍、array翻译了navy的教程。)目录1.使用open scene graph几何11.1背景11.2代码12.使用stateset产生有纹理的几何体42.1本章目标42.2背景42.3加载纹理,生成状态集合并将他们附加到节点上63.使用shape,改变state83.1本章目标83.2使用shape类83.3设置状态94.更多的stateset104.1stateset如何工作104.2例子及代码105.从文件中加载模型并放入到场景中125.1本章目标125.2加载几何模型并加入到场景中126.osg text、hud、renderbins156.1本章目标156.2摘要
4、156.3代码157.搜索并控制开关和dof(自由度)节点(finding and manipulating a switch and dof node)207.1搜索场景图形中的一个有名节点207.2按照“访问器”模式搜索有名节点228.使用更新回调来更改模型268.1本章目标268.2回调概览268.3创建一个更新回调269.处理键盘输入299.1本章目标299.2 gui(图形用户接口)事件处理器:299.3简单的键盘接口类309.4使用键盘接口类329.5处理键盘输入实现更新回调329.5.1本节目标329.5.2问题的提出329.5.3解决方案3310.使用自定义矩阵来放置相机(po
5、sitioning a camera with a user-defined matrix)3610.1本章目标3610.2设置矩阵的方向和位置3610.3声明一个用于设置相机的矩阵3710.4使用矩阵设置视口摄相机3811. 实现跟随节点的相机3811.1本章目标3811.2概述3911.3实现3911.4环绕(始终指向)场景中节点的相机4211.4.1本节目标4211.4.2实现431.使用open scene graph几何本节涵盖了生成基本几何形状的一些方法。生成几何物体的方法有这么几种:在最底层对opengl基本几何进行松散的包装,中级是使用open scene graph的基本形状
6、,以及更高级一些的从文件读取。这篇教程涵盖的是最低层的。这种方法弹性最大但最费力。通常在scene graph级别,几何形状是从文件加载的。文件加载器完成了跟踪顶点的大部分工作。1.1背景对一下几个类的简单解释:geode类: geode类继承自node类。在一个scene graph中,node(当然包含geode)可以作为叶子节点。geode实例可以有多个相关的drawable。drawable类层次: 基类drawable是一个有六个具体子类的抽象类。 geometry类可以直接有vertex和vertex数据,或者任意个primitiveset实例。 vertex和vertex属性数据
7、(颜色、法线、纹理坐标)存放在数组中。既然多个顶点可以共享相同的颜色、法线或纹理坐标,那么数组索引就可以用来将顶点数组映射到颜色、法线、或纹理坐标数组。primitiveset类: 这个类松散的包装了opengl的基本图形points,lines,line_strip,line_loop,.,polygon. 1.2代码以下这节代码安装了一个viewer来观察我们创建的场景,一个group实例作为scene graph的根节点,一个几何节点(geode)来收集drawable,和一个geometry实例来关联顶点和顶点数据。(这个例子中渲染的形状是一个四面体).int main() .osgp
8、roducer:viewer viewer; osg:group* root = new osg:group(); osg:geode* pyramidgeode = new osg:geode(); osg:geometry* pyramidgeometry = new osg:geometry();下一步,需要将锥体geometry和锥体geode关联起来,并将pyramid geode加到scene graph的根节点上。pyramidgeode-adddrawable(pyramidgeometry); root-addchild(pyramidgeode);声明一个顶点数组。每个顶点
9、由一个三元组表示vec3类的实例。这些三元组用osg:vec3array类的实例存贮。既然osg:vec3array继承自stl的vector类,那么我们就可以使用push_back方法来添加数组成员。push_back将元素加到向量的尾端,因此第一个元素的索引是0,第二个是1,依此类推。 使用z轴向上的右手坐标系系统,下面的0.4数组元素代表着产生一个简单锥体所需的5个点。osg:vec3array* pyramidvertices = new osg:vec3array; pyramidvertices-push_back( osg:vec3( 0, 0, 0) ); / front le
10、ft pyramidvertices-push_back( osg:vec3(10, 0, 0) ); / front right pyramidvertices-push_back( osg:vec3(10,10, 0) ); / back right pyramidvertices-push_back( osg:vec3( 0,10, 0) ); / back left pyramidvertices-push_back( osg:vec3( 5, 5,10) ); / peak 将这个顶点集合和与我们加到场景中的geode相关的geometry关联起来。pyramidgeometry-s
11、etvertexarray( pyramidvertices );下一步,产生一个基本集合并将其加入到pyramid geometry中。使用pyramid的前四个点通过drawelementsuint类的实例来定义基座。这个类也继承自stl的vector,所以push_back方法会顺序添加元素。为了保证合适的背面剔除,顶点的顺序应当是逆时针方向的。构造器的参数是基本的枚举类型(和opengl的基本枚举类型一致),和起始的顶点数组索引。osg:drawelementsuint* pyramidbase = new osg:drawelementsuint(osg:primitiveset:q
12、uads, 0); pyramidbase-push_back(3); pyramidbase-push_back(2); pyramidbase-push_back(1); pyramidbase-push_back(0); pyramidgeometry-addprimitiveset(pyramidbase);对每个面重复相同的动作。顶点仍要按逆时针方向指定。osg:drawelementsuint* pyramidfaceone = new osg:drawelementsuint(osg:primitiveset:triangles, 0); pyramidfaceone-push_
13、back(0); pyramidfaceone-push_back(1); pyramidfaceone-push_back(4); pyramidgeometry-addprimitiveset(pyramidfaceone); osg:drawelementsuint* pyramidfacetwo = new osg:drawelementsuint(osg:primitiveset:triangles, 0); pyramidfacetwo-push_back(1); pyramidfacetwo-push_back(2); pyramidfacetwo-push_back(4); p
14、yramidgeometry-addprimitiveset(pyramidfacetwo); osg:drawelementsuint* pyramidfacethree = new osg:drawelementsuint(osg:primitiveset:triangles, 0); pyramidfacethree-push_back(2); pyramidfacethree-push_back(3); pyramidfacethree-push_back(4); pyramidgeometry-addprimitiveset(pyramidfacethree); osg:drawel
15、ementsuint* pyramidfacefour = new osg:drawelementsuint(osg:primitiveset:triangles, 0); pyramidfacefour-push_back(3); pyramidfacefour-push_back(0); pyramidfacefour-push_back(4); pyramidgeometry-addprimitiveset(pyramidfacefour)声明并加载一个vec4为元素的数组来存储颜色。osg:vec4array* colors = new osg:vec4array; colors-pu
16、sh_back(osg:vec4(1.0f, 0.0f, 0.0f, 1.0f) ); /index 0 red colors-push_back(osg:vec4(0.0f, 1.0f, 0.0f, 1.0f) ); /index 1 green colors-push_back(osg:vec4(0.0f, 0.0f, 1.0f, 1.0f) ); /index 2 blue colors-push_back(osg:vec4(1.0f, 1.0f, 1.0f, 1.0f) ); /index 3 white声明的这个变量可以将顶点数组元素和颜色数组元素匹配起来。这个容器的元素数应当和顶点
17、数一致。这个容器是顶点数组和颜色数组的连接。这个索引数组中的条目就对应着顶点数组中的元素。他们的值就是颜色数组中的索引。顶点数组元素与normal和纹理坐标数组的匹配也是遵循这种模式。 注意,这种情况下,我们将5个顶点指定4种颜色。顶点数组的0和4元素都被指定为颜色数组的0元素。osg:templateindexarray *colorindexarray; colorindexarray = new osg:templateindexarray; colorindexarray-push_back(0); / vertex 0 assigned color array element 0 c
18、olorindexarray-push_back(1); / vertex 1 assigned color array element 1 colorindexarray-push_back(2); / vertex 2 assigned color array element 2 colorindexarray-push_back(3); / vertex 3 assigned color array element 3 colorindexarray-push_back(0); / vertex 4 assigned color array element 0下一步,将颜色数组和geom
19、etry关联起来,将上面产生的颜色索引指定给geometry,设定绑定模式为_per_vertex。pyramidgeometry-setcolorarray(colors); pyramidgeometry-setcolorindices(colorindexarray); pyramidgeometry-setcolorbinding(osg:geometry:bind_per_vertex); osg:vec2array* texcoords = new osg:vec2array(5); (*texcoords)0.set(0.00f,0.0f); (*texcoords)1.set(
20、0.25f,0.0f); (*texcoords)2.set(0.50f,0.0f); (*texcoords)3.set(0.75f,0.0f); (*texcoords)4.set(0.50f,1.0f); pyramidgeometry-settexcoordarray(0,texcoords);注:一下部分可能错误。/ declare and initialize a transform node. osg:positionattitudetransform* pyramidtwoxform = new osg:positionattitudetransform(); / use th
21、e addchild method of the osg:group class to / add the transform as a child of the root node and the / pyramid node as a child of the transform. root-addchild(pyramidtwoxform); pyramidtwoxform-addchild(pyramidgeode); / declare and initialize a vec3 instance to change the / position of the tank model
22、in the scene osg:vec3 pyramidtwoposition(15,0,0); pyramidtwoxform-setposition( pyramidtwoposition );既然我们生成了一个geometry节点并将它加到了场景中,我们就可以重用这个geometry。例如,如果我们想让另一个pyramid在第一个的右侧15个单位处,我们就可以在我们的scene graph中将这个geode加到transform节点的子节点上。 最后一步,建立并进入一个仿真循环。viewer.setupviewer(osgproducer:viewer:standard_setting
23、s); viewer.setscenedata( root ); viewer.realize(); while( !viewer.done() ) viewer.sync(); viewer.update(); viewer.frame(); 2.使用stateset产生有纹理的几何体2.1本章目标为教程1中介绍的由opengl基本绘制单位定义的几何体添加纹理。2.2背景前一节教程介绍了包含由opengl基本单位产生的基本形状的视景。本节讲解如何为这些形状添加纹理。为了使代码更方便使用,我们将pyramid的代码放到一个函数中,产生geode并返回它的指针。下面的代码来自教程1。osg:ge
24、ode* createpyramid() osg:geode* pyramidgeode = new osg:geode(); osg:geometry* pyramidgeometry = new osg:geometry(); pyramidgeode-adddrawable(pyramidgeometry); / specify the vertices: osg:vec3array* pyramidvertices = new osg:vec3array; pyramidvertices-push_back( osg:vec3(0, 0, 0) ); / front left pyra
25、midvertices-push_back( osg:vec3(2, 0, 0) ); / front right pyramidvertices-push_back( osg:vec3(2, 2, 0) ); / back right pyramidvertices-push_back( osg:vec3( 0,2, 0) ); / back left pyramidvertices-push_back( osg:vec3( 1, 1,2) ); / peak / associate this set of vertices with the geometry associated with
26、 the / geode we added to the scene. pyramidgeometry-setvertexarray( pyramidvertices ); / create a quad primitive for the base by specifying the / vertices from our vertex list that make up this quad: osg:drawelementsuint* pyramidbase = new osg:drawelementsuint(osg:primitiveset:quads, 0); pyramidbase
27、-push_back(3); pyramidbase-push_back(2); pyramidbase-push_back(1); pyramidbase-push_back(0); /add this primitive to the geometry: pyramidgeometry-addprimitiveset(pyramidbase); / code to create other faces goes here! / (removed to save space, see tutorial two) osg:vec4array* colors = new osg:vec4arra
28、y; colors-push_back(osg:vec4(1.0f, 0.0f, 0.0f, 1.0f) ); /index 0 red colors-push_back(osg:vec4(0.0f, 1.0f, 0.0f, 1.0f) ); /index 1 green colors-push_back(osg:vec4(0.0f, 0.0f, 1.0f, 1.0f) ); /index 2 blue colors-push_back(osg:vec4(1.0f, 1.0f, 1.0f, 1.0f) ); /index 3 white osg:templateindexarray *colo
29、rindexarray; colorindexarray = new osg:templateindexarray; colorindexarray-push_back(0); / vertex 0 assigned color array element 0 colorindexarray-push_back(1); / vertex 1 assigned color array element 1 colorindexarray-push_back(2); / vertex 2 assigned color array element 2 colorindexarray-push_back
30、(3); / vertex 3 assigned color array element 3 colorindexarray-push_back(0); / vertex 4 assigned color array element 0 pyramidgeometry-setcolorarray(colors); pyramidgeometry-setcolorindices(colorindexarray); pyramidgeometry-setcolorbinding(osg:geometry:bind_per_vertex); / since the mapping from vert
31、ices to texture coordinates is 1:1, / we dont need to use an index array to map vertices to texture / coordinates. we can do it directly with the settexcoordarray / method of the geometry class. / this method takes a variable that is an array of two dimensional / vectors (osg:vec2). this variable ne
32、eds to have the same / number of elements as our geometry has vertices. each array element / defines the texture coordinate for the cooresponding vertex in the / vertex array. osg:vec2array* texcoords = new osg:vec2array(5); (*texcoords)0.set(0.00f,0.0f); / tex coord for vertex 0 (*texcoords)1.set(0
33、.25f,0.0f); / tex coord for vertex 1 (*texcoords)2.set(0.50f,0.0f); / (*texcoords)3.set(0.75f,0.0f); / (*texcoords)4.set(0.50f,1.0f); / pyramidgeometry-settexcoordarray(0,texcoords); return pyramidgeode; 2.3加载纹理,生成状态集合并将他们附加到节点上渲染基本单位的方法是使用stateset。这节代码演示了怎样从文件中加载纹理,产生此纹理起作用的一个stateset,并将这个stateset附
34、加到场景中的一个节点上。前面开始的代码和上一节教程中的一样,初始化一个viewer并建立有一个pyramid的场景。int main() osgproducer:viewer viewer; / declare a group to act as root node of a scene: osg:group* root = new osg:group(); osg:geode* pyramidgeode = createpyramid(); root-addchild(pyramidgeode);现在,准备加纹理。这里我们会声明一个纹理实例并将它的数据不一致性设为dynamic。(如果不把纹
35、理声明为dynamic,osg的一些优化程序会删除它。)这个texture类包装了opengl纹理模式(wrap,filter,等等)和一个osg:image。下面的代码说明了如何从文件里读取osg:image实例并把这个图像和纹理关联起来。osg:texture2d* kln89facetexture = new osg:texture2d; / protect from being optimized away as static state: kln89facetexture-setdatavariance(osg:object:dynamic); / load an image by
36、reading a file: osg:image* klnface = osgdb:readimagefile(kln89faceb.tga); if (!klnface) std:cout couldnt find texture, quiting. setimage(klnface);纹理可以和渲染stateset关联起来。下一步就产生一个stateset,关联并启动我们的纹理,并将这个stateset附加到我们的geometry上。/ create a new stateset with default settings: osg:stateset* stateone = new os
37、g:stateset(); / assign texture unit 0 of our new stateset to the texture / we just created and enable the texture. stateone-settextureattributeandmodes(0,kln89facetexture,osg:stateattribute:on); / associate this state set with the geode that contains / the pyramid: pyramidgeode-setstateset(stateone)
38、;最后一步是仿真循环:/the final step is to set up and enter a simulation loop. viewer.setupviewer(osgproducer:viewer:standard_settings); viewer.setscenedata( root ); viewer.realize(); while( !viewer.done() ) viewer.sync(); viewer.update(); viewer.frame(); return 0; 3.使用shape,改变state3.1本章目标用osg:shape实例构建场景。使用o
39、sg:stateset控制shape的渲染。3.2使用shape类shape类是所有形状类别的基类。shape既可用于剪裁和碰撞检测也可用于定义程序性地产生几何体的那些基本形状。下面的类继承自shape类: trianglemesh sphere infiniteplane heightfield cylinder cone compositeshape box 为了使这些形状可以被渲染,我们需要把他们和drawable类的实例关联起来。shapedrawable类提供了这样的功能。这个类继承自drawable并允许我们把shape实例附加到可以被渲染的东西上。既然shapedrawable类
40、继承自drawable,shapdrawable实例就可以被加到geode类实例上。下面的步骤演示了将一个单位立方体加到空场景中时是如何做到这些的。/ declare a group to act as root node of a scene: osg:group* root = new osg:group(); / declare a box class (derived from shape class) instance / this constructor takes an osg:vec3 to define the center / and a float to define t
41、he height, width and depth. / (an overloaded constructor allows you to specify unique / height, width and height values.) osg:box* unitcube = new osg:box( osg:vec3(0,0,0), 1.0f); / declare an instance of the shape drawable class and initialize / it with the unitcube shape we created above. / this cl
42、ass is derived from drawable so instances of this / class can be added to geode instances. osg:shapedrawable* unitcubedrawable = new osg:shapedrawable(unitcube); / declare a instance of the geode class: osg:geode* basicshapesgeode = new osg:geode(); / add the unit cube drawable to the geode: basicsh
43、apesgeode-adddrawable(unitcubedrawable); / add the goede to the scene: root-addchild(basicshapesgeode); 产生一个球体和上面的代码基本相似。没有太多的注释的代码看起来是这个样子:/ create a sphere centered at the origin, unit radius: osg:sphere* unitsphere = new osg:sphere( osg:vec3(0,0,0), 1.0); osg:shapedrawable* unitspheredrawable=new
44、 osg:shapedrawable(unitsphere); 现在,我们可以使用transform节点将这个球体加到场景中,以便让它离开原点。unitspheredrawable不能直接添加到场景中(因为它不是继承自node类),所以我们需要一个新的geode以便添加它。osg:positionattitudetransform* spherexform = new osg:positionattitudetransform(); spherexform-setposition(osg:vec3(2.5,0,0); osg:geode* unitspheregeode = new osg:g
45、eode(); root-addchild(spherexform); spherexform-addchild(unitspheregeode); unitspheregeode-adddrawable(unitspheredrawable);3.3设置状态前面的教程讲解了如何生成纹理,将其指定为从文件加载的图像,生成一个带纹理的stateset。下面的代码建立了两个状态集合一个是blend纹理模式,另一个是decal纹理模式。blend模式:/ declare a state set for blend texture mode osg:stateset* blendstateset =
46、new osg:stateset(); / declare a texenv instance, set the mode to blend osg:texenv* blendtexenv = new osg:texenv; blendtexenv-setmode(osg:texenv:blend); / turn the attribute of texture 0 - the texture we loaded above - on blendstateset-settextureattributeandmodes(0,kln89facetexture,osg:stateattribute
47、:on); / set the texture texture environment for texture 0 to the / texture envirnoment we declared above: blendstateset-settextureattribute(0,blendtexenv); 重复这些步骤,产生decal纹理模式的状态集合。osg:stateset* decalstateset = new osg:stateset();osg:texenv* decaltexenv = new osg:texenv(); decaltexenv-setmode(osg:tex
48、env:decal); decalstateset-settextureattributeandmodes(0,kln89facetexture,osg:stateattribute:on); decalstateset-settextureattribute(0,decaltexenv);产生了状态集合后我们就可以把它们应用在场景中的节点上。在scene graph的绘制遍历(root-leaf)中状态是积累的。除非这个节点有一个它自己的状态,否则它会继承其父节点的状态。(如果一个节点有一个以上的父节点,它会使用一个以上的状态渲染。)root-setstateset(blendstatese
49、t); unitspheregeode-setstateset(decalstateset); 最后一步是进入仿真循环。viewer.setupviewer(osgproducer:viewer:standard_settings); viewer.setscenedata( root ); viewer.realize(); while( !viewer.done() ) viewer.sync(); viewer.update(); viewer.frame(); return 0;4.更多的stateset4.1stateset如何工作场景图管理器遍历scene graph,决定哪些几何
50、体需要送到图形管道渲染。在遍历的过程中,场景图管理器也搜集几何体如何被渲染的信息。这些信息存在osg:stateset实例中。stateset包含opengl的属性/数值对列表。这些stateset可以和scenegraph中的节点关联起来。在预渲染的遍历中,stateset从根节点到叶子节点是积累的。一个节点的没有变化的状态属性也是简单的遗传自父节点。 几个附加的特性允许更多的控制和弹性。一个状态的属性值可以被设为override。这意味着这个节点的所有孩子节点不管其属性值是什么会遗传其父节点的属性值。override意味着可以被覆盖。如果一个孩子节点把那个属性设为protected,那么它
51、就可以设置自己的属性值,而不用理会父节点的设置。4.2例子及代码下面的场景示范了state如何影响scene graph。根节点有一个blend模式纹理的基本状态。这个基本状态会被所有子节点继承,除非这个状态的参数改变。根节点的右子树就是这样的。右孩子没有被指定任何状态,所以它使用和根节点一样的状态来渲染。对于节点6,纹理的混合模式没有改变但是使用了一个新纹理。 根节点的左子树的纹理模式被设为decal。其他的参数是一样的,所以它们从根节点继承。在节点3中,fog被打开了并设置为override。这个节点节点3的左孩子将fog设为了off。既然它没有设置protected并且它父节点设置了ov
52、erride,所以就像父节点一样fog仍然是on。右孩子4设置fog属性值为protected,因此可以覆盖其父节点的设置。a.jpg (86.96 kb)2007-11-23 01:05 pm下面是操作状态配置并用节点将这些状态关联起来的代码。/ set an osg:texenv instances mode to blend, / make this texenv current for texture unit 0 and assign / a valid texture to texture unit 0 blendtexenv-setmode(osg:texenv:blend);
53、staterootblend-settextureattribute(0,blendtexenv,osg:stateattribute:on); staterootblend-settextureattributeandmodes(0,ocotillotexture,osg:stateattribute:on); / for state five, change the texture associated with texture unit 0 /all other attributes will remain unchanged as inherited from above. / (te
54、xture mode will still be blend) statefivedusttexture-settextureattributeandmodes(0,dusttexture,osg:stateattribute:on); / set the mode of an osg:texenv instance to decal /use this mode for stateonedecal. decaltexenv-setmode(osg:texenv:decal); stateonedecal-settextureattribute(0,decaltexenv,osg:statea
55、ttribute:on); / for statetwo, turn fog off and set to override. /descendants in this sub-tree will not be able to change fog unless /they set the fog attribute value to protected statetwofogon_ovrd-setattribute(fog, osg:stateattribute:on); statetwofogon_ovrd-setmode(gl_fog, osg:stateattribute:on | osg:stateattribute:override); / for statethree, try to turn fog off.since the attribute is not protect
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度人工智能企业实习生保密协议与AI算法保护合同3篇
- 家居装修合同签订与维权考核试卷
- 酒店宾馆装修合同新
- 南京市建筑装饰装修合同
- 单间出租合同
- 公司合并协议范本
- 2025年全球及中国插座式电源电涌保护器行业头部企业市场占有率及排名调研报告
- 2025-2030全球便携式四合一气体检测仪行业调研及趋势分析报告
- 2025年全球及中国手持式热合机行业头部企业市场占有率及排名调研报告
- 2024年度河南省国家保安员资格考试题库练习试卷B卷附答案
- 三年级数学寒假作业每日一练30天
- 移动商务内容运营(吴洪贵)任务三 APP的品牌建立与价值提供
- 电子竞技范文10篇
- 人美版初中美术知识点汇总九年级全册
- 食堂服务质量控制方案与保障措施
- VI设计辅助图形设计(2022版)
- 眼科学常考简答题
- 物料分类帐的应用
- 乳房整形知情同意书
- 2022-2023年人教版九年级物理上册期末考试(真题)
- 根因分析(huangyan)课件
评论
0/150
提交评论