// 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(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_RUNNING 9 #define RSID_LEAVES_COUNT 10 #define RSID_RIPPLE_MAP 1 #define RSID_REFRACTION_MAP 2 #define RSID_LEAVES 3 #define RSID_GL_STATE 4 #define RSID_GL_WIDTH 0 #define RSID_GL_HEIGHT 1 #define LEAF_STRUCT_FIELDS_COUNT 11 #define LEAF_STRUCT_X 0 #define LEAF_STRUCT_Y 1 #define LEAF_STRUCT_SCALE 2 #define LEAF_STRUCT_ANGLE 3 #define LEAF_STRUCT_SPIN 4 #define LEAF_STRUCT_U1 5 #define LEAF_STRUCT_U2 6 #define LEAF_STRUCT_ALTITUDE 7 #define LEAF_STRUCT_RIPPLED 8 #define LEAF_STRUCT_DELTAX 9 #define LEAF_STRUCT_DELTAY 10 #define LEAVES_TEXTURES_COUNT 4 #define LEAF_SIZE 0.55f #define REFRACTION 1.333f #define DAMP 3 #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--; } } int refraction(int d, int wave) { int* map = loadArrayI32(RSID_REFRACTION_MAP, 0); int i = d; if (i < 0) i = -i; if (i > 512) i = 512; int w = (wave + 0x10000) >> 8; w &= ~(w >> 31); int r = (map[i] * w) >> 3; if (d < 0) { return -r; } return r; } 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; int offsetx = refraction(dx, wave) >> 16; int u = (width - w) + offsetx; u &= ~(u >> 31); if (u >= width) u = width - 1; int offsety = refraction(dy, wave) >> 16; int v = (height - h) + offsety; v &= ~(v >> 31); if (v >= height) v = height - 1; vertices[(offset + w) * 8 + 3] = u / (float) width; vertices[(offset + w) * 8 + 4] = v / (float) height; // 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; // V2 v2x = vertices[(yOffset + width + x + 1) * 8 + 5]; v2y = vertices[(yOffset + width + x + 1) * 8 + 6]; v2z = vertices[(yOffset + width + x + 1) * 8 + 7]; // N1 n1x = v2x - v1x; n1y = v2y - v1y; n1z = v2z - v1z; // N2 n2x = v3x - v1x; n2y = v3y - v1y; n2z = v3z - v1z; // Avegare of previous normal and N1 x N2 n3x = n3x / 2.0f + (n1y * n2z - n1z * n2y) / 2.0f; n3y = n3y / 2.0f + (n1z * n2x - n1x * n2z) / 2.0f; n3z = n3z / 2.0f + (n1x * n2y - n1y * n2x) / 2.0f; // Normalize 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; // reset Z //vertices[(yOffset + x) * 8 + 7] = 0.0f; } } } void drawNormals() { int width = loadI32(RSID_STATE, RSID_MESH_WIDTH); int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT); float *vertices = loadTriangleMeshVerticesF(NAMED_mesh); bindProgramVertex(NAMED_PVLines); color(1.0f, 0.0f, 0.0f, 1.0f); int y = 0; for ( ; y < height; y++) { int yOffset = y * width; int x = 0; for ( ; x < width; x++) { int offset = (yOffset + x) * 8; float vx = vertices[offset + 5]; float vy = vertices[offset + 6]; float vz = vertices[offset + 7]; float nx = vertices[offset + 0]; float ny = vertices[offset + 1]; float nz = vertices[offset + 2]; drawLine(vx, vy, vz, vx + nx / 10.0f, vy + ny / 10.0f, vz + nz / 10.0f); } } } float averageZ(float x1, float x2, float y1, float y2, float* vertices, int meshWidth, int meshHeight, float glWidth, float glHeight) { x1 = ((x1 + glWidth / 2.0f) / glWidth) * meshWidth; x2 = ((x2 + glWidth / 2.0f) / glWidth) * meshWidth; y1 = ((y1 + glHeight / 2.0f) / glHeight) * meshHeight; y2 = ((y2 + glHeight / 2.0f) / glHeight) * meshHeight; int quadX1 = clamp(x1, 0, meshWidth); int quadX2 = clamp(x2, 0, meshWidth); int quadY1 = clamp(y1, 0, meshHeight); int quadY2 = clamp(y2, 0, meshHeight); float z = 0.0f; int vertexCount = 0; int y = quadY1; for ( ; y < quadY2; y++) { int x = quadX1; int yOffset = y * meshWidth; for ( ; x < quadX2; x++) { z += vertices[(yOffset + x) * 8 + 7]; vertexCount++; } } return 75.0f * z / vertexCount; } void drawLeaf(int index, int frameCount, float* vertices, int meshWidth, int meshHeight, float glWidth, float glHeight) { float *leafStruct = loadArrayF(RSID_LEAVES, index); float x = leafStruct[LEAF_STRUCT_X]; float x1 = x - LEAF_SIZE; float x2 = x + LEAF_SIZE; float y = leafStruct[LEAF_STRUCT_Y]; float y1 = y - LEAF_SIZE; float y2 = y + LEAF_SIZE; float u1 = leafStruct[LEAF_STRUCT_U1]; float u2 = leafStruct[LEAF_STRUCT_U2]; float z1 = 0.0f; float z2 = 0.0f; float z3 = 0.0f; float z4 = 0.0f; float a = leafStruct[LEAF_STRUCT_ALTITUDE]; float s = leafStruct[LEAF_STRUCT_SCALE]; float r = leafStruct[LEAF_STRUCT_ANGLE]; float tz = 0.0f; if (a > 0.0f) { tz = -a; } else { z1 = averageZ(x1, x, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight); z2 = averageZ(x, x2, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight); z3 = averageZ(x, x2, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight); z4 = averageZ(x1, x, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight); } x1 -= x; x2 -= x; y1 -= y; y2 -= y; float matrix[16]; matrixLoadIdentity(matrix); matrixTranslate(matrix, x, y, tz); matrixScale(matrix, s, s, 1.0f); matrixRotate(matrix, r, 0.0f, 0.0f, 1.0f); vpLoadModelMatrix(matrix); drawQuadTexCoords(x1, y1, z1, u1, 1.0f, x2, y1, z2, u2, 1.0f, x2, y2, z3, u2, 0.0f, x1, y2, z4, u1, 0.0f); float spin = leafStruct[LEAF_STRUCT_SPIN]; if (a <= 0.0f) { float rippled = leafStruct[LEAF_STRUCT_RIPPLED]; if (rippled < 0.0f) { drop(((x + glWidth / 2.0f) / glWidth) * meshWidth, meshHeight - ((y + glHeight / 2.0f) / glHeight) * meshHeight, DROP_RADIUS); spin /= 4.0f; leafStruct[LEAF_STRUCT_SPIN] = spin; leafStruct[LEAF_STRUCT_RIPPLED] = 1.0f; } leafStruct[LEAF_STRUCT_X] = x + leafStruct[LEAF_STRUCT_DELTAX]; leafStruct[LEAF_STRUCT_Y] = y + leafStruct[LEAF_STRUCT_DELTAY]; r += spin; leafStruct[LEAF_STRUCT_ANGLE] = r; } else { a -= 0.005f; leafStruct[LEAF_STRUCT_ALTITUDE] = a; r += spin * 2.0f; leafStruct[LEAF_STRUCT_ANGLE] = r; } if (-LEAF_SIZE * s + x > glWidth / 2.0f || LEAF_SIZE * s + x < -glWidth / 2.0f || LEAF_SIZE * s + y < -glHeight / 2.0f) { int sprite = randf(LEAVES_TEXTURES_COUNT); leafStruct[LEAF_STRUCT_X] = randf2(-1.0f, 1.0f); leafStruct[LEAF_STRUCT_Y] = glHeight / 2.0f + LEAF_SIZE * 2 * randf(1.0f); leafStruct[LEAF_STRUCT_SCALE] = randf2(0.4f, 0.5f); leafStruct[LEAF_STRUCT_SPIN] = degf(randf2(-0.02f, 0.02f)) / 4.0f; leafStruct[LEAF_STRUCT_U1] = sprite / (float) LEAVES_TEXTURES_COUNT; leafStruct[LEAF_STRUCT_U2] = (sprite + 1) / (float) LEAVES_TEXTURES_COUNT; leafStruct[LEAF_STRUCT_DELTAX] = randf2(-0.02f, 0.02f) / 100.0f; leafStruct[LEAF_STRUCT_DELTAY] = -0.08f * randf2(0.9f, 1.1f) / 100.0f; } } void drawLeaves(int frameCount) { bindProgramFragment(NAMED_PFLeaf); bindProgramFragmentStore(NAMED_PFSLeaf); bindTexture(NAMED_PFLeaf, 0, NAMED_TLeaves); int leavesCount = loadI32(RSID_STATE, RSID_LEAVES_COUNT); int count = leavesCount * LEAF_STRUCT_FIELDS_COUNT; int width = loadI32(RSID_STATE, RSID_MESH_WIDTH); int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT); float glWidth = loadF(RSID_GL_STATE, RSID_GL_WIDTH); float glHeight = loadF(RSID_GL_STATE, RSID_GL_HEIGHT); float *vertices = loadTriangleMeshVerticesF(NAMED_mesh); int i = 0; for ( ; i < count; i += LEAF_STRUCT_FIELDS_COUNT) { drawLeaf(i, frameCount, vertices, width, height, glWidth, glHeight); } } int main(int index) { int frameCount = loadI32(RSID_STATE, RSID_FRAME_COUNT); 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); } int isRunning = loadI32(RSID_STATE, RSID_RUNNING); if (isRunning) { updateRipples(); generateRipples(); updateTriangleMesh(NAMED_mesh); } float matrix[16]; matrixLoadIdentity(matrix); vpLoadModelMatrix(matrix); bindTexture(NAMED_PFBackground, 0, NAMED_TRiverbed); drawTriangleMesh(NAMED_mesh); ambient(0.0f, 0.0f, 0.0f, 1.0f); diffuse(0.0f, 0.0f, 0.0f, 1.0f); specular(0.44f, 0.44f, 0.44f, 1.0f); shininess(40.0f); bindProgramFragment(NAMED_PFLighting); drawTriangleMesh(NAMED_mesh); drawLeaves(frameCount); if (!isRunning) { drawNormals(); } frameCount++; storeI32(RSID_STATE, RSID_FRAME_COUNT, frameCount); return 1; }