RenderScript Balls : RSSurfaceView « UI « Android






RenderScript Balls

   
//
//src\com\example\android\rs\balls\Balls.java
/*
 * Copyright (C) 2008 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.example.android.rs.balls;

import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScript;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings.System;
import android.util.Config;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ListView;

import java.lang.Runtime;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class Balls extends Activity implements SensorEventListener {
    //EventListener mListener = new EventListener();

    private static final String LOG_TAG = "libRS_jni";
    private static final boolean DEBUG  = false;
    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;

    private BallsView mView;
    private SensorManager mSensorManager;

    // get the current looper (from your Activity UI thread for instance


    public void onSensorChanged(SensorEvent event) {
        //android.util.Log.d("rs", "sensor: " + event.sensor + ", x: " + event.values[0] + ", y: " + event.values[1] + ", z: " + event.values[2]);
        synchronized (this) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                if(mView != null) {
                    mView.setAccel(event.values[0], event.values[1], event.values[2]);
                }
            }
        }
    }

    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

        // Create our Preview view and set it as the content of our
        // Activity
        mView = new BallsView(this);
        setContentView(mView);
    }

    @Override
    protected void onResume() {
        mSensorManager.registerListener(this,
                                        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                                        SensorManager.SENSOR_DELAY_FASTEST);

        // Ideally a game should implement onResume() and onPause()
        // to take appropriate action when the activity looses focus
        super.onResume();
        mView.resume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mView.pause();
        Runtime.getRuntime().exit(0);
    }

    @Override
    protected void onStop() {
        mSensorManager.unregisterListener(this);
        super.onStop();
    }

    static void log(String message) {
        if (LOG_ENABLED) {
            Log.v(LOG_TAG, message);
        }
    }


}




//src\com\example\android\rs\balls\BallsRS.java
/*
 * Copyright (C) 2008 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.example.android.rs.balls;

import android.content.res.Resources;
import android.renderscript.*;
import android.util.Log;

public class BallsRS {
    public static final int PART_COUNT = 900;

    public BallsRS() {
    }

    private Resources mRes;
    private RenderScriptGL mRS;
    private ScriptC_balls mScript;
    private ScriptC_ball_physics mPhysicsScript;
    private ProgramFragment mPFLines;
    private ProgramFragment mPFPoints;
    private ProgramVertex mPV;
    private ScriptField_Point mPoints;
    private ScriptField_VpConsts mVpConsts;

    void updateProjectionMatrices() {
        mVpConsts = new ScriptField_VpConsts(mRS, 1,
                                             Allocation.USAGE_SCRIPT |
                                             Allocation.USAGE_GRAPHICS_CONSTANTS);
        ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
        Matrix4f mvp = new Matrix4f();
        mvp.loadOrtho(0, mRS.getWidth(), mRS.getHeight(), 0, -1, 1);
        i.MVP = mvp;
        mVpConsts.set(i, 0, true);
    }

    private void createProgramVertex() {
        updateProjectionMatrices();

        ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS);
        String t =  "varying vec4 varColor;\n" +
                    "void main() {\n" +
                    "  vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" +
                    "  pos.xy = ATTRIB_position;\n" +
                    "  gl_Position = UNI_MVP * pos;\n" +
                    "  varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
                    "  gl_PointSize = ATTRIB_size;\n" +
                    "}\n";
        sb.setShader(t);
        sb.addConstant(mVpConsts.getType());
        sb.addInput(mPoints.getElement());
        ProgramVertex pvs = sb.create();
        pvs.bindConstants(mVpConsts.getAllocation(), 0);
        mRS.bindProgramVertex(pvs);
    }

    private Allocation loadTexture(int id) {
        final Allocation allocation =
            Allocation.createFromBitmapResource(mRS, mRes,
                id, Allocation.MipmapControl.MIPMAP_NONE,
                Allocation.USAGE_GRAPHICS_TEXTURE);
        return allocation;
    }

    ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
        builder.setDitherEnabled(false);
        builder.setDepthMaskEnabled(false);
        return builder.create();
    }

    public void init(RenderScriptGL rs, Resources res, int width, int height) {
        mRS = rs;
        mRes = res;

        ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
        pfb.setPointSpriteTexCoordinateReplacement(true);
        pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
        pfb.setVaryingColor(true);
        mPFPoints = pfb.create();

        pfb = new ProgramFragmentFixedFunction.Builder(rs);
        pfb.setVaryingColor(true);
        mPFLines = pfb.create();

        android.util.Log.e("rs", "Load texture");
        mPFPoints.bindTexture(loadTexture(R.drawable.flares), 0);

        mPoints = new ScriptField_Point(mRS, PART_COUNT, Allocation.USAGE_SCRIPT);

        Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
        smb.addVertexAllocation(mPoints.getAllocation());
        smb.addIndexSetType(Mesh.Primitive.POINT);
        Mesh smP = smb.create();

        mPhysicsScript = new ScriptC_ball_physics(mRS, mRes, R.raw.ball_physics);

        mScript = new ScriptC_balls(mRS, mRes, R.raw.balls);
        mScript.set_partMesh(smP);
        mScript.set_physics_script(mPhysicsScript);
        mScript.bind_point(mPoints);
        mScript.bind_balls1(new ScriptField_Ball(mRS, PART_COUNT, Allocation.USAGE_SCRIPT));
        mScript.bind_balls2(new ScriptField_Ball(mRS, PART_COUNT, Allocation.USAGE_SCRIPT));

        mScript.set_gPFLines(mPFLines);
        mScript.set_gPFPoints(mPFPoints);
        createProgramVertex();

        mRS.bindProgramStore(BLEND_ADD_DEPTH_NONE(mRS));

        mPhysicsScript.set_gMinPos(new Float2(5, 5));
        mPhysicsScript.set_gMaxPos(new Float2(width - 5, height - 5));

        mScript.invoke_initParts(width, height);

        mRS.bindRootScript(mScript);
    }

    public void newTouchPosition(float x, float y, float pressure, int id) {
        mPhysicsScript.invoke_touch(x, y, pressure, id);
    }

    public void setAccel(float x, float y) {
        mPhysicsScript.set_gGravityVector(new Float2(x, y));
    }

}



//src\com\example\android\rs\balls\BallsView.java
/*
 * Copyright (C) 2008 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.example.android.rs.balls;

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;

public class BallsView extends RSSurfaceView {

    public BallsView(Context context) {
        super(context);
        //setFocusable(true);
    }

    private RenderScriptGL mRS;
    private BallsRS mRender;

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        super.surfaceChanged(holder, format, w, h);
        if (mRS == null) {
            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
            mRS = createRenderScriptGL(sc);
            mRS.setSurface(holder, w, h);
            mRender = new BallsRS();
            mRender.init(mRS, getResources(), w, h);
        }
        mRender.updateProjectionMatrices();
    }

    @Override
    protected void onDetachedFromWindow() {
        if(mRS != null) {
            mRS = null;
            destroyRenderScriptGL();
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        int act = ev.getActionMasked();
        if (act == ev.ACTION_UP) {
            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
            return false;
        } else if (act == MotionEvent.ACTION_POINTER_UP) {
            // only one pointer going up, we can get the index like this
            int pointerIndex = ev.getActionIndex();
            int pointerId = ev.getPointerId(pointerIndex);
            mRender.newTouchPosition(0, 0, 0, pointerId);
            return false;
        }
        int count = ev.getHistorySize();
        int pcount = ev.getPointerCount();

        for (int p=0; p < pcount; p++) {
            int id = ev.getPointerId(p);
            mRender.newTouchPosition(ev.getX(p),
                                     ev.getY(p),
                                     ev.getPressure(p),
                                     id);

            for (int i=0; i < count; i++) {
                mRender.newTouchPosition(ev.getHistoricalX(p, i),
                                         ev.getHistoricalY(p, i),
                                         ev.getHistoricalPressure(p, i),
                                         id);
            }
        }
        return true;
    }

    void setAccel(float x, float y, float z) {
        if (mRender == null) {
            return;
        }
        mRender.setAccel(x, -y);
    }

}





//src\com\example\android\rs\balls\ball_physics.rs
#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.balls)

#include "balls.rsh"

float2 gGravityVector = {0.f, 9.8f};

float2 gMinPos = {0.f, 0.f};
float2 gMaxPos = {1280.f, 700.f};

static float2 touchPos[10];
static float touchPressure[10];

void touch(float x, float y, float pressure, int id) {
    if (id >= 10) {
        return;
    }

    touchPos[id].x = x;
    touchPos[id].y = y;
    touchPressure[id] = pressure;
}

void root(const Ball_t *ballIn, Ball_t *ballOut, const BallControl_t *ctl, uint32_t x) {
    float2 fv = {0, 0};
    float2 pos = ballIn->position;

    int arcID = -1;
    float arcInvStr = 100000;

    const Ball_t * bPtr = rsGetElementAt(ctl->ain, 0);
    for (uint32_t xin = 0; xin < ctl->dimX; xin++) {
        float2 vec = bPtr[xin].position - pos;
        float2 vec2 = vec * vec;
        float len2 = vec2.x + vec2.y;

        if (len2 < 10000) {
            //float minDist = ballIn->size + bPtr[xin].size;
            float forceScale = ballIn->size * bPtr[xin].size;
            forceScale *= forceScale;

            if (len2 > 16 /* (minDist*minDist)*/)  {
                // Repulsion
                float len = sqrt(len2);
                fv -= (vec / (len * len * len)) * 20000.f * forceScale;
            } else {
                if (len2 < 1) {
                    if (xin == x) {
                        continue;
                    }
                    ballOut->delta = 0.f;
                    ballOut->position = ballIn->position;
                    if (xin > x) {
                        ballOut->position.x += 1.f;
                    } else {
                        ballOut->position.x -= 1.f;
                    }
                    //ballOut->color.rgb = 1.f;
                    //ballOut->arcID = -1;
                    //ballOut->arcStr = 0;
                    return;
                }
                // Collision
                float2 axis = normalize(vec);
                float e1 = dot(axis, ballIn->delta);
                float e2 = dot(axis, bPtr[xin].delta);
                float e = (e1 - e2) * 0.45f;
                if (e1 > 0) {
                    fv -= axis * e;
                } else {
                    fv += axis * e;
                }
            }
        }
    }

    fv /= ballIn->size * ballIn->size * ballIn->size;
    fv -= gGravityVector * 4.f;
    fv *= ctl->dt;

    for (int i=0; i < 10; i++) {
        if (touchPressure[i] > 0.1f) {
            float2 vec = touchPos[i] - ballIn->position;
            float2 vec2 = vec * vec;
            float len2 = max(2.f, vec2.x + vec2.y);
            fv -= (vec / len2) * touchPressure[i] * 300.f;
        }
    }

    ballOut->delta = (ballIn->delta * (1.f - 0.004f)) + fv;
    ballOut->position = ballIn->position + (ballOut->delta * ctl->dt);

    const float wallForce = 400.f;
    if (ballOut->position.x > (gMaxPos.x - 20.f)) {
        float d = gMaxPos.x - ballOut->position.x;
        if (d < 0.f) {
            if (ballOut->delta.x > 0) {
                ballOut->delta.x *= -0.7;
            }
            ballOut->position.x = gMaxPos.x;
        } else {
            ballOut->delta.x -= min(wallForce / (d * d), 10.f);
        }
    }

    if (ballOut->position.x < (gMinPos.x + 20.f)) {
        float d = ballOut->position.x - gMinPos.x;
        if (d < 0.f) {
            if (ballOut->delta.x < 0) {
                ballOut->delta.x *= -0.7;
            }
            ballOut->position.x = gMinPos.x + 1.f;
        } else {
            ballOut->delta.x += min(wallForce / (d * d), 10.f);
        }
    }

    if (ballOut->position.y > (gMaxPos.y - 20.f)) {
        float d = gMaxPos.y - ballOut->position.y;
        if (d < 0.f) {
            if (ballOut->delta.y > 0) {
                ballOut->delta.y *= -0.7;
            }
            ballOut->position.y = gMaxPos.y;
        } else {
            ballOut->delta.y -= min(wallForce / (d * d), 10.f);
        }
    }

    if (ballOut->position.y < (gMinPos.y + 20.f)) {
        float d = ballOut->position.y - gMinPos.y;
        if (d < 0.f) {
            if (ballOut->delta.y < 0) {
                ballOut->delta.y *= -0.7;
            }
            ballOut->position.y = gMinPos.y + 1.f;
        } else {
            ballOut->delta.y += min(wallForce / (d * d * d), 10.f);
        }
    }

    ballOut->size = ballIn->size;

    //rsDebug("physics pos out", ballOut->position);
}




//src\com\example\android\rs\balls\balls.rs
#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.balls)
#include "rs_graphics.rsh"

#include "balls.rsh"

#pragma stateVertex(parent)
#pragma stateStore(parent)

rs_program_fragment gPFPoints;
rs_program_fragment gPFLines;
rs_mesh partMesh;

typedef struct __attribute__((packed, aligned(4))) Point {
    float2 position;
    float size;
} Point_t;
Point_t *point;

typedef struct VpConsts {
    rs_matrix4x4 MVP;
} VpConsts_t;
VpConsts_t *vpConstants;

rs_script physics_script;

Ball_t *balls1;
Ball_t *balls2;

static int frame = 0;

void initParts(int w, int h)
{
    uint32_t dimX = rsAllocationGetDimX(rsGetAllocation(balls1));

    for (uint32_t ct=0; ct < dimX; ct++) {
        balls1[ct].position.x = rsRand(0.f, (float)w);
        balls1[ct].position.y = rsRand(0.f, (float)h);
        balls1[ct].delta.x = 0.f;
        balls1[ct].delta.y = 0.f;
        balls1[ct].size = 1.f;

        float r = rsRand(100.f);
        if (r > 90.f) {
            balls1[ct].size += pow(10.f, rsRand(0.f, 2.f)) * 0.07;
        }
    }
}



int root() {
    rsgClearColor(0.f, 0.f, 0.f, 1.f);

    BallControl_t bc;
    Ball_t *bout;

    if (frame & 1) {
        rsSetObject(&bc.ain, rsGetAllocation(balls2));
        rsSetObject(&bc.aout, rsGetAllocation(balls1));
        bout = balls2;
    } else {
        rsSetObject(&bc.ain, rsGetAllocation(balls1));
        rsSetObject(&bc.aout, rsGetAllocation(balls2));
        bout = balls1;
    }

    bc.dimX = rsAllocationGetDimX(bc.ain);
    bc.dt = 1.f / 30.f;

    rsForEach(physics_script, bc.ain, bc.aout, &bc);

    for (uint32_t ct=0; ct < bc.dimX; ct++) {
        point[ct].position = bout[ct].position;
        point[ct].size = 6.f /*+ bout[ct].color.g * 6.f*/ * bout[ct].size;
    }

    frame++;
    rsgBindProgramFragment(gPFPoints);
    rsgDrawMesh(partMesh);
    rsClearObject(&bc.ain);
    rsClearObject(&bc.aout);
    return 1;
}




//src\com\example\android\rs\balls\balls.rsh

typedef struct __attribute__((packed, aligned(4))) Ball {
    float2 delta;
    float2 position;
    //float3 color;
    float size;
    //int arcID;
    //float arcStr;
} Ball_t;
Ball_t *balls;


typedef struct BallControl {
    uint32_t dimX;
    rs_allocation ain;
    rs_allocation aout;
    float dt;
} BallControl_t;

   
    
    
  








Related examples in the same category

1.Render Script Demo
2.RenderScript Fountain