亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

3D編程指南第五部分:使用M3G渲染heightmap地形

2019-11-18 16:12:52
字體:
來源:轉載
供稿:網友

在“使用M3G(JSR184)進行移動3D編程”入門指南系列的前四部分的基礎上,Mikael Baros,Redikod的高級程序員,現在將指導你學習heightmap和地形渲染的基礎知識。

下面是指南前四部分的鏈接:

l         第一部分:快速進入移動java 3D編程世界

l         第二部分:光的3D理論與定位

l         第三部分:粒子系統和立即模式渲染

l         第四部分:M3G內建碰撞、光照物理學和照相機視點

介紹

歡迎來到M3G指南系列的第五部份。今天我將向你展示一種簡單的技術,它幾乎在所有的3D游戲中都用得到(以一種或者另外一種形式),heightmap

通過使用heightmap,設計者/開發者能夠容易地創建自然地形(可能相當于使用一個花邊噪音生成器,但不是必須的),幾乎不花費時間。Heightmap的魅力在于它有一個復雜的概念,例如一個漂亮的現實的3D地形,則把這個問題簡化為一個簡單的2D圖像。

和以前一樣,無論什么時候你感到了困惑就參考這里。

首先,或許也是最重要的,就是在索尼愛立信開發者世界上專業的移動Java 3D網絡區。其次,如果你碰到困難,就去索尼愛立信移動Java 3D論壇。對于其他的任何情況,使用索尼愛立信開發者世界網絡門戶,在那里你可以找到你的問題的答案,并且可以了解到更多。

預備知識

在通讀指南的這一部分之前你應該已經閱讀過前四部分,因為我將在這里使用前面已經寫過的代碼。

3D地形

我們先來對地形進行定義,好嗎?地形是真實世界的一個模型,有平原、山脈、河流、懸崖和丘陵等。地形的要點就是給用戶如在“真實”或者“現實”世界中漫步的印象。然而,如果從一個更加抽象的觀點看,我們會很快的意識到地形僅僅是在高度上的變化。例如,一個草原就是一個高度為常數的地形(除了可能有一些起伏和山丘外)。一個山區就是一個具有很大高度變化的地形,在空地間創建鴻溝,如此創建山區的幻覺。一個河流就是由一個平原和穿過它的曲線組成,這個曲線比它周圍的平原高度稍低。檢查這張地形圖:

3D編程指南第五部分:使用M3G渲染heightmap地形(圖一)

如你所見,上面的地形由三塊高一些的區域描述(三個灰色的山丘),剩下的是一個深深的峽谷,里面充滿了水。此外,除了高度變化沒有什么。

Heightmap

下面就是heightmap了。它們是存儲高度變化的和使表面平滑的上好解決方案。在我開始講解有啟發的東西之前,現在我們觀察這個圖片。

3D編程指南第五部分:使用M3G渲染heightmap地形(圖二)

這個一個灰度等比變化的圖像。沒有任何奇特的。它看起來像一個中間有個白色斑點的圓環。如此看來什么是一個灰色等比變化的圖像呢?那么,簡單來看,它是一個像素的集合,每個像素都是在灰度上從0到255上等比變化,0是黑色,255是全白。對嗎?聽起來很熟悉吧?那么如果你使用像素確定高度,如何?如果黑色的像素(值0)是最小的高度,白色的像素(值255)是最大的高度,如此你就有了一個描述高度的地圖,heightmap!對此另外一個重要的事情就是,如果你只想模糊圖像,因為像素是從0到255,所以你要對地形自動插補。

那么,你所有需要做的就是打開最中意的圖像程序,使用畫筆畫一些白色素材,然后建立一個heightmap。這聽起來很容易,當然,但是我們怎么樣將它轉化為可以渲染的網面呢?

Quad

將一個heightmap轉化為可以渲染的網面一點都不難。我們需要讀取heightmap的像素,然后創建由高度變化反應出的平面。在這個問題上一個非常易用的平面當然是Quad(四邊形)。因為所有圖像都是規則的矩形,四方形能夠很好地適合他們。那么Quad是什么呢?很簡單,一個Quad就是兩個三角形放在一起形成一個矩形平面。

3D編程指南第五部分:使用M3G渲染heightmap地形(圖三)

上面的圖像代表了由兩個三角形組成的一個四邊形。如你所見,這個四邊形有四個脫節的端點,因為我們使用了兩個三角形描述它。四個角都被給予不同的高度,如此我們就開始了在3D世界中描述高度。然而一個四邊形描述整個地形還是遠遠不夠的,如果想要地形看起來有一點真實的感覺,需要很多四邊形。我們稍后再來講解這些,首先來看如何使用代碼創建一個四邊形。我們將在xz平面上創建一個有y變量的四邊形,因而有變化的高度。我們為前篇指南中創建的MeshFactory類引入一個新的方法,叫做createQuad。這個方法需要知道的就是在這個四邊形不同的角上有不同的高度,和選擇標志。下面是這個方法的第一個片段:


public static Mesh createQuad(short[] heights, int cullFlags)
    {
        // The vertrices of the quad
        short[] vertrices = {-255, heights[0], -255,
                255, heights[1], -255,
                255, heights[2], 255,
                -255, heights[3], 255};

看起來熟悉嗎?一個普通的四邊形由四個頂點組成,每一個頂點都有一個變化的y坐標,但是x和z不變。

// Create the model's vertex colors
        VertexArray colorArray = new VertexArray(color.length/3, 3, 1);
        colorArray.set(0, color.length / 3, color);
       
        // Compose a VertexBuffer out of the PRevious vertrices and texture coordinates
        VertexBuffer vertexBuffer = new VertexBuffer();
        vertexBuffer.setPositions(vertexArray, 1.0f, null);
        vertexBuffer.setColors(colorArray);
       
        // Create indices and face lengths
        int indices[] = new int[] {0, 1, 3, 2};
        int[] stripLengths = new int[] {4};
       
        // Create the model's triangles
        triangles = new TriangleStripArray(indices, stripLengths);

這里我們創建在M3G系統中描述一個網面所需要的數組。VertexBuffer保存兩個頂點數組、顏色數組和位置數組。我在上面代碼中有意省去對顏色數組的分配,因為我將在稍后討論?,F在我們把注意力放在創建四邊形上。

// Create the appearance
        Appearance appearance = new Appearance();
        PolygonMode pm = new PolygonMode();
        pm.setCulling(cullFlags);
        pm.setPerspectiveCorrectionEnable(true);
        pm.setShading(PolygonMode.SHADE_SMOOTH);
        appearance.setPolygonMode(pm);

這是一些標準的Appearance材料,然而我想讓你明白如何使用平滑陰影,這就意味著頂點的顏色在整個平面上以內插值替換,創建一個平滑的外觀。我們需要在以后說明為什么?,F在剩下的就是創建網面,相當直截了當:


// Finally create the Mesh
        Mesh mesh = new Mesh(vertexBuffer, triangles, appearance);

從Heightmap創建四邊形

使用上面的方法,我們可以創建一個高度變化的四邊形,但是如我前面所說,我們需要很多四邊形來使地形看起來更真實,那么現在的問題就是如何將heightmap轉化為四邊形。這實際上并不算問題??催@個圖片:

3D編程指南第五部分:使用M3G渲染heightmap地形(圖四)

我在前面的heightmap上面畫了一個白色的柵格。如果觀察柵格每一個小塊,你會發現矩形的格子區域就是另外一個heightmap,只是小了些。如果我們創建一個分辨率很高的柵格,你可能會意識到格子的區域變得非常小,這樣也非常容易接近一個四邊形。把它簡單化,為了接近一個heightmap,我們將它劃分成很多十分小的部分,每一部分代表一個四邊形。我們如何創建一個四邊形呢?簡單,下面就是必須的步驟:

l         劃分圖像為很多小部分(最小尺寸為2*2像素)

l         得到每一部分的角的像素,讀取它們的值(0-255)

l         將這些值作為高度賦給四邊形(參考方法聲明)

那么,從heightmap創建四邊形實在是簡單。在創建后,你只需渲染這些四邊形,一個接一個?,F在有些事情你必須了解。隨著heightmap柵格分辨率的不斷增加,而地形的平滑度也在不斷的提高,因為你使用了更多的四邊形來代表這個地形。然而,你也在激劇增加內存的使用空間和GPU需要運算的多邊形的數量。這是在每一個移動電話都需要依據可用內存、GPU運算能力等等進行平衡的。

實現

我們來看在M3G中如何實現一個heightmap。我們已經有了一個方法可以根據不同的高度來創建四邊形,那么我們需要作的就是:

1、  裝載一個heightmap

2、  建立一個數組,等比劃分柵格的尺寸

3、  從heightmap中讀取像素值,并且保存在一個新的數組里

4、  使用上述數組根據不同的高度產生四邊形(Quads)

這是一個簡單的四步程序。來,我們先來查看HeightMap類的私有成員:


// The actual heightmap containing the Y-coords of our triangles
    private short[] heightMap;
    private int[] data;
    private int imgw, imgh;
   
    // Map dimensions
    private int mapWidth;
    private int mapHeight;
   
    // Actual quads
    private Mesh[][] map;
   
    // Water
    private Mesh water;
   
    // Local transform used for internal calculations
    private Transform localTransform = new Transform();

它們是相當地自明,但是我將講解其中的一部分。首先heightMap數組是保存高度的等比數組。它不保存來自heightmap圖像中的像素。Mesh保存產生的所有要渲染的四邊形。最后,water網面是一個普通的平面,代表地形中的水(用來創建河流等等)。然后,我們看如何創建一個HeightMap:

public HeightMap(String imageName, float resolution, int waterLevel) throws IOException
    {
        // Check for invalid resolution values
        if(resolution <= 0.0001f resolution > 1.0f)
            throw new IllegalArgumentException("Resolution too small or too large");
       
        // Load image and allocate the internal array
        loadImage(imageName, resolution);
       
        // Create quads
        createQuads();
       
        // Create the water
        createWater(waterLevel);
    }

我們將上面的代碼劃分為幾個步驟。首先檢查非法的分辨率值。非法值大于1.0f(一個四邊形有四個角,因此最小的柵格區域就是2*2)或者小于0.0001f(一個非常低的分辨率就是或多或少的使用一個四邊形創建整個地形)。

接下來我們要裝載圖像,作為一個構造器參數提供。然而,我想你要明白在loadImage方法中還有一些其它有趣的事情。代碼如下:


// Load actual image
        Image img = Image.createImage(path);
       
        // Allocate temporary memory to store pixels
        data = new int[img.getWidth() * img.getHeight()];
       
        // Get its rgb values
        img.getRGB(data, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
       
        imgw = img.getWidth();
        imgh = img.getHeight();
       
        // Clear image
        img = null;
        System.gc();
       
        // Calculate new width and height
        mapWidth = (int)(res * imgw);
        mapHeight = (int)(res * imgh);
       
        // Allocate heightmap
        heightMap = new short[mapWidth * mapHeight];
       
        // Calculate height and width offset into image
        int xoff = imgw / mapWidth;
        int yoff = imgh / mapHeight;
       
        // Set height values
        for(int y = 0; y < mapHeight; y++)
        {
            for(int x = 0; x < mapWidth; x++)
            {
                heightMap[x + y * mapWidth] = (short)((data[x * xoff + y * yoff * imgw] & 0x000000ff) * 10);
            }
        }       
       
        // Clear data
        data = null;
   img = null;
        System.gc();

我將不再講述上面代碼的細節,因為我希望你能把它作為一個練習來審查??傊覀兿葘嶋H的圖像裝載到內存中,然后抽取它的像素值。接著利用構造函數提供的分辨率參數,我們依據尺寸創建一個柵格,并且使用像素值進行填充。最后我們做一個手動的垃圾收集釋放不必要的數據。這是非常重要的,因為loadImage方法是一個大量使用內存的方法,我們想要確保被接下來的少許認為所需要的至關重要的內存不被垃圾數據占有。

構造函數中的下一個方法就是createQuads。這是一個十分易懂的方法,得到生成的heightMap數組并且從其中創建四邊形。我們來看它的具體內容:


private void createQuads()
    {
        map = new Mesh[mapWidth][mapHeight];
        short[] heights = new short[4];
       
        for(int x = 0; x < (mapWidth - 1); x++)
        {
            for(int y = 0; y < (mapHeight - 1); y++)
            {
                // Set heights
                setQuadHeights(heights, x, y, mapWidth);
               
                // Create mesh
                map[x][y] = MeshFactory.createQuad(heights, PolygonMode.CULL_NONE);
            }
        }
    }

如你所見,我們所有需要做的就是反復遍歷heightMap表和抽取四個值,在MeshFactory.createQuad方法中我們作為高度值使用。

我要留給你的是檢查createWater方法。在這一點上它應該是你爛熟于胸的東西。我們僅僅使用MeshFactory.createPlane方法創建一個具有水面紋理的大平面。

渲染

如何渲染生成的四邊形呢?你應該知道這個問題的答案,不過無論如何我們還是來看看HeightMap類的render方法。如下:

public void render(Graphics3D g3d, Transform t)
    {
        for(int x = 0; x < map.length - 1; x++)
        {
            for(int y = 0; y < map[x].length - 1; y++)
            {
                localTransform.setIdentity();
                localTransform.postTranslate(x * 5.0f, 0.0f, (mapHeight - y) * -5.0f);
                localTransform.postMultiply(t);
                g3d.render(map[x][y], localTransform);
            }
        }
       
        localTransform.setIdentity();
        localTransform.postScale(255, 255, 255);
        localTransform.postRotate(-90, 1.0f, 0.0f, 0.0f);
        g3d.render(water, localTransform);
    }

所有你需要做的就是遍歷四邊形表,并對它們在空間中給定的位置上進行渲染。Render方法的使用者可能會在本地變換后提供一個應用于每個四邊形的變換,本地變化僅僅是將每一個四邊形放在自己的位置上。最后,我們將water網面放在heightmap創建期間定義好的高度上。

集合所有

現在使用稱心的HeightMap類需要的事情如下:

1、  從一個現有的灰度等變化圖像裝載一個HeightMap

2、  渲染

聽起來很簡單吧?因為的確是這樣。我們來看看裝載HeightMap的代碼:


private void createScene()
    {
        try
        {
            // We're using a pretty high resolution. If you want to test this on an actual
            // handheld, try using a lower resolution, sUCh as 0.20 or 0.10
         hm = new HeightMap("/res/heightmap4.png", 0.30f, 40);       
                 
         t.postTranslate(0.0f, -2.0f, -5.0f);
         t.postScale(0.01f, 0.01f, 0.01f);
        
         camTrans.postTranslate(0.0f, 5.0f, 0.0f);
         //camTrans.postTranslate(0.0f, 5.0f, 2.0f);
        }
        catch(Exception e)
        {
            System.out.println("Heightmap error: " + e.getMessage());
            e.printStackTrace();
            TutorialMidlet.die();
        }
    }

這里沒有任何陌生的。我們只是裝載heightmap,并且對它進行一些變換,因為它將是提供給HeightMap渲染方法的變換。我們只是想讓它在屏幕后面向上一點點。我們還應該以很大數量測量它,因為一個地形通常是無限的,但是我只想你看到一個小的總攬圖。

另外一件重要的事情就是這篇指南中的HeightMap完全沒有使用任何剔出渲染。這是很必要,特別是很大的地形。然而為了使代碼保持清晰,我選擇移除任何種類的空間分割或者軟件剔出。你可以把它作為一個練習,只發送網面給可見的渲染者(也就是說,沒有網面離得太遠,或者在照相機后面)。

最后,渲染HeightMap的代碼是什么呢?下面就是主要的draw方法:


// Get the Graphics3D context
            g3d = Graphics3D.getInstance();
           
         // First bind the graphics object. We use our pre-defined rendering hints.
         g3d.bindTarget(g, true, RENDERING_HINTS);
        
         // Clear background
         g3d.clear(back);
        
         // Bind camera at fixed position in origo
         g3d.setCamera(cam, camTrans);
        
         // Render everything
         hm.render(g3d, t);
        
         // Check controls for camera movement
         if(key[UP])
         {
             camTrans.postTranslate(0.0f, 1.0f, 0.0f);
         }
         if(key[DOWN])
         {
             camTrans.postTranslate(0.0f, -1.0f, 0.0f);
         }
         if(key[LEFT])
         {
             camTrans.postRotate(5, 0.0f, 1.0f, 0.0f);
         }
         if(key[RIGHT])
         {
             camTrans.postRotate(-5, 0.0f, 1.0f, 0.0f);
         }
        
         // Fly forward
         if(key[FIRE])
             camTrans.postTranslate(0.0f, 0.0f, -1.0f);

真是沒有多說的了。HeightMap.render(g3d,t)方法相當干凈和直觀。盡管控制對你來說可能有些多余。你可以使用操縱桿移動照相機。上、下和旋轉左、右。使用FIRE鍵向前移動照相機。

總結

那么,為了延伸這個課程,你為什么不試著裝載源代碼zip文件中提供的其它的heightmap呢?看看出現了什么樣的地形。甚至更近一步,創建你自己的heightmap圖像!

放飛你的思緒,把它放在MIDlet中,在你的風景中巡游!


TutorialMidlet

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class TutorialMidlet extends MIDlet implements CommandListener
{
    // A variable that holds the unique display
 private Display display = null;
 
 // The canvas
 private M3GCanvas canvas = null;
 
 // The MIDlet itself
 private static MIDlet self = null;

 /** Called when the application starts, and when it is resumed.
  * We ignore the resume here and allocate data for our canvas
  * in the startApp method. This is generally very bad practice.
  */
 protected void startApp() throws MIDletStateChangeException
 {
     // Allocate
  display = Display.getDisplay(this);
  canvas = new M3GCanvas(30);
  
  // Add a quit command to the canvas
  // This command won't be seen, as we
  // are running in fullScreen mode
  // but it's always nice to have a quit command
  canvas.addCommand(new Command("Quit", Command.EXIT, 1));
  
  // Set the listener to be the MIDlet
  canvas.setCommandListener(this);
  
  // Start canvas
  canvas.start();
  display.setCurrent(canvas);
  
  // Set the self
  self = this;
 }

 /** Called when the game should pause, such as during a call */
 protected void pauseApp()
 {
  
 }

 /** Called when the application should shut down */
 protected void destroyApp(boolean unconditional) throws MIDletStateChangeException
 {
     // Method that shuts down the entire MIDlet
  notifyDestroyed();
 }

 /** Listens to commands and processes */
    public void commandAction(Command c, Displayable d) {
        // If we get an EXIT command we destroy the application
        if(c.getCommandType() == Command.EXIT)
            notifyDestroyed();
    }
   
    /** Static method that quits our application
     * by using the static field 'self' */
    public static void die()
    {
        self.notifyDestroyed();
    }
}


M3GCanvas

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.m3g.Background;
import javax.microedition.m3g.Camera;
import javax.microedition.m3g.Graphics3D;
import javax.microedition.m3g.Transform;

public class M3GCanvas
extends GameCanvas
implements Runnable {
    // Thread-control
    boolean running = false;
    boolean done = true;
   
    // If the game should end
    public static boolean gameOver = false;
   
    // Rendering hints
    public static final int STRONG_RENDERING_HINTS = Graphics3D.TRUE_COLOR Graphics3D.DITHER;
    public static final int WEAK_RENDERING_HINTS = 0;
    public static int RENDERING_HINTS = STRONG_RENDERING_HINTS;
   
    // Key array
    boolean[] key = new boolean[9];
   
    // Global identity matrix
    Transform identity = new Transform();
   
    // Global Graphics3D object
    Graphics3D g3d = null;
   
    // The background
    Background back = null;
   
    // The global camera object
    Camera cam = null;
    Transform camTrans = new Transform();
   
    // Transforms
   
    /** Constructs the canvas
     */
    public M3GCanvas(int fps)
    {
        // We don't want to capture keys normally
        super(true);
       
        // We want a fullscreen canvas
        setFullScreenMode(true);
       
        // Create our scene
        createScene();
       
        // Load our camera
        loadCamera();
       
        // Load our background
        loadBackground();
       
        // Set up graphics 3d
        setUp();
    }
   
    /** Prepares the Graphics3D engine */
    private void setUp()
    {
        // Get the instance
        g3d = Graphics3D.getInstance();
    }
   
    /** When fullscreen mode is set, some devices will call
     * this method to notify us of the new width/height.
     * However, we don't really care about the width/height
     * in this tutorial so we just let it be
     */
    public void sizeChanged(int newWidth, int newHeight)
    {
       
    }
   
    /** Loads our camera */
    private void loadCamera()
    {
        // Create a new camera
        cam = new Camera();
       
        // Set the perspective of our camera. On a handheld you might want to set the far plane
        // (currently 150.0f) to a lower value such as 50.0f
        cam.setPerspective(60.0f, (float)getWidth() / (float)getHeight(), 0.1f, 150.0f);
    }
   
    /** Loads the background */
    private void loadBackground()
    {
        // Create a new background, set bg color to black
        back = new Background();
        back.setColor(0);
    }
   
    // The heightmap and its transform
    HeightMap hm;
    Transform t = new Transform();
   
    /** Creates our scene */
    private void createScene()
    {
        try
        {
            // We're using a pretty high resolution. If you want to test this on an actual
            // handheld, try using a lower resolution, such as 0.20 or 0.10
         hm = new HeightMap("/res/heightmap4.png", 0.30f, 40);       
                 
         t.postTranslate(0.0f, -2.0f, -5.0f);
         t.postScale(0.01f, 0.01f, 0.01f);
        
         camTrans.postTranslate(0.0f, 5.0f, 0.0f);
         //camTrans.postTranslate(0.0f, 5.0f, 2.0f);
        }
        catch(Exception e)
        {
            System.out.println("Heightmap error: " + e.getMessage());
            e.printStackTrace();
            TutorialMidlet.die();
        }
    }


    /** Draws to screen
     */   
    private void draw(Graphics g)
    {
        // Envelop all in a try/catch block just in case
        try
        {           
            // Get the Graphics3D context
            g3d = Graphics3D.getInstance();
           
         // First bind the graphics object. We use our pre-defined rendering hints.
         g3d.bindTarget(g, true, RENDERING_HINTS);
        
         // Clear background
         g3d.clear(back);
        
         // Bind camera at fixed position in origo
         g3d.setCamera(cam, camTrans);
        
         // Render everything
         hm.render(g3d, t);
        
         // Check controls for camera movement
         if(key[UP])
         {
             camTrans.postTranslate(0.0f, 1.0f, 0.0f);
         }
         if(key[DOWN])
         {
             camTrans.postTranslate(0.0f, -1.0f, 0.0f);
         }
         if(key[LEFT])
         {
             camTrans.postRotate(5, 0.0f, 1.0f, 0.0f);
         }
         if(key[RIGHT])
         {
             camTrans.postRotate(-5, 0.0f, 1.0f, 0.0f);
         }
        
         // Fly forward
         if(key[FIRE])
             camTrans.postTranslate(0.0f, 0.0f, -1.0f);
        }
        catch(Exception e)
        {
            reportException(e);
        }
        finally
        {
            // Always remember to release!
            g3d.releaseTarget();
        }
    }


    /** Starts the canvas by firing up a thread
     */
    public void start() {
        Thread myThread = new Thread(this);
       
        // Make sure we know we are running
        running = true;
        done = false;
       
        // Start
        myThread.start();
    }
   
    /** Runs the whole thread. Also keeps track of FPS
     */
    public void run() {
        while(running) {
            try {               
                // Call the process method (computes keys)
                process();
               
                // Draw everything
                draw(getGraphics());
                flushGraphics();
               
                // Sleep to prevent starvation
                try{ Thread.sleep(30); } catch(Exception e) {}
            }
            catch(Exception e) {
                reportException(e);
            }
        }
       
        // Notify completion
        done = true;
    }
   
    /**
     * @param e
     */
    private void reportException(Exception e) {
        System.out.println(e.getMessage());
        System.out.println(e);
        e.printStackTrace();
    }


    /** Pauses the game
     */
    public void pause() {}
   
    /** Stops the game
     */
    public void stop() { running = false; }
   
    /** Processes keys
     */
    protected void process()
    {
        int keys = getKeyStates();
       
        if((keys & GameCanvas.FIRE_PRESSED) != 0)
            key[FIRE] = true;
        else
            key[FIRE] = false;
       
        if((keys & GameCanvas.UP_PRESSED) != 0)
            key[UP] = true;
        else
            key[UP] = false;
       
        if((keys & GameCanvas.DOWN_PRESSED) != 0)
            key[DOWN] = true;
        else
            key[DOWN] = false;
       
        if((keys & GameCanvas.LEFT_PRESSED) != 0)
            key[LEFT] = true;
        else
            key[LEFT] = false;
       
        if((keys & GameCanvas.RIGHT_PRESSED) != 0)
            key[RIGHT] = true;
        else
            key[RIGHT] = false;
    }
   
    /** Checks if thread is running
     */
    public boolean isRunning() { return running; }
   
    /** checks if thread has finished its execution completely
     */
    public boolean isDone() { return done; }
}

MeshFactory

import java.io.IOException;

import javax.microedition.lcdui.Image;
import javax.microedition.m3g.Appearance;
import javax.microedition.m3g.Image2D;
import javax.microedition.m3g.IndexBuffer;
import javax.microedition.m3g.Light;
import javax.microedition.m3g.Material;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Texture2D;
import javax.microedition.m3g.TriangleStripArray;
import javax.microedition.m3g.VertexArray;
import javax.microedition.m3g.VertexBuffer;


/**
 * Static class that handles creation of code-generated Meshes
 */
public class MeshFactory
{
    /** Creates a texture plane that is alpha-blended
     *
     * @param texFilename The name of the texture image file
     * @param cullFlags The flags for culling. See PolygonMode.
     * @param alpha The alpha value of blending. Is a full color in 0xAARRGGBB format
     * @return The finished textured mesh
     */
    public static Mesh createAlphaPlane(String texFilename, int cullFlags, int alpha)
    {
        // Create a normal mesh
        Mesh mesh = createPlane(texFilename, cullFlags);
       
        // Make it blended
        MeshOperator.convertToBlended(mesh, alpha, Texture2D.FUNC_BLEND);

        return mesh;
    }
   
    /**
     * Creates a colored quad consisting of two triangles.
     * The heights parameter is an array that holds the heights of the new quad.
     * The array must have a length of 4 and goes counter-clockwise starting from the
     * northwestern position of the quad (-1, -1)
     * @param heights The height array, denoting the heights (y-coords) of the four corners.
     * @param cullFlags
     * @param texFilename
     * @return
     */
    public static Mesh createQuad(short[] heights, int cullFlags)
    {
        // The vertrices of the quad
        short[] vertrices = {-255, heights[0], -255,
                255, heights[1], -255,
                255, heights[2], 255,
                -255, heights[3], 255};
       
        // The arrays
        VertexArray vertexArray;
        IndexBuffer triangles;

        // Create the model's vertrices
        vertexArray = new VertexArray(vertrices.length/3, 3, 2);
        vertexArray.set(0, vertrices.length/3, vertrices);
       
        // Allocate color array
        byte[] color = new byte[12];
       
        for(int i = 0; i < heights.length; i++)
        {
            int j = i * 3;
         // Altitude check
            if(heights[i] >= 1000)
            {
                byte col = (byte)(57 + (heights[i] / 1550.0f) * 70);
                color[j] = col;
                color[j + 1] = col;
                color[j + 2] = col;
            }
         else
         {
             byte gCol = 110;
             byte bCol = 25;
            
             color[j] = 0;
             color[j + 1] = (byte)(gCol - (heights[i] / 1000.0f) * 85);
             color[j + 2] = (byte)(bCol - (heights[i] / 1000.0f) * 20);
         }
        }
       
        // Create the model's vertex colors
        VertexArray colorArray = new VertexArray(color.length/3, 3, 1);
        colorArray.set(0, color.length / 3, color);
       
        // Compose a VertexBuffer out of the previous vertrices and texture coordinates
        VertexBuffer vertexBuffer = new VertexBuffer();
        vertexBuffer.setPositions(vertexArray, 1.0f, null);
        vertexBuffer.setColors(colorArray);
       
        // Create indices and face lengths
        int indices[] = new int[] {0, 1, 3, 2};
        int[] stripLengths = new int[] {4};
       
        // Create the model's triangles
        triangles = new TriangleStripArray(indices, stripLengths);


        // Create the appearance
        Appearance appearance = new Appearance();
        PolygonMode pm = new PolygonMode();
        pm.setCulling(cullFlags);
        pm.setPerspectiveCorrectionEnable(true);
        pm.setShading(PolygonMode.SHADE_SMOOTH);
        appearance.setPolygonMode(pm);
       
        // Finally create the Mesh
        Mesh mesh = new Mesh(vertexBuffer, triangles, appearance);

        // All done
        return mesh;
    }

    /**
     * @param texFilename
     * @return
     * @throws IOException
     */
    public static Texture2D createTexture2D(String texFilename) throws IOException {
        // Open image
        Image texImage = Image.createImage(texFilename);
        Texture2D theTexture = new Texture2D(new Image2D(Image2D.RGBA, texImage));
       
        // Modulated blending
        theTexture.setBlending(Texture2D.FUNC_MODULATE);
       
        // Set wrapping and filtering
        theTexture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP);
        theTexture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST);
        return theTexture;
    }

    /**
     * Creates a textured plane.
     * @param texFilename The name of the texture image file
     * @param cullFlags The flags for culling. See PolygonMode.
     * @return The finished textured mesh
     */
    public static Mesh createPlane(String texFilename, int cullFlags)
    {
        // The vertrices of the plane
        short vertrices[] = new short[] {-1, -1, 0,
                                       1, -1, 0,
                                       1, 1, 0,
                                       -1, 1, 0};


        // Texture coords of the plane
        short texCoords[] = new short[] {0, 255,
                                         255, 255,
                                         255, 0,
                                         0, 0};

       
        // The classes
        VertexArray vertexArray, texArray;
        IndexBuffer triangles;

        // Create the model's vertrices
        vertexArray = new VertexArray(vertrices.length/3, 3, 2);
        vertexArray.set(0, vertrices.length/3, vertrices);
       
        // Create the model's texture coords
        texArray = new VertexArray(texCoords.length / 2, 2, 2);
        texArray.set(0, texCoords.length / 2, texCoords);
       
        // Compose a VertexBuffer out of the previous vertrices and texture coordinates
        VertexBuffer vertexBuffer = new VertexBuffer();
        vertexBuffer.setPositions(vertexArray, 1.0f, null);
        vertexBuffer.setTexCoords(0, texArray, 1.0f/255.0f, null);
       
        // Create indices and face lengths
        int indices[] = new int[] {0, 1, 3, 2};
        int[] stripLengths = new int[] {4};
       
        // Create the model's triangles
        triangles = new TriangleStripArray(indices, stripLengths);


        // Create the appearance
        Appearance appearance = new Appearance();
        PolygonMode pm = new PolygonMode();
        pm.setCulling(cullFlags);
        appearance.setPolygonMode(pm);

        // Create and set the texture
        try
        {
            // Open image
            Texture2D theTexture = createTexture2D(texFilename);

            // Add texture to the appearance
            appearance.setTexture(0, theTexture);

        }
        catch(Exception e)
        {
            // Something went wrong
            System.out.println("Failed to create texture");
            System.out.println(e);
        }
       
        // Finally create the Mesh
        Mesh mesh = new Mesh(vertexBuffer, triangles, appearance);

        // All done
        return mesh;
    }
}

MeshOperator

import javax.microedition.m3g.CompositingMode;
import javax.microedition.m3g.Mesh;
/**
 * Performs some basic operations on Mesh objects
 */
public class MeshOperator
{
    /** Sets the alpha blending of a mesh. Only meaningful if the mesh already is alpha blended */
    public static void setMeshAlpha(Mesh m, int alpha)
    {
        m.getVertexBuffer().setDefaultColor(alpha);
    }
   
    /**
     *
     * @param m The mesh to convert to a blended one
     * @param alpha The alpha color to blend with
     * @param textureBlending The texture blending parameter.
     */
    public static void convertToBlended(Mesh m, int alpha, int textureBlending)
    {
        // Set the alpha
        setMeshAlpha(m, alpha);
       
        // Fix the compositing mode
        CompositingMode cm = new CompositingMode();
        cm.setBlending(CompositingMode.ALPHA);
        m.getAppearance(0).setCompositingMode(cm);
        m.getAppearance(0).getTexture(0).setBlending(textureBlending);
    }
   
    public static void setPerspectiveCorrection(Mesh m, boolean on)
    {
        m.getAppearance(0).getPolygonMode().setPerspectiveCorrectionEnable(on);
    }
}


HeightMap
import java.io.IOException;

import javax.microedition.lcdui.Image;
import javax.microedition.m3g.Graphics3D;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Transform;

/**
 *
 */
public class HeightMap
{
    // The actual heightmap containing the Y-coords of our triangles
    private short[] heightMap;
    private int[] data;
    private int imgw, imgh;
   
    // Map dimensions
    private int mapWidth;
    private int mapHeight;
   
    // Actual quads
    private Mesh[][] map;
   
    // Water
    private Mesh water;
   
    // Local transform used for internal calculations
    private Transform localTransform = new Transform();
   
    /**
     * Allocates the internal heightmap and prepares it for rendering.
     * @param imageName The path to the actual heightmap BW image
     * @param resolution The resolution of the heightmap. A value of 1.0 means that each pixel is one quad.
     * @param waterLevel TODO
     * @throws IOException if any error occurs while loading the heightmap image or the texture images.
     */
    public HeightMap(String imageName, float resolution, int waterLevel) throws IOException
    {
        // Check for invalid resolution values
        if(resolution <= 0.0001f resolution > 1.0f)
            throw new IllegalArgumentException("Resolution too small or too large");
       
        // Load image and allocate the internal array
        loadImage(imageName, resolution);
       
        // Create quads
        createQuads();
       
        // Create the water
        createWater(waterLevel);
    }
   
    /** Create water in a very simple manner by representing it with
     * a textured quad.
     */
    private void createWater(int level) throws IOException
    {
        water = MeshFactory.createPlane("/res/water0.png", PolygonMode.CULL_NONE);
    }
   
    /**
     * Create all quads (Triangle pairs) that our
     */
    private void createQuads()
    {
        map = new Mesh[mapWidth][mapHeight];
        short[] heights = new short[4];
       
        for(int x = 0; x < (mapWidth - 1); x++)
        {
            for(int y = 0; y < (mapHeight - 1); y++)
            {
                // Set heights
                setQuadHeights(heights, x, y, mapWidth);
               
                // Create mesh
                map[x][y] = MeshFactory.createQuad(heights, PolygonMode.CULL_NONE);
            }
        }
    }


    /**
     * @param heights
     * @param x
     * @param y
     */
    private void setQuadHeights(short[] heights, int x, int y, int scanline)
    {
        heights[0] = heightMap[x + y * scanline];
        heights[1] = heightMap[x + y * scanline + 1];
        heights[3] = heightMap[x + (y + 1) * scanline];
        heights[2] = heightMap[x + (y + 1) * scanline + 1];
    }

    private void loadImage(String path, float res) throws IOException
    {
        // Load actual image
        Image img = Image.createImage(path);
       
        // Allocate temporary memory to store pixels
        data = new int[img.getWidth() * img.getHeight()];
       
        // Get its rgb values
        img.getRGB(data, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
       
        imgw = img.getWidth();
        imgh = img.getHeight();
       
        // Clear image
        img = null;
        System.gc();
       
        // Calculate new width and height
        mapWidth = (int)(res * imgw);
        mapHeight = (int)(res * imgh);
       
        // Allocate heightmap
        heightMap = new short[mapWidth * mapHeight];
       
        // Calculate height and width offset into image
        int xoff = imgw / mapWidth;
        int yoff = imgh / mapHeight;
       
        // Set height values
        for(int y = 0; y < mapHeight; y++)
        {
            for(int x = 0; x < mapWidth; x++)
            {
                heightMap[x + y * mapWidth] = (short)((data[x * xoff + y * yoff * imgw] & 0x000000ff) * 10);
            }
        }       
       
        // Clear data
        data = null;
        img = null;
        System.gc();
    }
   
    /**
     * Renders this heightmap using a specified graphics context and a transform.
     * @param g3d
     * @param t
     */
    public void render(Graphics3D g3d, Transform t)
    {
        for(int x = 0; x < map.length - 1; x++)
        {
            for(int y = 0; y < map[x].length - 1; y++)
            {
                localTransform.setIdentity();
                localTransform.postTranslate(x * 5.0f, 0.0f, (mapHeight - y) * -5.0f);
                localTransform.postMultiply(t);
                g3d.render(map[x][y], localTransform);
            }
        }
       
        localTransform.setIdentity();
        localTransform.postScale(255, 255, 255);
        localTransform.postRotate(-90, 1.0f, 0.0f, 0.0f);
        g3d.render(water, localTransform);
    }
}


相關資源鏈接:

源代碼、應用程序(JAR/JAD)

原文地址

(出處:http://www.49028c.com)



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
人人澡人人澡人人看欧美| www国产精品com| 亚洲成人黄色网址| 欧美美女操人视频| 国产一区二区三区18| 亚洲色图色老头| 久久久久久国产精品美女| 欧美激情一区二区久久久| 欧美视频在线观看免费| 成人网中文字幕| 午夜精品久久久久久久久久久久| 最近2019免费中文字幕视频三| 日本欧美一级片| 亚洲综合成人婷婷小说| 久久久久久91| 日韩精品视频在线观看免费| 国产亚洲人成网站在线观看| 国产精品视频白浆免费视频| 91国产美女在线观看| 日韩欧美国产黄色| 中文日韩在线观看| 欧美日韩一区二区三区| 国产精品精品视频| 亚洲精品理论电影| 欧美日韩国产精品一区二区不卡中文| 欧美另类极品videosbestfree| 一区二区三区国产视频| 欧美国产高跟鞋裸体秀xxxhd| 中文字幕日韩av| 最近2019中文字幕mv免费看| 亚洲视频电影图片偷拍一区| 91久久国产综合久久91精品网站| 色七七影院综合| 亚洲娇小xxxx欧美娇小| 91成人在线观看国产| 日韩成人激情视频| 久久精品中文字幕免费mv| 欧美一级黄色网| 在线观看久久av| 久久福利视频导航| 欧洲亚洲免费视频| 91国语精品自产拍在线观看性色| 精品人伦一区二区三区蜜桃网站| 高清在线视频日韩欧美| 亚洲a∨日韩av高清在线观看| www.日韩不卡电影av| 欧美日韩国产色| 欧美一区二区大胆人体摄影专业网站| 国产精品91视频| 亚洲视频欧美视频| 国产精品99蜜臀久久不卡二区| 91在线观看免费网站| 欧美性xxxxxx| 日韩精品一区二区三区第95| 国产精品777| 久久精品视频va| 国产综合在线视频| 中文字幕精品一区久久久久| 欧美xxxx18国产| 精品视频在线播放| 色偷偷888欧美精品久久久| 麻豆国产va免费精品高清在线| 久久视频在线视频| 国产一区二区三区日韩欧美| 欧美精品久久久久| 亚洲а∨天堂久久精品9966| 久久免费高清视频| 亚洲国产精品系列| 日韩成人av在线播放| 色偷偷噜噜噜亚洲男人的天堂| 欧美电影免费播放| 91精品国产高清久久久久久91| 伊人青青综合网站| 精品国产91久久久久久| 久久精品夜夜夜夜夜久久| 欧美高清无遮挡| 国产成人精品视频| 日韩在线视频观看| 午夜精品免费视频| 最近2019年好看中文字幕视频| 性欧美在线看片a免费观看| 亚洲综合日韩中文字幕v在线| 高清一区二区三区四区五区| 精品久久久久久国产| 91香蕉嫩草影院入口| 国产欧美日韩最新| 国产一区二区美女视频| 亚洲综合自拍一区| 欧美日韩中文字幕在线| 4k岛国日韩精品**专区| 中文字幕国内精品| 亚洲片av在线| 成人情趣片在线观看免费| 麻豆国产精品va在线观看不卡| 亚洲第一区第一页| 国产美女直播视频一区| 欧美精品久久一区二区| 成人春色激情网| 日韩中文字幕在线精品| 国产性猛交xxxx免费看久久| 国产精品国语对白| 日韩精品视频免费在线观看| 日韩专区中文字幕| 亚洲视频欧美视频| 亚洲精品国产拍免费91在线| 久久午夜a级毛片| 精品亚洲一区二区三区| 欧美猛男性生活免费| 九九热视频这里只有精品| 国产亚洲a∨片在线观看| 97成人精品视频在线观看| 国产一区二区视频在线观看| 久久久久一本一区二区青青蜜月| 亚洲精品狠狠操| 国产精品色午夜在线观看| 成人字幕网zmw| 亚洲成在人线av| 亚洲第一视频在线观看| 久久久久国产精品免费| 欧美激情一区二区三区高清视频| 欧美专区在线观看| 日韩精品视频免费在线观看| 国产精品久久久久高潮| 亚洲欧洲视频在线| 国产亚洲欧洲高清| 欧美黑人一级爽快片淫片高清| 亚洲一级免费视频| 欧美精品一本久久男人的天堂| 97超级碰在线看视频免费在线看| 精品久久久久久久久久久久| 九九热这里只有精品免费看| 奇门遁甲1982国语版免费观看高清| 欧美巨乳在线观看| 欧美日韩中国免费专区在线看| 国产视频精品一区二区三区| 日韩欧美精品中文字幕| 91沈先生作品| 怡红院精品视频| 992tv成人免费视频| 97在线观看视频国产| 亚洲综合第一页| 国产z一区二区三区| 日韩高清免费观看| 中文字幕日韩有码| 欧美日韩美女在线观看| 国产欧美 在线欧美| 欧美xxxx18性欧美| 大胆欧美人体视频| 久久国产精彩视频| 国产精品第七影院| 国产va免费精品高清在线| 日韩精品中文字幕在线观看| 久久精品国产成人| 成人免费网站在线观看| 色香阁99久久精品久久久| 热久久免费国产视频| 国产一区二区三区三区在线观看| 欧美中文字幕视频在线观看| 亚洲精品电影网| 2019日本中文字幕| 国产日韩欧美一二三区| 欧美最猛性xxxxx(亚洲精品)| 91精品国产免费久久久久久| 亚洲免费电影在线观看|