Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c35e2c5 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 20400

* changes:
  Add lighting to animated water ripples.
parents bce3ab90 b62627ea
Loading
Loading
Loading
Loading
+207 −2
Original line number Diff line number Diff line
// Copyright (C) 2009 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.

#pragma version(1)
#pragma stateVertex(PVBackground)
#pragma stateFragment(PFBackground)
#pragma stateFragmentStore(PSBackground)
#pragma stateFragmentStore(PFSBackground)

#define RSID_STATE 0
#define RSID_FRAME_COUNT 0
#define RSID_WIDTH 1
#define RSID_HEIGHT 2
#define RSID_MESH_WIDTH 3
#define RSID_MESH_HEIGHT 4
#define RSID_RIPPLE_MAP_SIZE 5
#define RSID_RIPPLE_INDEX 6
#define RSID_DROP_X 7
#define RSID_DROP_Y 8
    
#define RSID_TEXTURES 1

#define RSID_RIPPLE_MAP 2

#define RSID_REFRACTION_MAP 3

#define REFRACTION 1.333f
#define DAMP 4

#define DROP_RADIUS 2
// The higher, the smaller the ripple
#define RIPPLE_HEIGHT 10.0f

int offset(int x, int y, int width) {
    return x + 1 + (y + 1) * (width + 2);
}

void drop(int x, int y, int r) {
    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);

    if (x < r) x = r;
    if (y < r) y = r;
    if (x >= width - r) x = width - r - 1;
    if (y >= height - r) x = height - r - 1;
    
    x = width - x;

    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
    int origin = offset(0, 0, width);

    int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
    int sqr = r * r;

    int h = 0;
    for ( ; h < r; h++) {
        int sqv = h * h;
        int yn = origin + (y - h) * (width + 2);
        int yp = origin + (y + h) * (width + 2);
        int w = 0;
        for ( ; w < r; w++) {
            int squ = w * w;
            if (squ + sqv < sqr) {
                int v = -sqrtf((sqr - (squ + sqv)) << 16);
                current[yn + x + w] = v;
                current[yp + x + w] = v;
                current[yn + x - w] = v;
                current[yp + x - w] = v;
            }
        }
    }
}

void updateRipples() {
    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
    int origin = offset(0, 0, width);

    int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
    int* next = loadArrayI32(RSID_RIPPLE_MAP, (1 - index) * rippleMapSize + origin);

    storeI32(RSID_STATE, RSID_RIPPLE_INDEX, 1 - index);

    int a = 1;
    int b = width + 2;
    int h = height;
    while (h) {
        int w = width;
        while (w) {
            int droplet = ((current[-b] + current[b] + current[-a] + current[a]) >> 1) - next[0];
            droplet -= (droplet >> DAMP);
            next[0] = droplet;
            current++;
            next++;
            w--;
        }
        current += 2;
        next += 2;
        h--;
    }
}

void generateRipples() {
    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
    int origin = offset(0, 0, width);

    int b = width + 2;

    int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
    float *vertices = loadTriangleMeshVerticesF(NAMED_mesh);

    int h = height - 1;
    while (h >= 0) {
        int w = width - 1;
        int wave = current[0];
        int offset = h * width;
        while (w >= 0) {
            int nextWave = current[1];
            int dx = nextWave - wave;
            int dy = current[b] - wave;

            // Update Z coordinate of the vertex
            vertices[(offset + w) * 8 + 7] = (dy / 512.0f) / RIPPLE_HEIGHT;
            
            w--;
            current++;
            wave = nextWave;
        }
        h--;
        current += 2;
    }

    // Compute the normals for lighting
    int y = 0;
    for ( ; y < height; y++) {
        int x = 0;
        int yOffset = y * width;
        for ( ; x < width; x++) {
            // V1
            float v1x = vertices[(yOffset + x) * 8 + 5];
            float v1y = vertices[(yOffset + x) * 8 + 6];
            float v1z = vertices[(yOffset + x) * 8 + 7];

            // V2
            float v2x = vertices[(yOffset + x + 1) * 8 + 5];
            float v2y = vertices[(yOffset + x + 1) * 8 + 6];
            float v2z = vertices[(yOffset + x + 1) * 8 + 7];
            
            // V3
            float v3x = vertices[(yOffset + width + x) * 8 + 5];
            float v3y = vertices[(yOffset + width + x) * 8 + 6];
            float v3z = vertices[(yOffset + width + x) * 8 + 7];

            // N1
            float n1x = v2x - v1x;
            float n1y = v2y - v1y;
            float n1z = v2z - v1z;

            // N2
            float n2x = v3x - v1x;
            float n2y = v3y - v1y;
            float n2z = v3z - v1z;

            // N1 x N2
            float n3x = n1y * n2z - n1z * n2y;
            float n3y = n1z * n2x - n1x * n2z;
            float n3z = n1x * n2y - n1y * n2x;

            // Normalize
            float len = magf3(n3x, n3y, n3z);
            n3x /= len;
            n3y /= len;
            n3z /= len;

            vertices[(yOffset + x) * 8 + 0] = -n3x;
            vertices[(yOffset + x) * 8 + 1] = -n3y;
            vertices[(yOffset + x) * 8 + 2] = -n3z;
        }
    }
}   

int main(int index) {
    color(1.0f, 0.0f, 0.0f, 1.0f);
    int dropX = loadI32(RSID_STATE, RSID_DROP_X);
    if (dropX != -1) {
        int dropY = loadI32(RSID_STATE, RSID_DROP_Y);
        drop(dropX, dropY, DROP_RADIUS);
        storeI32(RSID_STATE, RSID_DROP_X, -1);
        storeI32(RSID_STATE, RSID_DROP_Y, -1);
    }

    updateRipples();
    generateRipples();
    updateTriangleMesh(NAMED_mesh);

    ambient(0.0f, 0.1f, 0.9f, 1.0f);
    diffuse(0.0f, 0.1f, 0.9f, 1.0f);
    drawTriangleMesh(NAMED_mesh);    

    return 1;
+94 −35
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.renderscript.ProgramVertex;
import android.renderscript.Allocation;
import android.renderscript.Sampler;
import android.renderscript.Element;
import android.renderscript.Light;
import static android.renderscript.Sampler.Value.LINEAR;
import static android.renderscript.Sampler.Value.CLAMP;
import static android.renderscript.ProgramStore.DepthFunc.*;
@@ -38,16 +39,26 @@ import android.graphics.Bitmap;
import java.util.TimeZone;

class FallRS {
    private static final int MESH_RESOLUTION = 32;
    private static final int MESH_RESOLUTION = 48;

    private static final int RSID_STATE = 0;
    private static final int RSID_STATE_FRAMECOUNT = 0;
    private static final int RSID_STATE_WIDTH = 1;
    private static final int RSID_STATE_HEIGHT = 2;
    private static final int RSID_STATE_MESH_WIDTH = 3;
    private static final int RSID_STATE_MESH_HEIGHT = 4;
    private static final int RSID_STATE_RIPPLE_MAP_SIZE = 5;
    private static final int RSID_STATE_RIPPLE_INDEX = 6;
    private static final int RSID_STATE_DROP_X = 7;
    private static final int RSID_STATE_DROP_Y = 8;
    
    private static final int RSID_TEXTURES = 1;
    private static final int TEXTURES_COUNT = 0;

    private static final int RSID_RIPPLE_MAP = 2;

    private static final int RSID_REFRACTION_MAP = 3;

    private Resources mResources;
    private RenderScript mRS;
    private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
@@ -61,6 +72,7 @@ class FallRS {
    private ProgramStore mPfsBackground;
    private ProgramVertex mPvBackground;
    private ProgramVertex.MatrixAllocation mPvOrthoAlloc;
    private Light mLight;

    private Allocation mTexturesIDs;
    private Allocation[] mTextures;
@@ -68,6 +80,11 @@ class FallRS {

    private Allocation mState;
    private RenderScript.TriangleMesh mMesh;
    private int mMeshWidth;
    private int mMeshHeight;

    private Allocation mRippleMap;
    private Allocation mRefractionMap;

    public FallRS(int width, int height) {
        mWidth = width;
@@ -96,6 +113,9 @@ class FallRS {
        mState.destroy();
        mTextureBufferIDs = null;
        mMesh.destroy();
        mLight.destroy();
        mRippleMap.destroy();
        mRefractionMap.destroy();
    }

    @Override
@@ -111,8 +131,9 @@ class FallRS {
        createProgramVertex();
        createProgramFragmentStore();
        createProgramFragment();
        createScriptStructures();
        createMesh();
        createScriptStructures();
        loadTextures();

        ScriptC.Builder sb = new ScriptC.Builder(mRS);
        sb.setScript(mResources, R.raw.fall);
@@ -121,16 +142,17 @@ class FallRS {
        mScript.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        mScript.setTimeZone(TimeZone.getDefault().getID());

        loadSkyTextures();
        mScript.bindAllocation(mState, RSID_STATE);
        mScript.bindAllocation(mTexturesIDs, RSID_TEXTURES);
        mScript.bindAllocation(mRippleMap, RSID_RIPPLE_MAP);
        mScript.bindAllocation(mRefractionMap, RSID_REFRACTION_MAP);

        mRS.contextBindRootScript(mScript);
    }

    private void createMesh() {
        final RenderScript rs = mRS;
        rs.triangleMeshBegin(Element.XYZ_F32, Element.INDEX_16);
        rs.triangleMeshBegin(Element.NORM_ST_XYZ_F32, Element.INDEX_16);

        int wResolution;
        int hResolution;
@@ -146,13 +168,20 @@ class FallRS {
            hResolution = MESH_RESOLUTION;
        }

        final float quadWidth = width / (float) wResolution;
        final float quadHeight = height / (float) hResolution;
        final float glHeight = 2.0f * height / (float) width;
        final float quadWidth = 2.0f / (float) wResolution;
        final float quadHeight = glHeight / (float) hResolution;

        wResolution += 2;
        hResolution += 2;        
        
        for (int y = 0; y <= hResolution; y++) {
            final float yOffset = y * quadHeight;
            final float yOffset = y * quadHeight - glHeight / 2.0f - quadHeight;
            for (int x = 0; x <= wResolution; x++) {
                rs.triangleMeshAddVertex_XYZ(x * quadWidth, yOffset, 0.0f);
                rs.triangleMeshAddVertex_XYZ_ST_NORM(
                        -1.0f + x * quadWidth - quadWidth, yOffset, 0.0f,
                        x / (float) wResolution, y / (float) wResolution,
                        0.0f, 0.0f, -1.0f);
            }
        }

@@ -167,18 +196,41 @@ class FallRS {

        mMesh = rs.triangleMeshCreate();
        mMesh.setName("mesh");

        mMeshWidth = wResolution + 1;
        mMeshHeight = hResolution + 1;
    }

    private void createScriptStructures() {
        final int[] data = new int[3];
        final int rippleMapSize = (mMeshWidth + 2) * (mMeshHeight + 2);

        final int[] data = new int[9];
        mState = Allocation.createSized(mRS, USER_I32, data.length);
        data[RSID_STATE_FRAMECOUNT] = 0;
        data[RSID_STATE_WIDTH] = mWidth;
        data[RSID_STATE_HEIGHT] = mHeight;
        data[RSID_STATE_MESH_WIDTH] = mMeshWidth;
        data[RSID_STATE_MESH_HEIGHT] = mMeshHeight;
        data[RSID_STATE_RIPPLE_MAP_SIZE] = rippleMapSize;
        data[RSID_STATE_RIPPLE_INDEX] = 0;
        data[RSID_STATE_DROP_X] = mMeshWidth / 2;
        data[RSID_STATE_DROP_Y] = mMeshHeight / 2;
        mState.data(data);

        final int[] rippleMap = new int[rippleMapSize * 2];
        mRippleMap = Allocation.createSized(mRS, USER_I32, rippleMap.length);

        final int[] refractionMap = new int[513];
        float ir = 1.0f / 1.333f;
        for (int i = 0; i < refractionMap.length; i++) {
            float d = (float) Math.tan(Math.asin(Math.sin(Math.atan(i * (1.0f / 256.0f))) * ir));
            refractionMap[i] = (int) Math.floor(d * (1 << 16) + 0.5f);
        }
        mRefractionMap = Allocation.createSized(mRS, USER_I32, refractionMap.length);
        mRefractionMap.data(refractionMap);
    }

    private void loadSkyTextures() {
    private void loadTextures() {
        mTextureBufferIDs = new int[TEXTURES_COUNT];
        mTextures = new Allocation[TEXTURES_COUNT];
        mTexturesIDs = Allocation.createSized(mRS, USER_FLOAT, TEXTURES_COUNT);
@@ -215,42 +267,49 @@ class FallRS {
    }

    private void createProgramFragment() {
        Sampler.Builder bs = new Sampler.Builder(mRS);
        bs.setMin(LINEAR);
        bs.setMag(LINEAR);
        bs.setWrapS(CLAMP);
        bs.setWrapT(CLAMP);
        mSampler = bs.create();

        ProgramFragment.Builder b;
        b = new ProgramFragment.Builder(mRS, null, null);
        b.setTexEnable(true, 0);
        b.setTexEnvMode(REPLACE, 0);
        mPfBackground = b.create();
        Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
        sampleBuilder.setMin(LINEAR);
        sampleBuilder.setMag(LINEAR);
        sampleBuilder.setWrapS(CLAMP);
        sampleBuilder.setWrapT(CLAMP);
        mSampler = sampleBuilder.create();

        ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null);
        builder.setTexEnable(true, 0);
        builder.setTexEnvMode(REPLACE, 0);
        mPfBackground = builder.create();
        mPfBackground.setName("PFBackground");
        mPfBackground.bindSampler(mSampler, 0);
    }

    private void createProgramFragmentStore() {
        ProgramStore.Builder b;
        b = new ProgramStore.Builder(mRS, null, null);

        b.setDepthFunc(ALWAYS);
        b.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
        b.setDitherEnable(true);
        b.setDepthMask(false);
        mPfsBackground = b.create();
        ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
        builder.setDepthFunc(LESS);
        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
        builder.setDitherEnable(true);
        builder.setDepthMask(true);
        mPfsBackground = builder.create();
        mPfsBackground.setName("PFSBackground");
    }

    private void createProgramVertex() {
        mPvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
        mPvOrthoAlloc.setupOrthoWindow(mWidth, mHeight);
        mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);

        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
        pvb.setTextureMatrixEnable(true);
        mPvBackground = pvb.create();
        mLight = new Light.Builder(mRS).create();
        mLight.setPosition(0.0f, 0.0f, -1.0f);

        ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null);
        builder.setTextureMatrixEnable(true);
        builder.addLight(mLight);
        mPvBackground = builder.create();
        mPvBackground.bindAllocation(mPvOrthoAlloc);
        mPvBackground.setName("PVBackground");
    }

    public void addDrop(float x, float y) {
        mState.subData1D(RSID_STATE_DROP_X, 2, new int[] {
                (int) ((x / mWidth) * mMeshWidth), (int) ((y / mHeight) * mMeshHeight)
        });
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.fall.rs;

import android.content.Context;
import android.view.SurfaceHolder;
import android.view.MotionEvent;
import android.renderscript.RenderScript;
import android.renderscript.RSSurfaceView;

@@ -41,4 +42,20 @@ class FallView extends RSSurfaceView {
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mRender != null) mRender.destroy();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                mRender.addDrop(event.getX(), event.getY());
                try {
                    Thread.sleep(16);
                } catch (InterruptedException e) {
                    // Ignore
                }
                break;
        }
        return true;
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ class GrassRS {
        createProgramFragmentStore();
        createProgramFragment();
        createScriptStructures();
        loadSkyTextures();
        loadTextures();

        ScriptC.Builder sb = new ScriptC.Builder(mRS);
        sb.setScript(mResources, R.raw.grass);
@@ -172,7 +172,7 @@ class GrassRS {
        blades[index + BLADE_STRUCT_B] = random(0.65f) + 0.35f;
    }

    private void loadSkyTextures() {
    private void loadTextures() {
        mTextureBufferIDs = new int[TEXTURES_COUNT];
        mTextures = new Allocation[TEXTURES_COUNT];
        mTexturesIDs = Allocation.createSized(mRS, USER_FLOAT, TEXTURES_COUNT);
+1 −1
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ void ProgramVertex::setupGL(ProgramVertexState *state)
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (mLightCount) {
        int v = 1;
        int v = 0;
        glEnable(GL_LIGHTING);
        glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v);
        for (uint32_t ct = 0; ct < mLightCount; ct++) {
Loading