精华内容
下载资源
问答
  • [数据库]数据库存储层级结构数据

    千次阅读 2014-06-03 21:21:02
    无论你想建立自己的论坛,在你的网站上从个邮件列表发布消息,或编写自己的cms:你将会遇到种情况:将分层数据(层级结构)存储在数据库中。除非你使用个类似xml的数据库,通用的关系型数据库是很难做到这一点。...

    无论你想建立自己的论坛,在你的网站上从一个邮件列表发布消息,或编写自己的cms:你将会遇到一种情况:将分层数据(层级结构)存储在数据库中。除非你使用一个类似xml的数据库,通用的关系型数据库是很难做到这一点。关系型数据库中的表不分层;他们只是一个简单列表。你必须找到一个方法来把层次结构数据转换为一个简单文件数据。

    存储树是一种常见的问题,可以有多个解决方案来实现。有两种主要方法:邻接表模型和修改后的树前序遍历的算法。

    在本文中,我们将探讨这两种方法来存储层级结构数据。我将使用从一个虚构的在线食品商店虚拟的这棵树为例。这个食品商店通过类别、颜色以及种类来来组织它的食品。

    这颗树如下:

                     

    这篇文章包含一些代码示例展示了如何保存和检索数据。原文是使用的PHP,在这我将使用java来演示它的代码展示。

    [一] 邻接列表模型 

    我们最先尝试或者最优雅的方式我们称为“邻接表模式”或者我们称它为“递归方法”。

    这是一种优雅的方法,因为你只需要一个简单的函数就可以遍历树。在我们的食品商店的例子中,邻接表的表可以这样表示:

       

    如您所见,在邻接表的方法,你只保存每个节点的父节点。我们可以从表上清楚的看到,“Pear”是“Green”的一个孩子,同时“Green”又是'Fruit'的孩子。

    根节点“Food”没有父节点。为简单起见,我使用了“标题”值来确定每个节点。当然,在一个真正的数据库,可以使用id来标示。

      

    Give Me the Tree

    既然我们已经在数据库中插入我们的树,是时候写一个显示功能。这个函数将不得不从根节点(没有父节点)开始,应该显示所有该节点的孩子。对于这些孩子节点来说,这个函数应该检索和显示这些孩子节点的所有子节点。然后在显示这些孩子节点的子节点,递归的进行。

    public void DisplayTree(int parentId,int level){
    		String sql = "select NodeId,NodeName,ParentId from Tree where parentid="+ parentId;
    		conn = sqlManager.GetConn();
    		try {
    			statement = conn.createStatement();
    			ResultSet rs = statement.executeQuery(sql);
    			while(rs.next()){
    				for(int i = 0;i < level*2;i++){
    					System.out.print("-");
    				}
    				System.out.println(rs.getString("NodeName"));
    				DisplayTree(rs.getInt("NodeId"),level+1);
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}

    注:sqlManager类是数据库操作类。

    数据库存储的数据:

    打印整个树

    DisplayTree(0,0)

    结果:


    如果你只是想看到一个子树,你可以告诉函数这个开始节点的Id即可。

    例如,显示“Fruit”子树,运行DisplayTree(2,0);

    The Path to a Node

    有时候我们需要知道某个节点所在的路径。举例来说,“Cherry”所在的路径为Food > Fruit > Red > Cherry。在这里,我们可以从Cherry开始查起,然后递归查询查询节点前的节点,直到某节点的父节点ID为0。

    //获取某个节点所在路径
    	public ArrayList<String> GetPath(int nodeId){
    		String sql = "select ParentId,NodeName from Tree where NodeId = "+nodeId;
    		ArrayList<String> path = new ArrayList<String>();
    		try {
    			conn = sqlManager.GetConn();
    			statement = conn.createStatement();
    			ResultSet rs = statement.executeQuery(sql);
    			rs.next();
    			int parentId = rs.getInt("ParentId");
    			String nodeName = rs.getString("NodeName");
    			if(parentId != 0){
    				path.addAll(GetPath(parentId));
    			}
    			path.add(nodeName);
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		return path;
    	}

    ArrayList<String> path = main.GetPath(9);
    		int index = 0;
    		for (String node : path) {
    			if(index != 0){
    				System.out.print("->");
    			}
    			System.out.print(node);
    			index++;
    		}


    id= 9 对应为Banana  路径为:Food->Fruit->Yellow->Banana


    优缺点
    我们可以看到,用邻接表模型确实是个不错的方法。它简单易懂,而且实现的代码写起来也很容易。那么,缺点是什么呢?那就是,在大多数语言中,邻接表模型执行起来效率低下和缓慢。这主要是由于递归引起的,我们查询树中的每个节点的时候都需要进行依次数据库查询。因为每个查询需要一些时间,这使得函数非常缓慢的在处理大树时。

    第二个原因是在你可能使用的编程语言这个方法不是那么快,。对于一门程序语言来说,除了Lisp这种,大多数不是为了递归而设计。当一个节点深度为4时,它得同时生成4个函数实例,它们都需要花费时间、占用一定的内存空间。所以,邻接表模型效率的低下可想而知。

    [二] 树前序遍历的算法变形

    那么就让我们来看另外一种存储树形结构的方法。如之前所讲,我们希望能够减少查询的数量,最好是只做到查询一次数据库。

    现在我们把树“横”着放。如下图所示,我们首先从根节点(“Food”)开始,先在它左侧标记“1”,然后我们到“Fruit”,左侧标记“2”,接着按照前序遍历的顺序遍历完树,依次在每个节点的左右侧标记数字。最后一个数字写在“Food”节点右边的。在这张图片里,你可以看到用数字标记的整个树和几个箭头指示编号顺序。


    我们叫这些数字为Left和Right(例如“Food”的Left值是1,Right值是18)。正如你所看到的,这些数字表示每个节点之间的关系。

    比如,“Red”节点左边的数为3、右边的数为6,它是Food(1-18)的后代。同样的,我们可以注意到,左数大于2、右数小于11的节点都是“Fruit”的子孙。现在,所有的节点将以左数-右数的方式存储,这种通过遍历一个树、然后给每一个节点标注左数、右数的方式称为修改过的前序遍历算法。

    在我们继续之前,让我们来看看在我们的表的这些值:


    注意,“Left”和“Right”在SQL中有特殊的意义。因此,我们必须使用“lft”和“rgt”标识列。还要注意,我们并不真的需要“parent”列了。我们现在有lft和rgt值存储树结构。

    Retrieve the Tree

    如果你想使用一个带有左值和右值的表显示树与,你首先要确定你想要检索的节点。例如,如果您希望检索“Fruit”子树,你将不得不选择左值在2至11之间的那些节点。
    sql语句:

    SELECT * FROM FoodTree WHERE Lft BETWEEN 2 AND 11;

    注:FoodTree是存储数据的表

    返回:


    整个树只需要一次查询。

    显示这棵树就像我们做递归函数,我们将不得不ORDER BY子句添加到查询语句中。如果你从你的表添加和删除行,你的表可能不会以正确的顺序存储。我们应该按左值进行排序。

    SELECT * FROM FoodTree WHERE Lft BETWEEN 2 AND 11 ORDER BY Lft ASC;

    现在唯一的问题是缩进问题。

    显示树结构,孩子应该缩进略高于他们的父母。这里,我们可以维护一个只保存右数的栈。每次你从一个节点的孩子节点开始,你将该节点的Rgt值添加到堆栈中。我们知道该节点所有的孩子的Rgt值小于父节点的Rgt值,所以通过比较当前节点的Rgt值和堆栈中最后一个节点的Rgt值。当当前节点的Rgt值大于栈顶元素的值(说明栈顶元素的子树都以遍历完毕),这个时候弹出栈顶值。再循环检查栈顶值,直到栈顶值小于当前查询节点的Rgt值。这个时候只要检查栈中元素,有多少个元素说明当前查询节点有多少个祖先节点代码如下:

    public void DisplayTree2(String nodeName) {
    		String sql = "select Lft,Rgt from FoodTree where NodeName ='" + nodeName+"'";
    		conn = sqlManager.GetConn();
    		Stack<Integer> RgtStack = new Stack<Integer>();
    		try {
    			statement = conn.createStatement();
    			ResultSet rs = statement.executeQuery(sql);
    			rs.next();
    			int lft = rs.getInt("Lft");
    			int rgt = rs.getInt("Rgt");
    			
    			sql = "select NodeName,Lft,Rgt from FoodTree where Lft between "+lft+" and "+rgt +" order by Lft asc";
    			
    			statement = conn.createStatement();
    			ResultSet rs2 = statement.executeQuery(sql);
    			
    			while (rs2.next()) {
    				int rightValue = rs2.getInt("Rgt");
    				if(RgtStack.size() > 0){
    					//栈顶元素
    					int cur = RgtStack.peek();
    					while(cur < rightValue){
    						RgtStack.pop();
    						if(RgtStack.size() > 0){
    							cur = RgtStack.peek();
    						}
    						else{
    							break;
    						}
    					}
    				}
    				for(int i = 0;i < RgtStack.size()*2;i++){
    					System.out.print("-");
    				}
    				System.out.println(rs2.getString("NodeName"));
    				RgtStack.push(rightValue);
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}

    运行代码,打印结果和之前邻接表模型打印的结果一样。但是新方法更快,原因就是:没有递归,且一共只使用两次查询。

    The Path to a Node

    使用这种新算法,我们还将必须找到一种新的方式去找到一个特定节点的路径。这条路径,我们将需要一个列表来保存所有祖先节点。

    例如,当你看4 - 5“Cherry”节点,您将看到所有的祖先节点都是左值小于4,右值大于5。得到所有的祖先,我们可以使用这个查询:

    SELECT NodeName,Lft,Rgt FROM FoodTree WHERE Lft < 4 AND Rgt > 5 ORDER BY Lft ASC;

    +-------+
    | title |
    +-------+
    | Food |
    | Fruit |
    | Red |
    +-------+
    How Many Descendants

    如果你给我一个节点的左和右值,我可以利用数学公式告诉你有多少子节点。

    descendants = (right - left - 1) / 2 

    这个简单的公式,我可以告诉你,2 - 11“Fruit”节点有4个子节点,8 - 9“Banana”节点没有子节点。

    Automating the Tree Traversal

    这儿的自动生成表指的是:如何把一个表从邻接表模型转换成修改过的前序遍历模型。我们在开始的临界表上增加"lft“和”rgt“字段。执行以下代码,完成转换:

    //邻接表模型转换为修改后前序遍历遍历算法模型
    	public int RebuildTree(int parentId,int lft){
    		int rgt = lft + 1;
    		String sql = "select NodeId,NodeName from AdjTree where  ParentId=" + parentId;
    		conn = sqlManager.GetConn();
    		try {
    			statement = conn.createStatement();
    			ResultSet rs = statement.executeQuery(sql);
    			while(rs.next()){
    				rgt = RebuildTree(rs.getInt("NodeId"), rgt);
    			}   
    			sql = "update AdjTree set Lft = "+lft+", Rgt = "+rgt + " where NodeId = "+parentId;
    			statement = conn.createStatement();
    			statement.executeUpdate(sql);
    			return rgt + 1;
    		} catch (SQLException e) {
    			e.printStackTrace();
    			return 0;
    		}
    	}


    结果:


    我们所写的运行函数是一个递归函数。对于某一节点,如果其没有子孙节点,那么他的右数值等于左数值+1;如果有那么返回其子树右数值+1。这个函数稍微有点复杂,不过梳理通了以后就不难理解。

    这个函数将会从根节点开始遍历整个树。运行了可以发现和我们之前手动所建的表一样。这里有个快速检查的方法:那就是检查根节点的右数值,如果它等于节点总数的2倍,那就是正确的。

    Adding a Node

    我们如何将一个节点添加到树中?这里有两种方法:(1)你可以在你的表中保存父节点字段‘ParentId’并重新运行RebuildTree()函数。这是一个简单但是效率不高的方法;

    你可以使用邻接表的方法进行更新和修改后的前序遍历树算法进行检索。如果你想添加一个新节点,您就将其添加到表并设置父节点。然后,只需重新运行RebuildTree()函数就可以了。对一个大树来说这是一个很容易,但不是非常高效方法。

    (2)第二种方法为添加、删除节点时更新新插入节点右边所有节点的左值和右值。让我们来看一个例子。我们想添加“Strawberry“到”Red“节点下,那么“Red”节点的右数就得从6到8,而“Yellow”就得从7-10变成9-12,以此类推。更新Red节点就意味着大于5的左数和右数都要增加2。

    UPDATE FoodTree SET Rgt = Rgt+2 WHERE Rgt > 5;   
    UPDATE FoodTree SET Lft = Lft+2 WHERE Lft > 5;

    现在我们可以添加一个新的节点“Strawberry”来填补新腾出的空间。这个节点左值为6和右值为7。

    INSERT INTO FoodTree (Lft,Rgt,NodeName)values(6,7,'Strawberry');

    如果我们运行DisplayTree2()函数,我们将看到新的“Strawberry”节点已经成功插入到树:

    Food
    --Fruit
    ----Red
    ------Cherry
    ------Strawberry
    ----Yellow
    ------Banana
    --Meet
    ----Beef
    ----Pork

    Disadvantages

    首先,修改过的前序遍历算法似乎更难理解。但是它有着邻接表模型无法比拟的速度优势,虽然,在增或着删数据的时候步骤多了些,但是,查询的时候只需要一条SQL语句。不过,这里我要提醒,当使用前序遍历算法存储树的时候,要注意临界区问题,就是在增或者删的时候,不要出现其他的数据库操作。


    注:SqlManager类

    package com.sql;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class SqlManager {
    	private String SqlConectionString = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; 
    	private String ip = "localhost";
    	private String databaseName = "SJF";
    	private String user = "sa";
    	private String password = "123";
    	private String url = null;
    	private Connection conn = null;
    	
    	public SqlManager(){
    		//加载驱动
    		try {
    			Class.forName(SqlConectionString);
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    			System.out.println("加载数据库驱动失败:\n"+e.getMessage());
    		}
    		url = "jdbc:sqlserver://" + ip + ":1433;DatabaseName="+databaseName+";";
    	}
    	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    	public Connection GetConn(){
    		try {
    			conn = DriverManager.getConnection(url, user, password);
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		return conn;
    	}
    	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    	//关闭
    	public void CloseConn(){
    		try {
    			conn.close();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    }
    



    代码下载




    展开全文
  • 嵌入式Linux移植和Uboot、什么是Bootloader二、Bootloader的特点三、Bootloader的执行模式四、一些bootloader介绍Red BootloadersARM BootloadersBlobU-boot介绍U-Boot工程简介U-boot的特点U-Boot目录结构U-boot...

    一、什么是Bootloader

    在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。

    Bootloader,不同的处理器架构都有不同的BootloaderBootloader不但依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的配置。对于2块不同的嵌入式板而言,即使它们使用同一种处理器,要想让运行在一块板子上的Bootloader程序也能运行在另一块板子上,一般也都需要修改Bootloader的源程序。

    反过来,大部分Bootloader仍然具有很多共性,某些Bootloader也能够支持多种体系结构的嵌入式系统。例如,U-Boot就同时支持PowerPC、ARM、MIPS和X86等体系结构,支持的板子有上百种。通常,它们都能够自动从存储介质上启动,都能够引导操作系统启动,并且大部分都可以支持串口和以太网接口。

    对于Bootloader可总结以下四点:

    1. Bootloader是硬件启动时执行的引导程序,是运行操作系统的前提;
    2. Bootloader是在操作系统内核或用户应用程序运行之前运行的一段代码;
    3. 在嵌入式系统中,整个系统的初始化和加载任务一般由Bootloader来完成;
    4. 对硬件进行相应的初始化和设定,最终为操作系统准备好环境。

    二、Bootloader的特点

    Bootloader不属于操作系统,一般采用汇编语言和C语言开发。需要针对特定的硬件平台编写。在移植过程时,首先为开发板移植Bootloader。Bootloader不但依赖于CPU的体系架构,而且依赖于嵌入式系统板级设备的配置。可总结为以下三点:

    1. Bootloader运行通常分为两个阶段。
    2. Bootloader独立于操作系统。
    3. Bootloader不仅依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的配置。

    三、Bootloader的执行模式

    自启动模式: 在这种模式下,Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,该模式下,Bootloader按照预先设定的命令自动运行,整个过程并没有用户的介入。这种模式是Bootloader的正常工作模式,因此在嵌入式产品发布时,Bootloader必须工作在这种模式下

    交互模式: 在这种模式下,目标机上的Bootloader将通过串口或网络等通信手段从开发板(Host)上下载内核映射和根文件系统映像等待RAM中。可以被Bootloader写到目标机上的固态存储媒介质中,或者直接进入系统的引导。也可以通过串口接收用户的指令。该模式下,Bootloader通过串口和PC端通信,接收用户的命令。启动加载模式通常用于第一次烧写内核与根文件系统到固态存储媒质时或者以后的系统更新时使用;下载模式多用于开发人员在前期开发的过程中,工作于这种模式下的Bootloader通常都会向它的终端用户提供一个简单的命令行接口。

    四、一些bootloader介绍

    在这里插入图片描述

    Red Bootloaders

    Redboot支持的处理器构架有ARM,MIPS,MN10300,PowerPC, Renesas SHx,v850,x86等,是一个完善的嵌入式系统Boot Loader 。

    Redboot是在ECOS的基础上剥离出来的,继承了ECOS的简洁、轻巧、可灵活配置、稳定可靠等品质优点。它可以使用X-modem或Y-modem协议经由串口下载,也可以经由以太网口通过BOOTP/DHCP服务获得IP参数,使用TFTP方式下载程序映像文件,常用于调试支持和系统初始化(Flash下载更新和网络启动)。Redboot可以通过串口和以太网口与GDB进行通信,调试应用程序,甚至能中断被GDB运行的应用程序。Redboot为管理FLASH映像,映像下载,Redboot配置以及其他如串口、以太网口提供了一个交互式命令行接口,自动启动后,REDBOOT用来从TFTP服务器或者从Flash下载映像文件加载系统的引导脚本文件保存在Flash上。当前支持单板机的移植版特性有 :

    • 支持ECOS,Linux操作系统引导
    • 在线读写Flash
    • 支持串行口kermit,S-record下载代码
    • 监控(minitor)命令集:读写I/O,内存,寄存器、 内存、外设测试功能等

    Red Bootloaders相关网站:http://sourceware.org/redboot/

    ARM Bootloaders

    Redboot是Redhat公司随eCos发布的一个BOOT方案,是一个开源项目。支持arm720, arm920, arm926, CortexA8, sa1100, xscale。

    ARMboot的目标是成为通用的、容易使用和移植的引导程序,非常轻便地运用于新的平台上。ARMboot是GPL下的ARM固件项目中唯一支持Flash闪存,BOOTP、DHCP、TFTP网络下载,PCMCLA寻线机等多种类型来引导系统的。特性为:

    • 支持多种类型的FLASH;
    • 允许映像文件经由BOOTP、DHCP、TFTP从网络传输;
    • 支持串行口下载S-record或者binary文件;
    • 允许内存的显示及修改;
    • 支持jffs2文件系统等

    (1)U-Boot是ARM bootloader标准
    (2)armboot 加入到 ppcboot 形成了 u-boot

    ARM Bootloaders相关网站:http://armboot.sourceforge.net/

    Blob

    blob是LART工程使用的bootloader,移植到多个ARM平台上

    Blob也提供两种工作模式,在启动时处于正常的启动加载模式,但是它会延时 10 秒等待终端用户按下任意键而将 Blob 切换到下载模式。如果在 10 秒内没有用户按键,则 Blob 继续启动 Linux内核。其基本功能为 :
    初始化硬件(CPU速度,存储器,中断,RS232串口)

    • 引导Linux内核并提供ramdisk;
    • 给LART下载一个内核或者ramdisk;
    • 给FLASH片更新内核或者ramdisk;
    • 测定存储配置并通知内核;
    • 给内核提供一个命令行 [5] 。

    Blob相关网站:http://www.lart.tudelft.nl/lartware/blob

    U-boot介绍

    u-boot(Universal Boot Loader)是德国DENX小组开发的用于多种嵌入式CPU的bootloader程序。遵循GPL条款。8xxROM 、PPCBOOT、Armboot逐步发展演化而来。
    在这里插入图片描述

    U-Boot工程简介

    U-Boot提供两种操作模式:启动加载(Boot loading)模式和下载(Downloading)模式,并具有大型Boot Loader的全部功能。主要特性为:

    • SCC/FEC以太网支持;
    • BOOTP/TFTP引导;
    • IP,MAC预置功能;
    • 在线读写FLASH,DOC, IDE,IIC,EEROM,RTC;
    • 支持串行口kermit,S-record下载代码;
    • 识别二进制、ELF32、pImage格式的Image,对Linux引导有特别的支持;
    • 监控(minitor)命令集:读写I/O,内存,寄存器、内存、外设测试功能等;
    • 脚本语言支持(类似BASH脚本);
    • 支持WatchDog,LCD logo,状态指示功能等。
      现在U-Boot已经能够支持PowerPC、ARM、X86、MIPS体系结构的上百种开发板,已经成为功能最多、灵活性最强并且开发最积极的开放源码Bootloader。目前仍然由DENX的Wolfgang Denk维护。

    U-Boot的源码包可以从sourceforge网站下载,还可以订阅该网站活跃的U-Boot Users邮件论坛,这个邮件论坛对于U-Boot的开发和使用都很有帮助。

    U-Boot软件包下载网站:http://sourceforge.net/project/u-boot
    U-Boot邮件列表网站:http://lists.sourceforge.net/lists/listinfo/u-boot-users/
    DENX相关的网站:http://www.denx.de/re/DPLG.html

    U-boot的特点

    • 代码结构清晰、易于移植(见程序结构)
    • 支持多种处理器体系结构(见程序结构cpu目录)
    • 支持众多参考板(目前官方包中有200多种,见程序结构board目录)命令丰富、有监控功能
    • 支持网络协议、USB、SD等多种协议和设备
    • 支持文件系统
    • 更新较活跃,使用者多,有助于解决问题

    U-Boot目录结构

    1. 与平台相关的目录结构
      board, cpu, lib_arm, include…

    2. 与平台无关的目录结构
      common, net, fs, drivers…

    3. 工具和文档
      tools, doc

    以下对目录结构一一介绍:
    在这里插入图片描述
    在这里插入图片描述

    U-boot命令介绍

    请关注后文,接上。。。。。

    展开全文
  • 文献来源网址:... 在本节教程中,我们将会向您介绍OGRE最基本的组成结构:场景管理器(SceneManager),场景节点(SceneNode)和实体对象(Entity objects),我们不会涉及

    文献来源网址:http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Basic+Tutorial+1&structure=Tutorials

        导言:

        在本节教程中,我们将会向您介绍OGRE最基本的组成结构:场景管理器(SceneManager),场景节点(SceneNode)和实体对象(Entity objects),我们不会涉及大量的源代码,相反地,由于开始学习OGRE,我们会着重给你介绍几本概念。

        当您浏览本教程的时候,你可以慢慢地将代码加入到你自己的项目,然后查看编译结果。没有比实际编程更好的方法去熟悉这些概念,如果你仅仅是阅读,我劝你还是打住你的念头吧。(个人注:实践是学习编程最好的方法)

        预备知识:

        1,本教程假设你已经拥有C++知识和能够建立和编译OGRE运用程序

         2,本教程假设你已经用 Ogre Wiki Tutorial Framework创建了一个工程,或者,手动地,用CMake或者Ogre运用程序向导--请查看Setting Up An Application 使用说明  

    开始:

        从这里开始在这篇教程里,我们将使用已经写好的代码作为模版。除了我们将要在createScene函数里面添加的代码之外,您可以暂时忽略其他的东西。在后面的教程里我会深入讲解OGRE程序是如何工作的,现在我们只需要从最简单的地方学起就行了。在您的IDE创建一个名叫Tutorial工程,然后把教程运用程序框架(tutorial application framework)下面的代码添加进去:

    BaseApplication.h
    BaseApplication.cpp
    TutorialApplication.h
    TutorialApplication.cpp

        从这里获取以上文件: Ogre Wiki Tutorial Framework 
        或者,使用Ogre AppWizard(external link).

        TutorialApplication.cpp是本教程中我们唯一使用的文件,并且我们只在成员函数createScene()进行工作。

        TutorialApplication.cpp应当包含以下代码 

       

    #include "TutorialApplication.h"
     
    TutorialApplication::TutorialApplication(void)
    {
    }
     
    TutorialApplication::~TutorialApplication(void)
    {
    }
     
    //-------------------------------------------------------------------------------------
    void TutorialApplication::createScene(void)
    {
        // Set the scene's ambient light
        mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));
     
        // Create an Entity
        Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
     
        // Create a SceneNode and attach the Entity to it
        Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
        headNode->attachObject(ogreHead);
     
        // Create a Light and set its position
        Ogre::Light* light = mSceneMgr->createLight("MainLight");
        light->setPosition(20.0f, 80.0f, 50.0f);
    }
     
     
     
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    #define WIN32_LEAN_AND_MEAN
    #include "windows.h"
    #endif
     
    #ifdef __cplusplus
    extern "C" {
    #endif
     
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
    #else
        int main(int argc, char *argv[])
    #endif
        {
            // Create application object
            TutorialApplication app;
     
            try 
            {
                app.go();
            } catch( Ogre::Exception& e ) {
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
                MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR| MB_TASKMODAL);
    #else
                std::cerr << "An exception has occured: " << e.getFullDescription().c_str() << std::endl;
    #endif
            }
     
            return 0;
        }
     
    #ifdef __cplusplus
    }
    #endif
    


        继续编译和运行这个程序,当然你要保证你的环境配置正确的,一旦你的程序是正常运行起来,使用WASD键移动,鼠标键用来环顾四周,ESC键退出程序。

       

        个人注:在IDE中创建工程时,可以选择控制台,也可以选择win32。

        小细节:上面可以看出,average  fps 有个fps重叠的字符串fps,这应该是个bug,修改文件SdkTray.h,将2768行与2774行中的 values.push_back(s); 改为values.push_back(str);修改后运行的截图:多出来的fps字符串没有了,感觉清爽多了,微笑

       

    解决问题:

        如果你有问题,请检查下运用程序的设置使你的编译器配置正确,或者查看Ogre.log文件获取更多的详细信息。如果你需要帮助。可以去搜索论坛(http://www.ogre3d.org/forums/search.php),可能你的问题,别人已经遇到了很多次。如果这是一个新的问题,阅读论坛规则,然后再问。确保从您的Ogre.log提供相关细节,异常,错误消息,和/或调试后的痕迹。

        需要注意的是以后的教程中,将不包含此类问题解决的信息,所以请如果您有问题,要特别注意以下几个部分。

        1,MessageBox的问题

        如果你使用支持UNICODE字符集的VS,你可能会遇到如下问题:

    error C2664: 'MessageBoxW' : cannot convert parameter 2 from 'const char *' to 'LPCWSTR'
             Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or  function-style cast

        这个问题是MessageBox函数(在这种情况下)是期待的是Unicode类型的字符串(个人注:此时的形参的类型必须是宽字符类型的),而我们给的是你ANSI字符串,修正这个错误,我们只需要修改下面的代码

    MessageBox( NULL, e.what(), "An exception has occurred!", MB_OK |   MB_IConerror  | MB_TASKMODAL);

    为:

    MessageBoxA( NULL, e.what(), "An exception has occurred!", MB_OK |   MB_IConerror  | MB_TASKMODAL);

         或者,你也可以将编译器的字符集从Unicode改为ANSI,然后,你这样做,会失去国际语言的支持。

        这样做的原因是,"MessageBox "将被自动解析为,要么是MessageBoxA(ANSI),要么是MessageBoxW(宽/ Unicode),根据项目配置,我们明确它使用ANSI修复该错误。

       2,缺失配置文件或者dll

        如果你试图启动你刚生成的应用程序,但该程序报错,缺少DLL或配置文件(*.cfg),,那么你可能没有将它们从OgreSDK的文件夹中复制过来,在VS中,当你在release模式下编译你的运用程序,它将release版的可执行文件放在[ProjectFolder]\ BIN \ release文件夹中,debug版的可执行文件放在[ProjectFolder] \ BIN \ debug文件夹中。你必须从OgreSDK中复制所有的".dll",".cfg" 的文件到相应的文件夹中。也就是说,将这些文件从[OgreSDK]\ BIN \release 文件夹复制到[ProjectFolder]\ BIN\release文件夹,从[OgreSDK] \ BIN \debug文件夹复制到[ProjectFolder] \ BIN \debug文件夹。您还需要编辑的resources.cfg文件以便指向正确的路径。想获取更多信息,请参阅下一节。

        3,资源或插件问题

        确保你有一个plugins.cfg和resources.cfg文件在与可执行文件相同的目录中。Plugins.cfg告诉OGRE哪个渲染库是可用的(Direct3D9,OpenGL等)。resources.cfg被ExampleApplication使用,用于指定纹理,网格模型和脚本的路径。它们两个都是文本文件,所以可以编辑它们以保证这些路径是正确的。否则,您的OGRE设置对话框可能没有任何渲染库,或者您可能会收到一个错误,在你的屏幕上或在Ogre.log文件中,看起来像这样:

    Description: ../../Media/packs/OgreCore.zip - error whilst opening archive: Unable to read zip file

        如果是这种情况,打开你的resources.cfg文件,修改它包含的路径,使它指向Media文件夹(附带有食人魔(Ogre))的位置。注意:您不能使用环境变量在这些路径上,例如$(SomeVariable)。

    ogre是怎么工作的?

        OGRE是怎样工作的这是一个很广的话题,我们将从场景管理器开始然后进一步了解场景节点和实体。这三个类是所有OGRE程序的基石。

         1,场景管理器基础:

         在屏幕上显示的所有东西都是由场景管理器来管理。当您在场景中添加物体时,场景管理器会记录这些物体的位置。当您添加摄像机来观看某个场景时,场景管理器会记录摄像机的位置。当您添加平面、广告牌、灯光时,场景管理器同样会管理他们。 OGRE里有很多种场景管理器。有的场景管理器渲染地面,有的场景管理器渲染BSP表等等。在后面,我们将进一步了解场景管理器。

         2,实体基础:

        一个实体是可以在场景中渲染的物体之一。您可以把实体理解为任何一个3D模型。一个机器人可以是一个实体,一条鱼可以是一个实体,大地草原可以是一个非常大的实体。灯光,摄像机,粒子,广告牌等不能成为实体。

        关于OGRE需要注意的一点是,它从对象的位置和方向分离出渲染对象,这就意味着,在 Ogre中你不能够直接将一个实体放入到场景中,而是将实体与场景节点绑在一起,这个场景节点则包括了实体的方位信息。

         3,场景节点基础:

        场景节点将持续跟踪与它绑在一起的实体的方位。当你创建了一个实体时,它直到与一个场景节点绑定后才会被渲染。同样,一个场景节点也不能单独的在屏幕上显示出来,只有与一个实体绑定后才能在屏幕上显示。

        场景节点可以绑定多个实体。例如在屏幕上有在行走的一个人物对象,并且希望这个对象产生发光效果,要实现这些,首先你需要创建一个场景节点,然后再创建一个人物对象的实体并与场景节点绑定在一起,之后你还需要创建一个光照模型也与这个场景节点绑定在一起。场景节点同样可以与其它场景节点绑定以描述更完整的对象。我们在后续的章节中介绍场景节点更多的用法。

        在场景中,场景节点的位置总是与它的父节点相关。每一个场景管理器都包含一个根节点。

    你的第一个OGRE程序

        返回到我们刚才创建的代码,找到 TutorialApplication::createScene成员函数。首先需要为整个场景设置环境光,这样才可以看到要显示的内容,通过调用setAmbientLight函数并指定环境光的颜色就可以做到这些。指定的颜色由红、绿、蓝三种颜色组成,且每种色数值范围在01之间。将下一句添加到 createScene 中:

    mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));

        下一步创建一个 Entity,通过调用 SceneManager createEntity 方法来创建:

    Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

        好的,一些问题需要提出来,首先,mSceneMgr来自哪里?我们调用函数的参数是什么?mSceneMgr变量是当前场景管理器对象(这个是BaseApplication类为我们做的)。createEntity函数的第一个参数是我们创建实体对象的名字,所有的实体必须有唯一的名字,如果你创建两个名字相同的实体对象,你会得到一个错误,第二个参数 "ogrehead.mesh" 指明我们用于实体的网格模型,"ogrehead.mesh"是一个来自Ogre SDK的资源。资源加载和网格模型将会在后面的教程中涉及,现在,我们直接使用具有资源加载能力的BaseApplication类。我们已经创建了一个实体,但还需要创建一个场景节点来与它绑定在一起。既然每个场景管理器都有一个根节点,那我们就在根节点下创建一个场景节点。

    Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");

        这句代码首先调用场景管理器的 getRootSceneNode方法来获取根节点,再使用根节点的 createChildSceneNode方法创建一个名为"HeadNode"的场景节点。与实体一样,场景节点的名字也是唯一的。

        然后,将实体绑定到场景节点,指定了Ogre Head渲染位置

    headNode->attachObject(ogreHead);

        尽管灯光知道下一个章节才会涉及,但是,我们在这里添加一个灯光,这样我们看到模型带有适当的阴影,而不是来自自然光的平坦阴影,当我们创建灯光时,我们也要给一个唯一的名字。

    Ogre::Light* light = mSceneMgr->createLight( "MainLight" );

        一旦灯光被创建,可以用setPosition函数设置它的位置,。给定的这三个参数是要设置的新的位置的X,Y和Z坐标。坐标将在下面详细讨论。

    light->setPosition(20, 80, 50);

        编译运行你的程序,你将会在屏幕中看到食人魔的头像。

       

    坐标与向量

        待续.....

     

    展开全文
  • HTML5结构元素

    千次阅读 2017-05-18 12:40:31
    article元素“在文档,页面,应用或是站点上的个独立部分,并且大体上,是可独立分配,或是重复使用的,例如在发布时。这个可以是论坛帖子,杂志或是新闻,博客条目,用户提交的评论,互动的小工具或小工具,或...

    article元素

    article元素“在文档,页面,应用或是站点上的一个独立部分,并且大体上,是可独立分配,或是重复使用的,例如在发布时。这个可以是论坛帖子,杂志或是新闻,博客条目,用户提交的评论,互动的小工具或小工具,或任何其他独立项目的内容。”article>元素专用于结构化文章,总的来说,article用于发表文章,代码如下:

    <article>

    <header>

    <h2>标题党</h2>

    <p>小标题</p>

    </header>

    <div class="post-content">

        Sweet roll halvah biscuit toffee liquorice tart pudding sesame snaps.
        Biscuit powder jelly-o fruitcake faworki chocolate bar. Pudding oat
        cake tootsie roll sesame snaps lollipop gingerbread bonbon. Gummies
        halvah gummies danish biscuit applicake gingerbread jelly-o pastry.
    </div>
    </article>
    
    
    section元素
    section用于区分文章章块的节,同article元素相比,article元素更加有独立性。可将section元素嵌套于section元素中,
    作为容器时,建议应div元素嵌套;,<article>元素还可与section元素结合,需要的时候,可以使用<section>元素将文章分为几个段落。
    <div classname="test"> 
    <section>
    <h2>标题</h2>
    <p>文章片段文章片段</p>
    <p>文章片段文章片段</p>
    </section>
    </div>
    注:不要将article元素作为容器放置信息,应在外层嵌套div元素,
    article元素跟section元素可以互相嵌套。
    
    
    nav元素
    nav元素可以作为导航的元素组,如首页导航,侧边导航,页内导航,通常里面的元素用列表元素,亦可以将nav元素放置在article元素中
    作为子元素,网页页脚用footer元素
    <article classname="first">
    <header>
    <h1>标题</h1>
    <nav name="daohang">
    <ul>
    <li>导航元素</li>
    <li>导航元素</li>
    <li>导航元素</li>
    </ul>
    </nav>
    </header>
    <p>文章内容 文章内容</p>
    <footer>
    <a href="#">页脚内容</a>
    </footer>
    </article>
    <footer>
    <a href="#">页脚内容</a>
    </footer>aside元素
    aside元素用来表示当前页面或文章的附属信息部分,它可以包含与当前页面或主要内容相关的引用、侧边栏、广告、导航条,以及其他类似的有别于主要内容的部分。
    
    <header>
        <h1>F#入门</h1>
    </header>
    <article>
        <h1>第四节 词法闭包</h1>
        <p>lambda表达式可以创建词法闭包...(文章正文)</p>
        <aside>
            <!-- 因为这个aside元素被放置在一个article元素内部,
            所以分析器将这个aside元素的内容理解成是和article元素的内容相关联的。 -->
            <h1>名词解释</h1>
            <dl>
                <dt>F#</dt>
                <dd>F#为.Net2010中引入的新型函数型编程语言</dd>
            </dl>
            <dl>
                <dt>词法闭包</dt>
                <dd>词法闭包是指,将创建lambda表达式时的环境保存起来...(详细解释)</dd>
            </dl>
        </aside>
    </article>
    笔者将header元素作为头部,将article元素作为章节内容,将aside元素放置于章节中。
    time元素
    time元素用于获取系统时间,其中的pubdate属性用于编写当前文章发布的时间
    <article>
        <h1>系统时间</h1>
        <p><time datetime="2017-06-06">2017-06-06</time></p>
        <h1>文章发布时间</h1>
        <p><time datetime="2017-06-07" pubdate="true">2017-06-07</time></p>
    </article>
    
    
    
    
    
    
    
    
    
    

    展开全文
  • MOA网站结构分析

    千次阅读 2014-12-22 09:47:17
    :文章为作者科研分析所得,请注意版权问题,引用时请告知原作者或做说明。 摘要:本文对MOA(Massive Online Analysis)的官方网站内容及MOA项目代码结构进行分析。截至2014年9月20日,MOA的最新版本是Release ...
  • 数据结构889考研真题练习册

    千次阅读 2019-04-02 08:34:37
    对了,2018年12月考889的题型较往年多了两个类型的题目:外排序另外个记不得了,可以去王道论坛找找经验贴。 这里就直接给出我做好的PDF的百度云链接吧: 链接: ...
  • Oracle树形结构的sql语句

    千次阅读 2015-06-11 16:31:27
    关于oracle树状结构查询 关于oracle树状结构查询... 1 、基本概念:... 1 1.  树结构的描述... 1 2. 关于PRIOR. 2 3. 定义查找起始节点... 2 4.运用 LEVEL. 2 5.节点和分支的裁剪... 2 6....
  • Composer 的结构

    千次阅读 2016-07-25 17:14:52
    、Root Package(根目录包) 根目录包就是在你的项目的根目录由 composer.json 定义的包。主要就是由 composer.json 来定义你的项目的依赖。 某些字段只能在根目录包的中使用,比如 config 字段,只有根目录...
  • IplImage结构及与其相关的读写函数

    千次阅读 2012-03-28 16:39:23
    IplImage结构 由于OpenCV主要针对的是...IplImage结构来源于Intel的另外个函数库Intel Image Processing Library (IPL),该函数库主要是针对图像处理。IplImage结构具体定义如下: typedef struct _IplImage  {
  • 【第部分-django论坛从搭建到部署】个完整的Django入门指南学习笔记 【第二部分-django论坛从搭建到部署】个完整的Django入门指南学习笔记 【第三部分-django论坛从搭建到部署】个完整的Django入门指南...
  • 数据结构学习—二叉树

    千次阅读 2006-02-08 16:01:00
    这些天参与了CSDN论坛的讨论,改变了我以前的一些看法。回头看我以前的东西,我虽对这本书很不满,但我还是按照它的安排在点点的写;这样就导致了,我过多的在意书中的偏漏,我写的更多是说“这本书怎样”,而偏离...
  • 结构之法 算法之道

    千次阅读 2012-04-08 11:32:42
    程序员面试、算法研究、编程艺术、红黑树4大经典原创系列集锦与总结作者:July--结构之法算法之道blog之博主。时间:2010年10月-2012年3月 (一直在收录本blog最新updated文章)。出处:http://blog.csdn.net/v_JULY_v...
  • discuz结构分析

    千次阅读 2008-11-19 17:26:00
    :想搞DZ开发,就得弄懂DZ中每个文件的功能。 a) Admin:后台管理功能模块 b) Api:DZ系统与其它系统之间接口程序 c) Archiver:DZ中,用以搜索引擎优化的无图版 d) Attachments:DZ中 ,用户上传附件的存放目录 e)...
  • 从myspace数据库看分布式系统数据结构变迁  MySpace已经成为全球众口皆碑的社区网站之王。尽管一流和营销和管理经验自然是每个IT企业取得成功的首要因素,但是我们却抛弃这一点,而主要着眼于探讨在数次面临系统...
  • DZ 结构分析

    万次阅读 2007-12-13 10:29:00
    :想搞DZ开发,就得弄懂DZ中每个文件的功能。 a) Admin:后台管理功能模块 b) Api:DZ系统与其它系统之间接口程序 c) Archiver:DZ中,用以搜索引擎优化的无图版 d) Attachments:DZ中 ,用户上传附件的存放...
  • Redis 5种数据结构使用及注意事项

    千次阅读 2017-03-14 17:40:39
    1优缺点 非常非常的快,有测评说比Memcached还快(当大家都是...丰富的数据结构,超越了一般的Key-Value数据库而被认为是个数据结构服务器。组合各种结构,限制Redis用途的是你自己的想象力,作者自己捉刀写的用途入门
  • 数组和广义表 - [数据结构]

    千次阅读 2009-02-22 18:31:00
    2005-09-07数组和广义表 - [数据结构]第五章 数组和广义表——非线性数据结构5.1 数组的定义和运算☆二维数组的逻辑结构形式定义为: 2_Array=( D, R )其中 D={ aij | i=c1,c1+1,...,d1, j=c2,c2+1,...,d2, aijD0} ...
  • 先决条件这个教程假设你有C++编程的基础并且可以配置并编译OGRE应用程序 (如果你在配置环境方面有问题,请看 ... 介绍在这个教程中,我会介绍给你OGRE中最基本的结构: SceneManager, SceneNode, 还有Entity 对象.我们
  • 篇《四旋翼飞行器结构和原理》 第二篇《四旋翼飞行diy全套入门教程》 ============================================================================================== 四旋翼飞行器结构和原理 1.结构...
  • 你讲学到 各大存储引擎介绍csv存储引擎archive存储引擎memory 存储引擎MyisamInnodb对比二 理解mysql体系结构三 基于查询执行路径理解查询机制1. mysql 客户端 / 服务端通信2 查询缓存不会缓存的情况:查询缓存...
  • PE文件详解------PE文件结构剖析

    千次阅读 2013-11-19 17:06:15
    .PE文件结构纵览 PE文件的结构如上图所示,由低地址到高地址分别为:Dos头,PE头,块表,块,调试信息。其中真正的PE文件头是位于Dos头的后面的部分。 上图为利用PE工具打开的个可执行文件在...
  • 作者:mjmpurples 序: 本文是我的自传的第四部,讲述我毕业第年的生活历程。往事的种种,如风如梦,却又每每在夜深人静时,冷冷鞭打着我日渐淡漠的心灵。我性耿直,有言必吐。但生性畏缩,在其位不说其政。我的...
  • 本资料所有问题及代码均摘选自matlab中文论坛,主要供自己学习使用。非常感谢论坛的所有提出以及解答问题的会员。 目 录 1、GUI新手之——教你读懂GUI的M文件....4、handles结构中句柄和对象的关联问题... 17 5、M
  • 查看keras各种网络结构各层的名字方式更多python视频教程请到菜鸟教程https://www.piaodoo.com/ 举例 base_model = ResNet50(weights=‘imagenet', include_top=True) print(base_model.summary()) 得到这...
  • 最近一直在研究 WebRTC源码,发现目前网上分析WebRTC源码的资料非常少。...所以,我想在分析WebRTC代码的过程中,将自己的一些分析心得写下来分享给大家,这样即是对自己的种鞭策,同时也可以帮助那些想入门的同学。
  • 转自:http://www.xxling.com/blog/article/58.aspx、为什么要读取表结构对于个程序员的平常工作当中,我们常用的都是用来从数据库表中读取数据的sql,而对于表结构的读取就比较少用了。因为有客户端,没事我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,615
精华内容 8,646
关键字:

一注结构论坛