1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.scenegraph;
18
19import java.io.BufferedInputStream;
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.Writer;
25import java.lang.Math;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31import com.android.scenegraph.Camera;
32import com.android.scenegraph.FragmentShader;
33import com.android.scenegraph.MatrixTransform;
34import com.android.scenegraph.Scene;
35import com.android.scenegraph.VertexShader;
36import com.android.testapp.R;
37
38import android.content.res.Resources;
39import android.graphics.Bitmap;
40import android.graphics.BitmapFactory;
41import android.os.AsyncTask;
42import android.renderscript.*;
43import android.renderscript.Allocation.MipmapControl;
44import android.renderscript.Mesh;
45import android.renderscript.RenderScriptGL;
46import android.util.Log;
47import android.view.SurfaceHolder;
48
49/**
50 * @hide
51 */
52public class SceneManager extends SceneGraphBase {
53
54    HashMap<String, Allocation> mAllocationMap;
55
56    ScriptC_render mRenderLoop;
57    ScriptC mCameraScript;
58    ScriptC mLightScript;
59    ScriptC mObjectParamsScript;
60    ScriptC mFragmentParamsScript;
61    ScriptC mVertexParamsScript;
62    ScriptC mCullScript;
63    ScriptC_transform mTransformScript;
64    ScriptC_export mExportScript;
65
66    RenderScriptGL mRS;
67    Resources mRes;
68    Mesh mQuad;
69    int mWidth;
70    int mHeight;
71
72    Scene mActiveScene;
73    private static SceneManager sSceneManager;
74
75    private Allocation mDefault2D;
76    private Allocation mDefaultCube;
77
78    private FragmentShader mColor;
79    private FragmentShader mTexture;
80    private VertexShader mDefaultVertex;
81
82    private RenderState mDefaultState;
83    private Transform mDefaultTransform;
84
85    private static Allocation getDefault(boolean isCube) {
86        final int dimension = 4;
87        final int bytesPerPixel = 4;
88        int arraySize = dimension * dimension * bytesPerPixel;
89
90        RenderScriptGL rs = sSceneManager.mRS;
91        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));
92        b.setX(dimension).setY(dimension);
93        if (isCube) {
94            b.setFaces(true);
95            arraySize *= 6;
96        }
97        Type bitmapType = b.create();
98
99        Allocation.MipmapControl mip = Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
100        int usage =  Allocation.USAGE_GRAPHICS_TEXTURE;
101        Allocation defaultImage = Allocation.createTyped(rs, bitmapType, mip, usage);
102
103        byte imageData[] = new byte[arraySize];
104        defaultImage.copyFrom(imageData);
105        return defaultImage;
106    }
107
108    static Allocation getDefaultTex2D() {
109        if (sSceneManager == null) {
110            return null;
111        }
112        if (sSceneManager.mDefault2D == null) {
113            sSceneManager.mDefault2D = getDefault(false);
114        }
115        return sSceneManager.mDefault2D;
116    }
117
118    static Allocation getDefaultTexCube() {
119        if (sSceneManager == null) {
120            return null;
121        }
122        if (sSceneManager.mDefaultCube == null) {
123            sSceneManager.mDefaultCube = getDefault(true);
124        }
125        return sSceneManager.mDefaultCube;
126    }
127
128    public static boolean isSDCardPath(String path) {
129        int sdCardIndex = path.indexOf("sdcard/");
130        // We are looking for /sdcard/ or sdcard/
131        if (sdCardIndex == 0 || sdCardIndex == 1) {
132            return true;
133        }
134        sdCardIndex = path.indexOf("mnt/sdcard/");
135        if (sdCardIndex == 0 || sdCardIndex == 1) {
136            return true;
137        }
138        return false;
139    }
140
141    static Bitmap loadBitmap(String name, Resources res) {
142        InputStream is = null;
143        boolean loadFromSD = isSDCardPath(name);
144        try {
145            if (!loadFromSD) {
146                is = res.getAssets().open(name);
147            } else {
148                File f = new File(name);
149                is = new BufferedInputStream(new FileInputStream(f));
150            }
151        } catch (IOException e) {
152            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
153            return null;
154        }
155
156        Bitmap b = BitmapFactory.decodeStream(is);
157        try {
158            is.close();
159        } catch (IOException e) {
160            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
161        }
162        return b;
163    }
164
165    static Allocation createFromBitmap(Bitmap b, RenderScriptGL rs, boolean isCube) {
166        if (b == null) {
167            return null;
168        }
169        MipmapControl mip = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
170        int usage = Allocation.USAGE_GRAPHICS_TEXTURE;
171        if (isCube) {
172            return Allocation.createCubemapFromBitmap(rs, b, mip, usage);
173        }
174        return Allocation.createFromBitmap(rs, b, mip, usage);
175    }
176
177    public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) {
178        return createFromBitmap(loadBitmap(name, res), rs, true);
179    }
180
181    public static Allocation loadCubemap(int id, RenderScriptGL rs, Resources res) {
182        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, true);
183    }
184
185    public static Allocation loadTexture2D(String name, RenderScriptGL rs, Resources res) {
186        return createFromBitmap(loadBitmap(name, res), rs, false);
187    }
188
189    public static Allocation loadTexture2D(int id, RenderScriptGL rs, Resources res) {
190        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, false);
191    }
192
193    public static ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
194        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
195        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
196        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
197        builder.setDitherEnabled(false);
198        builder.setDepthMaskEnabled(false);
199        return builder.create();
200    }
201
202    static Allocation getStringAsAllocation(RenderScript rs, String str) {
203        if (str == null) {
204            return null;
205        }
206        if (str.length() == 0) {
207            return null;
208        }
209        byte[] allocArray = null;
210        byte[] nullChar = new byte[1];
211        nullChar[0] = 0;
212        try {
213            allocArray = str.getBytes("UTF-8");
214            Allocation alloc = Allocation.createSized(rs, Element.U8(rs),
215                                                      allocArray.length + 1,
216                                                      Allocation.USAGE_SCRIPT);
217            alloc.copy1DRangeFrom(0, allocArray.length, allocArray);
218            alloc.copy1DRangeFrom(allocArray.length, 1, nullChar);
219            return alloc;
220        }
221        catch (Exception e) {
222            throw new RSRuntimeException("Could not convert string to utf-8.");
223        }
224    }
225
226    static Allocation getCachedAlloc(String str) {
227        if (sSceneManager == null) {
228            throw new RuntimeException("Scene manager not initialized");
229        }
230        return sSceneManager.mAllocationMap.get(str);
231    }
232
233    static void cacheAlloc(String str, Allocation alloc) {
234        if (sSceneManager == null) {
235            throw new RuntimeException("Scene manager not initialized");
236        }
237        sSceneManager.mAllocationMap.put(str, alloc);
238    }
239
240    public static class SceneLoadedCallback implements Runnable {
241        public Scene mLoadedScene;
242        public String mName;
243        public void run() {
244        }
245    }
246
247    public Scene getActiveScene() {
248        return mActiveScene;
249    }
250
251    public void setActiveScene(Scene s) {
252        mActiveScene = s;
253
254        if (mActiveScene == null) {
255            return;
256        }
257
258        // Do some sanity checking
259        if (mActiveScene.getCameras().size() == 0) {
260            Matrix4f camPos = new Matrix4f();
261            camPos.translate(0, 0, 10);
262            MatrixTransform cameraTransform = new MatrixTransform();
263            cameraTransform.setName("_DefaultCameraTransform");
264            cameraTransform.setMatrix(camPos);
265            mActiveScene.appendTransform(cameraTransform);
266            Camera cam = new Camera();
267            cam.setName("_DefaultCamera");
268            cam.setTransform(cameraTransform);
269            mActiveScene.appendCamera(cam);
270        }
271
272        mActiveScene.appendShader(getDefaultVS());
273        mActiveScene.appendTransform(getDefaultTransform());
274    }
275
276    static RenderScriptGL getRS() {
277        if (sSceneManager == null) {
278            return null;
279        }
280        return sSceneManager.mRS;
281    }
282
283    static Resources getRes() {
284        if (sSceneManager == null) {
285            return null;
286        }
287        return sSceneManager.mRes;
288    }
289
290    // Provides the folowing inputs to fragment shader
291    // Assigned by default if nothing is present
292    // vec3 varWorldPos;
293    // vec3 varWorldNormal;
294    // vec2 varTex0;
295    public static VertexShader getDefaultVS() {
296        if (sSceneManager == null) {
297            return null;
298        }
299
300        if (sSceneManager.mDefaultVertex == null) {
301            RenderScriptGL rs = getRS();
302            Element.Builder b = new Element.Builder(rs);
303            b.add(Element.MATRIX_4X4(rs), "model");
304            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
305
306            b = new Element.Builder(rs);
307            b.add(Element.MATRIX_4X4(rs), "viewProj");
308            Type.Builder shaderConstBuilder = new Type.Builder(rs, b.create());
309
310            b = new Element.Builder(rs);
311            b.add(Element.F32_4(rs), "position");
312            b.add(Element.F32_2(rs), "texture0");
313            b.add(Element.F32_3(rs), "normal");
314            Element defaultIn = b.create();
315
316            final String code = "\n" +
317                "varying vec3 varWorldPos;\n" +
318                "varying vec3 varWorldNormal;\n" +
319                "varying vec2 varTex0;\n" +
320                "void main() {" +
321                "   vec4 objPos = ATTRIB_position;\n" +
322                "   vec4 worldPos = UNI_model * objPos;\n" +
323                "   gl_Position = UNI_viewProj * worldPos;\n" +
324                "   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);\n" +
325                "   vec3 worldNorm = model3 * ATTRIB_normal;\n" +
326                "   varWorldPos = worldPos.xyz;\n" +
327                "   varWorldNormal = worldNorm;\n" +
328                "   varTex0 = ATTRIB_texture0;\n" +
329                "}\n";
330
331            VertexShader.Builder sb = new VertexShader.Builder(rs);
332            sb.addInput(defaultIn);
333            sb.setObjectConst(objConstBuilder.setX(1).create());
334            sb.setShaderConst(shaderConstBuilder.setX(1).create());
335            sb.setShader(code);
336            sSceneManager.mDefaultVertex = sb.create();
337        }
338
339        return sSceneManager.mDefaultVertex;
340    }
341
342    public static FragmentShader getColorFS() {
343        if (sSceneManager == null) {
344            return null;
345        }
346        if (sSceneManager.mColor == null) {
347            RenderScriptGL rs = getRS();
348            Element.Builder b = new Element.Builder(rs);
349            b.add(Element.F32_4(rs), "color");
350            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
351
352            final String code = "\n" +
353                "varying vec2 varTex0;\n" +
354                "void main() {\n" +
355                "   lowp vec4 col = UNI_color;\n" +
356                "   gl_FragColor = col;\n" +
357                "}\n";
358            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
359            fb.setShader(code);
360            fb.setObjectConst(objConstBuilder.create());
361            sSceneManager.mColor = fb.create();
362        }
363
364        return sSceneManager.mColor;
365    }
366
367    public static FragmentShader getTextureFS() {
368        if (sSceneManager == null) {
369            return null;
370        }
371        if (sSceneManager.mTexture == null) {
372            RenderScriptGL rs = getRS();
373
374            final String code = "\n" +
375                "varying vec2 varTex0;\n" +
376                "void main() {\n" +
377                "   lowp vec4 col = texture2D(UNI_color, varTex0).rgba;\n" +
378                "   gl_FragColor = col;\n" +
379                "}\n";
380
381            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
382            fb.setShader(code);
383            fb.addTexture(Program.TextureType.TEXTURE_2D, "color");
384            sSceneManager.mTexture = fb.create();
385            sSceneManager.mTexture.mProgram.bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(rs), 0);
386        }
387
388        return sSceneManager.mTexture;
389    }
390
391    static RenderState getDefaultState() {
392        if (sSceneManager == null) {
393            return null;
394        }
395        if (sSceneManager.mDefaultState == null) {
396            sSceneManager.mDefaultState = new RenderState(getDefaultVS(), getColorFS(), null, null);
397            sSceneManager.mDefaultState.setName("__DefaultState");
398        }
399        return sSceneManager.mDefaultState;
400    }
401
402    static Transform getDefaultTransform() {
403        if (sSceneManager == null) {
404            return null;
405        }
406        if (sSceneManager.mDefaultTransform == null) {
407            sSceneManager.mDefaultTransform = new MatrixTransform();
408            sSceneManager.mDefaultTransform.setName("__DefaultTransform");
409        }
410        return sSceneManager.mDefaultTransform;
411    }
412
413    public static SceneManager getInstance() {
414        if (sSceneManager == null) {
415            sSceneManager = new SceneManager();
416        }
417        return sSceneManager;
418    }
419
420    protected SceneManager() {
421    }
422
423    public void loadModel(String name, SceneLoadedCallback cb) {
424        ColladaScene scene = new ColladaScene(name, cb);
425        scene.init(mRS, mRes);
426    }
427
428    public Mesh getScreenAlignedQuad() {
429        if (mQuad != null) {
430            return mQuad;
431        }
432
433        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
434                                           3, Mesh.TriangleMeshBuilder.TEXTURE_0);
435
436        tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 1.0f);
437        tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 1.0f);
438        tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 1.0f);
439        tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 1.0f);
440
441        tmb.addTriangle(0, 1, 2);
442        tmb.addTriangle(2, 3, 0);
443
444        mQuad = tmb.create(true);
445        return mQuad;
446    }
447
448    public Renderable getRenderableQuad(String name, RenderState state) {
449        Renderable quad = new Renderable();
450        quad.setTransform(new MatrixTransform());
451        quad.setMesh(getScreenAlignedQuad());
452        quad.setName(name);
453        quad.setRenderState(state);
454        quad.setCullType(1);
455        return quad;
456    }
457
458    public void initRS(RenderScriptGL rs, Resources res, int w, int h) {
459        mRS = rs;
460        mRes = res;
461        mAllocationMap = new HashMap<String, Allocation>();
462
463        mQuad = null;
464        mDefault2D = null;
465        mDefaultCube = null;
466        mDefaultVertex = null;
467        mColor = null;
468        mTexture = null;
469        mDefaultState = null;
470        mDefaultTransform = null;
471
472        mExportScript = new ScriptC_export(rs, res, R.raw.export);
473
474        mTransformScript = new ScriptC_transform(rs, res, R.raw.transform);
475        mTransformScript.set_gTransformScript(mTransformScript);
476
477        mCameraScript = new ScriptC_camera(rs, res, R.raw.camera);
478        mLightScript = new ScriptC_light(rs, res, R.raw.light);
479        mObjectParamsScript = new ScriptC_object_params(rs, res, R.raw.object_params);
480        mFragmentParamsScript = new ScriptC_object_params(rs, res, R.raw.fragment_params);
481        mVertexParamsScript = new ScriptC_object_params(rs, res, R.raw.vertex_params);
482        mCullScript = new ScriptC_cull(rs, res, R.raw.cull);
483
484        mRenderLoop = new ScriptC_render(rs, res, R.raw.render);
485        mRenderLoop.set_gTransformScript(mTransformScript);
486        mRenderLoop.set_gCameraScript(mCameraScript);
487        mRenderLoop.set_gLightScript(mLightScript);
488        mRenderLoop.set_gObjectParamsScript(mObjectParamsScript);
489        mRenderLoop.set_gFragmentParamsScript(mFragmentParamsScript);
490        mRenderLoop.set_gVertexParamsScript(mVertexParamsScript);
491        mRenderLoop.set_gCullScript(mCullScript);
492
493        mRenderLoop.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
494    }
495
496    public ScriptC getRenderLoop() {
497        return mRenderLoop;
498    }
499}
500
501
502
503
504