summaryrefslogtreecommitdiffstats
path: root/libs/rs/java/ModelViewer/src/com/android/modelviewer
diff options
context:
space:
mode:
authorAlex Sakhartchouk <alexst@google.com>2011-02-16 16:08:49 -0800
committerAlex Sakhartchouk <alexst@google.com>2011-02-16 16:08:49 -0800
commitdc165b3365ca3e8fd7c4eb11b1154646977a6d0a (patch)
tree0ebac4e7ef44f00cdbb56e2c9e7271e4b013d8a2 /libs/rs/java/ModelViewer/src/com/android/modelviewer
parent95d6b782a2b96f6f1c4657de09b34a6bef1b36ed (diff)
downloadframeworks_base-dc165b3365ca3e8fd7c4eb11b1154646977a6d0a.zip
frameworks_base-dc165b3365ca3e8fd7c4eb11b1154646977a6d0a.tar.gz
frameworks_base-dc165b3365ca3e8fd7c4eb11b1154646977a6d0a.tar.bz2
Adding better navigation to model viewer (pinch to zoom, rotations)
Adding ability to load a3d files from disk. Change-Id: I8697483cf0e3136113e74f9239dc5bc3de770cee
Diffstat (limited to 'libs/rs/java/ModelViewer/src/com/android/modelviewer')
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java110
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java43
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java97
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java130
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs124
5 files changed, 405 insertions, 99 deletions
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java
new file mode 100644
index 0000000..0e2004f
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.modelviewer;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * A list view where the last item the user clicked is placed in
+ * the "activated" state, causing its background to highlight.
+ */
+public class A3DSelector extends ListActivity {
+
+ File[] mCurrentSubList;
+ File mCurrentFile;
+
+ class A3DFilter implements FileFilter {
+ public boolean accept(File file) {
+ if (file.isDirectory()) {
+ return true;
+ }
+ return file.getName().endsWith(".a3d");
+ }
+ }
+
+ private void populateList(File file) {
+
+ mCurrentFile = file;
+ setTitle(mCurrentFile.getAbsolutePath() + "/*.a3d");
+ List<String> names = new ArrayList<String>();
+ names.add("..");
+
+ mCurrentSubList = mCurrentFile.listFiles(new A3DFilter());
+
+ if (mCurrentSubList != null) {
+ for (int i = 0; i < mCurrentSubList.length; i ++) {
+ String fileName = mCurrentSubList[i].getName();
+ if (mCurrentSubList[i].isDirectory()) {
+ fileName = "/" + fileName;
+ }
+ names.add(fileName);
+ }
+ }
+
+ // Use the built-in layout for showing a list item with a single
+ // line of text whose background is changes when activated.
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_activated_1, names));
+ getListView().setTextFilterEnabled(true);
+
+ // Tell the list view to show one checked/activated item at a time.
+ getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ populateList(new File("/sdcard/"));
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (position == 0) {
+ File parent = mCurrentFile.getParentFile();
+ if (parent == null) {
+ return;
+ }
+ populateList(parent);
+ return;
+ }
+
+ // the first thing in list is parent directory
+ File selectedFile = mCurrentSubList[position - 1];
+ if (selectedFile.isDirectory()) {
+ populateList(selectedFile);
+ return;
+ }
+
+ Intent resultIntent = new Intent();
+ resultIntent.setData(Uri.fromFile(selectedFile));
+ setResult(RESULT_OK, resultIntent);
+ finish();
+ }
+
+}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
index 9910970..01cb709 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
@@ -21,6 +21,7 @@ import android.renderscript.RenderScript;
import android.app.Activity;
import android.content.res.Configuration;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -31,9 +32,11 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.MenuInflater;
import android.view.Window;
import android.widget.Button;
import android.widget.ListView;
+import android.net.Uri;
import java.lang.Runtime;
@@ -67,5 +70,45 @@ public class SimpleModel extends Activity {
mView.pause();
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.loader_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.load_model:
+ loadModel();
+ return true;
+ case R.id.display_options:
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private static final int FIND_A3D_MODEL = 10;
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == RESULT_OK) {
+ if (requestCode == FIND_A3D_MODEL) {
+ Uri selectedImageUri = data.getData();
+ Log.e("Selected Path: ", selectedImageUri.getPath());
+ mView.loadA3DFile(selectedImageUri.getPath());
+ }
+ }
+ }
+
+ public void loadModel() {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_PICK);
+ intent.setClassName("com.android.modelviewer",
+ "com.android.modelviewer.A3DSelector");
+ startActivityForResult(intent, FIND_A3D_MODEL);
+ }
+
}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
index b18a327..63ef466 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,24 +26,20 @@ import android.util.Log;
public class SimpleModelRS {
- private final int STATE_LAST_FOCUS = 1;
-
- int mWidth;
- int mHeight;
- int mRotation;
-
public SimpleModelRS() {
}
- public void init(RenderScriptGL rs, Resources res, int width, int height) {
+ public void init(RenderScriptGL rs, Resources res) {
mRS = rs;
mRes = res;
- mWidth = width;
- mHeight = height;
- mRotation = 0;
initRS();
}
+ public void surfaceChanged() {
+ mRS.getWidth();
+ mRS.getHeight();
+ }
+
private Resources mRes;
private RenderScriptGL mRS;
private Sampler mSampler;
@@ -55,34 +51,23 @@ public class SimpleModelRS {
private Allocation mGridImage;
private Allocation mAllocPV;
- private Mesh mMesh;
-
private Font mItalic;
private Allocation mTextAlloc;
+ private ScriptField_MeshInfo mMeshes;
private ScriptC_simplemodel mScript;
- int mLastX;
- int mLastY;
- public void touchEvent(int x, int y) {
- int dx = mLastX - x;
- if (Math.abs(dx) > 50 || Math.abs(dx) < 3) {
- dx = 0;
- }
-
- mRotation -= dx;
- if (mRotation > 360) {
- mRotation -= 360;
- }
- if (mRotation < 0) {
- mRotation += 360;
- }
+ public void onActionDown(float x, float y) {
+ mScript.invoke_onActionDown(x, y);
+ }
- mScript.set_gRotate((float)mRotation);
+ public void onActionScale(float scale) {
+ mScript.invoke_onActionScale(scale);
+ }
- mLastX = x;
- mLastY = y;
+ public void onActionMove(float x, float y) {
+ mScript.invoke_onActionMove(x, y);
}
private void initPFS() {
@@ -130,12 +115,48 @@ public class SimpleModelRS {
mScript.set_gTGrid(mGridImage);
}
- private void initTextAllocation() {
- String allocString = "Displaying file: R.raw.robot";
+ private void initTextAllocation(String fileName) {
+ String allocString = "Displaying file: " + fileName;
mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT);
mScript.set_gTextAlloc(mTextAlloc);
}
+ private void initMeshes(FileA3D model) {
+ int numEntries = model.getIndexEntryCount();
+ int numMeshes = 0;
+ for (int i = 0; i < numEntries; i ++) {
+ FileA3D.IndexEntry entry = model.getIndexEntry(i);
+ if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+ numMeshes ++;
+ }
+ }
+
+ if (numMeshes > 0) {
+ mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
+
+ for (int i = 0; i < numEntries; i ++) {
+ FileA3D.IndexEntry entry = model.getIndexEntry(i);
+ if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+ Mesh mesh = entry.getMesh();
+ mMeshes.set_mMesh(i, mesh, false);
+ mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
+ }
+ }
+ mMeshes.copyAll();
+ } else {
+ throw new RSRuntimeException("No valid meshes in file");
+ }
+
+ mScript.bind_gMeshes(mMeshes);
+ mScript.invoke_updateMeshInfo();
+ }
+
+ public void loadA3DFile(String path) {
+ FileA3D model = FileA3D.createFromFile(mRS, path);
+ initMeshes(model);
+ initTextAllocation(path);
+ }
+
private void initRS() {
mScript = new ScriptC_simplemodel(mRS, mRes, R.raw.simplemodel);
@@ -147,18 +168,12 @@ public class SimpleModelRS {
loadImage();
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
- FileA3D.IndexEntry entry = model.getIndexEntry(0);
- if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
- Log.e("rs", "could not load model");
- } else {
- mMesh = (Mesh)entry.getObject();
- mScript.set_gTestMesh(mMesh);
- }
+ initMeshes(model);
mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
mScript.set_gItalic(mItalic);
- initTextAllocation();
+ initTextAllocation("R.raw.robot");
mRS.bindRootScript(mScript);
}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
index 5c5956d..9ab0f22 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,81 +16,129 @@
package com.android.modelviewer;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
import android.renderscript.RenderScriptGL;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.ScaleGestureDetector;
+import android.util.Log;
public class SimpleModelView extends RSSurfaceView {
- public SimpleModelView(Context context) {
- super(context);
- //setFocusable(true);
- }
-
private RenderScriptGL mRS;
private SimpleModelRS mRender;
+ private ScaleGestureDetector mScaleDetector;
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- super.surfaceChanged(holder, format, w, h);
+ private static final int INVALID_POINTER_ID = -1;
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ public SimpleModelView(Context context) {
+ super(context);
+ ensureRenderScript();
+ mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+ }
+
+ private void ensureRenderScript() {
if (mRS == null) {
RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
sc.setDepth(16, 24);
mRS = createRenderScriptGL(sc);
- mRS.setSurface(holder, w, h);
mRender = new SimpleModelRS();
- mRender.init(mRS, getResources(), w, h);
+ mRender.init(mRS, getResources());
}
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ensureRenderScript();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+ mRender.surfaceChanged();
+ }
+
+ @Override
protected void onDetachedFromWindow() {
+ mRender = null;
if (mRS != null) {
mRS = null;
destroyRenderScriptGL();
}
}
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event)
- {
- // break point at here
- // this method doesn't work when 'extends View' include 'extends ScrollView'.
- return super.onKeyDown(keyCode, event);
+ public void loadA3DFile(String path) {
+ mRender.loadA3DFile(path);
}
-
@Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- boolean ret = true;
- int act = ev.getAction();
- if (act == ev.ACTION_UP) {
- ret = false;
+ public boolean onTouchEvent(MotionEvent ev) {
+ mScaleDetector.onTouchEvent(ev);
+
+ boolean ret = false;
+ float x = ev.getX();
+ float y = ev.getY();
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ mRender.onActionDown(x, y);
+ mActivePointerId = ev.getPointerId(0);
+ ret = true;
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (!mScaleDetector.isInProgress()) {
+ mRender.onActionMove(x, y);
+ }
+ mRender.onActionDown(x, y);
+ ret = true;
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_POINTER_UP: {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ x = ev.getX(newPointerIndex);
+ y = ev.getY(newPointerIndex);
+ mRender.onActionDown(x, y);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ }
+ break;
+ }
}
- mRender.touchEvent((int)ev.getX(), (int)ev.getY());
return ret;
}
+
+ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mRender.onActionScale(detector.getScaleFactor());
+ return true;
+ }
+ }
}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
index 419de62..1034b85 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,17 +22,112 @@ rs_program_vertex gPVBackground;
rs_program_fragment gPFBackground;
rs_allocation gTGrid;
-rs_mesh gTestMesh;
rs_program_store gPFSBackground;
-float gRotate;
-
rs_font gItalic;
rs_allocation gTextAlloc;
+typedef struct MeshInfo {
+ rs_mesh mMesh;
+ int mNumIndexSets;
+ float3 bBoxMin;
+ float3 bBoxMax;
+} MeshInfo_t;
+
+MeshInfo_t *gMeshes;
+
+static float3 gLookAt;
+
+static float gRotateX;
+static float gRotateY;
+static float gZoom;
+
+static float gLastX;
+static float gLastY;
+
+void onActionDown(float x, float y) {
+ gLastX = x;
+ gLastY = y;
+}
+
+void onActionScale(float scale) {
+
+ gZoom *= 1.0f / scale;
+ gZoom = max(0.1f, min(gZoom, 500.0f));
+}
+
+void onActionMove(float x, float y) {
+ float dx = gLastX - x;
+ float dy = gLastY - y;
+
+ if (fabs(dy) <= 2.0f) {
+ dy = 0.0f;
+ }
+ if (fabs(dx) <= 2.0f) {
+ dx = 0.0f;
+ }
+
+ gRotateY -= dx;
+ if (gRotateY > 360) {
+ gRotateY -= 360;
+ }
+ if (gRotateY < 0) {
+ gRotateY += 360;
+ }
+
+ gRotateX -= dy;
+ gRotateX = min(gRotateX, 80.0f);
+ gRotateX = max(gRotateX, -80.0f);
+
+ gLastX = x;
+ gLastY = y;
+}
+
void init() {
- gRotate = 0.0f;
+ gRotateX = 0.0f;
+ gRotateY = 0.0f;
+ gZoom = 50.0f;
+ gLookAt = 0.0f;
+}
+
+void updateMeshInfo() {
+ rs_allocation allMeshes = rsGetAllocation(gMeshes);
+ int size = rsAllocationGetDimX(allMeshes);
+ gLookAt = 0.0f;
+ float minX, minY, minZ, maxX, maxY, maxZ;
+ for (int i = 0; i < size; i++) {
+ MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
+ rsgMeshComputeBoundingBox(info->mMesh,
+ &minX, &minY, &minZ,
+ &maxX, &maxY, &maxZ);
+ info->bBoxMin = (minX, minY, minZ);
+ info->bBoxMax = (maxX, maxY, maxZ);
+ gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
+ }
+ gLookAt = gLookAt / (float)size;
+}
+
+static void renderAllMeshes() {
+ rs_allocation allMeshes = rsGetAllocation(gMeshes);
+ int size = rsAllocationGetDimX(allMeshes);
+ gLookAt = 0.0f;
+ float minX, minY, minZ, maxX, maxY, maxZ;
+ for (int i = 0; i < size; i++) {
+ MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
+ rsgDrawMesh(info->mMesh);
+ }
+}
+
+void drawDescription() {
+ uint width = rsgGetWidth();
+ uint height = rsgGetHeight();
+ int left = 0, right = 0, top = 0, bottom = 0;
+
+ rsgBindFont(gItalic);
+
+ rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
+ rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom);
}
int root(int launchID) {
@@ -43,7 +138,7 @@ int root(int launchID) {
rsgBindProgramVertex(gPVBackground);
rs_matrix4x4 proj;
float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
- rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+ rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
rsgProgramVertexLoadProjectionMatrix(&proj);
rsgBindProgramFragment(gPFBackground);
@@ -52,20 +147,15 @@ int root(int launchID) {
rs_matrix4x4 matrix;
rsMatrixLoadIdentity(&matrix);
- // Position our model on the screen
- rsMatrixTranslate(&matrix, 0.0f, -0.3f, -10.0f);
- rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
- rsMatrixRotate(&matrix, 25.0f, 1.0f, 0.0f, 0.0f);
- rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f);
+ // Position our models on the screen
+ rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
+ rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
+ rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
rsgProgramVertexLoadModelMatrix(&matrix);
- rsgDrawMesh(gTestMesh);
-
- rsgFontColor(0.3f, 0.3f, 0.3f, 1.0f);
- rsgDrawText("Renderscript model test", 30, 695);
+ renderAllMeshes();
- rsgBindFont(gItalic);
- rsgDrawText(gTextAlloc, 30, 730);
+ drawDescription();
return 10;
}