diff --git a/README.md b/README.md index af7f87c..e02a1c1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#SmartGL for Android# +# SmartGL for Android # **SmartGL** is a Graphic Engine for creating Android Games and Apps. It is based on **OpenGL** and handles **2D Sprites** and **3D Textured Objects**. @@ -6,7 +6,7 @@ SmartGL is used by several games and apps. ![alt text](extras/smartgl_screenshot.png?raw=true "Screenshot example") -##How to use## +## How to use ## The scene is displayed inside a **SmartGLView**. @@ -50,7 +50,16 @@ If the View is resized for any reason (rotation of the device for example), **on At every OpenGL frame, **onTick(...)** is called. -Finally, every touch interaction calls **onTouchEvent(...)** +Finally, every touch interaction calls **onTouchEvent(...)**. Note that you have to enable the touch in the view, by code or in the XML: + +```xml + +``` + +Here are the callbacks: + ```java public class MainActivity extends AppCompatActivity implements SmartGLViewController { @@ -77,7 +86,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro } ``` -###LifeCycle### +### LifeCycle ### It is really important to inform the SmartGLView when the Activity or Fragment is paused or resumed. **If you miss this step the scene will not be initialized or restored correctly**. @@ -102,11 +111,11 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro } ``` -###Playing with the Sprites### +### Playing with the Sprites ### The draw pipeline includes Render passes. For example you can have several 3D objects in the background pass, and several Sprites in the forground pass. -Let's start with a simple **Sprite** using a **Texture** (picture) loaded from the Android drawables. It is highly recommended to share the same texture between several Sprites or Objects 3D when possible: the textures are loaded into the GPU memory and it is often not that big. +Let's start with a simple **Sprite** using a **Texture** (picture) loaded from the Android drawables. It is highly recommended to share the same texture between several Sprites or Objects 3D when possible: the textures are loaded into the GPU memory which size is limited. ```java public class MainActivity extends AppCompatActivity implements SmartGLViewController { @@ -120,6 +129,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro RenderPassSprite renderPassSprite = new RenderPassSprite(); renderer.addRenderPass(renderPassSprite); // add it only once for all Sprites + // the drawable must be a picture (jpg or png), not a vector (xml) mSpriteTexture = new Texture(context, R.drawable.planet); mSprite = new Sprite(120, 120); // 120 x 120 pixels @@ -133,6 +143,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro public void onReleaseView(SmartGLView smartGLView) { if (mSpriteTexture != null) { mSpriteTexture.release(); + mSpriteTexture = null; } } @@ -143,7 +154,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro ![alt text](extras/sprite_screenshot.png?raw=true "Sprite on screen") -###Add movement### +### Add movement ### Let's add some basic moves. @@ -174,7 +185,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro Your Sprite is now moving and rotating on the screen! -###Playing with 3D Objects### +### Playing with 3D Objects ### Now let's add a moving 3D Object! There is a **WavefrontModel** class that can load a Wavefront model from an .obj file. @@ -201,6 +212,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro RenderPassObject3D renderPassObject3D = new RenderPassObject3D(); renderer.addRenderPass(renderPassObject3D); // add it only once for all 3D Objects + // the drawable must be a picture (jpg or png), not a vector (xml) mShipTexture = new Texture(context, R.drawable.ship_picture); WavefrontModel model = new WavefrontModel.Builder(context, R.raw.spaceship_obj) @@ -229,6 +241,7 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro public void onReleaseView(SmartGLView smartGLView) { if (mShipTexture != null) { mShipTexture.release(); + mShipTexture = null; } } } @@ -240,32 +253,65 @@ public class MainActivity extends AppCompatActivity implements SmartGLViewContro ![alt text](extras/2d3d_screenshot.png?raw=true "Sprite & Object 3D") -More advanced Tutorials will be put in the Wiki later. +Note that if your Model does not have a texture but colors on each vertex, you'll have to use a specific "color" shader instead of the default one: + +```java + + new RenderPassObject3D(RenderPassObject3D.ShaderType.SHADER_COLOR, true, true); + + +``` + +Also if there are no textures and no colors on vertex, you can specify a default color in the WavefrontModel.Builder using: + +```java + + builder.setColor(0.5f, 0.3, 0.8f); // RGB + +``` + +## Advanced use of SmartGL ## + +Read the [**ADVANCED DOCUMENTATION ON WIKI**](https://github.com/smart-fun/smartGL/wiki) + +## Samples ## + +See the **sample app** for a complete implementation (download or clone the repository) + +You can also have a look to these apps that use smartGL: + +- [ZenDay](https://play.google.com/store/apps/details?id=com.mobisysteme.zime), a 3D Calendar and Task manager +- [Slippy Slope](https://play.google.com/store/apps/details?id=fr.smartfun.pingoo), a funny game which uses a lot 2D Sprites +- [Bulldozer Rampage](https://play.google.com/store/apps/details?id=org.lunapark.dev.bullramp), a race game which uses 3D objects +- [Time Shift](https://www.youtube.com/watch?v=A4hfyvfj-d8) a demo that combines 2D and 3D effects + + +![alt text](extras/releases.png?raw=true "projects using smartGL") -## Installation with gradle +## Installation with gradle ## Add the following maven{} line to your **PROJECT** build.gradle file ``` allprojects { repositories { - jcenter() + mavenCentral() maven { url "https://jitpack.io" } // add this line } } ``` -Add the libary dependency to your **APP** build.gradle file +Add the libary dependency to your **APP** module build.gradle file ``` dependencies { - compile 'com.github.smart-fun:smartGL:1.0.0' // add this line + implementation 'com.github.smart-fun:smartGL:1.2.9' // add this line } ``` -##License## +## License ## -Copyright 2016 Arnaud Guyon +Copyright 2016-2025 Arnaud Guyon Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -279,3 +325,4 @@ 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. + diff --git a/build.gradle b/build.gradle index c20bca1..72038bc 100644 --- a/build.gradle +++ b/build.gradle @@ -2,22 +2,14 @@ buildscript { repositories { - jcenter() + mavenCentral() + maven { + url 'https://maven.google.com/' + name 'Google' + } + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.android.tools.build:gradle:8.8.2' } } - -allprojects { - repositories { - jcenter() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/extras/medusa.gif b/extras/medusa.gif new file mode 100644 index 0000000..524a19f Binary files /dev/null and b/extras/medusa.gif differ diff --git a/extras/medusa_ani.png b/extras/medusa_ani.png new file mode 100644 index 0000000..8e61d5f Binary files /dev/null and b/extras/medusa_ani.png differ diff --git a/extras/releases.png b/extras/releases.png new file mode 100644 index 0000000..392088d Binary files /dev/null and b/extras/releases.png differ diff --git a/extras/slippy_small.png b/extras/slippy_small.png new file mode 100644 index 0000000..be4a11f Binary files /dev/null and b/extras/slippy_small.png differ diff --git a/gradle.properties b/gradle.properties index 1d3591c..915f0e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ce3ee27..af30b60 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Nov 17 19:49:27 CET 2016 +#Sun Mar 23 19:45:14 CET 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/settings.gradle b/settings.gradle index bafc9a8..5b5e062 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,10 @@ -include ':smartglapp', ':smartgl' +//include ':smartglapp', ':smartgl' +include ':smartgl' +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url 'https://jitpack.io' } + } +} diff --git a/smartgl/build.gradle b/smartgl/build.gradle index d5e4bc0..a8a2fc7 100644 --- a/smartgl/build.gradle +++ b/smartgl/build.gradle @@ -1,14 +1,12 @@ apply plugin: 'com.android.library' - +apply plugin: 'maven-publish' android { - compileSdkVersion 24 - buildToolsVersion "24.0.0" - + compileSdk 34 defaultConfig { minSdkVersion 14 - targetSdkVersion 24 - versionCode 12 - versionName "1.0.0" + targetSdkVersion 34 + versionCode 39 + versionName "1.2.9" } buildTypes { release { @@ -16,9 +14,29 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + namespace 'fr.arnaudguyon.smartgl' + } dependencies { - compile 'com.android.support:appcompat-v7:24.0.0' - compile fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.annotation:annotation:1.9.1' +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + //from components.findByName('release') + groupId = 'com.github.smart-fun' + artifactId = 'smartgl' + version = '1.2.9' + artifact("$buildDir/outputs/aar/smartgl-release.aar") { + builtBy tasks.named("assembleRelease") + extension "aar" + } + } + } + } } + diff --git a/smartgl/src/main/AndroidManifest.xml b/smartgl/src/main/AndroidManifest.xml index 30f91b8..c8006a2 100644 --- a/smartgl/src/main/AndroidManifest.xml +++ b/smartgl/src/main/AndroidManifest.xml @@ -1,10 +1,15 @@ - + + + android:supportsRtl="true"> diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/math/Vector3D.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/math/Vector3D.java new file mode 100644 index 0000000..d301f21 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/math/Vector3D.java @@ -0,0 +1,76 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.math; + +import androidx.annotation.NonNull; + +/** + * Created by aguyon on 18.01.17. + */ + +public class Vector3D { + + private float[] mVector = new float[3]; + + public Vector3D(float x, float y, float z) { + mVector[0] = x; + mVector[1] = y; + mVector[2] = z; + } + + public float[] getArray() { + return mVector; + } + + public Vector3D normalize() { + float norm = (float) Math.sqrt(mVector[0]*mVector[0] + mVector[1]*mVector[1] + mVector[2]*mVector[2]); + if (norm > 0) { + mVector[0] /= norm; + mVector[1] /= norm; + mVector[2] /= norm; + } else { + mVector[0] = 1; + mVector[1] = 0; + mVector[2] = 0; + } + return this; + } + + public float dot(@NonNull Vector3D other) { + return (mVector[0] * other.mVector[0]) + (mVector[1] * other.mVector[1]) + (mVector[2] * other.mVector[2]); + } + + public Vector3D add(@NonNull Vector3D other) { + mVector[0] += other.mVector[0]; + mVector[1] += other.mVector[1]; + mVector[2] += other.mVector[2]; + return this; + } + public Vector3D multiply(float scale) { + mVector[0] *= scale; + mVector[1] *= scale; + mVector[2] *= scale; + return this; + } + + public Vector3D vectorProduct(@NonNull Vector3D o) { + float x = mVector[1] * o.mVector[2] - mVector[2] * o.mVector[1]; + float y = mVector[2] * o.mVector[0] - mVector[0] * o.mVector[2]; + float z = mVector[0] * o.mVector[1] - mVector[1] * o.mVector[0]; + return new Vector3D(x, y, z); + } + +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AnimatedSprite.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AnimatedSprite.java index 6799b3e..23979a0 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AnimatedSprite.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AnimatedSprite.java @@ -44,17 +44,21 @@ public void addFrame(float duration, float uStart, float vStart, float uStop, fl uvList.add(uStop, vStop); uvList.finalizeBuffer(); AnimatedFace3D face = (AnimatedFace3D) getFace(); - face.addFrame(duration, uvList); + if (face != null) { + face.addFrame(duration, uvList); + } } public final int getFrameNumber() { AnimatedFace3D aniFace = (AnimatedFace3D) getFace(); - return aniFace.getFrameNumber(); + return (aniFace != null) ? aniFace.getFrameNumber() : 0; } public void setFrame(int frameNumber) { AnimatedFace3D aniFace = (AnimatedFace3D) getFace(); - aniFace.setFrame(frameNumber); + if (aniFace != null) { + aniFace.setFrame(frameNumber); + } } @Override diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AttribList.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AttribList.java index a52c483..3f8ed39 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AttribList.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/AttribList.java @@ -118,6 +118,7 @@ public final void add(float a, float b, float c, float d) { } public final void finalizeBuffer() { + mCurrentIndex = mInternalBuffer.length; mFloatBuffer = ByteBuffer.allocateDirect(mCurrentIndex * Display.BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); mFloatBuffer.put(mInternalBuffer).position(0); } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionCircle.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionCircle.java index b0a5d5a..3b1d22e 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionCircle.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionCircle.java @@ -15,7 +15,7 @@ */ package fr.arnaudguyon.smartgl.opengl; -import junit.framework.Assert; +import fr.arnaudguyon.smartgl.tools.Assert; /** * Created by Arnaud Guyon on 07/05/2015. diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionRectangle.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionRectangle.java index 9f9daea..94ff53b 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionRectangle.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/CollisionRectangle.java @@ -17,7 +17,7 @@ import android.util.Log; -import junit.framework.Assert; +import fr.arnaudguyon.smartgl.tools.Assert; /** * Created by Arnaud Guyon on 09/05/2015. @@ -113,27 +113,45 @@ public boolean collideWithCircle(CollisionCircle circle) { if (circleIsInside(circle)) { // already computes position & size for both return true; } else { -// computePositionAndSize(); -// circle.computePositionAndSize(); float circlePosX = circle.getPosX(); float circlePosY = circle.getPosY(); float radius = circle.getRadius(); - if (circlePosY - radius > mBottomPos) { - return false; + // Vertical collision in "left to right" segment + if (circlePosX >= mLeftPos && circlePosX <= mRightPos) { + if ((circlePosY + radius >= getTop()) && (circlePosY - radius <= getBottom())) { + return true; + } } - if (circlePosY + radius < mTopPos) { - return false; + // Horizontal collision in "top to bottom" segment + if (circlePosY >= mBottomPos && circlePosY <= getTop()) { + if ((circlePosX + radius >= mLeftPos) && (circlePosX - radius <= mRightPos)) { + return true; + } } - if (circlePosX - radius > mRightPos) { - return false; + // Corners + float leftDist2 = (mLeftPos - circlePosX) * (mLeftPos - circlePosX); + float topDist2 = (mTopPos - circlePosY) * (mTopPos - circlePosY); + float radius2 = radius * radius; + // Top-Left corner + if (leftDist2 + topDist2 < radius2) { + return true; } - if (circlePosX + radius < mLeftPos) { - return false; + float rightDist2 = (mRightPos - circlePosX) * (mRightPos - circlePosX); + // Top-Right corner + if (rightDist2 + topDist2 < radius2) { + return true; } - // "squared circle" collides square - // TODO: how to be more precise in angles ? - return true; + float bottomDist2 = (mBottomPos - circlePosY) * (mBottomPos - circlePosY); + // Bottom-Left corner + if (leftDist2 + bottomDist2 < radius2) { + return true; + } + // Bottom-Right corner + if (rightDist2 + bottomDist2 < radius2) { + return true; + } + return false; } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ConfigGLSelector.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ConfigGLSelector.java index 2771477..fd43ad3 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ConfigGLSelector.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ConfigGLSelector.java @@ -30,9 +30,11 @@ public class ConfigGLSelector implements EGLConfigChooser { private final static int EGL_OPENGL_ES2_BIT = 4; // WTF, defined nowhere private final static int[][] CONFIGS_PARAMS = { - { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }, - - { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 8, EGL10.EGL_NONE } }; + { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }, + { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 8, EGL10.EGL_NONE }, + { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }, + { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 8, EGL10.EGL_NONE }, + }; @SuppressWarnings("unused") private ConfigGLSelector() { diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Face3D.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Face3D.java index e9d9559..da79190 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Face3D.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Face3D.java @@ -19,16 +19,16 @@ * @author Arnaud Guyon * * Defines a face for an Object3D. A Face3D is composed of a Texture, a - * VertexList and a UVList + * VertexList, a UVList and a NormalList * */ - public class Face3D { private Texture mTexture; private VertexList mVertexList; private UVList mUVList; private ColorList mColorList; + private NormalList mNormalList; private boolean mVisible = true; public Face3D() { @@ -61,6 +61,14 @@ final public ColorList getColorList() { return mColorList; } + final public void setNormalList(NormalList normalList) { + mNormalList = normalList; + } + + final public NormalList getNormalList() { + return mNormalList; + } + final public void setTexture(Texture texture) { mTexture = texture; } @@ -98,6 +106,11 @@ public void releaseResources() { mColorList.destroyFloatBuffer(); mColorList = null; } + + if (mNormalList != null) { + mNormalList.destroyFloatBuffer(); + mNormalList = null; + } } public boolean shouldDisplay(OpenGLRenderer renderer) { diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Light.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Light.java new file mode 100644 index 0000000..9c4857c --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Light.java @@ -0,0 +1,23 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +/** + * Parent class for Lights + */ + +public abstract class Light { +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightAmbiant.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightAmbiant.java new file mode 100644 index 0000000..d020f5e --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightAmbiant.java @@ -0,0 +1,35 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +import androidx.annotation.FloatRange; + +/** + * Ambiant Light for OpenGL + */ + +public class LightAmbiant extends Light { + + private SmartColor mColor; + + public LightAmbiant(@FloatRange(from=0, to=1) float red, @FloatRange(from=0, to=1) float green, @FloatRange(from=0, to=1) float blue) { + mColor = new SmartColor(red, green, blue, 1); + } + + public float[] getArray() { + return mColor.getArray(); + } +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightParallel.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightParallel.java new file mode 100644 index 0000000..650f47b --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/LightParallel.java @@ -0,0 +1,45 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +import androidx.annotation.NonNull; + +import fr.arnaudguyon.smartgl.math.Vector3D; + +/** + * Parallel Light for OpenGL + */ + +public class LightParallel extends Light { + + private SmartColor mColor; + private Vector3D mDirection; + + public LightParallel(@NonNull SmartColor color, @NonNull Vector3D direction) { + mColor = color; + mDirection = direction; + mDirection.normalize(); + } + + public @NonNull SmartColor getColor() { + return mColor; + } + + public @NonNull Vector3D getDirection() { + return mDirection; + } + +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/NormalList.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/NormalList.java new file mode 100644 index 0000000..72210a4 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/NormalList.java @@ -0,0 +1,31 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +/** + * List of Normals for a Face3D + */ + +public class NormalList extends AttribList { + + public NormalList() { + super(3); + } + + protected NormalList(float[] other, int otherElementsPerVector) { + super(other, otherElementsPerVector, 3); + } +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLRenderer.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLRenderer.java index 165d3a4..180d983 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLRenderer.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLRenderer.java @@ -17,6 +17,7 @@ import java.lang.ref.WeakReference; import java.nio.FloatBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Vector; @@ -24,16 +25,19 @@ import javax.microedition.khronos.opengles.GL10; import android.content.Context; +import android.graphics.Bitmap; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; +import android.os.Handler; import android.os.SystemClock; import android.util.SparseArray; -import junit.framework.Assert; +import androidx.annotation.NonNull; import fr.arnaudguyon.smartgl.R; import fr.arnaudguyon.smartgl.math.Vector2D; +import fr.arnaudguyon.smartgl.tools.Assert; /** * Base class for the Renderer. Handles the list of the RenderObject to display, the camera. @@ -42,74 +46,84 @@ public abstract class OpenGLRenderer implements GLSurfaceView.Renderer { - private WeakReference mOpenGLView; - private int mWidth, mHeight; + private WeakReference mOpenGLView; + private int mWidth, mHeight; - private long mPreviousTime = 0; - private long mFrameDuration = 0; + private long mPreviousTime = 0; + private long mFrameDurationRaw = 0; + private float mFrameDurationSmoothed = 0.02f; - private float[] mProj3DMatrix = new float[16]; - private float[] mProj2DMatrix = new float[16]; - private float[] mTmpMatrix = new float[16]; + private float[] mProj3DMatrix = new float[16]; + private float[] mProj2DMatrix = new float[16]; + private float[] mTmpMatrix = new float[16]; - private Vector mRenderPasses; - private boolean mInitDone; - private float[] mClearColor = {0.2f, 0.5f, 0.7f, 1}; // RGBA + private Vector mRenderPasses; + private boolean mInitDone; + private float[] mClearColor = {0.2f, 0.5f, 0.7f, 1}; // RGBA private OpenGLCamera mCamera; - - private Shader mPreviousShader = null; - private boolean mUseTexture = false; - private boolean mUseColor = false; - private int mVertexAttribId = -1; - private int mUvAttribId = -1; - private int mColorAttribId = -1; - private int mProjMatrixId = -1; + + private Shader mPreviousShader = null; + private boolean mUseTexture = false; + private boolean mUseColor = false; + private int mVertexAttribId = -1; + private int mUvAttribId = -1; + private int mColorAttribId = -1; + private int mProjMatrixId = -1; private Boolean mDoubleSided = true; + private static final float[] DEFAULT_AMBIANT_LIGHT = {1, 1, 1, 1}; // RVBA + private LightAmbiant mLightAmbiant; + private LightParallel mLightParallel; + // DEBUG private boolean mDebugMode = false; private Sprite mColCircleSprite; private Sprite mColSquareSprite; - private Sprite mColSegmentSprite; + private Sprite mColSegmentSprite; - public OpenGLRenderer(Context context) { - super(); - mRenderPasses = new Vector<>(); + public OpenGLRenderer(Context context) { + super(); + mRenderPasses = new Vector<>(); // mTouchHelper = new TouchHelper(); - } - - public int getWidth() { - return mWidth; - } - - public int getHeight() { - return mHeight; - } - - public float getFrameDuration() { - return (mFrameDuration / 1000.f); - } - - public float[] getProjection3DMatrix() { - return mProj3DMatrix; - } - - public float[] getProjection2DMatrix() { - return mProj2DMatrix; - } - - public void setClearColor(float r, float g, float b, float a) { - mClearColor[0] = r; - mClearColor[1] = g; - mClearColor[2] = b; - mClearColor[3] = a; - } - - final void setListener(OpenGLView listener) { - mOpenGLView = new WeakReference<>(listener); - } - final OpenGLView getListener() { + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + public float getFrameDuration() { + return mFrameDurationSmoothed; + } + + public float getRawFrameDuration() { + return mFrameDurationRaw; + } + + public float[] getProjection3DMatrix() { + return mProj3DMatrix; + } + + public float[] getProjection2DMatrix() { + return mProj2DMatrix; + } + + public void setClearColor(float r, float g, float b, float a) { + mClearColor[0] = r; + mClearColor[1] = g; + mClearColor[2] = b; + mClearColor[3] = a; + } + + final void setListener(OpenGLView listener) { + mOpenGLView = new WeakReference<>(listener); + } + + final OpenGLView getListener() { if (mOpenGLView != null) { return mOpenGLView.get(); } else { @@ -134,249 +148,270 @@ public void setDebugMode(Context context) { mDebugMode = true; } - public void setRenderPasses(Vector renderPasses) { - synchronized (this) { - mRenderPasses = renderPasses; - } - } // TODO: release lists inside programs when setting null - - public void addRenderPass(RenderPass renderPass) { - synchronized (this) { - mRenderPasses.add(renderPass); - } - } - - public void removeRenderPass(RenderPass renderPass) { - synchronized (this) { - mRenderPasses.remove(renderPass); - } - } + public void setRenderPasses(Vector renderPasses) { + synchronized (this) { + mRenderPasses = renderPasses; + } + } // TODO: release lists inside programs when setting null - protected void onPreRender(GL10 gl) { - } + public void addRenderPass(RenderPass renderPass) { + synchronized (this) { + mRenderPasses.add(renderPass); + } + } - protected void onPreRenderPass(GL10 gl, int passNumber) { - } + void clearRenderPasses() { + synchronized (this) { + mRenderPasses.clear(); + } + } - protected void onPostRenderPass(GL10 gl, int passNumber) { - } + public void removeRenderPass(RenderPass renderPass) { + synchronized (this) { + mRenderPasses.remove(renderPass); + } + } - protected void onPostRender(GL10 gl) { - } + public boolean removeObject(@NonNull RenderObject renderObject) { + boolean removed = false; + synchronized (this) { + for (RenderPass renderPass : mRenderPasses) { + removed |= renderPass.removeObject(renderObject); + } + } + return removed; + } + + protected void onPreRender(GL10 gl) { + } - @Override - public void onDrawFrame(GL10 gl10) { + protected void onPreRenderPass(GL10 gl, int passNumber) { + } + + protected void onPostRenderPass(GL10 gl, int passNumber) { + } + + protected void onPostRender(GL10 gl) { + } - computeFps(); + @Override + public void onDrawFrame(GL10 gl10) { - GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]); // RGBA - GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); + computeFps(); + + GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]); // RGBA + GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); checkDoubleSided(gl10); - synchronized (this) { - - if (!mInitDone) { - return; - } - - if (mOpenGLView != null) { - OpenGLView view = mOpenGLView.get(); - if (view != null) { - view.onPreRender(this); - } - } + synchronized (this) { + + if (!mInitDone) { + return; + } + + if (mOpenGLView != null) { + OpenGLView view = mOpenGLView.get(); + if (view != null) { + view.onPreRender(this); + } + } if ((mCamera != null) && mCamera.isDirty()) { computeProjMatrix3D(mProj3DMatrix); } - - onPreRender(gl10); - if (mRenderPasses != null) { + onPreRender(gl10); + + if (mRenderPasses != null) { - int passNumber = 0; - final int prgSize = mRenderPasses.size(); - for (int prgIt = 0; prgIt < prgSize; ++prgIt) { - final RenderPass renderPass = mRenderPasses.get(prgIt); - if (!renderPass.isLoaded()) { - renderPass.load(); - } + int passNumber = 0; + final int prgSize = mRenderPasses.size(); + for (int prgIt = 0; prgIt < prgSize; ++prgIt) { + final RenderPass renderPass = mRenderPasses.get(prgIt); + if (!renderPass.isLoaded()) { + renderPass.load(); + } renderPass.sortObjects(); - ++passNumber; - onPreRenderPass(gl10, passNumber); - - Vector mainObjectList = renderPass.getRenderObjects(); - //Vector objectList = buildObjectList(mainObjectList); // recursive with Container Objects - - if (renderPass.useZBuffer()) { - gl10.glEnable(GL10.GL_DEPTH_TEST); - if (renderPass.clearZBuffer()) { - GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); - } - } else { - gl10.glDisable(GL10.GL_DEPTH_TEST); - } - - mPreviousShader = null; - mUseTexture = false; - mUseColor = false; - mVertexAttribId = -1; - mUvAttribId = -1; - mColorAttribId = -1; - mProjMatrixId = -1; - - // OBJECTS - for (int renderObjIt = 0; renderObjIt < mainObjectList.size(); ++renderObjIt) { - RenderObject object = mainObjectList.get(renderObjIt); - boolean render = object.shouldDisplay(this); - if (object.isContainer()) { - renderContainer(renderPass, (RenderObjectContainer) object, render); - } else { - renderObject(renderPass, object, render); - } - - } - - onPostRenderPass(gl10, passNumber); - } - } - //handleTouchEvents(); - onPostRender(gl10); - } - } - - private void renderContainer(final RenderPass renderPass, RenderObjectContainer container, boolean render) { - container.tick(this); - if (render) { - container.onPreRenderObject(this); - } - Vector objects = container.getRenderObjects(); - if (objects != null) { - for(int i=0; i faces = object.getFaces(); - - final int faceSize = faces.size(); - for (int faceIt = 0; faceIt < faceSize; ++faceIt) { - Face3D face = faces.get(faceIt); - if (!face.shouldDisplay(this)) { - continue; - } - GLES20.glUseProgram(renderPass.getProgramId()); - - // Check for new Shader - Shader shader = renderPass.getShader(); - if (shader != mPreviousShader) { - mPreviousShader = shader; - mUseTexture = shader.useTexture(); - mUseColor = shader.useColor(); - mVertexAttribId = shader.getVertexAttribId(); - mUvAttribId = shader.getUVAttribId(); - mColorAttribId = shader.getColorAttribId(); - mProjMatrixId = shader.getProjMatrixId(); - - GLES20.glEnableVertexAttribArray(mVertexAttribId); - if (mUseTexture) { - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glEnableVertexAttribArray(mUvAttribId); - - GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); - //GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); // premultiply alpha - GLES20.glEnable(GLES20.GL_BLEND); - } - if (mUseColor) { - GLES20.glEnableVertexAttribArray(mColorAttribId); - } - } - shader.onPreRender(object); - - // Vertex - VertexList vertexList = face.getVertexList(); - FloatBuffer vertexBuffer = vertexList.getFloatBuffer(); - vertexBuffer.position(0); - GLES20.glVertexAttribPointer(mVertexAttribId, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); - - // Texture - if (mUseTexture) { - Texture tex = face.getTexture(); - if (tex == null) { // not ready yet - continue; - } - if (!tex.isBinded()) { - tex.bindTexture(); - } - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.getId()); - // UVs - UVList uvList = face.getUVList(); - FloatBuffer uvBuffer = (uvList != null) ? uvList.getFloatBuffer() : null; - if (uvBuffer == null) { - continue; - } - uvBuffer.position(0); - GLES20.glVertexAttribPointer(mUvAttribId, 2, GLES20.GL_FLOAT, false, 0, uvBuffer); - } - - // Colors - if (mUseColor) { - ColorList colorList = face.getColorList(); - FloatBuffer colorBuffer = (colorList != null) ? colorList.getFloatBuffer() : null; - if (colorBuffer == null) { - continue; - } - colorBuffer.position(0); - GLES20.glVertexAttribPointer(mColorAttribId, 4, GLES20.GL_FLOAT, false, 0, colorBuffer); - } - - object.onPreRenderFace(this, shader, face); - face.onPreRenderFace(this, object, shader); - - // Transformations - if (object.is3D()) { - Matrix.multiplyMM(mTmpMatrix, 0, mProj3DMatrix, 0, modelViewMatrix, 0); - } else { - Matrix.multiplyMM(mTmpMatrix, 0, mProj2DMatrix, 0, modelViewMatrix, 0); - } - GLES20.glUniformMatrix4fv(mProjMatrixId, 1, false, mTmpMatrix, 0); - - // Render - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexList.getNbElements()); - } + ++passNumber; + onPreRenderPass(gl10, passNumber); + + Vector mainObjectList = renderPass.getRenderObjects(); + //Vector objectList = buildObjectList(mainObjectList); // recursive with Container Objects + + if (renderPass.useZBuffer()) { + gl10.glEnable(GL10.GL_DEPTH_TEST); + if (renderPass.clearZBuffer()) { + GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); + } + } else { + gl10.glDisable(GL10.GL_DEPTH_TEST); + } + + mPreviousShader = null; + mUseTexture = false; + mUseColor = false; + mVertexAttribId = -1; + mUvAttribId = -1; + mColorAttribId = -1; + mProjMatrixId = -1; + + // OBJECTS + for (int renderObjIt = 0; renderObjIt < mainObjectList.size(); ++renderObjIt) { + RenderObject object = mainObjectList.get(renderObjIt); + boolean render = object.shouldDisplay(this); + if (object.isContainer()) { + renderContainer(renderPass, (RenderObjectContainer) object, render); + } else { + renderObject(renderPass, object, render); + } + + } + + onPostRenderPass(gl10, passNumber); + } + } + //handleTouchEvents(); + onPostRender(gl10); + + if (mScreenshotListener != null) { + doTakeScreenshot(gl10); + } + } + + } + + private void renderContainer(final RenderPass renderPass, RenderObjectContainer container, boolean render) { + container.tick(this); + if (render) { + container.onPreRenderObject(this); + } + Vector objects = container.getRenderObjects(); + if (objects != null) { + for (int i = 0; i < objects.size(); ++i) { + RenderObject object = objects.get(i); + if (object.isContainer()) { + renderContainer(renderPass, (RenderObjectContainer) object, render && object.shouldDisplay(this)); + } else { + renderObject(renderPass, object, render && object.shouldDisplay(this)); + } + } + } + } + + private void renderObject(final RenderPass renderPass, RenderObject object, boolean render) { + + object.tick(this); + if (!render) { + return; + } + object.onPreRenderObject(this); + + float[] modelViewMatrix = object.getMatrix(); // get transformation matrix from object + Vector faces = object.getFaces(); + + final int faceSize = faces.size(); + for (int faceIt = 0; faceIt < faceSize; ++faceIt) { + Face3D face = faces.get(faceIt); + if (!face.shouldDisplay(this)) { + continue; + } + GLES20.glUseProgram(renderPass.getProgramId()); + + // Check for new Shader + Shader shader = renderPass.getShader(); + if (shader != mPreviousShader) { + mPreviousShader = shader; + mUseTexture = shader.useTexture(); + mUseColor = shader.useColor(); + mVertexAttribId = shader.getVertexAttribId(); + mUvAttribId = shader.getUVAttribId(); + mColorAttribId = shader.getColorAttribId(); + mProjMatrixId = shader.getProjMatrixId(); + + GLES20.glEnableVertexAttribArray(mVertexAttribId); + if (mUseTexture) { + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glEnableVertexAttribArray(mUvAttribId); + + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + //GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); // premultiply alpha + GLES20.glEnable(GLES20.GL_BLEND); + } + if (mUseColor) { + GLES20.glEnableVertexAttribArray(mColorAttribId); + } + } + + // Vertex + VertexList vertexList = face.getVertexList(); + FloatBuffer vertexBuffer = vertexList.getFloatBuffer(); + vertexBuffer.position(0); + GLES20.glVertexAttribPointer(mVertexAttribId, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); + + // Texture + if (mUseTexture) { + Texture tex = face.getTexture(); + if (tex == null) { // not ready yet + continue; + } + if (!tex.isBinded()) { + tex.bindTexture(); + } + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.getId()); + // UVs + UVList uvList = face.getUVList(); + FloatBuffer uvBuffer = (uvList != null) ? uvList.getFloatBuffer() : null; + if (uvBuffer == null) { + continue; + } + uvBuffer.position(0); + GLES20.glVertexAttribPointer(mUvAttribId, 2, GLES20.GL_FLOAT, false, 0, uvBuffer); + } + + // Colors + if (mUseColor) { + ColorList colorList = face.getColorList(); + FloatBuffer colorBuffer = (colorList != null) ? colorList.getFloatBuffer() : null; + if (colorBuffer == null) { + continue; + } + colorBuffer.position(0); + GLES20.glVertexAttribPointer(mColorAttribId, 4, GLES20.GL_FLOAT, false, 0, colorBuffer); + } + + object.onPreRenderFace(this, shader, face); + face.onPreRenderFace(this, object, shader); + + // Transformations + if (object.is3D()) { + Matrix.multiplyMM(mTmpMatrix, 0, mProj3DMatrix, 0, modelViewMatrix, 0); + } else { + Matrix.multiplyMM(mTmpMatrix, 0, mProj2DMatrix, 0, modelViewMatrix, 0); + } + GLES20.glUniformMatrix4fv(mProjMatrixId, 1, false, mTmpMatrix, 0); + + // Render + shader.onPreRender(this, object, face); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexList.getNbElements()); + } if (mDebugMode) { if (object instanceof Sprite) { Sprite sprite = (Sprite) object; SparseArray> collisions = sprite.getAllCollisions(); if (collisions != null) { - for(int i=0; i colList = collisions.valueAt(i); if (colList != null) { - for(int iCol=0; iCol segments = colSeg.getSegments(); - for(Vector2D vector : segments) { - float middleX = (vector.getXStart() + vector.getXStop()) / 2; - float middleY = (vector.getYStart() + vector.getYStop()) / 2; - mColSegmentSprite.setPos(middleX, middleY); - float width = vector.getSize(); - float height = width / 32f; - if (width < 1) { - width = 1; - } - if (height < 1) { - height = 1; - } - mColSegmentSprite.resize((int)width, (int)height); - float angle = vector.computeAngle() * 180 / 3.14159f; - mColSegmentSprite.setRotation(angle); - renderObject(renderPass, mColSegmentSprite, true); - } + CollisionRectangle rectangleCol = (CollisionRectangle) collision; + float posX = rectangleCol.getLeft(); + float posY = rectangleCol.getTop(); + mColSquareSprite.setPos(posX, posY); + mColSquareSprite.resize((int) (rectangleCol.getRight() - posX), (int) (rectangleCol.getBottom() - posY)); + renderObject(renderPass, mColSquareSprite, true); + } else if (collision instanceof CollisionSegments) { + CollisionSegments colSeg = (CollisionSegments) collision; + ArrayList segments = colSeg.getSegments(); + for (Vector2D vector : segments) { + float middleX = (vector.getXStart() + vector.getXStop()) / 2; + float middleY = (vector.getYStart() + vector.getYStop()) / 2; + mColSegmentSprite.setPos(middleX, middleY); + float width = vector.getSize(); + float height = width / 32f; + if (width < 1) { + width = 1; + } + if (height < 1) { + height = 1; + } + mColSegmentSprite.resize((int) width, (int) height); + float angle = vector.computeAngle() * 180 / 3.14159f; + mColSegmentSprite.setRotation(angle); + renderObject(renderPass, mColSegmentSprite, true); + } } else { Assert.assertTrue(false); // not handled yet (and probably never) @@ -423,43 +458,43 @@ private void renderObject(final RenderPass renderPass, RenderObject object, bool } } - } + } - // Called from OpenGL Thread by Android - @Override - public void onSurfaceChanged(GL10 glUnused, int width, int height) { + // Called from OpenGL Thread by Android + @Override + public void onSurfaceChanged(GL10 glUnused, int width, int height) { mPreviousTime = 0; // Prevent from calling acquireResources or onViewResized twice, because onSurfaceChanged is often called twice - if (mInitDone && (mWidth==width) && (mHeight==height)) { + if (mInitDone && (mWidth == width) && (mHeight == height)) { return; } - GLES20.glViewport(0, 0, width, height); + GLES20.glViewport(0, 0, width, height); - mWidth = width; - mHeight = height; + mWidth = width; + mHeight = height; - computeProjMatrix2D(mProj2DMatrix); - computeProjMatrix3D(mProj3DMatrix); + computeProjMatrix2D(mProj2DMatrix); + computeProjMatrix3D(mProj3DMatrix); OpenGLView view = getListener(); - if (view != null) { - if (mInitDone) { - view.onViewResized(width, height); - } else { - view.acquireResources(); - } - } - - mInitDone = true; - } - - private void computeProjMatrix2D(float [] matrix2D) { - Matrix.orthoM(matrix2D, 0, 0f, mWidth, mHeight, 0, -1f, 1f); - } - - private void computeProjMatrix3D(float [] matrix3D) { + if (view != null) { + if (mInitDone) { + view.onViewResized(width, height); + } else { + view.acquireResources(); + } + } + + mInitDone = true; + } + + private void computeProjMatrix2D(float[] matrix2D) { + Matrix.orthoM(matrix2D, 0, 0f, mWidth, mHeight, 0, -1f, 1f); + } + + private void computeProjMatrix3D(float[] matrix3D) { // float ratio = (float) mWidth / (float) mHeight; // float near = 0.1f; // Matrix.frustumM(matrix3D, 0, -near, near, -near / ratio, near / ratio, near, 100); @@ -492,19 +527,19 @@ private void computeProjMatrix3D(float [] matrix3D) { Matrix.translateM(matrix3D, 0, x, y, z); mCamera.setDirty(false); - } + } - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { mPreviousTime = 0; - gl.glClearDepthf(1.0f); - gl.glEnable(GL10.GL_DEPTH_TEST); - gl.glDepthFunc(GL10.GL_LEQUAL); + gl.glClearDepthf(1.0f); + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glDepthFunc(GL10.GL_LEQUAL); checkDoubleSided(gl); - } + } private void checkDoubleSided(GL10 gl) { if (mDoubleSided != null) { @@ -518,58 +553,68 @@ private void checkDoubleSided(GL10 gl) { } } - private void computeFps() { - long newTime = SystemClock.uptimeMillis(); - if (mPreviousTime == 0) { - mFrameDuration = 20; // consider 50 fps at start - mPreviousTime = newTime - mFrameDuration; - } else { - mFrameDuration = newTime - mPreviousTime; - mPreviousTime = newTime; - } - } - - public Vector getToucheableSprites() { - Vector result = null; - if (mRenderPasses != null) { - result = new Vector<>(); - for(RenderPass renderPass : mRenderPasses) { - Vector objects = renderPass.getRenderObjects(); - if (objects != null) { - for(RenderObject object : objects) { - if (object.isContainer()) { - RenderObjectContainer container = (RenderObjectContainer) object; - addToucheableSprites(container, result); - } else if (object instanceof Sprite) { - Sprite sprite = (Sprite) object; - if (sprite.handlesInput()) { - result.add(sprite); - } - } - } - } - } - } - return result; - } - - private void addToucheableSprites(RenderObjectContainer container, Vector result) { - Vector objects = container.getRenderObjects(); - if ((objects != null) && (!objects.isEmpty())) { - for(RenderObject object : objects) { - if (object.isContainer()) { - RenderObjectContainer subContainer = (RenderObjectContainer) object; - addToucheableSprites(subContainer, result); - } else if (object instanceof Sprite) { - Sprite sprite = (Sprite) object; - if (sprite.handlesInput()) { - result.add(sprite); - } - } - } - } - } - + private void computeFps() { + + long newTime = SystemClock.uptimeMillis(); + if (mPreviousTime == 0) { // First frame, set standard values at 50fps + mFrameDurationRaw = 20; + mPreviousTime = newTime - mFrameDurationRaw; + mFrameDurationSmoothed = (mFrameDurationRaw / 1000.f); + } else { // Other frames, let's calculate the FPS + mFrameDurationRaw = newTime - mPreviousTime; + mPreviousTime = newTime; + float instantFrameDurationSecond = (mFrameDurationRaw / 1000.f); + // normal smooth for normal values + if ((instantFrameDurationSecond >= 0.001f) && (instantFrameDurationSecond <= 0.5f)) { + mFrameDurationSmoothed = (mFrameDurationSmoothed * 0.9f) + (instantFrameDurationSecond * 0.1f); + } else { // smooth more for extreme values (like starting freeze) + mFrameDurationSmoothed = (mFrameDurationSmoothed * 0.95f) + (instantFrameDurationSecond * 0.05f); + } + } + + } + + public Vector getToucheableSprites() { + Vector result = null; + if (mRenderPasses != null) { + result = new Vector<>(); + for (RenderPass renderPass : mRenderPasses) { + Vector objects = renderPass.getRenderObjects(); + if (objects != null) { + for (RenderObject object : objects) { + if (object.isContainer()) { + RenderObjectContainer container = (RenderObjectContainer) object; + addToucheableSprites(container, result); + } else if (object instanceof Sprite) { + Sprite sprite = (Sprite) object; + if (sprite.handlesInput()) { + result.add(sprite); + } + } + } + } + } + } + return result; + } + + private void addToucheableSprites(RenderObjectContainer container, Vector result) { + Vector objects = container.getRenderObjects(); + if ((objects != null) && (!objects.isEmpty())) { + for (RenderObject object : objects) { + if (object.isContainer()) { + RenderObjectContainer subContainer = (RenderObjectContainer) object; + addToucheableSprites(subContainer, result); + } else if (object instanceof Sprite) { + Sprite sprite = (Sprite) object; + if (sprite.handlesInput()) { + result.add(sprite); + } + } + } + } + } + // public void onTouchEvent(View view, MotionEvent event) { // mTouchHelper.onTouchEvent(view, event); // } @@ -608,29 +653,125 @@ private void addToucheableSprites(RenderObjectContainer container, Vector> 16) | ((raw & 0x000000FF) << 16); + dest[offsetDst++] = color; + } + } + + final Bitmap bitmap = Bitmap.createBitmap(dest, width, height, Bitmap.Config.ARGB_8888); + mScreenshotHandler.post(new Runnable() { + @Override + public void run() { + synchronized (this) { + if (mScreenshotListener != null) { + if (bitmap != null) { + mScreenshotListener.screenshotTaken(bitmap); + } + mScreenshotListener = null; + } + } + } + }); + + } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLView.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLView.java index af8edcc..7affad4 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLView.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/OpenGLView.java @@ -21,59 +21,66 @@ /* package */ abstract class OpenGLView extends GLSurfaceView { - private OpenGLRenderer mOpenGLRenderer; - - public OpenGLView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public OpenGLView(Context context) { - super(context); - } - - @Override - public void setRenderer(Renderer renderer) throws RuntimeException { - - if (renderer instanceof OpenGLRenderer) { - mOpenGLRenderer = (OpenGLRenderer) renderer; - mOpenGLRenderer.setListener(this); - setEGLContextClientVersion(2); - ConfigGLSelector selector = new ConfigGLSelector(this); - if (selector != null) { - setEGLConfigChooser(selector); - //setPreserveEGLContextOnPause(true); - super.setRenderer(mOpenGLRenderer); - } - } else { - throw new RuntimeException("renderer must be a OpenGLRenderer"); - } - } - - public OpenGLRenderer getOpenGLRenderer() { - return mOpenGLRenderer; - } - - public void onPreRender(OpenGLRenderer renderer) { - } - - @Override - public void onPause() { - releaseResources(); - if (mOpenGLRenderer != null) { - mOpenGLRenderer.onPause(); - } - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - if (mOpenGLRenderer != null) { - mOpenGLRenderer.onResume(); - } - } - - protected void acquireResources() {} - protected void releaseResources() {} - protected abstract void onViewResized(int width, int height); + private OpenGLRenderer mOpenGLRenderer; + + public OpenGLView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public OpenGLView(Context context) { + super(context); + } + + @Override + public void setRenderer(Renderer renderer) throws RuntimeException { + + if (renderer instanceof OpenGLRenderer) { + mOpenGLRenderer = (OpenGLRenderer) renderer; + mOpenGLRenderer.setListener(this); + setEGLContextClientVersion(2); + ConfigGLSelector selector = new ConfigGLSelector(this); + if (selector != null) { + setEGLConfigChooser(selector); + //setPreserveEGLContextOnPause(true); + super.setRenderer(mOpenGLRenderer); + } + } else { + throw new RuntimeException("renderer must be a OpenGLRenderer"); + } + } + + public OpenGLRenderer getOpenGLRenderer() { + return mOpenGLRenderer; + } + + public void onPreRender(OpenGLRenderer renderer) { + } + + @Override + public void onPause() { + if (mOpenGLRenderer != null) { + mOpenGLRenderer.onPause(); + } + releaseResources(); + if (mOpenGLRenderer != null) { + mOpenGLRenderer.clearRenderPasses(); + } + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + if (mOpenGLRenderer != null) { + mOpenGLRenderer.onResume(); + } + } + + protected void acquireResources() { + } + + protected void releaseResources() { + } + + protected abstract void onViewResized(int width, int height); } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderObject.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderObject.java index 425557a..1914fe3 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderObject.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderObject.java @@ -19,19 +19,19 @@ import android.opengl.Matrix; +import androidx.annotation.NonNull; + public abstract class RenderObject implements IShaderTextureFade { private boolean mIs3D; private boolean mVisible; - private Vector mFaces; - private float[] mMatrix; + private @NonNull Vector mFaces = new Vector<>(); + private final @NonNull float[] mMatrix = new float[16]; private float mAlpha = 1; private boolean mInvalidMatrix = true; public RenderObject(boolean is3D) { mIs3D = is3D; - mFaces = new Vector(); - mMatrix = new float[16]; mVisible = true; Matrix.setIdentityM(mMatrix, 0); } @@ -52,14 +52,14 @@ public final boolean isHidden() { return !mVisible; } - public final Vector getFaces() { + public final @NonNull Vector getFaces() { return mFaces; } - public final void setFaces(Vector faces) { + public final void setFaces(@NonNull Vector faces) { mFaces = faces; } - public final void addFace(Face3D face) { + public final void addFace(@NonNull Face3D face) { mFaces.add(face); } @@ -86,11 +86,8 @@ protected void invalidMatrix() { } public void forceReleaseAll() { - // force deleteOpenGLResource - final int faceSize = mFaces.size(); - for (int faceIt = 0; faceIt < faceSize; ++faceIt) { - Face3D face = mFaces.get(faceIt); + for(Face3D face : mFaces) { Texture texture = face.getTexture(); if (texture != null) { texture.unbindTexture(); @@ -101,19 +98,14 @@ public void forceReleaseAll() { } protected void releaseResources() { - final int faceSize = mFaces.size(); - for (int faceIt = 0; faceIt < faceSize; ++faceIt) { - Face3D face = mFaces.get(faceIt); + for(Face3D face : mFaces) { face.releaseResources(); } mFaces.clear(); } public void releaseTextures() { - Vector faces = getFaces(); - final int faceSize = faces.size(); - for (int faceIt = 0; faceIt < faceSize; ++faceIt) { - Face3D face = faces.get(faceIt); + for(Face3D face : mFaces) { Texture texture = face.getTexture(); if (texture != null) { texture.unbindTexture(); diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPass.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPass.java index ede42a4..b48daa0 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPass.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPass.java @@ -17,87 +17,99 @@ import java.util.Vector; -import junit.framework.Assert; import android.opengl.GLES20; +import androidx.annotation.NonNull; + +import fr.arnaudguyon.smartgl.tools.Assert; + public class RenderPass { - private final static int NOPROGRAM = 0; - - private int mProgramId = NOPROGRAM; - private Shader mShaders; - private Vector mRenderObjects = new Vector<>(); - private boolean mUseZBuffer = false; - private boolean mClearZBuffer = false; - - /* package */ boolean isLoaded() { - return (mProgramId != NOPROGRAM); - } - - /* package */ void load() { - Assert.assertTrue(!isLoaded()); - if (mShaders != null) { - mProgramId = GLES20.glCreateProgram(); - - Assert.assertTrue(mProgramId != NOPROGRAM); - if (!mShaders.isLoaded()) { - mShaders.loadShader(mProgramId); - } - GLES20.glAttachShader(mProgramId, mShaders.getVertexScriptId()); - GLES20.glAttachShader(mProgramId, mShaders.getPixelScriptId()); - GLES20.glLinkProgram(mProgramId); - int[] linkStatus = new int[1]; - GLES20.glGetProgramiv(mProgramId, GLES20.GL_LINK_STATUS, linkStatus, 0); - Assert.assertTrue(linkStatus[0] == GLES20.GL_TRUE); - mShaders.init(mProgramId); - } - } - - public Vector getRenderObjects() { - return mRenderObjects; - } - - public boolean useZBuffer() { - return mUseZBuffer; - } - - public boolean clearZBuffer() { - return mClearZBuffer; - } - - public void clearObjects() { - mRenderObjects.clear(); - } - - public RenderPass() { - } - - public RenderPass(boolean useZBuffer, boolean clearZBuffer) { - this(); - mUseZBuffer = useZBuffer; - mClearZBuffer = clearZBuffer; - } - - public void setShader(Shader shader) { - mShaders = shader; - } - - public int getProgramId() { - return mProgramId; - } - - public Shader getShader() { - return mShaders; - } - - public void releaseResources() { - final int renderSize = mRenderObjects.size(); - for (int renderIt = 0; renderIt < renderSize; ++renderIt) { - RenderObject renderObject = mRenderObjects.get(renderIt); - renderObject.releaseResources(); - } - mRenderObjects.clear(); - } - - /* package */ void sortObjects() {} + private final static int NOPROGRAM = 0; + + private int mProgramId = NOPROGRAM; + private Shader mShaders; + + private final @NonNull + Vector mRenderObjects = new Vector<>(); + + private boolean mUseZBuffer = false; + private boolean mClearZBuffer = false; + + /* package */ boolean isLoaded() { + return (mProgramId != NOPROGRAM); + } + + /* package */ void load() { + Assert.assertTrue(!isLoaded()); + if (mShaders != null) { + mProgramId = GLES20.glCreateProgram(); + + Assert.assertTrue(mProgramId != NOPROGRAM); + if (!mShaders.isLoaded()) { + mShaders.loadShader(mProgramId); + } + GLES20.glAttachShader(mProgramId, mShaders.getVertexScriptId()); + GLES20.glAttachShader(mProgramId, mShaders.getPixelScriptId()); + GLES20.glLinkProgram(mProgramId); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(mProgramId, GLES20.GL_LINK_STATUS, linkStatus, 0); + Assert.assertTrue(linkStatus[0] == GLES20.GL_TRUE); + mShaders.init(mProgramId); + } + } + + public @NonNull + Vector getRenderObjects() { + return mRenderObjects; + } + + public boolean removeObject(@NonNull RenderObject renderObject) { + return mRenderObjects.remove(renderObject); + } + + public boolean useZBuffer() { + return mUseZBuffer; + } + + public boolean clearZBuffer() { + return mClearZBuffer; + } + + public void clearObjects() { + mRenderObjects.clear(); + } + + public RenderPass() { + } + + public RenderPass(boolean useZBuffer, boolean clearZBuffer) { + this(); + mUseZBuffer = useZBuffer; + mClearZBuffer = clearZBuffer; + } + + public void setShader(Shader shader) { + mShaders = shader; + } + + public int getProgramId() { + return mProgramId; + } + + public Shader getShader() { + return mShaders; + } + + public void releaseResources() { + final int renderSize = mRenderObjects.size(); + for (int renderIt = 0; renderIt < renderSize; ++renderIt) { + RenderObject renderObject = mRenderObjects.get(renderIt); + renderObject.releaseResources(); + } + mRenderObjects.clear(); + } + + /* package */ void sortObjects() { + } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassObject3D.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassObject3D.java index 9cd3881..d336a4c 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassObject3D.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassObject3D.java @@ -21,10 +21,47 @@ public class RenderPassObject3D extends RenderPass { + public enum ShaderType { + SHADER_TEXTURE, + SHADER_TEXTURE_AMBIANT, + SHADER_TEXTURE_LIGHTS, + SHADER_COLOR, + SHADER_COLOR_LIGHTS + } + public RenderPassObject3D() { super(true, true); - ShaderTexture shader = new ShaderTexture(); - setShader(shader); + setShader(ShaderType.SHADER_TEXTURE); + } + public RenderPassObject3D(ShaderType shaderType, boolean useZBuffer, boolean clearZBuffer) { + super(useZBuffer, clearZBuffer); + setShader(shaderType); + } + // TODO: see if change shader during renderpass... + + private void setShader(ShaderType shaderType) { + switch (shaderType) { + case SHADER_TEXTURE: + ShaderTexture shaderTexture = new ShaderTexture(); + setShader(shaderTexture); + break; + case SHADER_TEXTURE_AMBIANT: + ShaderTextureAmbiant shaderTextureAmbiant = new ShaderTextureAmbiant(); + setShader(shaderTextureAmbiant); + break; + case SHADER_TEXTURE_LIGHTS: + ShaderTextureLights shaderTextureLights = new ShaderTextureLights(); + setShader(shaderTextureLights); + break; + case SHADER_COLOR: + ShaderColor shaderColor = new ShaderColor(); + setShader(shaderColor); + break; + case SHADER_COLOR_LIGHTS: + ShaderColorLights shaderColorLights = new ShaderColorLights(); + setShader(shaderColorLights); + break; + } } public void addObject(Object3D object3D) { diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassSprite.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassSprite.java index 0e8e419..918f65b 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassSprite.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/RenderPassSprite.java @@ -38,7 +38,10 @@ public void addSprite(Sprite sprite) { @Override void sortObjects() { Vector objects = getRenderObjects(); - Collections.sort(objects, new Comparator() { + Collections.sort(objects, SPRITE_COMPARATOR); + } + + private static final Comparator SPRITE_COMPARATOR = new Comparator() { @Override public int compare(RenderObject leftO, RenderObject rightO) { if (leftO instanceof Sprite) { @@ -62,6 +65,6 @@ public int compare(RenderObject leftO, RenderObject rightO) { return 0; } } - }); - } + }; + } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Shader.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Shader.java index 175911c..ed8b864 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Shader.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Shader.java @@ -15,10 +15,11 @@ */ package fr.arnaudguyon.smartgl.opengl; -import junit.framework.Assert; import android.opengl.GLES20; import android.util.Log; +import fr.arnaudguyon.smartgl.tools.Assert; + abstract public class Shader { private static final String TAG = "Shader"; @@ -130,7 +131,7 @@ protected void init(final int programId) { Assert.assertTrue(mProjMatrixId >= 0); } - public void onPreRender(RenderObject object) { + public void onPreRender(OpenGLRenderer renderer, RenderObject object, Face3D face) { } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderColorLights.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderColorLights.java new file mode 100644 index 0000000..bf1f683 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderColorLights.java @@ -0,0 +1,117 @@ +package fr.arnaudguyon.smartgl.opengl; + +import android.opengl.GLES20; + +import java.nio.FloatBuffer; + +import fr.arnaudguyon.smartgl.tools.Assert; + +public class ShaderColorLights extends Shader { + + private final static String VERTEX_SHADER = + "uniform mat4 m_ProjectionMatrix;" + + "uniform mat4 mModelMatrix;" + + "uniform vec3 mParallelLightDirection;" + + "uniform vec4 mParallelLightColor;" + + "uniform vec4 mAmbiantColor;" + + "attribute vec4 m_Position;" + + "attribute vec3 mNormals;" + + "varying vec4 vLightColor;" + + "attribute vec4 m_Color;\n" + + "varying vec4 vColor;\n" + + "void main() {" + + " gl_Position = m_ProjectionMatrix * m_Position;" + + " vec3 modelViewNormal = vec3(mModelMatrix * vec4(mNormals, 0.0));" + + " float intensity = max(dot(modelViewNormal, -mParallelLightDirection), 0.0);" + + " vLightColor = mAmbiantColor + (mParallelLightColor * intensity);" + + " vColor = m_Color;\n" + + "}"; + + private final static String PIXEL_SHADER = + "precision mediump float;" + + "varying vec4 vLightColor;" + + "uniform sampler2D sTexture;" + + "varying vec4 vColor;\n" + + "void main() {" + + " gl_FragColor = vColor * vLightColor;" + + "}"; + + private int mModelMatrixId; + private int mParallelLightDirectionId; + private int mParallelLightColorId; + private int mLightAmbiantId; + private int mNormalsId; + + public ShaderColorLights() { + super(VERTEX_SHADER, PIXEL_SHADER); + } + + @Override + public boolean useTexture() { + return false; + } + + @Override + public boolean useColor() { + return true; + } + + @Override + protected String getVertexAttribName() { + return "m_Position"; + } + + @Override + protected String getUVAttribName() { + return null; + } + + @Override + protected String getColorAttribName() { + return "m_Color"; + } + + @Override + protected String getProjMatrixAttribName() { + return "m_ProjectionMatrix"; + } + + @Override + protected void init(int programId) { + super.init(programId); + + mModelMatrixId = GLES20.glGetUniformLocation(programId, "mModelMatrix"); + mParallelLightDirectionId = GLES20.glGetUniformLocation(programId, "mParallelLightDirection"); + mParallelLightColorId = GLES20.glGetUniformLocation(programId, "mParallelLightColor"); + mLightAmbiantId = GLES20.glGetUniformLocation(programId, "mAmbiantColor"); + mNormalsId = GLES20.glGetAttribLocation(programId, "mNormals"); + Assert.assertTrue(mModelMatrixId >= 0); + Assert.assertTrue(mParallelLightDirectionId >= 0); + Assert.assertTrue(mParallelLightColorId >= 0); + Assert.assertTrue(mLightAmbiantId >= 0); + Assert.assertTrue(mNormalsId >= 0); + } + + @Override + public void onPreRender(OpenGLRenderer renderer, RenderObject object, Face3D face3D) { + + float[] modelMatrix = object.getMatrix(); + GLES20.glUniformMatrix4fv(mModelMatrixId, 1, false, modelMatrix, 0); + + float[] lightDirection = renderer.getLightDirection(); + GLES20.glUniform3fv(mParallelLightDirectionId, 1, lightDirection, 0); + + float[] lightColor = renderer.getLightColor(); + GLES20.glUniform4fv(mParallelLightColorId, 1, lightColor, 0); + + float[] ambiant = renderer.getLightAmbiant(); + GLES20.glUniform4fv(mLightAmbiantId, 1, ambiant, 0); + + GLES20.glEnableVertexAttribArray(mNormalsId); + NormalList normalList = face3D.getNormalList(); + FloatBuffer vertexBuffer = normalList.getFloatBuffer(); + vertexBuffer.position(0); + GLES20.glVertexAttribPointer(mNormalsId, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); + } + +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTexture.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTexture.java index 1df9ad6..7193da6 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTexture.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTexture.java @@ -20,21 +20,21 @@ public class ShaderTexture extends Shader { // @formatter:off private final static String VERTEX_SHADER_TEXTURE_SCRIPT = - "uniform mat4 m_ProjectionMatrix;\n" + - "attribute vec4 m_Position;\n" + - "attribute vec2 m_UV;\n" + - "varying vec2 vTextureCoord;\n" + - "void main() {\n" + - " gl_Position = m_ProjectionMatrix * m_Position;\n" + - " vTextureCoord = m_UV;\n" + - "}\n"; + "uniform mat4 m_ProjectionMatrix;" + + "attribute vec4 m_Position;" + + "attribute vec2 m_UV;" + + "varying vec2 vTextureCoord;" + + "void main() {" + + " gl_Position = m_ProjectionMatrix * m_Position;" + + " vTextureCoord = m_UV;" + + "}"; private final static String PIXEL_SHADER_TEXTURE_SCRIPT = - "precision mediump float;\n" + - "varying vec2 vTextureCoord;\n" + - "uniform sampler2D sTexture;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + - "}\n"; + "precision mediump float;" + + "varying vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + + "void main() {" + + " gl_FragColor = texture2D(sTexture, vTextureCoord);" + + "}"; public ShaderTexture() { super(VERTEX_SHADER_TEXTURE_SCRIPT, PIXEL_SHADER_TEXTURE_SCRIPT); @@ -42,7 +42,7 @@ public ShaderTexture() { @Override public boolean useTexture() { return true; } @Override public boolean useColor() { return false; } - + @Override protected String getVertexAttribName() { return "m_Position"; } @Override protected String getUVAttribName() { return "m_UV"; } @Override protected String getColorAttribName() { return null; } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureAmbiant.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureAmbiant.java new file mode 100644 index 0000000..28e5b83 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureAmbiant.java @@ -0,0 +1,74 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +import android.opengl.GLES20; + +import fr.arnaudguyon.smartgl.tools.Assert; + +/** + * Created by aguyon on 18.01.17. + */ + +public class ShaderTextureAmbiant extends Shader { + + private final static String VERTEX_SHADER_TEXTURE_SCRIPT = + "uniform mat4 m_ProjectionMatrix;" + + "attribute vec4 m_Position;" + + "attribute vec2 m_UV;" + + "varying vec2 vTextureCoord;" + + "void main() {" + + " gl_Position = m_ProjectionMatrix * m_Position;" + + " vTextureCoord = m_UV;" + + "}"; + private final static String PIXEL_SHADER_TEXTURE_SCRIPT = + "precision mediump float;" + + "uniform vec4 mAmbiantColor;" + + "varying vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + + "void main() {" + + " gl_FragColor = texture2D(sTexture, vTextureCoord) * mAmbiantColor;" + + "}"; + + private int mLightAmbiantId; + + public ShaderTextureAmbiant() { + super(VERTEX_SHADER_TEXTURE_SCRIPT, PIXEL_SHADER_TEXTURE_SCRIPT); + } + + @Override public boolean useTexture() { return true; } + @Override public boolean useColor() { return false; } + + @Override protected String getVertexAttribName() { return "m_Position"; } + @Override protected String getUVAttribName() { return "m_UV"; } + @Override protected String getColorAttribName() { return null; } + @Override protected String getProjMatrixAttribName() { return "m_ProjectionMatrix"; } + + @Override + protected void init(int programId) { + super.init(programId); + + mLightAmbiantId = GLES20.glGetUniformLocation(programId, "mAmbiantColor"); + Assert.assertTrue(mLightAmbiantId >= 0); + } + + @Override + public void onPreRender(OpenGLRenderer renderer, RenderObject object, Face3D face) { + float[] ambiant = renderer.getLightAmbiant(); +// GLES20.glUniform4f(mLightAmbiantId, 1, 0, 0, 0.3f); // r,v,b,a + GLES20.glUniform4fv(mLightAmbiantId, 1, ambiant, 0); + } +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureFade.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureFade.java index 09b4d69..d4db34c 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureFade.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureFade.java @@ -48,14 +48,12 @@ public ShaderTextureFade() { @Override public boolean useTexture() { return true; } @Override public boolean useColor() { return false; } - + @Override protected String getVertexAttribName() { return "m_Position"; } @Override protected String getUVAttribName() { return "m_UV"; } @Override protected String getColorAttribName() { return null; } @Override protected String getProjMatrixAttribName() { return "m_ProjectionMatrix"; } - - @Override protected void init(int programId) { super.init(programId); @@ -65,9 +63,8 @@ protected void init(int programId) { } @Override - public void onPreRender(RenderObject object) { - super.onPreRender(object); - + public void onPreRender(OpenGLRenderer renderer, RenderObject object, Face3D face) { + float fadeValue = 1; if (object instanceof IShaderTextureFade) { IShaderTextureFade user = object; diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureLights.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureLights.java new file mode 100644 index 0000000..0002b83 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/ShaderTextureLights.java @@ -0,0 +1,139 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +import android.opengl.GLES20; + +import java.nio.FloatBuffer; + +import fr.arnaudguyon.smartgl.tools.Assert; + +/** + * Created by aguyon on 18.01.17. + */ + +public class ShaderTextureLights extends Shader { + + private final static String VERTEX_SHADER = + "uniform mat4 m_ProjectionMatrix;" + + "uniform mat4 mModelMatrix;" + + "uniform vec3 mParallelLightDirection;" + + "uniform vec4 mParallelLightColor;" + + "uniform vec4 mAmbiantColor;" + + "attribute vec4 m_Position;" + + "attribute vec2 m_UV;" + + "attribute vec3 mNormals;" + + "varying vec2 vTextureCoord;" + + "varying vec4 vDirectColor;" + + "varying vec4 vIndirectColor;" + + "void main() {" + + " gl_Position = m_ProjectionMatrix * m_Position;" + + " vTextureCoord = m_UV;" + + " vec3 modelViewNormal = vec3(mModelMatrix * vec4(mNormals, 0.0));" + + " float intensity = max(dot(modelViewNormal, -mParallelLightDirection), 0.0);" + + " vDirectColor = mAmbiantColor + (mParallelLightColor * intensity);" + + " vIndirectColor = mParallelLightColor * intensity * 0.25;" + + "}"; + + private final static String PIXEL_SHADER = + "precision mediump float;" + + "varying vec2 vTextureCoord;" + + "varying vec4 vDirectColor;" + + "varying vec4 vIndirectColor;" + + "uniform sampler2D sTexture;" + + "void main() {" + + " gl_FragColor = (texture2D(sTexture, vTextureCoord) * vDirectColor) + vIndirectColor;" + + "}"; + + private int mModelMatrixId; + private int mParallelLightDirectionId; + private int mParallelLightColorId; + private int mLightAmbiantId; + private int mNormalsId; + + public ShaderTextureLights() { + super(VERTEX_SHADER, PIXEL_SHADER); + } + + @Override + public boolean useTexture() { + return true; + } + + @Override + public boolean useColor() { + return false; + } + + @Override + protected String getVertexAttribName() { + return "m_Position"; + } + + @Override + protected String getUVAttribName() { + return "m_UV"; + } + + @Override + protected String getColorAttribName() { + return null; + } + + @Override + protected String getProjMatrixAttribName() { + return "m_ProjectionMatrix"; + } + + @Override + protected void init(int programId) { + super.init(programId); + + mModelMatrixId = GLES20.glGetUniformLocation(programId, "mModelMatrix"); + mParallelLightDirectionId = GLES20.glGetUniformLocation(programId, "mParallelLightDirection"); + mParallelLightColorId = GLES20.glGetUniformLocation(programId, "mParallelLightColor"); + mLightAmbiantId = GLES20.glGetUniformLocation(programId, "mAmbiantColor"); + mNormalsId = GLES20.glGetAttribLocation(programId, "mNormals"); + Assert.assertTrue(mModelMatrixId >= 0); + Assert.assertTrue(mParallelLightDirectionId >= 0); + Assert.assertTrue(mParallelLightColorId >= 0); + Assert.assertTrue(mLightAmbiantId >= 0); + Assert.assertTrue(mNormalsId >= 0); + } + + @Override + public void onPreRender(OpenGLRenderer renderer, RenderObject object, Face3D face3D) { + + float[] modelMatrix = object.getMatrix(); + GLES20.glUniformMatrix4fv(mModelMatrixId, 1, false, modelMatrix, 0); + + float[] lightDirection = renderer.getLightDirection(); + GLES20.glUniform3fv(mParallelLightDirectionId, 1, lightDirection, 0); + + float[] lightColor = renderer.getLightColor(); + GLES20.glUniform4fv(mParallelLightColorId, 1, lightColor, 0); + + float[] ambiant = renderer.getLightAmbiant(); + GLES20.glUniform4fv(mLightAmbiantId, 1, ambiant, 0); + + GLES20.glEnableVertexAttribArray(mNormalsId); + NormalList normalList = face3D.getNormalList(); + FloatBuffer vertexBuffer = normalList.getFloatBuffer(); + vertexBuffer.position(0); + GLES20.glVertexAttribPointer(mNormalsId, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); + } + +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartColor.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartColor.java new file mode 100644 index 0000000..f315c88 --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartColor.java @@ -0,0 +1,46 @@ +/* + Copyright 2017 Arnaud Guyon + + 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 fr.arnaudguyon.smartgl.opengl; + +import androidx.annotation.FloatRange; + +/** + * Created by aguyon on 18.01.17. + */ + +public class SmartColor { + + private float[] mColor = new float[4]; + + public SmartColor(@FloatRange(from=0, to=1) float red, @FloatRange(from=0, to=1) float green, @FloatRange(from=0, to=1) float blue) { + mColor[0] = red; + mColor[1] = green; + mColor[2] = blue; + mColor[3] = 1; + } + + public SmartColor(@FloatRange(from=0, to=1) float red, @FloatRange(from=0, to=1) float green, @FloatRange(from=0, to=1) float blue, @FloatRange(from=0, to=1) float alpha) { + mColor[0] = red; + mColor[1] = green; + mColor[2] = blue; + mColor[3] = alpha; + } + + public float[] getArray() { + return mColor; + } + +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartGLView.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartGLView.java index 9039ca9..45a32cd 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartGLView.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/SmartGLView.java @@ -19,11 +19,12 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import androidx.annotation.NonNull; + import fr.arnaudguyon.smartgl.touch.TouchHelper; import fr.arnaudguyon.smartgl.touch.TouchHelperEvent; @@ -33,21 +34,22 @@ */ public class SmartGLView extends OpenGLView { - - private TouchHelper mTouchHelper; - private Sprite mInputSprite; + + private TouchHelper mTouchHelper; + private Sprite mInputSprite; private SmartGLViewController mListener; - public SmartGLView(Context context) { - super(context); - } + public SmartGLView(Context context) { + super(context); + } - public SmartGLView(Context context, AttributeSet attrs) { - super(context, attrs); - } + public SmartGLView(Context context, AttributeSet attrs) { + super(context, attrs); + } /** * set the SmartGLRenderer as scene renderer + * * @param context */ public void setDefaultRenderer(@NonNull Context context) { @@ -57,18 +59,20 @@ public void setDefaultRenderer(@NonNull Context context) { /** * returns the current SmartGLRenderer + * * @return the current SmartGLRenderer or null if none */ - public SmartGLRenderer getSmartGLRenderer() { + public SmartGLRenderer getSmartGLRenderer() { OpenGLRenderer renderer = getOpenGLRenderer(); if (renderer instanceof SmartGLRenderer) { return (SmartGLRenderer) renderer; } - return null; - } + return null; + } /** * set the SmartGLViewController + * * @param controller */ public void setController(SmartGLViewController controller) { @@ -77,147 +81,148 @@ public void setController(SmartGLViewController controller) { /** * gets the SmartGLViewController + * * @return */ public SmartGLViewController getController() { return mListener; } - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mTouchHelper != null) { - mTouchHelper.onTouchEvent(this, event, false); - } - return true; - } + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + if (isClickable()) { + if (mTouchHelper == null) { + mTouchHelper = new TouchHelper(); + } + mTouchHelper.onTouchEvent(this, event, false); + return true; + } else { + mTouchHelper = null; + return false; + } + } /** * to call if there is an Android view on top of the SmartGLView and there is a need that the SmartGL view handles the touch. * It propagates the touch to the SmartGLView. + * * @param fromView an Android View - * @param event the touch event received in this view + * @param event the touch event received in this view */ public void onTouchEventFromOtherView(View fromView, MotionEvent event) { - if (mTouchHelper != null) { + if (isClickable()) { + if (mTouchHelper == null) { + mTouchHelper = new TouchHelper(); + } mTouchHelper.onTouchEvent(fromView, event, true); + } else { + mTouchHelper = null; } } /** * called at every frame of OpenGL. Handles the Touch Events + * * @param renderer */ - @Override - public void onPreRender(OpenGLRenderer renderer) { - super.onPreRender(renderer); + @Override + public void onPreRender(OpenGLRenderer renderer) { + super.onPreRender(renderer); + + // Handle Touch Events: skip several moves in a row and send OnTouchEvent (on OpenGLThread) + if (mTouchHelper != null) { + TouchHelperEvent event = mTouchHelper.getNextEvent(); + if (event != null) { + Vector touchEvents = new Vector(); + while (event != null) { + //Log.i(TAG, "Touch " + event.getX(0) + " ; " + event.getY(0)); + int tabSize = touchEvents.size(); + TouchHelperEvent.TouchEventType newType = event.getType(); + if ((tabSize > 0) && (newType == TouchHelperEvent.TouchEventType.SINGLEMOVE || newType == TouchHelperEvent.TouchEventType.MULTIMOVE) && (touchEvents.get(tabSize - 1).getType() == newType)) { + touchEvents.remove(tabSize - 1); + } + touchEvents.add(event); + event = mTouchHelper.getNextEvent(); + } + + float frameDuration = renderer.getFrameDuration(); + + for (int eventIndex = 0; eventIndex < touchEvents.size(); ++eventIndex) { + event = touchEvents.get(eventIndex); + + boolean handledBySprites = touchEventOnSprites(event, frameDuration); + if (handledBySprites) { + continue; + } + + onTouchEvent(event); + } + + } + } if (mListener != null) { mListener.onTick(this); } + } - // Handle Touch Events: skip several moves in a row and send OnTouchEvent (on OpenGLThread) - if (mTouchHelper != null) { - TouchHelperEvent event = mTouchHelper.getNextEvent(); - if (event != null) { - Vector touchEvents = new Vector(); - while(event != null) { - //Log.i(TAG, "Touch " + event.getX(0) + " ; " + event.getY(0)); - int tabSize = touchEvents.size(); - TouchHelperEvent.TouchEventType newType = event.getType(); - if ((tabSize > 0) && (newType == TouchHelperEvent.TouchEventType.SINGLEMOVE || newType == TouchHelperEvent.TouchEventType.MULTIMOVE) && (touchEvents.get(tabSize - 1).getType() == newType)) { - touchEvents.remove(tabSize - 1); - } - touchEvents.add(event); - event = mTouchHelper.getNextEvent(); - } - - float frameDuration = renderer.getFrameDuration(); - - for (int eventIndex = 0; eventIndex < touchEvents.size(); ++eventIndex) { - event = touchEvents.get(eventIndex); - - boolean handledBySprites = touchEventOnSprites(event, frameDuration); - if (handledBySprites) { - continue; - } - - onTouchEvent(event); - } - - } - } - } - - private boolean touchEventOnSprites(TouchHelperEvent event, float frameDuration) { - - boolean spriteDisappeared = ((mInputSprite != null) && (mInputSprite.isHidden() || !mInputSprite.handlesInput())); - - if (spriteDisappeared || (event.getType() == TouchHelperEvent.TouchEventType.SINGLETOUCH)) { - final float x = event.getX(0); - final float y = event.getY(0); - - OpenGLRenderer renderer = getOpenGLRenderer(); - - Vector hudSprites = renderer.getToucheableSprites(); - final int touchSize = hudSprites.size(); - for (int touchIt = touchSize - 1; touchIt >= 0; --touchIt) { - RenderObject renderObject = hudSprites.get(touchIt); - Sprite sprite = (Sprite) renderObject; - if (sprite != null && sprite.handlesInput() && sprite.isVisible() && sprite.touchedBy(x, y)) { - if (sprite.onTouch(event, frameDuration, mInputSprite)) { - mInputSprite = sprite; - return true; - } - } - } - mInputSprite = null; - return false; - } else if (mInputSprite == null) { - return false; - } else { - mInputSprite.onTouch(event, frameDuration, mInputSprite); - if (event.getType() == TouchHelperEvent.TouchEventType.SINGLEUNTOUCH) { - mInputSprite = null; - } - return true; - } - } - - protected void onTouchEvent(TouchHelperEvent event) { + private boolean touchEventOnSprites(TouchHelperEvent event, float frameDuration) { + + boolean spriteDisappeared = ((mInputSprite != null) && (mInputSprite.isHidden() || !mInputSprite.handlesInput())); + + if (spriteDisappeared || (event.getType() == TouchHelperEvent.TouchEventType.SINGLETOUCH)) { + final float x = event.getX(0); + final float y = event.getY(0); + + OpenGLRenderer renderer = getOpenGLRenderer(); + + Vector hudSprites = renderer.getToucheableSprites(); + final int touchSize = hudSprites.size(); + for (int touchIt = touchSize - 1; touchIt >= 0; --touchIt) { + RenderObject renderObject = hudSprites.get(touchIt); + Sprite sprite = (Sprite) renderObject; + if (sprite != null && sprite.handlesInput() && sprite.isVisible() && sprite.touchedBy(x, y)) { + if (sprite.onTouch(event, frameDuration, mInputSprite)) { + mInputSprite = sprite; + return true; + } + } + } + mInputSprite = null; + return false; + } else if (mInputSprite == null) { + return false; + } else { + mInputSprite.onTouch(event, frameDuration, mInputSprite); + if (event.getType() == TouchHelperEvent.TouchEventType.SINGLEUNTOUCH) { + mInputSprite = null; + } + return true; + } + } + + protected void onTouchEvent(TouchHelperEvent event) { if (mListener != null) { mListener.onTouchEvent(this, event); } - } + } - @Override + @Override protected void acquireResources() { - super.acquireResources(); - activateTouch(true); + super.acquireResources(); if (mListener != null) { mListener.onPrepareView(this); } } - @Override - protected void releaseResources() { - super.releaseResources(); - mTouchHelper = null; + @Override + protected void releaseResources() { + super.releaseResources(); + mTouchHelper = null; if (mListener != null) { mListener.onReleaseView(this); } - } - - /** - * Enable or Disable the Touch events on the view - * @param activate true to enable the Touch, false to disable the Touch - */ - public void activateTouch(boolean activate) { - if (activate) { - mTouchHelper = new TouchHelper(); - } else { - mTouchHelper = null; - } } @Override @@ -226,4 +231,10 @@ protected void onViewResized(int width, int height) { mListener.onResizeView(this); } } + + public void cancelLongPressEvents() { + if (mTouchHelper != null) { + mTouchHelper.cancelLongPressEvents(); + } + } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Sprite.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Sprite.java index 7a0ac6e..eb09806 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Sprite.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Sprite.java @@ -19,7 +19,10 @@ import android.opengl.Matrix; import android.util.SparseArray; +import androidx.annotation.Nullable; + import java.util.ArrayList; +import java.util.Vector; import fr.arnaudguyon.smartgl.touch.SpriteTouchListener; import fr.arnaudguyon.smartgl.touch.TouchHelperEvent; @@ -106,7 +109,10 @@ public void unregisterTouchListener() { } public void setTexture(Texture texture) { - getFace().setTexture(texture); + Face3D face = getFace(); + if (face != null) { + face.setTexture(texture); + } } public void setScale(float scaleX, float scaleY) { @@ -208,8 +214,12 @@ final public float getPosY() { return mPosY; } - final public Face3D getFace() { - return getFaces().firstElement(); + final public @Nullable Face3D getFace() { + Vector faces = getFaces(); + if (!faces.isEmpty()) { + return getFaces().firstElement(); + } + return null; } public float getWidth() { @@ -271,15 +281,18 @@ public void unflipMappingH() { } public void flipVertexH() { - VertexList vertexList = getFace().getVertexList(); - float[] internalBuffer = vertexList.getInternalBuffer(); + Face3D face = getFace(); + if (face != null) { + VertexList vertexList = face.getVertexList(); + float[] internalBuffer = vertexList.getInternalBuffer(); - internalBuffer[0] = mWidth - internalBuffer[0]; - internalBuffer[3] = mWidth - internalBuffer[3]; - internalBuffer[6] = mWidth - internalBuffer[6]; - internalBuffer[9] = mWidth - internalBuffer[9]; + internalBuffer[0] = mWidth - internalBuffer[0]; + internalBuffer[3] = mWidth - internalBuffer[3]; + internalBuffer[6] = mWidth - internalBuffer[6]; + internalBuffer[9] = mWidth - internalBuffer[9]; - vertexList.getFloatBuffer().put(internalBuffer).position(0); + vertexList.getFloatBuffer().put(internalBuffer).position(0); + } } public void rebindUVs(int xMin, int xMax, int yMin, int yMax, float texWidth, float texHeight) { @@ -315,9 +328,13 @@ public void resize(int width, int height) { if ((width == mWidth) && (height == mHeight)) { return; } + Face3D face = getFace(); + if (face == null) { + return; + } mWidth = width; mHeight = height; - VertexList vertexList = getFace().getVertexList(); + VertexList vertexList = face.getVertexList(); float[] internalBuffer = vertexList.getInternalBuffer(); //internalBuffer[0] = 0; diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Texture.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Texture.java index c87e22d..5525bd1 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Texture.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/Texture.java @@ -24,78 +24,88 @@ import android.opengl.GLES20; import android.opengl.GLUtils; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + public class Texture { - - private final static int UNBIND_VALUE = 0; - - private int[] mId; - private int mWidth; - private int mHeight; - private Bitmap mBitmap; - private boolean mRecycleWhenBinded = true; - - public final int getId() { - return mId[0]; - } - - public final int getWidth() { - return mWidth; - } - - public final int getHeight() { - return mHeight; - } - - public final boolean isBinded() { - return (mId[0] != UNBIND_VALUE); - } - - public final void recycleWhenBinded(boolean recycle) { - mRecycleWhenBinded = recycle; - } - - private Texture() { - mId = new int[1]; - mId[0] = UNBIND_VALUE; - } - - public Texture(int width, int height, Bitmap bitmap) { - this(); - mWidth = width; - mHeight = height; - mBitmap = bitmap; - } - public Texture(Bitmap bitmap) { - this(); - mWidth = bitmap.getWidth(); - mHeight = bitmap.getHeight(); - mBitmap = bitmap; - } - public Texture(Context context, int resourceId) { - this(); - Resources resources = context.getResources(); - mBitmap = BitmapFactory.decodeResource(resources, resourceId); - mWidth = mBitmap.getWidth(); - mHeight = mBitmap.getHeight(); - } - - boolean bindTexture() { - if ((mBitmap != null) && (!mBitmap.isRecycled())) { - GLES20.glGenTextures(1, mId, 0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mId[0]); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0); // GLES20.GL_RGBA - if (mRecycleWhenBinded) { - mBitmap.recycle(); - mBitmap = null; - } - return true; - } - return false; - } + + private final static int UNBIND_VALUE = 0; + + private int[] mId; + private int mWidth; + private int mHeight; + private Bitmap mBitmap; + + public final int getId() { + return mId[0]; + } + + public final int getWidth() { + return mWidth; + } + + public final int getHeight() { + return mHeight; + } + + public final boolean isBinded() { + return (mId[0] != UNBIND_VALUE); + } + + private Texture() { + mId = new int[1]; + mId[0] = UNBIND_VALUE; + } + + public Texture(int width, int height, @Nullable Bitmap bitmap) { + this(); + mWidth = width; + mHeight = height; + mBitmap = bitmap; + } + + public Texture(Bitmap bitmap) { + this(); + mWidth = bitmap.getWidth(); + mHeight = bitmap.getHeight(); + mBitmap = bitmap; + } + + public Texture(Context context, @DrawableRes int resourceId) { + this(); + Resources resources = context.getResources(); + mBitmap = BitmapFactory.decodeResource(resources, resourceId); + mWidth = mBitmap.getWidth(); + mHeight = mBitmap.getHeight(); + } + + public boolean setBitmap(int width, int height, @NonNull Bitmap bitmap) { + if (isBinded()) { + return false; + } else { + this.mWidth = width; + this.mHeight = height; + this.mBitmap = bitmap; + return true; + } + } + + protected boolean bindTexture() { + if ((mBitmap != null) && (!mBitmap.isRecycled())) { + GLES20.glGenTextures(1, mId, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mId[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0); // GLES20.GL_RGBA + mBitmap.recycle(); + mBitmap = null; + return true; + } + return false; + } public void release() { unbindTexture(); @@ -105,46 +115,46 @@ public void release() { } } - void unbindTexture() { - if (isBinded()) { - GLES20.glDeleteTextures(1, mId, 0); - mId[0] = UNBIND_VALUE; - } - } - - public static Bitmap loadAndTurnAndResize(Context context, String pictureName, int approxWidth) { - try { - final ExifInterface exif = new ExifInterface(pictureName); - final int srcWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, approxWidth); - final int subSample = (srcWidth / approxWidth); - BitmapFactory.Options resizeOptions = new BitmapFactory.Options(); - resizeOptions.inSampleSize = subSample; - Bitmap bitmap = BitmapFactory.decodeFile(pictureName, resizeOptions); - if (bitmap != null) { - final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - if (orientation != ExifInterface.ORIENTATION_NORMAL) { - int angle = 0; - if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { - angle = 90; - } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { - angle = 180; - } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { - angle = 270; - } - if (angle != 0f) { - Matrix matrix = new Matrix(); - matrix.preRotate(angle); - Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); - bitmap.recycle(); - return rotatedBitmap; - } - } - } - return bitmap; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } + protected void unbindTexture() { + if (isBinded()) { + GLES20.glDeleteTextures(1, mId, 0); + mId[0] = UNBIND_VALUE; + } + } + + public static Bitmap loadAndTurnAndResize(Context context, String pictureFileName, int approxWidth) { + try { + final ExifInterface exif = new ExifInterface(pictureFileName); + final int srcWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, approxWidth); + final int subSample = (srcWidth / approxWidth); + BitmapFactory.Options resizeOptions = new BitmapFactory.Options(); + resizeOptions.inSampleSize = subSample; + Bitmap bitmap = BitmapFactory.decodeFile(pictureFileName, resizeOptions); + if (bitmap != null) { + final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + if (orientation != ExifInterface.ORIENTATION_NORMAL) { + int angle = 0; + if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { + angle = 90; + } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { + angle = 180; + } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { + angle = 270; + } + if (angle != 0f) { + Matrix matrix = new Matrix(); + matrix.preRotate(angle); + Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + bitmap.recycle(); + return rotatedBitmap; + } + } + } + return bitmap; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/VertexList.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/VertexList.java index 8bddd6f..64a6ed6 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/VertexList.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/opengl/VertexList.java @@ -15,6 +15,9 @@ */ package fr.arnaudguyon.smartgl.opengl; +/** + * List of Vertex for a Face3D + */ public class VertexList extends AttribList { public VertexList() { diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/Assert.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/Assert.java new file mode 100644 index 0000000..f96367f --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/Assert.java @@ -0,0 +1,41 @@ +package fr.arnaudguyon.smartgl.tools; + +public class Assert { + + public static void assertTrue(boolean condition) { + assertTrue("", condition); + } + public static void assertTrue(String text, boolean condition) { + if (!condition) { + doAssert(text); + } + } + public static void assertNotNull(Object object) { + assertNotNull("", object); + } + public static void assertNotNull(String text, Object object) { + if (object == null) { + doAssert(text); + } + } + + public static void assertEquals(Object object1, Object object2) { + if (object1 == null) { + if (object2 != null) { + doAssert(""); + } + } else { + if (object2 == null) { + doAssert(""); + } else { + if (!object1.equals(object2)) { + doAssert(""); + } + } + } + } + + private static void doAssert(String text) { + throw new RuntimeException(text); + } +} diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/ColladaModel.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/ColladaModel.java new file mode 100644 index 0000000..d134b1b --- /dev/null +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/ColladaModel.java @@ -0,0 +1,2227 @@ +/* + Copyright 2019 Mj Mendoza IV + + 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 fr.arnaudguyon.smartgl.tools; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.FloatRange; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; +import java.util.Vector; + +import fr.arnaudguyon.smartgl.math.Vector3D; +import fr.arnaudguyon.smartgl.opengl.ColorList; +import fr.arnaudguyon.smartgl.opengl.Face3D; +import fr.arnaudguyon.smartgl.opengl.NormalList; +import fr.arnaudguyon.smartgl.opengl.Object3D; +import fr.arnaudguyon.smartgl.opengl.Texture; +import fr.arnaudguyon.smartgl.opengl.UVList; +import fr.arnaudguyon.smartgl.opengl.VertexList; + +/** + * Created by creek23 on 01.05.19. + * Helper to load Collada objects and convert them to Object3D + */ + +public class ColladaModel { + + private static final String TAG = "ColladaModel"; + + public static class Builder { + private Context mContext; + private int mRawResourceId; + private boolean mOptimizeModel = true; + private HashMap mTextures = new HashMap<>(); + private float[] mColor = {1, 1, 1}; + + public Builder(Context context, int rawFileResourceId) { + mContext = context; + mRawResourceId = rawFileResourceId; + } + public Builder optimize(boolean optimizeModel) { + mOptimizeModel = optimizeModel; + return this; + } + public Builder addTexture(String textureName, Texture texture) { + mTextures.put(textureName, texture); + return this; + } + public Builder setColor(@FloatRange(from=0, to=1) float red, @FloatRange(from=0, to=1) float green, @FloatRange(from=0, to=1) float blue) { + mColor[0] = red; + mColor[1] = green; + mColor[2] = blue; + return this; + } + + public ColladaModel create() { + ColladaModel collada = new ColladaModel(); + collada.loadObject(mContext, mRawResourceId); + if (mOptimizeModel) { + //wavefront.mergeStrips(); + } + collada.mTextures = mTextures; + collada.mColor = mColor; + return collada; + } + } + + private String xmlItem = ""; + + XmlPullParser parser; + private ColladaModel() { + // + } + private void loadObject(Context context, int rawResId) throws RuntimeException { + InputStream inputStream = context.getResources().openRawResource(rawResId); + BufferedReader reader = null; + try { + XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance(); + parser = parserFactory.newPullParser(); + parser.setFeature(XmlPullParser .FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(inputStream, null); + + processCollada(parser); + } catch (XmlPullParserException e) { + Log.d("COLLADA!", "Error " + e.getMessage()); + } catch (IOException e) { + //log the exception + Log.d("COLLADA!!", "Error " + e.getMessage() + " " + e.getCause()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + //log the exception + Log.d("COLLADA!!!", "Error " + e.getMessage()); + } + } + } + Log.d("@COLLADA", "done but did it load?"); + validate3D(); + } + + private float[] mColor = {1,0,0}; + private ArrayList mFaces = new ArrayList<>(); + private ArrayList mVertex = new ArrayList<>(); + private ArrayList mUVs = new ArrayList<>(); //DAE's TEXCOORD (Texture Coordinate) + private ArrayList mNormals = new ArrayList<>(); + private ArrayList mStrips = new ArrayList<>(); + + private static class IndexInfo { + int mVertexIndex; + int mUVIndex; + int mNormalIndex; + + static IndexInfo create(Integer vertexIndex, Integer uvIndex, Integer normalIndex) { + if (vertexIndex == null) { + return null; + } + IndexInfo indexInfo = new IndexInfo(); + indexInfo.mVertexIndex = vertexIndex; + indexInfo.mUVIndex = (uvIndex != null) ? uvIndex: -1; + indexInfo.mNormalIndex = (normalIndex != null) ? normalIndex: -1; + return indexInfo; + } + static IndexInfo create(int vertexIndex, int uvIndex, int normalIndex) { + IndexInfo indexInfo = new IndexInfo(); + indexInfo.mVertexIndex = vertexIndex; + indexInfo.mUVIndex = uvIndex; + indexInfo.mNormalIndex = normalIndex; + return indexInfo; + } + } + + private static class Strip { + String mTextureName; + ArrayList mIndexes = new ArrayList<>(); + + Strip(String textureName) { + mTextureName = textureName; + } + void addIndex(IndexInfo indexInfo) { mIndexes.add(indexInfo); } + void addAll(ArrayList indexes) { + mIndexes.addAll(indexes); + } + } + + private void validate3D() { + /*TODO: current implementation is meant for `randomshape.dae'; improve validation further with other DAEs + ideally, any single-geometry DAE with/without Texture (UV Map) can be loaded and displayed + FPS got boosted but currently have issues with rendering tho. + */ + Assert.assertNotNull(library_geometries); + Assert.assertNotNull(library_geometries.geometry); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.triangles); + + for (int i = 0; i < library_geometries.geometry.get(0).mesh.source.size(); ++i) { + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i)); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).float_array); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).float_array.value); + if (library_geometries.geometry.get(0).mesh.source.get(i).id.equals((library_geometries.geometry.get(0).id + "-positions"))) { + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor); + Log.i("COLLADA", "populating Vertex count " + library_geometries.geometry.get(0).mesh.source.get(i).float_array.count + " stride " + library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride); + for (int j = 0; j < library_geometries.geometry.get(0).mesh.source.get(i).float_array.count; j += library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride) { + Vertex l_vertex = new Vertex( + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j), + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j + 1), + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j + 2) + ); + mVertex.add(l_vertex); + } + } else if (library_geometries.geometry.get(0).mesh.source.get(i).id.equals((library_geometries.geometry.get(0).id + "-normals"))) { + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor); + Log.i("COLLADA", "populating Normals count " + library_geometries.geometry.get(0).mesh.source.get(i).float_array.count + " stride " + library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride); + for (int j = 0; j < library_geometries.geometry.get(0).mesh.source.get(i).float_array.count; j += library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride) { + Normal l_normal = new Normal( + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j), + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j + 1), + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j + 2) + ); + mNormals.add(l_normal); + } + } else if (library_geometries.geometry.get(0).mesh.source.get(i).id.equals((library_geometries.geometry.get(0).id + "-map-0"))) { + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common); + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor); + Log.i("COLLADA", "populating UVs count " + library_geometries.geometry.get(0).mesh.source.get(i).float_array.count + " stride " + library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride); + for (int j = 0; j < library_geometries.geometry.get(0).mesh.source.get(i).float_array.count; j += library_geometries.geometry.get(0).mesh.source.get(i).technique_common.accessor.stride) { + UV l_uv = new UV( + library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j), + 1-library_geometries.geometry.get(0).mesh.source.get(i).float_array.value.get(j + 1) + ); + mUVs.add(l_uv); + } + } + } + + for (triangleIndex = 0; triangleIndex < library_geometries.geometry.get(0).mesh.triangles.size(); ++triangleIndex) { + Assert.assertNotNull(library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input); + + for (int i = 0; i < library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).count; ++i) { + mFaces.add(new Face3D()); + } + + int INPUT_VERTEX = -1; + int INPUT_NORMAL = -1; + int INPUT_TEXCOORD = -1; + for (int i = 0; i < library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.size(); ++i) { + if (library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).semantic.equals("VERTEX")) { + INPUT_VERTEX = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).offset; + } else if (library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).semantic.equals("NORMAL")) { + INPUT_NORMAL = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).offset; + } else if (library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).semantic.equals("TEXCOORD")) { + INPUT_TEXCOORD = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).input.get(i).offset; + } + } + + int INPUT_increment = INPUT_VERTEX + INPUT_NORMAL + INPUT_TEXCOORD; + Assert.assertTrue((INPUT_increment != -3)); + if (INPUT_increment == -2) { + INPUT_increment = 1; + } else if (INPUT_increment == 0) { + INPUT_increment = 2; + } else { + INPUT_increment = 3; + } + int numberOfVertex = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).p.value.size(); + ArrayList indexInfos = new ArrayList<>(numberOfVertex); + for (int i = 0; i < library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).p.value.size(); i += INPUT_increment) { + Integer l_vertexIndex = null; + Integer l_normalIndex = null; + Integer l_uvIndex = null; + if (INPUT_VERTEX != -1) { l_vertexIndex = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).p.value.get(i + INPUT_VERTEX); } + if (INPUT_NORMAL != -1) { l_normalIndex = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).p.value.get(i + INPUT_NORMAL); } + if (INPUT_TEXCOORD != -1) { l_uvIndex = library_geometries.geometry.get(0).mesh.triangles.get(triangleIndex).p.value.get(i + INPUT_TEXCOORD); } + IndexInfo indexInfo = IndexInfo.create(l_vertexIndex, l_uvIndex, l_normalIndex); + indexInfos.add(indexInfo); + } + Strip strip = new Strip(""); + for(IndexInfo indexInfo : indexInfos) { + strip.addIndex(indexInfo); + } + mStrips.add(strip); + } + } + + private int triangleIndex = 0; + + public Object3D toObject3D() { + final boolean hasUV = (mUVs.size() > 0); + final boolean hasNormals = (mNormals.size() > 0); + + Object3D object3D = new Object3D(); + for(Strip strip : mStrips) { + + Face3D face3D = new Face3D(); + int nbIndex = strip.mIndexes.size(); //triangleCount * 3 + + VertexList vertexList = new VertexList(); + vertexList.init(nbIndex); + + UVList uvList = null; + ColorList colorList = null; + if (hasUV) { + uvList = new UVList(); + uvList.init(nbIndex); + } else { + colorList = new ColorList(); + colorList.init(nbIndex); + } + + NormalList normalList = null; + if (hasNormals) { + normalList = new NormalList(); + normalList.init(nbIndex); + } + + for(IndexInfo indexInfo : strip.mIndexes) { + int vertexIndex = indexInfo.mVertexIndex; + Vertex vertex = mVertex.get(vertexIndex); + vertexList.add(vertex.mX, vertex.mY, vertex.mZ); + + if (hasUV) { + int uvIndex = indexInfo.mUVIndex; + UV uv = mUVs.get(uvIndex); + uvList.add(uv.mU, uv.mV); + } else if (vertex.mHasColors) { + colorList.add(vertex.mR, vertex.mG, vertex.mB, 1); + } else { + Random r = new Random(); + mColor[0] = r.nextFloat(); + mColor[1] = r.nextFloat(); + mColor[2] = r.nextFloat(); + vertex.setColors(mColor[0], mColor[1], mColor[2]); + colorList.add(vertex.mR, vertex.mG, vertex.mB, 1); + } + + if (hasNormals) { + int normalIndex = indexInfo.mNormalIndex; + Normal normal = mNormals.get(normalIndex); + normalList.add(normal.mX, normal.mY, normal.mZ); + } + } + vertexList.finalizeBuffer(); + face3D.setVertexList(vertexList); + + if (hasUV) { + uvList.finalizeBuffer(); + face3D.setUVList(uvList); + Assert.assertNotNull(library_images); + Assert.assertNotNull(library_images.image); + Assert.assertNotNull(library_images.image.get(0)); + Texture texture = mTextures.get(library_images.image.get(0).id); + face3D.setTexture(texture); + } else { + colorList.finalizeBuffer(); + face3D.setColorList(colorList); + } + + if (hasNormals) { + normalList.finalizeBuffer(); + face3D.setNormalList(normalList); + } + object3D.addFace(face3D); + } + return object3D; + } + + private HashMap mTextures = new HashMap<>(); + + private class Vertex { + float mX; + float mY; + float mZ; + + float mR, mG, mB; + boolean mHasColors = false; + + Vertex(float x, float y, float z) { + mX = x; + mY = y; + mZ = z; + } + + void setColors(float r, float g, float b) { + mHasColors = true; + mR = r; + mG = g; + mB = b; + } + } + private class Normal { + float mX; + float mY; + float mZ; + + Normal(float x, float y, float z) { + mX = x; + mY = y; + mZ = z; + } + + Normal(Vector3D vector) { + float[] values = vector.getArray(); + mX = values[0]; + mY = values[1]; + mZ = values[2]; + } + } + private class UV { + float mU; + float mV; + UV(float u, float v) { + mU = u; + mV = v; + } + } + + public void processCollada(XmlPullParser parser) throws IOException, XmlPullParserException { + int eventType = parser.getEventType(); + + while (eventType != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case XmlPullParser.START_TAG: + xmlItem = parser.getName(); + Log.d("COLLADA?","COLLADA " + xmlItem); + if ("COLLADA".equals(xmlItem)) { + xmlns = parser.getAttributeValue(null,"xmlns"); + _version = parser.getAttributeValue(null,"version"); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("asset".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","asset IN"); + parseColladaAsset(); + Log.d("COLLADA!","asset OUT"); + } else if ("library_animations".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_animations IN"); + parseColladaLibraryAnimations(); + Log.d("COLLADA!","library_animations OUT"); + } else if ("library_cameras".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_cameras IN"); + parseColladaLibraryCameras(); + Log.d("COLLADA!","library_cameras OUT"); + } else if ("library_lights".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!", "library_lights " + xmlItem); + Log.d("COLLADA!", " getAttributeCount " + parser.getAttributeCount()); + Log.d("COLLADA!", " getNamespace " + parser.getNamespace()); + Log.d("COLLADA!", " getAttributeValue " + parser.getAttributeValue(null,"value")); + Log.d("COLLADA!", " getText " + parser.getText()); + if (parser.getAttributeCount() > 0) { + Log.d("COLLADA!", " getAttributeValue " + parser.getAttributeValue(0)); + Log.d("COLLADA!", " getAttributeName " + parser.getAttributeName(0)); + } + //TODO: implement lights parsing + //light + // -- technique_common + // -- point + // -- color + // -- constant_attenuation + // -- linear_attenuation + // -- quadratic_attenuation + // -- extra + // -- technique + // -- type + // -- flag + // -- mode + // -- gamma + // -- red + // -- green + // -- blue + // -- shadow_r + // -- shadow_g + // -- shadow_b + // -- energy + // -- dist + // -- spotsize + // -- spotblend + // -- halo_intensity + // -- att1 + // -- att2 + // -- falloff_type + // -- clipsta + // -- clipend + // -- bias + // -- soft + // -- compressthresh + // -- bufsize + // -- samp + // -- buffers + // -- filtertype + // -- bufflag + // -- buftype + // -- ray_samp + // -- ray_sampy + // -- ray_sampz + // -- ray_samp_type + // -- area_shape + // -- area_size + // -- area_sizey + // -- area_sizez + // -- adapt_thresh + // -- ray_samp_method + // -- shadhalostep + // -- sun_effect_type + // -- skyblendtype + // -- horizon_brightness + // -- spread + // -- sun_brightness + // -- sun_size + // -- backscattered_light + // -- sun_intensity + // -- atm_turbidity + // -- atm_extinction_factor + // -- atm_distance_factor + // -- skyblendfac + // -- sky_exposure + // -- sky_colorspace + } else if ("library_images".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_images IN"); + parseColladaLibraryImages(); + Log.d("COLLADA!","library_images OUT"); + } else if ("library_effects".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { //[optional?] + //TODO: implement effects parsing + //effect + // -- profile_COMMON + // -- technique + // -- phong + // -- emission + // -- color + // -- ambient + // -- color + // -- diffuse + // -- color + // -- specular + // -- color + // -- shininess + // -- color + // -- index_of_refraction + // -- float + } else if ("library_materials".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { //[optional?] + Log.d("COLLADA!","library_materials IN"); + parseColladaLibraryMaterials(); + Log.d("COLLADA!","library_materials OUT"); + } else if ("library_geometries".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_geometries IN"); + parseColladaLibraryGeometries(); + Log.d("COLLADA!","library_geometries OUT"); + } else if ("library_controllers".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_controllers IN"); + parseColladaLibraryControllers(); + Log.d("COLLADA!","library_controllers OUT"); + } else if ("library_visual_scenes".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","library_visual_scenes IN"); + parseColladaLibraryVisualScenes(); + Log.d("COLLADA!","library_visual_scenes OUT"); + } else if ("scene".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + scene = new SScene(); + parser.next(); + if ("instance_visual_scene".equals(parser.getName())) { + scene.instance_visual_scene_url = parser.getAttributeValue(null, "url"); + } + } + Log.d("COLLADA","COLLADA loop " + xmlItem); + } + Log.d("COLLADA","COLLADA exit " + xmlItem); + } + break; + } + + eventType = parser.next(); + Log.d("COLLADA","eventType " + eventType); + } + + } + + private void parseColladaAsset() { + //contributor + // -- author + // -- authoring_tool + // -- comments + // -- copyright + //created + //modified + //unit + //up_axis + try { + asset = new SAsset(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("contributor".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + asset.contributor = new SContributor(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("author".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.contributor.author = parser.getText(); + } else if ("authoring_tool".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.contributor.authoring_tool = parser.getText(); + } else if ("comments".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.contributor.comments = parser.getText(); + } else if ("copyright".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.contributor.copyright = parser.getText(); + } else if ("contributor".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("created".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.created = parser.getText(); + } else if ("modified".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.modified = parser.getText(); + } else if ("unit".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.unit = new SUnit(); + asset.unit.name = parser.getAttributeValue(null,"name"); + asset.unit.unit_value = parser.getAttributeValue(null,asset.unit.name); + } else if ("up_axis".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + eventType = parser.next(); + asset.up_axis = parser.getText(); + } else if (xmlItem == null) { + //do nothing + } else if ("asset".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryAnimations() { + //animation + // -- source + // -- float_array + // -- technique_common + // -- accessor + // -- param + // -- technique + // -- pre_infinity + // -- post_infinity + // -- sampler + // -- input + // -- channel + try { + library_animations = new SLibraryAnimations(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("animation".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_animations.animation == null) { library_animations.animation = new Vector<>(); } + SAnimation l_animation = new SAnimation(); + l_animation.id = parser.getAttributeValue(null,"id"); + library_animations.animation.add(l_animation); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- source + // -- sampler + // -- channel + if ("source".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","source IN"); + parseColladaLibraryAnimationsSource(l_animation); + Log.d("COLLADA!","source OUT"); + } else if ("sampler".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { +// SAnimation l_animation = new SAnimation(); + l_animation.sampler = new SSampler(); + l_animation.sampler.id = parser.getAttributeValue(null,"id"); + // -- input + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("input".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (l_animation.sampler.input == null) { l_animation.sampler.input = new Vector<>(); } + SSamplerInput l_input = new SSamplerInput(); + l_input.semantic = parser.getAttributeValue(null,"semantic"); + l_input.source = parser.getAttributeValue(null,"source"); + l_animation.sampler.input.add(l_input); + Log.d("COLLADA!xxx", "input exit " + xmlItem); + } else if (xmlItem == null) { + //do nothing + } else if ("sampler".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + Log.d("COLLADA!f", "sampler legit exiting " + xmlItem); + break; + } + } + } else if ("channel".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","channel IN"); + l_animation.channel = new SChannel(); + l_animation.channel.source = parser.getAttributeValue(null,"source"); + l_animation.channel.target = parser.getAttributeValue(null,"target"); + Log.d("COLLADA!","channel OUT"); + } else if (xmlItem == null) { + //do nothing + } else if ("animation".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_animations".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + Log.d("COLLADA!f", "library_animations legit exiting " + xmlItem); + break; + } + Log.d("COLLADA!f", "library_animations loop " + xmlItem); + } + Log.d("COLLADA!g", "library_animations exit " + xmlItem); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + Log.d("COLLADA", "library_animations.animation " + library_animations.animation.size()); + Log.d("COLLADA", "library_animations.animation.get(0).sources.size() " + library_animations.animation.get(0).sources.size()); +/* for (int i = 0; i < 999999999; i++){ + for (int j = 0; j < 999999999; j++) { + Math.abs(i*j/10); + } + } + Assert.assertTrue(false);*/ + } + private void parseColladaLibraryAnimationsSource(SAnimation p_animation) { + // -- float_array + // -- [Name_array] + // -- technique_common + // -- accessor + // -- param + // -- [technique] + // -- pre_infinity + // -- post_infinity + try { + if (p_animation.sources == null) { p_animation.sources = new Vector<>(); } + SAnimationSource l_source = new SAnimationSource(); + l_source.id = parser.getAttributeValue(null,"id"); + p_animation.sources.add(l_source); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("float_array".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { +// Assert.assertNotNull(l_source.float_array); + l_source.float_array = new SFloatArray(); + l_source.float_array.id = parser.getAttributeValue(null,"id"); + l_source.float_array.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + l_source.float_array.value = new Vector<>(); + parser.next(); + String[] l_float = parser.getText().split(" "); + for (int i = 0; i < l_float.length; ++i) { + if (l_float[i].isEmpty()) continue; + l_source.float_array.value.add(Float.parseFloat(l_float[i])); + } + Log.d("COLLADA!xxx", "float_array exit " + xmlItem); + } else if ("Number_array".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.Name_array = new SAnimationSourceNameArray(); + l_source.Name_array.id = parser.getAttributeValue(null,"id"); + l_source.Name_array.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + l_source.Name_array.value = new Vector<>(); + String[] l_Name = parser.getText().split(" "); + parser.next(); + for (int i = 0; i < l_Name.length; ++i) { + if (l_Name[i].isEmpty()) continue; + l_source.Name_array.value.add(l_Name[i]); + } + Log.d("COLLADA!xxx", "Number_array exit " + xmlItem); + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.technique_common = new SAnimationSourceTechniqueCommon(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- accessor + if ("accessor".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.technique_common.accessor = new SAnimationSourceTechniqueCommonAccessor(); + l_source.technique_common.accessor.source = parser.getAttributeValue(null,"source"); + l_source.technique_common.accessor.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + l_source.technique_common.accessor.stride = Integer.parseInt(parser.getAttributeValue(null,"stride")); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- param + if ("param".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (l_source.technique_common.accessor.params == null) { l_source.technique_common.accessor.params = new Vector<>(); } + SAnimationSourceTechniqueCommonAccessorParam l_param = new SAnimationSourceTechniqueCommonAccessorParam(); + l_param.name = parser.getAttributeValue(null,"name"); + l_param.type = parser.getAttributeValue(null,"type"); + l_source.technique_common.accessor.params.add(l_param); + } else if (xmlItem == null) { + //do nothing + } else if ("accessor".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + Log.d("COLLADA!xxx", "accessor exit " + xmlItem); + } else if (xmlItem == null) { + //do nothing + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + Log.d("COLLADA!xxx", "technique_common exit " + xmlItem); + } else if ("technique".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.technique = new SAnimationSourceTechnique(); + l_source.technique.profile = parser.getAttributeValue(null,"profile"); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- pre_infinity + // -- post_infinity + if ("pre_infinity".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.technique.pre_infinity = parser.getText(); + } else if ("post_infinity".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.technique.post_infinity = parser.getText(); + } else if (xmlItem == null) { + //do nothing + } else if ("technique".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + Log.d("COLLADA!xxx", "technique exit " + xmlItem); + } else if (xmlItem == null) { + //do nothing + } else if ("source".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + Log.d("COLLADA!f", "source legit exiting " + xmlItem); + break; + } + //Log.d("COLLADA!f", "source loop " + xmlItem); + } + Log.d("COLLADA!g", "source exit " + xmlItem); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryCameras() { + //camera + // -- optics + // -- technique_common + // -- perspective + // -- xfov + // -- aspect_ratio + // -- znear + // -- zfar + // -- extra + // -- technique + // -- shiftx + // -- shifty + // -- YF_dofdis + try { + library_cameras = new SLibraryCameras(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("camera".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_cameras.camera == null) { library_cameras.camera = new Vector<>(); } + SCamera l_camera = new SCamera(); + l_camera.id = parser.getAttributeValue(null,"id"); + l_camera.name = parser.getAttributeValue(null,"name"); + library_cameras.camera.add(l_camera); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- optics + // -- extra + if ("optics".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","optics IN"); + parseColladaLibraryCamerasCameraOptics(l_camera); + Log.d("COLLADA!","optics OUT"); + } else if ("extra".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","extra IN"); + parseColladaLibraryCamerasCameraExtra(l_camera); + Log.d("COLLADA!","extra OUT"); + } else if ("camera".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_cameras".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + Log.d("COLLADA!f", "library_cameras legit exiting " + xmlItem); + break; + } + Log.d("COLLADA!f", "library_cameras loop " + xmlItem); + } + Log.d("COLLADA!g", "library_cameras exit " + xmlItem); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryCamerasCameraOptics(SCamera p_camera) { + // -- technique_common + // -- perspective + // -- xfov + // -- aspect_ratio + // -- znear + // -- zfar + try { + p_camera.optics = new SOptic(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_camera.optics.technique_common = new STechniqueCommon(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- perspective + if ("perspective".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","perspective IN"); + parseColladaLibraryCamerasCameraOpticsTechniqueCommonPerspective(p_camera.optics.technique_common); + Log.d("COLLADA!","perspective OUT"); + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + Log.d("COLLADA!xxx","technique_common exit " + xmlItem); + } else if (xmlItem == null) { + //do nothing + } else if ("optics".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + Log.d("COLLADA!f", "optics legit exiting " + xmlItem); + break; + } + Log.d("COLLADA!f", "optics loop " + xmlItem); + } + Log.d("COLLADA!g", "optics exit " + xmlItem); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryCamerasCameraOpticsTechniqueCommonPerspective(STechniqueCommon p_techniqueCommon) { + // -- xfov + // -- aspect_ratio + // -- znear + // -- zfar + try { + p_techniqueCommon.perspective = new SPerspective(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("xfov".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_techniqueCommon.perspective.xfov = new SPerspectiveParam(); + p_techniqueCommon.perspective.xfov.sid = parser.getAttributeValue(null,"sid"); + parser.next(); + p_techniqueCommon.perspective.xfov.value = parser.getText(); + } else if ("aspect_ratio".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + parser.next(); + p_techniqueCommon.perspective.aspect_ratio = parser.getText(); + } else if ("znear".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_techniqueCommon.perspective.znear = new SPerspectiveParam(); + p_techniqueCommon.perspective.znear.sid = parser.getAttributeValue(null,"sid"); + parser.next(); + p_techniqueCommon.perspective.znear.value = parser.getText(); + } else if ("zfar".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_techniqueCommon.perspective.zfar = new SPerspectiveParam(); + p_techniqueCommon.perspective.zfar.sid = parser.getAttributeValue(null,"sid"); + parser.next(); + p_techniqueCommon.perspective.zfar.value = parser.getText(); + } else if (xmlItem == null) { + //do nothing + } else if ("perspective".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryCamerasCameraExtra(SCamera p_camera) { + // -- technique + // -- shiftx + // -- shifty + // -- YF_dofdis + try { + p_camera.extra = new SExtra(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("technique".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_camera.extra.technique = new STechnique(); + p_camera.extra.technique.profile = parser.getAttributeValue(null,"profile"); + + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- shiftx + // -- shifty + // -- YF_dofdis + if ("shiftx".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_camera.extra.technique.shiftx = new STechniqueParam(); + p_camera.extra.technique.shiftx.sid = parser.getAttributeValue(null,"sid"); + p_camera.extra.technique.shiftx.type = parser.getAttributeValue(null,"type"); + parser.next(); + p_camera.extra.technique.shiftx.value = parser.getText(); + } else if ("shifty".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_camera.extra.technique.shifty = new STechniqueParam(); + p_camera.extra.technique.shifty.sid = parser.getAttributeValue(null,"sid"); + p_camera.extra.technique.shifty.type = parser.getAttributeValue(null,"type"); + parser.next(); + p_camera.extra.technique.shifty.value = parser.getText(); + } else if ("YF_dofdist".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_camera.extra.technique.YF_dofdist = new STechniqueParam(); + p_camera.extra.technique.YF_dofdist.sid = parser.getAttributeValue(null,"sid"); + p_camera.extra.technique.YF_dofdist.type = parser.getAttributeValue(null,"type"); + parser.next(); + p_camera.extra.technique.YF_dofdist.value = parser.getText(); + } else if ("technique".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("extra".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryImages() { + //image + // -- init_from + try { + library_images = new SLibraryImages(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("image".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_images.image == null) { library_images.image = new Vector<>(); } + SImage l_image = new SImage(); + l_image.id = parser.getAttributeValue(null,"id"); + l_image.name = parser.getAttributeValue(null,"name"); + library_images.image.add(l_image); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- init_from + if ("init_from".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_image.init_from = parser.getText(); + } else if ("image".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_images".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryMaterials() { + //material + // -- instance_effect + try { + library_materials = new SLibraryMaterials(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("material".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_materials.material == null) { library_materials.material = new Vector<>(); } + SMaterial l_material = new SMaterial(); + l_material.id = parser.getAttributeValue(null,"id"); + l_material.name = parser.getAttributeValue(null,"name"); + library_materials.material.add(l_material); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- mesh + if ("instance_effect".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_material.instance_effect = new SInstanceEffect(); + l_material.instance_effect.url = parser.getAttributeValue(null,"url"); + } else if ("material".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_materials".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryGeometries() { + //geometry + // -- mesh + // -- source (Cube-mesh-positions) + // -- float_array + // -- technique_common + // -- accessor + // -- param (X) + // -- param (Y) + // -- param (Z) + // -- source (Cube-mesh-normals) + // -- float_array + // -- technique_common + // -- accessor + // -- param (X) + // -- param (Y) + // -- param (Z) + // -- source (Cube-mesh-map) [optional?] + // -- float_array + // -- technique_common + // -- accessor + // -- param (S) + // -- param (T) + // -- vertices + // -- input + // -- triangles + // -- input (VERTEX) + // -- input (NORMAL) + // -- input (TEXCOORD) [optional?] + // -- p + try { + library_geometries = new SLibraryGeometries(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("geometry".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_geometries.geometry ==null) { library_geometries.geometry = new Vector<>(); } + SGeometry l_geometry = new SGeometry(); + l_geometry.id = parser.getAttributeValue(null,"id"); + l_geometry.name = parser.getAttributeValue(null,"name"); + library_geometries.geometry.add(l_geometry); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- mesh + if ("mesh".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","mesh IN"); + parseColladaLibraryGeometriesMesh(l_geometry); + Log.d("COLLADA!","mesh OUT"); + } else if ("geometry".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_geometries".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryGeometriesMesh(SGeometry p_geometry) { + // -- source (Cube-mesh-positions) + // -- float_array + // -- technique_common + // -- accessor + // -- param (X) + // -- param (Y) + // -- param (Z) + // -- source (Cube-mesh-normals) + // -- float_array + // -- technique_common + // -- accessor + // -- param (X) + // -- param (Y) + // -- param (Z) + // -- source (Cube-mesh-map) [optional?] + // -- float_array + // -- technique_common + // -- accessor + // -- param (S) + // -- param (T) + // -- vertices + // -- input + // -- triangles + // -- input (VERTEX) + // -- input (NORMAL) + // -- input (TEXCOORD) [optional?] + // -- p + try { + p_geometry.mesh = new SMesh(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("source".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_geometry.mesh.source == null) { p_geometry.mesh.source = new Vector<>(); } + SSource l_source = new SSource(); + l_source.id = parser.getAttributeValue(null,"id"); + p_geometry.mesh.source.add(l_source); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- float_array + // -- technique_common + if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","technique_common IN"); + parseColladaLibraryGeometriesMeshTechniqueCommon(l_source); + Log.d("COLLADA!","technique_common OUT"); + } else if ("float_array".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_source.float_array = new SFloatArray(); + l_source.float_array.id = parser.getAttributeValue(null,"id"); + l_source.float_array.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + parser.next(); + l_source.float_array.value = new Vector<>(); + String[] l_float = parser.getText().split(" "); + for (int i = 0; i < l_float.length; ++i) { + if (l_float[i].isEmpty()) continue; + l_source.float_array.value.add(Float.parseFloat(l_float[i])); + } + } else if ("source".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("vertices".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_geometry.mesh.vertices == null) { p_geometry.mesh.vertices = new SVertices(); } + p_geometry.mesh.vertices.id = parser.getAttributeValue(null,"id"); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- float_array + // -- technique_common + if ("input".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_geometry.mesh.vertices.input = new SVerticesInput(); + p_geometry.mesh.vertices.input.semantic = parser.getAttributeValue(null,"semantic"); + p_geometry.mesh.vertices.input.source = parser.getAttributeValue(null,"source"); + } else if ("vertices".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("polylist".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.e("COLLADA", "Please 'Triangulate' all quads in your model."); + Assert.assertTrue(false); + } else if ("triangles".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_geometry.mesh.triangles == null) { p_geometry.mesh.triangles = new Vector<>(); } + STriangles l_triangles = new STriangles(); + l_triangles.material = parser.getAttributeValue(null,"material"); + l_triangles.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + p_geometry.mesh.triangles.add(l_triangles); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- float_array + // -- technique_common + if ("input".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (l_triangles.input == null) { l_triangles.input = new Vector<>(); } + STrianglesInput l_STrianglesInput = new STrianglesInput(); + l_STrianglesInput.semantic = parser.getAttributeValue(null,"semantic"); + l_STrianglesInput.source = parser.getAttributeValue(null,"source"); + l_STrianglesInput.offset = Integer.parseInt(parser.getAttributeValue(null,"offset")); + l_triangles.input.add(l_STrianglesInput); + } else if ("p".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_triangles.p = new SP(); + parser.next(); + l_triangles.p.value = new Vector<>(); + String[] l_value = parser.getText().split(" "); + Log.d("COLLADA", "l_value.length " + l_value.length); + for (int i = 0; i < l_value.length; ++i) { + if (l_value[i].isEmpty()) continue; + l_triangles.p.value.add(Integer.parseInt(l_value[i])); + } + } else if ("triangles".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("mesh".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryGeometriesMeshTechniqueCommon(SSource p_source) { + // -- accessor + // -- param (X) + // -- param (Y) + // -- param (Z) + try { + p_source.technique_common = new SGeometryTechniqueCommon(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("accessor".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_source.technique_common.accessor = new SAccessor(); + p_source.technique_common.accessor.source = parser.getAttributeValue(null,"source"); + p_source.technique_common.accessor.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + p_source.technique_common.accessor.stride = Integer.parseInt(parser.getAttributeValue(null,"stride")); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- param (X) + // -- param (Y) + // -- param (Z) + // ---- or ---- + // -- param (S) + // -- param (Y) + if ("param".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + String l_name = parser.getAttributeValue(null,"name"); + String l_type = parser.getAttributeValue(null,"type"); + if ("X".equals(l_name)) { + p_source.technique_common.accessor.paramX = new SAccessorParam(); + p_source.technique_common.accessor.paramX.name = l_name; + p_source.technique_common.accessor.paramX.name = l_type; + } else if ("Y".equals(l_name)) { + p_source.technique_common.accessor.paramY = new SAccessorParam(); + p_source.technique_common.accessor.paramY.name = l_name; + p_source.technique_common.accessor.paramY.name = l_type; + } else if ("Z".equals(l_name)) { + p_source.technique_common.accessor.paramZ = new SAccessorParam(); + p_source.technique_common.accessor.paramZ.name = l_name; + p_source.technique_common.accessor.paramZ.name = l_type; + } + } else if ("accessor".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryControllers() { + //controller[] + // -- skin + // -- bind_shape_matrix + // -- source[] + // -- Name_array |or| float_array + // -- technique_common + // -- accessor + // -- param + // -- joints + // -- input[] + // -- vertex_weights + // -- input[] + // -- vcount + // -- v + try { + library_controllers = new SLibraryControllers(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("controller".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_controllers.controller == null) { library_controllers.controller = new Vector<>(); } + SController l_controller = new SController(); + l_controller.id = parser.getAttributeValue(null,"id"); + l_controller.name = parser.getAttributeValue(null,"name"); + library_controllers.controller.add(l_controller); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- skin + if ("skin".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","skin IN"); + parseColladaLibraryControllersSkin(l_controller); + Log.d("COLLADA!","skin OUT"); + } else if ("controller".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_controllers".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryControllersSkin(SController p_controller) { + // -- bind_shape_matrix + // -- source[] + // -- Name_array |or| float_array + // -- technique_common + // -- accessor + // -- param + // -- joints + // -- input[] + // -- vertex_weights + // -- input[] + // -- vcount + // -- v + try { + p_controller.skin = new SSkin(); + p_controller.skin.source = parser.getAttributeValue(null,"source"); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("bind_shape_matrix".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_controller.skin.bind_shape_matrix = new SBindShapeMatrix(); + p_controller.skin.bind_shape_matrix.value = parser.getText(); + } else if ("source".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_controller.skin.sources == null) { p_controller.skin.sources = new Vector<>(); } + SControllerSource l_controllerSource = new SControllerSource(); + l_controllerSource.id = parser.getAttributeValue(null,"id"); + p_controller.skin.sources.add(l_controllerSource); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- Name_array |or| float_array + // -- technique_common + if ("Name_array".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_controllerSource.Name_array_id = parser.getAttributeValue(null,"id"); + l_controllerSource.Name_array_count = Integer.parseInt(parser.getAttributeValue(null,"count")); + l_controllerSource.Name_array_value = parser.getText(); + } else if ("float_array".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_controllerSource.float_array_id = parser.getAttributeValue(null,"id"); + l_controllerSource.float_array_count = Integer.parseInt(parser.getAttributeValue(null,"count")); + l_controllerSource.float_array_value = parser.getText(); + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","technique_common IN"); + parseColladaLibraryControllersSkinTechniqueCommon(l_controllerSource); + Log.d("COLLADA!","technique_common OUT"); + } else if ("source".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("joints".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + library_controllers.controller.get(library_controllers.controller.size()-1).skin.joints = new SJoints(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- input[] + if ("input".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_controllers.controller.get(library_controllers.controller.size()-1).skin.joints.input == null) { + library_controllers.controller.get(library_controllers.controller.size()-1).skin.joints.input = new Vector<>(); + } + SJointsInput l_jointsInput = new SJointsInput(); + l_jointsInput.semantic = parser.getAttributeValue(null,"semantic"); + l_jointsInput.source = parser.getAttributeValue(null,"source"); + library_controllers.controller.get(library_controllers.controller.size()-1).skin.joints.input.add(l_jointsInput); + } else if ("joints".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("vertex_weights".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_controller.skin.vertex_weights = new SVertexWeights(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- input[] + // -- vcount + // -- v + if ("input".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_controller.skin.vertex_weights.input == null) { p_controller.skin.vertex_weights.input = new Vector<>(); } + SVertexWeightsInput l_vertexWeightsInput = new SVertexWeightsInput(); + l_vertexWeightsInput.semantic = parser.getAttributeValue(null,"semantic"); + l_vertexWeightsInput.source = parser.getAttributeValue(null,"source"); + l_vertexWeightsInput.offset = Integer.parseInt(parser.getAttributeValue(null,"offset")); + p_controller.skin.vertex_weights.input.add(l_vertexWeightsInput); + } else if ("vcount".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_controller.skin.vertex_weights.vcount = parser.getText(); + } else if ("v".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_controller.skin.vertex_weights.v = parser.getText(); + } else if ("vertex_weights".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("skin".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryControllersSkinTechniqueCommon(SControllerSource p_sources) { + // -- accessor + // -- param + try { + p_sources.technique_common = new SControllerSourceTechniqueCommon(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("accessor".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_sources.technique_common.accessor = new SControllerSourceTechniqueCommonAccessor(); + p_sources.technique_common.accessor.source = parser.getAttributeValue(null,"source"); + p_sources.technique_common.accessor.count = Integer.parseInt(parser.getAttributeValue(null,"count")); + p_sources.technique_common.accessor.stride = Integer.parseInt(parser.getAttributeValue(null,"stride")); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- param + if ("param".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_sources.technique_common.accessor.param_name = parser.getAttributeValue(null,"name"); + p_sources.technique_common.accessor.param_type = parser.getAttributeValue(null,"type"); + } else if ("accessor".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("technique_common".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void parseColladaLibraryVisualScenes() { + //visual_scene + // -- node (Camera) + // -- matrix + // -- instance_camera + // -- node (Lamp) + // -- matrix + // -- instance_light + // -- node (Cube) + // -- matrix + // -- instance_controller (or instance_geometry?) + // -- bind_material [optional] + // -- technique_common + // -- instance_material[] + try { + library_visual_scenes = new SLibraryVisualScenes(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("visual_scene".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (library_visual_scenes.visual_scene == null) { library_visual_scenes.visual_scene = new Vector<>(); } + SVisualScene l_visualScene = new SVisualScene(); + l_visualScene.id = parser.getAttributeValue(null,"id"); + l_visualScene.name = parser.getAttributeValue(null,"name"); + library_visual_scenes.visual_scene.add(l_visualScene); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- node + if ("node".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","node IN"); + parseColladaLibraryVisualScenesNode(l_visualScene); + Log.d("COLLADA!","node OUT"); + } else if ("visual_scene".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if (xmlItem == null) { + //do nothing + } else if ("library_visual_scenes".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryVisualScenesNode(SVisualScene p_visualScene) { + // -- node (Camera) + // -- matrix + // -- instance_camera + // -- node (Lamp) + // -- matrix + // -- instance_light + // -- node (Cube) + // -- matrix + // -- instance_controller (or instance_geometry?) + // -- bind_material [optional] + // -- technique_common + // -- instance_material[] + try { + if (p_visualScene.node == null) { p_visualScene.node = new Vector<>(); } + SNode l_node = new SNode(); + l_node.id = parser.getAttributeValue(null,"id"); + l_node.name = parser.getAttributeValue(null,"name"); + l_node.type = parser.getAttributeValue(null,"type"); + Log.d("COLLADA?", "id " + l_node.id + " name " + l_node.name + " type " + l_node.type); + p_visualScene.node.add(l_node); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("translate".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_node.translate = new STranslate(); + l_node.translate.sid = parser.getAttributeValue(null, "sid"); + l_node.translate.values = parser.getText(); + } else if ("rotate".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (l_node.rotate == null) { l_node.rotate = new Vector<>(); } + SRotate l_rotate = new SRotate(); + l_rotate.sid = parser.getAttributeValue(null,"sid"); + l_rotate.values = parser.getText(); + l_node.rotate.add(l_rotate); + } else if ("instance_camera".equals(xmlItem) && eventType == XmlPullParser.START_TAG + || "instance_light".equals(xmlItem) && eventType == XmlPullParser.START_TAG + || "instance_geometry".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + l_node.instance_url = parser.getAttributeValue(null,"url"); + } else if ("node".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + Log.d("COLLADA!","node-inception IN"); + l_node.node = new SVisualSceneNode(); + parseColladaLibraryVisualScenesNodeInception(l_node.node); + Log.d("COLLADA!","node-inception OUT"); + } else if (xmlItem == null) { + //do nothing + } else if ("node".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryVisualScenesNodeInception(SVisualSceneNode p_node) { + // -- matrix + // -- node[] + // -- extra + try { +// p_node = new SVisualSceneNode(); + p_node.id = parser.getAttributeValue(null,"id"); + p_node.name = parser.getAttributeValue(null,"name"); + p_node.sid = parser.getAttributeValue(null,"sid"); + p_node.type = parser.getAttributeValue(null,"type"); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("matrix".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_node.matrix = new SMatrix(); + p_node.matrix.sid = parser.getAttributeValue(null, "sid"); + p_node.matrix.values = parser.getText(); + } else if ("extra".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_node.extra = new SVisualSceneNodeExtra(); + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + // -- node + if ("technique".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + parseColladaLibraryVisualScenesNodeInceptionTechnique(p_node.extra); + } else if ("extra".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } else if ("node".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + if (p_node.node == null) { p_node.node = new Vector<>(); } + SVisualSceneNode l_node = new SVisualSceneNode(); + p_node.node.add(l_node); + Log.d("COLLADA!"," node-inception IN"); + parseColladaLibraryVisualScenesNodeInception(l_node); + Log.d("COLLADA!"," node-inception OUT"); + } else if (xmlItem == null) { + //do nothing + } else if ("node".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void parseColladaLibraryVisualScenesNodeInceptionTechnique(SVisualSceneNodeExtra p_extra) { + // -- layer + // -- roll + // -- tip_x + // -- tip_y + // -- tip_z + try { + p_extra.technique = new SVisualSceneNodeExtraTechnique(); + p_extra.technique.profile = parser.getAttributeValue(null,"profile"); + int eventType = parser.getEventType(); + String l_tmp = ""; + while (eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + xmlItem = parser.getName(); + if ("layer".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_extra.technique.layer_sid = parser.getAttributeValue(null, "sid"); + p_extra.technique.layer_type = parser.getAttributeValue(null, "type"); + l_tmp = parser.getText(); + if (l_tmp == null) { continue; } + p_extra.technique.layer_value = Integer.parseInt(l_tmp); + } else if ("roll".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_extra.technique.roll_sid = parser.getAttributeValue(null, "sid"); + p_extra.technique.roll_type = parser.getAttributeValue(null, "type"); + if (l_tmp == null) { continue; } + p_extra.technique.roll_value = Float.parseFloat(l_tmp); + } else if ("tip_x".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_extra.technique.tip_x_sid = parser.getAttributeValue(null, "sid"); + p_extra.technique.tip_x_type = parser.getAttributeValue(null, "type"); + if (l_tmp == null) { continue; } + p_extra.technique.tip_x_value = Float.parseFloat(l_tmp); + } else if ("tip_y".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_extra.technique.tip_y_sid = parser.getAttributeValue(null, "sid"); + p_extra.technique.tip_y_type = parser.getAttributeValue(null, "type"); + if (l_tmp == null) { continue; } + p_extra.technique.tip_y_value = Float.parseFloat(l_tmp); + } else if ("tip_z".equals(xmlItem) && eventType == XmlPullParser.START_TAG) { + p_extra.technique.tip_z_sid = parser.getAttributeValue(null, "sid"); + p_extra.technique.tip_z_type = parser.getAttributeValue(null, "type"); + if (l_tmp == null) { continue; } + p_extra.technique.tip_z_value = Float.parseFloat(l_tmp); + } else if (xmlItem == null) { + //do nothing + } else if ("technique".equals(xmlItem) && eventType == XmlPullParser.END_TAG) { + break; + } + } + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //============================================================================================== + //============================================================================================== + //============================================================================================== + public String xmlns, _version, xmlns_xsi; + public SAsset asset; + public SLibraryAnimations library_animations; + public SLibraryCameras library_cameras; + public SLibraryLights library_lights; + public SLibraryImages library_images; + public SLibraryEffects library_effects; + public SLibraryMaterials library_materials; + public SLibraryGeometries library_geometries; + public SLibraryControllers library_controllers; + public SLibraryVisualScenes library_visual_scenes; + public SScene scene; + + class SAsset { + SContributor contributor; + String created; + String modified; + SUnit unit; + String up_axis; + } + class SContributor { + String author; + String authoring_tool; + String comments; + String copyright; + } + class SUnit { + String name; + String unit_value; + } + class SLibraryAnimations { + Vector animation; + } + class SAnimation { + String id; + Vector sources; + SSampler sampler; + SChannel channel; + } + class SAnimationSource { + String id; + SFloatArray float_array; + SAnimationSourceNameArray Name_array; + SAnimationSourceTechniqueCommon technique_common; + SAnimationSourceTechnique technique; + } + class SAnimationSourceTechniqueCommon { + SAnimationSourceTechniqueCommonAccessor accessor; + } + class SAnimationSourceTechniqueCommonAccessor { + String source; + int count; + int stride; + Vector params; + } + class SAnimationSourceTechniqueCommonAccessorParam { + String name; + String type; + } + class SAnimationSourceTechnique { + String profile; + String pre_infinity; + String post_infinity; + } + class SAnimationSourceNameArray { + String id; + int count; + Vector value; + } + class SSampler { + String id; + Vector input; + } + class SSamplerInput { + String semantic; + String source; + } + class SChannel { + String source; + String target; + } + + class SLibraryCameras { + Vector camera; + } + class SCamera { + String id; + String name; + SOptic optics; + SExtra extra; + } + class SOptic { + STechniqueCommon technique_common; + } + class STechniqueCommon { + SPerspective perspective; + } + class SPerspective { + SPerspectiveParam xfov; + String aspect_ratio; + SPerspectiveParam znear; + SPerspectiveParam zfar; + } + class SPerspectiveParam { + String sid; + String value; + } + class SExtra { + STechnique technique; + } + class STechnique { + String profile; + STechniqueParam shiftx; + STechniqueParam shifty; + STechniqueParam YF_dofdist; + } + class STechniqueParam { + String sid; + String type; + String value; + } + class SLibraryLights { + Vector light; + } + class SLight { + String id; + String name; + SLightTechniqueCommon technique_common; + SLightExtra extra; + } + class SLightTechniqueCommon { + SPoint point; + } + class SPoint { + SColor color; + String constant_attenuation; + String linear_attenuation; + String quadratic_attenuation; + } + class SColor { + String sid; + String value; + } + class SLightExtra { + SLightTechnique technique; + } + class SLightTechnique { + String profile; + STechniqueParam type; + STechniqueParam flag; + STechniqueParam mode; + STechniqueParam gamma; + STechniqueParam red; + STechniqueParam green; + STechniqueParam blue; + STechniqueParam shadow_r; + STechniqueParam shadow_g; + STechniqueParam shadow_b; + STechniqueParam energy; + STechniqueParam dist; + STechniqueParam spotsize; + STechniqueParam spotblend; + STechniqueParam halo_intensity; + STechniqueParam att1; + STechniqueParam att2; + STechniqueParam falloff; + STechniqueParam clipsta; + STechniqueParam clipend; + STechniqueParam bias; + STechniqueParam soft; + STechniqueParam compressthresh; + STechniqueParam buffsize; + STechniqueParam samp; + STechniqueParam buffers; + STechniqueParam filtertype; + STechniqueParam bufflag; + STechniqueParam buftype; + STechniqueParam ray_samp; + STechniqueParam ray_sampy; + STechniqueParam ray_sampz; + STechniqueParam ray_samp_type; + STechniqueParam area_shape; + STechniqueParam area_size; + STechniqueParam area_sizey; + STechniqueParam area_sizez; + STechniqueParam adapt_thresh; + STechniqueParam ray_samp_method; + STechniqueParam shadhalostep; + STechniqueParam sun_effect_type; + STechniqueParam skyblendedtype; + STechniqueParam horizon_brightness; + STechniqueParam sun_rise; + STechniqueParam backscattered_light; + STechniqueParam sun_intensity; + STechniqueParam atm_turbidity; + STechniqueParam atm_extinction_factor; + STechniqueParam atm_distance_factor; + STechniqueParam skyblendfac; + STechniqueParam sky_exposure; + STechniqueParam sky_colorspace; + } + class SLibraryImages { + Vector image; + } + class SImage { + String id; + String name; + String init_from; + SImageExtra extra; + } + class SImageExtra { + SImageTechnique technique; + } + class SImageTechnique { + String profile; + String dgnode_type; + int image_sequence; + } + class SLibraryEffects { + Vector effect; + } + class SEffect { + String id; + SProfileCommon profile_COMMON; + } + class SProfileCommon { + SEffectsTechnique technique; + } + class SEffectsTechnique { + String sid; + SPhong phong; + } + class SPhong { + SPongParam emmission; + SPongParam ambient; + SPongParam diffuse; + SPongParam specular; + SPongParamFloat shininess; + SPongParamFloat index_of_refraction; + } + class SPongParam { + SPhongColor color; + } + class SPhongColor { + String sid; + float r; + float g; + float b; + } + class SPongParamFloat { + SPhongFloat _float; + } + class SPhongFloat { + String sid; + String value; + } + class SLibraryMaterials { + Vector material; + } + class SMaterial { + String id; + String name; + SInstanceEffect instance_effect; + } + class SInstanceEffect { + String url; + } + class SLibraryGeometries { + Vector geometry; + } + class SGeometry { + String id; + String name; + SMesh mesh; + } + class SMesh { + Vector source; + SVertices vertices; + Vector triangles; + } + class SSource { + String id; + SFloatArray float_array; + SGeometryTechniqueCommon technique_common; + } + class SFloatArray { + String id; + int count; + Vector value; + } + class SGeometryTechniqueCommon { + SAccessor accessor; + } + class SAccessor { + String source; + int count; + int stride; + SAccessorParam paramX; + SAccessorParam paramY; + SAccessorParam paramZ; + } + class SAccessorParam { + String name; + String type; + } + class SVertices { + String id; + SVerticesInput input; + } + class SVerticesInput { + String semantic; + String source; + } + class STriangles { + String material; + int count; + Vector input; + SP p; + } + class STrianglesInput extends SVerticesInput { + int offset; + } + class SP { + Vector value; + } + class SLibraryControllers { + Vector controller; + } + class SController { + String id; + String name; + SSkin skin; + } + class SSkin { + String source; + SBindShapeMatrix bind_shape_matrix; + Vector sources; + SJoints joints; + SVertexWeights vertex_weights; + } + class SBindShapeMatrix { + String value; + } + class SControllerSource { + String id; + String Name_array_id; + int Name_array_count; + String Name_array_value; + String float_array_id; + int float_array_count; + String float_array_value; + SControllerSourceTechniqueCommon technique_common; + } + class SControllerSourceTechniqueCommon { + SControllerSourceTechniqueCommonAccessor accessor; + } + class SControllerSourceTechniqueCommonAccessor { + String source; + int count; + int stride; + String param_name; + String param_type; + } + class SJoints { + Vector input; + } + class SJointsInput { + String semantic; + String source; + } + class SVertexWeights { + int count; + Vector input; + String vcount; + String v; + } + class SVertexWeightsInput { + String semantic; + String source; + int offset; + } + class SLibraryVisualScenes { + Vector visual_scene; + } + class SVisualScene { + String id; + String name; + Vector node; + } + class SNode { + String id; + String name; + String type; + STranslate translate; + Vector rotate; + SScale scale; + String instance_url; //for use of camera/light/geometry + // SMatrix matrix; +// SInstanceGeometry instance_geometry; //for use of geometry + SInstanceController instance_controller; //the skeleton thingy + SVisualSceneNode node; + } + class SInstanceController { + String skeleton_value; + SBindMaterial bind_material; + } + class SVisualSceneNode { + String id; + String name; + String sid; + String type; + SMatrix matrix; + Vector node; //TODO: make sure recursive parsing is handled correctly + SVisualSceneNodeExtra extra; + } + class SVisualSceneNodeExtra { + SVisualSceneNodeExtraTechnique technique; + } + class SVisualSceneNodeExtraTechnique { + String profile; + String layer_sid; + String layer_type; + int layer_value; + String roll_sid; + String roll_type; + float roll_value; + String tip_x_sid; + String tip_x_type; + float tip_x_value; + String tip_y_sid; + String tip_y_type; + float tip_y_value; + String tip_z_sid; + String tip_z_type; + float tip_z_value; + } + class STranslate { + String sid; + String values; + } + class SRotate { + String sid; + String values; + } + class SScale { + String sid; + String values; + } + class SMatrix { + String sid; + String values; + } + class SInstanceGeometry { + String url; + String name; + SBindMaterial bind_material; + } + class SBindMaterial { + SVisualSceneTechniqueCommon technique_common; + } + class SVisualSceneTechniqueCommon { + Vector instance_material; + } + class SInstanceMaterial { + String symbol; + String target; + } + class SScene { + String instance_visual_scene_url; + } +} \ No newline at end of file diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/WavefrontModel.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/WavefrontModel.java index d56eea4..0ed75fe 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/WavefrontModel.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/tools/WavefrontModel.java @@ -18,6 +18,8 @@ import android.content.Context; import android.util.Log; +import androidx.annotation.FloatRange; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -25,7 +27,10 @@ import java.util.ArrayList; import java.util.HashMap; +import fr.arnaudguyon.smartgl.math.Vector3D; +import fr.arnaudguyon.smartgl.opengl.ColorList; import fr.arnaudguyon.smartgl.opengl.Face3D; +import fr.arnaudguyon.smartgl.opengl.NormalList; import fr.arnaudguyon.smartgl.opengl.Object3D; import fr.arnaudguyon.smartgl.opengl.Texture; import fr.arnaudguyon.smartgl.opengl.UVList; @@ -41,10 +46,11 @@ public class WavefrontModel { private static final String TAG = "WavefrontModel"; public static class Builder { - Context mContext; - int mRawResourceId; - boolean mOptimizeModel = true; - HashMap mTextures = new HashMap<>(); + private Context mContext; + private int mRawResourceId; + private boolean mOptimizeModel = true; + private HashMap mTextures = new HashMap<>(); + private float[] mColor = {1,1,1}; public Builder(Context context, int rawFileResourceId) { mContext = context; @@ -58,6 +64,12 @@ public Builder addTexture(String textureName, Texture texture) { mTextures.put(textureName, texture); return this; } + public Builder setColor(@FloatRange(from=0, to=1) float red, @FloatRange(from=0, to=1) float green, @FloatRange(from=0, to=1) float blue) { + mColor[0] = red; + mColor[1] = green; + mColor[2] = blue; + return this; + } public WavefrontModel create() { WavefrontModel wavefront = new WavefrontModel(); @@ -66,6 +78,7 @@ public WavefrontModel create() { wavefront.mergeStrips(); } wavefront.mTextures = mTextures; + wavefront.mColor = mColor; return wavefront; } @@ -108,11 +121,21 @@ private class Vertex { float mY; float mZ; + float mR, mG, mB; + boolean mHasColors = false; + Vertex(float x, float y, float z) { mX = x; mY = y; mZ = z; } + + void setColors(float r, float g, float b) { + mHasColors = true; + mR = r; + mG = g; + mB = b; + } } private class Normal { @@ -125,6 +148,13 @@ private class Normal { mY = y; mZ = z; } + + Normal(Vector3D vector) { + float[] values = vector.getArray(); + mX = values[0]; + mY = values[1]; + mZ = values[2]; + } } private class UV { @@ -141,6 +171,7 @@ private class UV { private ArrayList mNormals = new ArrayList<>(); private ArrayList mStrips = new ArrayList<>(); private HashMap mTextures = new HashMap<>(); + private float[] mColor; private WavefrontModel() { } @@ -155,8 +186,7 @@ private void loadObject(Context context, int rawResId) throws RuntimeException { int lineNumber = 0; while ((line = bufferedReader.readLine()) != null) { ++lineNumber; - line = line.replace(" ", " "); - String[] elements = line.split(" "); + String[] elements = line.split("\\s+"); switch (elements[0]) { case "v": // VERTEX if (elements.length > 3) { @@ -166,6 +196,19 @@ private void loadObject(Context context, int rawResId) throws RuntimeException { if ((x != null) && (y != null) && (z != null)) { Vertex vertex = new Vertex(x, y, z); mVertex.add(vertex); + + if (elements.length > 6) { // get vertex colors + Float r = fromString(elements[4]); + Float g = fromString(elements[5]); + Float b = fromString(elements[6]); + if ( (r != null) && (g != null) && (b != null) && + (r >= 0) && (r <= 1) && (g >= 0) && (g <=1) && (b >= 0) && (b <= 1)) { + vertex.setColors(r, g, b); + break; + } else { + throw new RuntimeException("Vertex Colors error line " + lineNumber); + } + } break; } } @@ -175,7 +218,7 @@ private void loadObject(Context context, int rawResId) throws RuntimeException { Float u = fromString(elements[1]); Float v = fromString(elements[2]); if ((u != null) && (v != null)) { - UV uv = new UV(u, v); + UV uv = new UV(u, 1 - v); // uv are upside down, use (u, 1-v) mUVs.add(uv); break; } @@ -199,67 +242,10 @@ private void loadObject(Context context, int rawResId) throws RuntimeException { } break; case "f": // FACES - if (elements.length == 5) { // Quad - Integer vA = intPart(elements, 1, 0); // Vertex A - Integer tA = intPart(elements, 1, 1); // Texture UV A - Integer nA = intPart(elements, 1 ,2); // Normal A - IndexInfo indexA = IndexInfo.create(vA, tA, nA); - - Integer vB = intPart(elements, 2, 0); // Vertex B - Integer tB = intPart(elements, 2, 1); // Texture UV B - Integer nB = intPart(elements, 2 ,2); // Normal B - IndexInfo indexB = IndexInfo.create(vB, tB, nB); - - Integer vC = intPart(elements, 3, 0); // Vertex C - Integer tC = intPart(elements, 3, 1); // Texture UV C - Integer nC = intPart(elements, 3 ,2); // Normal C - IndexInfo indexC = IndexInfo.create(vC, tC, nC); - - Integer vD = intPart(elements, 4, 0); // Vertex D - Integer tD = intPart(elements, 4, 1); // Texture UV D - Integer nD = intPart(elements, 4 ,2); // Normal D - IndexInfo indexD = IndexInfo.create(vD, tD, nD); - - if ((indexA != null) && (indexB != null) && (indexC != null) && (indexD != null)) { - Strip strip = new Strip(stripTextureName); - strip.addIndex(indexA); - strip.addIndex(indexB); - strip.addIndex(indexD); // quad converted to triangle strip - strip.addIndex(indexC); - mStrips.add(strip); - } else { - throw new RuntimeException("Quad error line " + lineNumber); - } - - } else if (elements.length == 4) { // Triangle - Integer vA = intPart(elements, 1, 0); // Vertex A - Integer tA = intPart(elements, 1, 1); // Texture UV A - Integer nA = intPart(elements, 1 ,2); // Normal A - IndexInfo indexA = IndexInfo.create(vA, tA, nA); - - Integer vB = intPart(elements, 2, 0); // Vertex B - Integer tB = intPart(elements, 2, 1); // Texture UV B - Integer nB = intPart(elements, 2 ,2); // Normal B - IndexInfo indexB = IndexInfo.create(vB, tB, nB); - - Integer vC = intPart(elements, 3, 0); // Vertex C - Integer tC = intPart(elements, 3, 1); // Texture UV C - Integer nC = intPart(elements, 3 ,2); // Normal C - IndexInfo indexC = IndexInfo.create(vC, tC, nC); - - if ((indexA != null) && (indexB != null) && (indexC != null)) { - Strip strip = new Strip(stripTextureName); - strip.addIndex(indexA); - strip.addIndex(indexB); - strip.addIndex(indexC); - mStrips.add(strip); - } else { - throw new RuntimeException("Triangle error line " + lineNumber); - } - + if (elements.length == 4) { // Triangle + addFaceStrips(lineNumber, stripTextureName, elements, 1, 3); } else { - // TODO: load polygonal faces (more than 4 vertex). I'm not sure how to split into triangles - throw new RuntimeException("Face Index error line " + lineNumber); + throw new RuntimeException("Only triangles supported, error line " + lineNumber); } break; } @@ -267,9 +253,69 @@ private void loadObject(Context context, int rawResId) throws RuntimeException { } catch (IOException e) { e.printStackTrace(); } + + if (mNormals.size() == 0) { + // compute normals + for(int faceIndex=0; faceIndex indexInfos = new ArrayList<>(numberOfVertex); + for(int i=0; i indexes = strip.mIndexes; -// for(IndexInfo indexInfo : indexes) { -// indexInfo.mVertexIndex = duplicateVertex.get(indexInfo.mVertexIndex, indexInfo.mVertexIndex); -// indexInfo.mUVIndex = duplicateUV.get(indexInfo.mUVIndex, indexInfo.mUVIndex); -// indexInfo.mNormalIndex = duplicateNormal.get(indexInfo.mNormalIndex, indexInfo.mNormalIndex); -// } -// } -// -// } - // Groups Strips of same material into 1 big strip (1 triangle strip per face) private void mergeStrips() { for(int iStrip = 0; iStrip 0); + final boolean hasNormals = (mNormals.size() > 0); + Object3D object3D = new Object3D(); for(Strip strip : mStrips) { + Face3D face3D = new Face3D(); int nbIndex = strip.mIndexes.size(); + VertexList vertexList = new VertexList(); vertexList.init(nbIndex); - UVList uvList = new UVList(); - uvList.init(nbIndex); + + UVList uvList = null; + ColorList colorList = null; + if (hasUV) { + uvList = new UVList(); + uvList.init(nbIndex); + } else { + colorList = new ColorList(); + colorList.init(nbIndex); + } + + NormalList normalList = null; + if (hasNormals) { + normalList = new NormalList(); + normalList.init(nbIndex); + } + for(IndexInfo indexInfo : strip.mIndexes) { int vertexIndex = indexInfo.mVertexIndex; Vertex vertex = mVertex.get(vertexIndex); vertexList.add(vertex.mX, vertex.mY, vertex.mZ); - if (mUVs.size() > 0) { + if (hasUV) { int uvIndex = indexInfo.mUVIndex; UV uv = mUVs.get(uvIndex); uvList.add(uv.mU, uv.mV); + } else if (vertex.mHasColors) { + colorList.add(vertex.mR, vertex.mG, vertex.mB, 1); // TODO: find a way to change the alpha channel? } else { - uvList.add((float) Math.random(), (float) Math.random()); + vertex.setColors(mColor[0], mColor[1], mColor[2]); + colorList.add(vertex.mR, vertex.mG, vertex.mB, 1); +// throw new RuntimeException("Model must have texture UVs or vertex Colors"); + } + + if (hasNormals) { + int normalIndex = indexInfo.mNormalIndex; + Normal normal = mNormals.get(normalIndex); + normalList.add(normal.mX, normal.mY, normal.mZ); } + } vertexList.finalizeBuffer(); face3D.setVertexList(vertexList); - uvList.finalizeBuffer(); - face3D.setUVList(uvList); - Texture texture = mTextures.get(strip.mTextureName); - face3D.setTexture(texture); + if (hasUV) { + uvList.finalizeBuffer(); + face3D.setUVList(uvList); + Texture texture = mTextures.get(strip.mTextureName); + face3D.setTexture(texture); + } else { + colorList.finalizeBuffer(); + face3D.setColorList(colorList); + } + + if (hasNormals) { + normalList.finalizeBuffer(); + face3D.setNormalList(normalList); + } object3D.addFace(face3D); } diff --git a/smartgl/src/main/java/fr/arnaudguyon/smartgl/touch/TouchHelper.java b/smartgl/src/main/java/fr/arnaudguyon/smartgl/touch/TouchHelper.java index bdf4b1d..904839f 100644 --- a/smartgl/src/main/java/fr/arnaudguyon/smartgl/touch/TouchHelper.java +++ b/smartgl/src/main/java/fr/arnaudguyon/smartgl/touch/TouchHelper.java @@ -63,6 +63,10 @@ private void addEvent(TouchHelperEvent event) { } } + public void cancelLongPressEvents() { + cancelLongPressDates(); + } + private void checkLongPress() { TouchFingerInfo finger = null; diff --git a/smartglapp/build.gradle b/smartglapp/build.gradle index 9cdf4fe..6977972 100644 --- a/smartglapp/build.gradle +++ b/smartglapp/build.gradle @@ -1,15 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 24 - buildToolsVersion "24.0.0" + compileSdkVersion 34 defaultConfig { applicationId "fr.arnaudguyon.smartglapp" minSdkVersion 14 - targetSdkVersion 24 - versionCode 12 - versionName "1.0.0" + targetSdkVersion 34 + versionCode 15 + versionName "1.0.3" } buildTypes { release { @@ -17,10 +16,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + namespace 'fr.arnaudguyon.smartglapp' } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:appcompat-v7:24.0.0' - compile project(':smartgl') + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.annotation:annotation:1.9.1' + implementation project(':smartgl') } diff --git a/smartglapp/src/main/AndroidManifest.xml b/smartglapp/src/main/AndroidManifest.xml index d193c9a..efedbb2 100644 --- a/smartglapp/src/main/AndroidManifest.xml +++ b/smartglapp/src/main/AndroidManifest.xml @@ -1,14 +1,20 @@ - + + + - + diff --git a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/GLViewController.java b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/GLViewController.java index 121dbf7..3daf72b 100644 --- a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/GLViewController.java +++ b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/GLViewController.java @@ -17,9 +17,14 @@ import android.content.Context; +import androidx.annotation.NonNull; + +import fr.arnaudguyon.smartgl.math.Vector3D; +import fr.arnaudguyon.smartgl.opengl.LightParallel; import fr.arnaudguyon.smartgl.opengl.Object3D; import fr.arnaudguyon.smartgl.opengl.RenderPassObject3D; import fr.arnaudguyon.smartgl.opengl.RenderPassSprite; +import fr.arnaudguyon.smartgl.opengl.SmartColor; import fr.arnaudguyon.smartgl.opengl.SmartGLRenderer; import fr.arnaudguyon.smartgl.opengl.SmartGLView; import fr.arnaudguyon.smartgl.opengl.SmartGLViewController; @@ -36,17 +41,32 @@ public class GLViewController implements SmartGLViewController { private Sprite mSprite; private Object3D mObject3D; - private float mRandomSpeed; + private float mRandomRotationSpeed; private float mSpeedX = 200; private float mSpeedY = 200; private Texture mSpriteTexture; private Texture mObjectTexture; + private Texture mSpaceFrigateTexture; + private Texture mSpaceCruiserTexture; + + private RenderPassObject3D mRenderPassObject3D; + private RenderPassObject3D mRenderPassObject3DColor; + private RenderPassSprite mRenderPassSprite; + + private Object3D mBus; + private Object3D mCube; + private Object3D mFrigate; + private Object3D mCruiser; + private Object3D mEarth; + + private Object3D mNextObject = null; + private Object3D mNextObjectColor = null; public GLViewController() { - mRandomSpeed = (float) ((Math.random() * 50) + 100); + mRandomRotationSpeed = (float) ((Math.random() * 50) + 100); if (Math.random() > 0.5f) { - mRandomSpeed *= -1; + mRandomRotationSpeed *= -1; } } @@ -57,149 +77,232 @@ public void onPrepareView(SmartGLView smartGLView) { // Add RenderPass for Sprites & Object3D SmartGLRenderer renderer = smartGLView.getSmartGLRenderer(); - RenderPassObject3D renderPassObject3D = new RenderPassObject3D(); - RenderPassSprite renderPassSprite = new RenderPassSprite(); - renderer.addRenderPass(renderPassObject3D); - renderer.addRenderPass(renderPassSprite); + mRenderPassObject3D = new RenderPassObject3D(RenderPassObject3D.ShaderType.SHADER_TEXTURE_LIGHTS, true, true); + mRenderPassObject3DColor = new RenderPassObject3D(RenderPassObject3D.ShaderType.SHADER_COLOR_LIGHTS, true, false); + mRenderPassSprite = new RenderPassSprite(); + renderer.addRenderPass(mRenderPassObject3D); + renderer.addRenderPass(mRenderPassObject3DColor); + renderer.addRenderPass(mRenderPassSprite); + + renderer.setDoubleSided(true); + + SmartColor lightColor = new SmartColor(1, 1, 1); + Vector3D lightDirection = new Vector3D(0.2f, -1, -1); + lightDirection.normalize(); + LightParallel lightParallel = new LightParallel(lightColor, lightDirection); + renderer.setLightParallel(lightParallel); mSpriteTexture = new Texture(context, R.drawable.planet); + mObjectTexture = new Texture(context, R.drawable.coloredbg); + mSpaceFrigateTexture = new Texture(context, R.drawable.space_frigate_6_color); + mSpaceCruiserTexture = new Texture(context, R.drawable.space_cruiser_4_color); + mSprite = new Sprite(120, 120); mSprite.setPivot(0.5f, 0.5f); mSprite.setPos(60, 60); mSprite.setTexture(mSpriteTexture); mSprite.setDisplayPriority(20); - renderPassSprite.addSprite(mSprite); - - mObjectTexture = new Texture(context, R.drawable.coloredbg); + mRenderPassSprite.addSprite(mSprite); -// // SPACE CRUISER 4 (734 faces) -// Texture mSpaceCruiser = new Texture(context, R.drawable.space_cruiser_4_color); -// WavefrontModel model = new WavefrontModel.Builder(context, R.raw.space_cruiser_obj) -// .addTexture("", mSpaceCruiser) -// .create(); -// mObject3D = model.toObject3D(); -// mObject3D.setScale(0.1f, 0.1f, 0.1f); -// mObject3D.setPos(0, 0, -7); -// renderPassObject3D.addObject(mObject3D); + mBus = loadBus(context); + mCube = loadCube(context); + mFrigate = loadFrigate(context); + mCruiser = loadCruiser(context); + mEarth = loadEarth(context); - // SPACE FRIGATE 6 ( faces) - Texture mSpaceCruiser = new Texture(context, R.drawable.space_frigate_6_color); - WavefrontModel model = new WavefrontModel.Builder(context, R.raw.space_frigate_obj) - .addTexture("", mSpaceCruiser) - .create(); - mObject3D = model.toObject3D(); - mObject3D.setScale(0.1f, 0.1f, 0.1f); - mObject3D.setPos(0, 0, -7); - renderPassObject3D.addObject(mObject3D); - -// // CUBE (6 faces) -// WavefrontModel model = new WavefrontModel.Builder(context, R.raw.cube_obj) -// .addTexture("Material.001", mObjectTexture) -// .create(); -// mObject3D = model.toObject3D(); -// mObject3D.setPos(0, 0, -5); -// renderPassObject3D.addObject(mObject3D); - -// // SPACESHIP (116 faces) -// WavefrontModel model = new WavefrontModel.Builder(context, R.raw.spaceship_obj) -// .addTexture("Base", mObjectTexture) -// .addTexture("Black", mSpriteTexture) -// .create(); -// mObject3D = model.toObject3D(); -// mObject3D.setPos(0, 0, -8); -// renderPassObject3D.addObject(mObject3D); - -// // BUS (2794 faces) -// WavefrontModel model = new WavefrontModel.Builder(context, R.raw.bus_obj) -// .addTexture("Mat_1", mObjectTexture) -// .addTexture("Mat_2", mSpriteTexture) -// .addTexture("Mat_3", mObjectTexture) -// .addTexture("Mat_4", mSpriteTexture) -// .addTexture("Mat_5", mObjectTexture) -// .addTexture("Mat_6", mSpriteTexture) -// .create(); -// mObject3D = model.toObject3D(); -// mObject3D.setScale(0.1f, 0.1f, 0.1f); -// mObject3D.setPos(0, 0, -50); -// renderPassObject3D.addObject(mObject3D); + //renderer.setDoubleSided(true); + switchToFrigate(); } - @Override - public void onReleaseView (SmartGLView smartGLView){ - if (mSpriteTexture != null) { - mSpriteTexture.release(); - } - if (mObjectTexture != null) { - mObjectTexture.release(); - } + @Override + public void onReleaseView(SmartGLView smartGLView) { + if (mSpriteTexture != null) { + mSpriteTexture.release(); + mSpriteTexture = null; + } + if (mObjectTexture != null) { + mObjectTexture.release(); + mObjectTexture = null; + } + if (mSpaceFrigateTexture != null) { + mSpaceFrigateTexture.release(); + mSpaceFrigateTexture = null; } + if (mSpaceCruiserTexture != null) { + mSpaceCruiserTexture.release(); + mSpaceCruiserTexture = null; + } + } - @Override - public void onResizeView (SmartGLView smartGLView){ + @Override + public void onResizeView(SmartGLView smartGLView) { // onReleaseView(smartGLView); // onPrepareView(smartGLView); + } + + @Override + public void onTick(SmartGLView smartGLView) { + + Object3D next = mNextObject; + Object3D nextColor = mNextObjectColor; + if (next != null) { + dropAllObject3D(); + mRenderPassObject3D.addObject(mNextObject); + mObject3D = mNextObject; + mNextObject = null; + } else if (nextColor != null) { + dropAllObject3D(); + mRenderPassObject3DColor.addObject(mNextObjectColor); + mObject3D = mNextObjectColor; + mNextObjectColor = null; } - @Override - public void onTick (SmartGLView smartGLView){ - SmartGLRenderer renderer = smartGLView.getSmartGLRenderer(); - float frameDuration = renderer.getFrameDuration(); - if (mSprite != null) { - - float angle = mSprite.getRotation() + frameDuration * mRandomSpeed; - mSprite.setRotation(angle); - - float x = mSprite.getPosX() + frameDuration * mSpeedX; - float y = mSprite.getPosY() + frameDuration * mSpeedY; - if (x < mSprite.getWidth() / 2) { - x = mSprite.getWidth() / 2; - mSpeedX = -mSpeedX; - if (mSpeedY > 0) { - mRandomSpeed = Math.abs(mRandomSpeed); - } else { - mRandomSpeed = -Math.abs(mRandomSpeed); - } - } else if (x + mSprite.getWidth() / 2 >= smartGLView.getWidth()) { - x = smartGLView.getWidth() - mSprite.getWidth() / 2; - mSpeedX = -mSpeedX; - if (mSpeedY > 0) { - mRandomSpeed = -Math.abs(mRandomSpeed); - } else { - mRandomSpeed = Math.abs(mRandomSpeed); - } + SmartGLRenderer renderer = smartGLView.getSmartGLRenderer(); + float frameDuration = renderer.getFrameDuration(); + if (mSprite != null) { + + float angle = mSprite.getRotation() + frameDuration * mRandomRotationSpeed; + mSprite.setRotation(angle); + + float x = mSprite.getPosX() + frameDuration * mSpeedX; + float y = mSprite.getPosY() + frameDuration * mSpeedY; + if (x < mSprite.getWidth() / 2) { + x = mSprite.getWidth() / 2; + mSpeedX = -mSpeedX; + if (mSpeedY > 0) { + mRandomRotationSpeed = Math.abs(mRandomRotationSpeed); + } else { + mRandomRotationSpeed = -Math.abs(mRandomRotationSpeed); } - if (y < mSprite.getHeight() / 2) { - y = mSprite.getHeight() / 2; - mSpeedY = -mSpeedY; - if (mSpeedX > 0) { - mRandomSpeed = -Math.abs(mRandomSpeed); - } else { - mRandomSpeed = Math.abs(mRandomSpeed); - } - } else if (y + mSprite.getHeight() / 2 >= smartGLView.getHeight()) { - y = smartGLView.getHeight() - mSprite.getHeight() / 2; - mSpeedY = -mSpeedY; - if (mSpeedX > 0) { - mRandomSpeed = Math.abs(mRandomSpeed); - } else { - mRandomSpeed = -Math.abs(mRandomSpeed); - } + } else if (x + mSprite.getWidth() / 2 >= smartGLView.getWidth()) { + x = smartGLView.getWidth() - mSprite.getWidth() / 2; + mSpeedX = -mSpeedX; + if (mSpeedY > 0) { + mRandomRotationSpeed = -Math.abs(mRandomRotationSpeed); + } else { + mRandomRotationSpeed = Math.abs(mRandomRotationSpeed); } - mSprite.setPos(x, y); } - - if (mObject3D != null) { - float rx = mObject3D.getRotX() + 50 * frameDuration; - float ry = mObject3D.getRotY() + 37 * frameDuration; - float rz = mObject3D.getRotZ() + 26 * frameDuration; - mObject3D.setRotation(rx, ry, rz); + if (y < mSprite.getHeight() / 2) { + y = mSprite.getHeight() / 2; + mSpeedY = -mSpeedY; + if (mSpeedX > 0) { + mRandomRotationSpeed = -Math.abs(mRandomRotationSpeed); + } else { + mRandomRotationSpeed = Math.abs(mRandomRotationSpeed); + } + } else if (y + mSprite.getHeight() / 2 >= smartGLView.getHeight()) { + y = smartGLView.getHeight() - mSprite.getHeight() / 2; + mSpeedY = -mSpeedY; + if (mSpeedX > 0) { + mRandomRotationSpeed = Math.abs(mRandomRotationSpeed); + } else { + mRandomRotationSpeed = -Math.abs(mRandomRotationSpeed); + } } - + mSprite.setPos(x, y); } - @Override - public void onTouchEvent (SmartGLView smartGLView, TouchHelperEvent event){ + if (mObject3D != null) { + float rx = mObject3D.getRotX() + 50 * frameDuration; + float ry = mObject3D.getRotY() + 37 * frameDuration; + float rz = mObject3D.getRotZ() + 26 * frameDuration; + mObject3D.setRotation(rx, ry, rz); + //mObject3D.setPos(mObject3D.getPosX() + 0.01f, mObject3D.getPosY(), mObject3D.getPosZ()); } + + } + + @Override + public void onTouchEvent(SmartGLView smartGLView, TouchHelperEvent event) { + } + + private void dropAllObject3D() { + mRenderPassObject3D.clearObjects(); + mRenderPassObject3DColor.clearObjects(); + } + + private Object3D loadCruiser(@NonNull Context context) { + WavefrontModel model = new WavefrontModel.Builder(context, R.raw.space_cruiser_obj) + .addTexture("", mSpaceCruiserTexture) + .create(); + +// ColladaModel model = new ColladaModel.Builder(context, R.raw.space_cruiser_collada_textured) +// .addTexture("space_cruiser_4_color_png", mSpaceCruiserTexture) // "space_cruiser_4_color_png" is defined in the space_cruiser_collada_textured +// .create(); + + Object3D object3D = model.toObject3D(); + object3D.setScale(0.2f, 0.2f, 0.2f); + object3D.setPos(0, 0, -5); + return object3D; + } + + void switchToCruiser() { + mNextObjectColor = null; + mNextObject = mCruiser; + } + + private Object3D loadFrigate(@NonNull Context context) { + WavefrontModel model = new WavefrontModel.Builder(context, R.raw.space_frigate_obj) + .addTexture("", mSpaceFrigateTexture) + .create(); + Object3D object3D = model.toObject3D(); + object3D.setScale(0.2f, 0.2f, 0.2f); + object3D.setPos(0, 0, -7); + return object3D; } + + void switchToFrigate() { + mNextObjectColor = null; + mNextObject = mFrigate; + } + + private Object3D loadCube(@NonNull Context context) { + WavefrontModel modelColored = new WavefrontModel.Builder(context, R.raw.cube_color_obj) + .create(); + Object3D object3D = modelColored.toObject3D(); + object3D.setPos(0, 0, -4); + return object3D; + } + + void switchToCube() { + mNextObject = null; + mNextObjectColor = mCube; + } + + private Object3D loadEarth(@NonNull Context context) { + WavefrontModel modelColored = new WavefrontModel.Builder(context, R.raw.earth_obj) + .setColor(0.2f, 0.6f, 1) + .create(); + Object3D object3D = modelColored.toObject3D(); + object3D.setPos(0, 0, -4); + return object3D; + } + void switchToEarth() { + mNextObject = null; + mNextObjectColor = mEarth; + } + + private Object3D loadBus(@NonNull Context context) { + WavefrontModel model = new WavefrontModel.Builder(context, R.raw.bus_obj) + .addTexture("Mat_1", mObjectTexture) + .addTexture("Mat_2", mSpriteTexture) + .addTexture("Mat_3", mObjectTexture) + .addTexture("Mat_4", mSpriteTexture) + .addTexture("Mat_5", mObjectTexture) + .addTexture("Mat_6", mSpriteTexture) + .create(); + Object3D object3D = model.toObject3D(); + object3D.setScale(0.1f, 0.1f, 0.1f); + object3D.setPos(0, 0, -50); + return object3D; + } + + void switchToBus() { + mNextObjectColor = null; + mNextObject = mBus; + } + +} diff --git a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainActivity.java b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainActivity.java index c9369c9..87b71aa 100644 --- a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainActivity.java +++ b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainActivity.java @@ -15,12 +15,17 @@ */ package fr.arnaudguyon.smartglapp; -import android.support.v7.app.AppCompatActivity; +import android.app.Activity; +import android.graphics.Bitmap; import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import fr.arnaudguyon.smartgl.opengl.OpenGLRenderer; +import fr.arnaudguyon.smartgl.opengl.SmartGLRenderer; import fr.arnaudguyon.smartgl.opengl.SmartGLView; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends Activity { private SmartGLView mActivityGLView; @@ -33,6 +38,14 @@ protected void onCreate(Bundle savedInstanceState) { mActivityGLView = (SmartGLView) findViewById(R.id.activityGLView); mActivityGLView.setDefaultRenderer(this); mActivityGLView.setController(new GLViewController()); + + View screenshotButton = findViewById(R.id.screenshotButton); + screenshotButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + takeScreenshot(); + } + }); } @Override @@ -50,4 +63,17 @@ protected void onResume() { mActivityGLView.onResume(); } } + + private void takeScreenshot() { + + SmartGLRenderer renderer = mActivityGLView.getSmartGLRenderer(); + renderer.takeScreenshot(new OpenGLRenderer.OnTakeScreenshot() { + @Override + public void screenshotTaken(Bitmap bitmap) { + ImageView screenshotView = (ImageView) findViewById(R.id.screenshotView); + screenshotView.setImageBitmap(bitmap); + } + }); + + } } diff --git a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainGLFragment.java b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainGLFragment.java index 62c344c..e80567a 100644 --- a/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainGLFragment.java +++ b/smartglapp/src/main/java/fr/arnaudguyon/smartglapp/MainGLFragment.java @@ -15,15 +15,16 @@ */ package fr.arnaudguyon.smartglapp; +import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import fr.arnaudguyon.smartgl.opengl.OpenGLCamera; +import androidx.annotation.Nullable; + +import fr.arnaudguyon.smartgl.opengl.LightAmbiant; import fr.arnaudguyon.smartgl.opengl.SmartGLView; /** @@ -45,12 +46,48 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { final Context context = view.getContext(); mFragmentGLView = (SmartGLView) view.findViewById(R.id.fragmentGLView); mFragmentGLView.setDefaultRenderer(context); - mFragmentGLView.setController(new GLViewController()); + final GLViewController controller = new GLViewController(); + mFragmentGLView.setController(controller); + + mFragmentGLView.getSmartGLRenderer().setClearColor(0.01f, 0.01f, 0.1f, 1); + + LightAmbiant lightAmbiant = new LightAmbiant(0.3f, 0.3f, 0.3f); + mFragmentGLView.getSmartGLRenderer().setLightAmbiant(lightAmbiant); + + view.findViewById(R.id.cruiserButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + controller.switchToCruiser(); + } + }); + + view.findViewById(R.id.fregateButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + controller.switchToFrigate(); + } + }); + + view.findViewById(R.id.busButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + controller.switchToBus(); + } + }); + + view.findViewById(R.id.cubeButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + controller.switchToCube(); + } + }); - OpenGLCamera camera = mFragmentGLView.getSmartGLRenderer().getCamera(); - camera.setFOV(90); - camera.setPosition(0, 0, -4); - camera.setRotation(0, 0, 20f); + view.findViewById(R.id.earthButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + controller.switchToEarth(); + } + }); } diff --git a/smartglapp/src/main/res/layout/activity_main.xml b/smartglapp/src/main/res/layout/activity_main.xml index b0e0339..e4701dd 100644 --- a/smartglapp/src/main/res/layout/activity_main.xml +++ b/smartglapp/src/main/res/layout/activity_main.xml @@ -1,25 +1,46 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/colorAccent" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context="fr.arnaudguyon.smartglapp.MainActivity"> + android:text="ACTIVITY GL VIEW"/> - + android:layout_height="wrap_content" + android:orientation="horizontal"> + + + + + + + +