From 23c9cc6cea65dcc61db912be262e3ef217919fe2 Mon Sep 17 00:00:00 2001 From: Nola Donato Date: Tue, 17 Jul 2018 03:04:37 -0700 Subject: [PATCH 1/3] Optimize animation data structure Use an array of floats for key data instead of separate vectors and quaternions. Update importer to use new APIs. --- .../java/org/gearvrf/GVRJassimpAdapter.java | 4 +- .../gearvrf/animation/GVRMorphAnimation.java | 2 +- .../keyframe/GVRAnimationChannel.java | 373 ++++++------------ .../animation/keyframe/GVRFloatAnimation.java | 158 +++++++- .../keyframe/GVRKeyFrameAnimation.java | 2 +- .../animation/keyframe/GVRQuatAnimation.java | 103 +++++ 6 files changed, 376 insertions(+), 266 deletions(-) create mode 100644 GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRQuatAnimation.java diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java index 5488d8990..e6faf52fe 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java @@ -365,7 +365,7 @@ private GVRAnimationChannel createAnimChannel(AiNodeAnim aiNodeAnim) { int i; for (i = 0; i < aiNodeAnim.getNumPosKeys(); ++i) { float[] pos = aiNodeAnim.getPosKeyVector(i, sWrapperProvider); - node.setPosKeyVector(i, (float)aiNodeAnim.getPosKeyTime(i), pos[0], pos[1], pos[2]); + node.setPosKeyVector(i, (float)aiNodeAnim.getPosKeyTime(i), pos); } // Rot keys @@ -377,7 +377,7 @@ private GVRAnimationChannel createAnimChannel(AiNodeAnim aiNodeAnim) { // Scale keys for (i = 0; i < aiNodeAnim.getNumScaleKeys(); ++i) { float[] scale = aiNodeAnim.getScaleKeyVector(i, sWrapperProvider); - node.setScaleKeyVector(i, (float)aiNodeAnim.getScaleKeyTime(i), scale[0], scale[1], scale[2]); + node.setScaleKeyVector(i, (float)aiNodeAnim.getScaleKeyTime(i), scale); } return node; diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/GVRMorphAnimation.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/GVRMorphAnimation.java index 1cf4dae57..036a22099 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/GVRMorphAnimation.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/GVRMorphAnimation.java @@ -33,7 +33,7 @@ public GVRMorphAnimation(GVRMeshMorph target, float[] keyData, int keySize) public void animate(GVRHybridObject object, float animationTime) { GVRMeshMorph morph = (GVRMeshMorph) mTarget; - mKeyInterpolator.animate(animationTime, mCurrentValues); + mKeyInterpolator.animate(animationTime * mDuration, mCurrentValues); morph.setWeights(mCurrentValues); } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRAnimationChannel.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRAnimationChannel.java index 6df6f7b5a..b78f471a3 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRAnimationChannel.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRAnimationChannel.java @@ -6,133 +6,25 @@ import org.joml.Quaternionf; import org.joml.Vector3f; -/** +/** * Describes the animation of a single node.

- * - * The node name ({@link #getNodeName()} specifies the bone/node which is - * affected by this animation channel. The keyframes are given in three - * separate series of values, one each for position, rotation and scaling. - * The transformation matrix computed from these values replaces the node's + * + * The node name ({@link #getNodeName()} specifies the bone/node which is + * affected by this animation channel. The keyframes are given in three + * separate series of values, one each for position, rotation and scaling. + * The transformation matrix computed from these values replaces the node's * original transformation matrix at a specific time.

- * + * * This means all keys are absolute and not relative to the bone default pose. - * The order in which the transformations are applied is - as usual - + * The order in which the transformations are applied is - as usual - * scaling, rotation, translation. */ public final class GVRAnimationChannel implements PrettyPrint { private static final String TAG = GVRAnimationChannel.class.getSimpleName(); - protected static interface ValueInterpolator { - T interpolate(T begin, T end, float factor); - } - - protected static ValueInterpolator sInterpolatorVector3f = new ValueInterpolator() { - public Vector3f interpolate(Vector3f begin, Vector3f end, float factor) { - return new Vector3f().set(end).sub(begin).mul(factor).add(begin); - } - }; - - protected static ValueInterpolator sInterpolatorQuaternion = new ValueInterpolator() { - public Quaternionf interpolate(Quaternionf begin, Quaternionf end, float factor) { - return new Quaternionf().set(begin).slerp(end, factor); - } - }; - - protected class KeyFrameInterplator { - GVRKeyFrame[] keys; - ValueInterpolator interpolator; - - private int lastKeyIndex; - - KeyFrameInterplator(GVRKeyFrame[] keys, ValueInterpolator interpolator) { - this.keys = keys; - this.interpolator = interpolator; - lastKeyIndex = -1; - } - - protected T interpolate(float time) { - int index = getKeyIndex(time); - int nextIndex = index + 1; - - if (index != -1 && keys[index].getTime() <= time && time < keys[nextIndex].getTime()) { - // interpolate - float deltaTime = (float)(keys[nextIndex].getTime() - keys[index].getTime()); - float factor = (float)((time - keys[index].getTime()) / deltaTime); - - T start = keys[index].getValue(); - T end = keys[nextIndex].getValue(); - - return interpolator.interpolate(start, end, factor); - } else { - // time is out of range of animation time frame - float firstFrameTime = keys[0].getTime(); - float lastFrameTime = keys[keys.length - 1].getTime(); - T firstFrameValue = keys[0].getValue(); - T lastFrameValue = keys[keys.length - 1].getValue(); - - if (time <= firstFrameTime) { - return firstFrameValue; - } else if (time >= lastFrameTime) { - return lastFrameValue; - } else { - // Shouldn't happen - return lastFrameValue; - } - } - } - - protected int getKeyIndex(float time) { - // Try cached key first - if (lastKeyIndex != -1) { - if (keys[lastKeyIndex].getTime() <= time && time < keys[lastKeyIndex + 1].getTime()) { - return lastKeyIndex; - } - - // Try neighboring keys - if (lastKeyIndex + 2 < keys.length && - keys[lastKeyIndex + 1].getTime() <= time && time < keys[lastKeyIndex + 2].getTime()) { - return ++lastKeyIndex; - } - - if (lastKeyIndex >= 1 && - keys[lastKeyIndex - 1].getTime() <= time && time < keys[lastKeyIndex].getTime()) { - return --lastKeyIndex; - } - } - - // Binary search for the interval - // Each of the index i represents an interval I(i) = [time(i), time(i + 1)). - int low = 0, high = keys.length - 2; - // invariant: I(low)...I(high) contains time if time can be found - // post-condition: |high - low| <= 1, only need to check I(low) and I(low + 1) - while (high - low > 1) { - int mid = (low + high) / 2; - if (time < keys[mid].getTime()) { - high = mid; - } else if (time >= keys[mid + 1].getTime()) { - low = mid + 1; - } else { - // time in I(mid) by definition - return lastKeyIndex = mid; - } - } - - if (keys[low].getTime() <= time && time < keys[low + 1].getTime()) { - return lastKeyIndex = low; - } - - if (low + 2 < keys.length && - keys[low + 1].getTime() <= time && time < keys[low + 2].getTime()) { - return lastKeyIndex = low + 1; - } - - Log.v(TAG, "Warning: interpolation failed at time " + time); - return lastKeyIndex = -1; - } - } /** * Constructor. - * + * * @param nodeName name of corresponding scene graph node * @param numPosKeys number of position keys * @param numRotKeys number of rotation keys @@ -140,29 +32,23 @@ protected int getKeyIndex(float time) { * @param preBehavior behavior before animation start * @param postBehavior behavior after animation end */ - public GVRAnimationChannel(String nodeName, int numPosKeys, int numRotKeys, - int numScaleKeys, GVRAnimationBehavior preBehavior, GVRAnimationBehavior postBehavior) { + public GVRAnimationChannel(String nodeName, int numPosKeys, int numRotKeys, + int numScaleKeys, GVRAnimationBehavior preBehavior, GVRAnimationBehavior postBehavior) { m_nodeName = nodeName; - mPositionKeys = new GVRPositionKey[numPosKeys]; - mRotationKeys = new GVRRotationKey[numRotKeys]; - mScaleKeys = new GVRScaleKey[numScaleKeys]; + mPosInterpolator = new GVRFloatAnimation(numPosKeys, 4); + mRotInterpolator = new GVRQuatAnimation(numRotKeys); + mSclInterpolator = new GVRFloatAnimation(numScaleKeys, 4); mPreState = preBehavior; mPostState = postBehavior; - - mPositionInterpolator = new KeyFrameInterplator(mPositionKeys, sInterpolatorVector3f); - mRotationInterpolator = new KeyFrameInterplator(mRotationKeys, sInterpolatorQuaternion); - mScaleInterpolator = new KeyFrameInterplator(mScaleKeys, sInterpolatorVector3f); - - mCurrentTransform = new Matrix4f(); } - /** + /** * Returns the name of the scene graph node affected by this animation.

- * + * * The node must exist and it must be unique. - * + * * @return the name of the affected node */ public String getNodeName() { @@ -170,133 +56,181 @@ public String getNodeName() { } - /** - * Returns the number of position keys. + /** + * Resize the position keys. + * This function will truncate the position keys if the + * initial setting was too large. * + * @oaran numPosKeys the desired size for the position keys + */ + public void resizePosKeys(int numPosKeys) + { + mPosInterpolator.resizeKeys(numPosKeys); + } + + /** + * Returns the number of position keys. + * * @return the number of position keys */ public int getNumPosKeys() { - return mPositionKeys.length; + return mPosInterpolator.getNumKeys(); } - + /** - * Returns the time component of the specified position key. - * + * Returns the time component of the specified position key. + * * @param keyIndex the index of the position key * @return the time component */ - public double getPosKeyTime(int keyIndex) { - return mPositionKeys[keyIndex].getTime(); + public float getPosKeyTime(int keyIndex) { + return mPosInterpolator.getTime(keyIndex); } /** * Returns the position as vector.

- * + * * @param keyIndex the index of the position key - * + * * @return the position as vector */ - public Vector3f getPosKeyVector(int keyIndex) { - return mPositionKeys[keyIndex].getValue(); + public void getPosKeyVector(int keyIndex, float[] pos) + { + mPosInterpolator.getKey(keyIndex, pos); + } + + public void setPosKeyVector(int keyIndex, float time, final float[] pos) + { + mPosInterpolator.setKey(keyIndex, time, pos); } - public void setPosKeyVector(int keyIndex, float time, final float x, final float y, final float z) { - mPositionKeys[keyIndex] = new GVRPositionKey(time, x, y, z); + public void setPosKeyVector(int keyIndex, float time, float x, float y, float z) + { + mPosInterpolator.setKey(keyIndex, time, new float[] { x, y, z }); } - /** + /** * Returns the number of rotation keys. - * + * * @return the number of rotation keys */ public int getNumRotKeys() { - return mRotationKeys.length; + return mRotInterpolator.getNumKeys(); } + /** + * Resize the rotation keys. + * This function will truncate the rotation keys if the + * initial setting was too large. + * + * @oaran numRotKeys the desired size for the rotation keys + */ + public void resizeRotKeys(int numRotKeys) + { + mRotInterpolator.resizeKeys(numRotKeys); + } /** - * Returns the time component of the specified rotation key. - * + * Returns the time component of the specified rotation key. + * * @param keyIndex the index of the position key * @return the time component */ - public double getRotKeyTime(int keyIndex) { - return mRotationKeys[keyIndex].getTime(); + public float getRotKeyTime(int keyIndex) { + return mRotInterpolator.getTime(keyIndex); } /** * Returns the rotation as quaternion.

- * - * + * + * * @param keyIndex the index of the rotation key - * + * * @return the rotation as quaternion */ - public Quaternionf getRotKeyQuaternion(int keyIndex) { - return mRotationKeys[keyIndex].getValue(); - } + public void getRotKeyQuaternion(int keyIndex, float[] rot) + { + mRotInterpolator.getKey(keyIndex, rot); + } - public void setRotKeyQuaternion(int keyIndex, float time, Quaternionf rot) { - mRotationKeys[keyIndex] = new GVRRotationKey(time, rot.x, rot.y, rot.z, rot.w); + public void setRotKeyQuaternion(int keyIndex, float time, float[] rot) + { + mRotInterpolator.setKey(keyIndex, time, rot); } - /** + public void setRotKeyQuaternion(int keyIndex, float time, Quaternionf rot) + { + mRotInterpolator.setKey(keyIndex, time, rot); + } + + /** * Returns the number of scaling keys. - * + * * @return the number of scaling keys */ public int getNumScaleKeys() { - return mScaleKeys.length; + return mSclInterpolator.getNumKeys(); } + /** + * Resize the scale keys. + * This function will truncate the scale keys if the + * initial setting was too large. + * + * @oaran numScaleKeys the desired size for the position keys + */ + public void resizeScaleKeys(int numScaleKeys) + { + mSclInterpolator.resizeKeys(numScaleKeys); + } /** - * Returns the time component of the specified scaling key. - * + * Returns the time component of the specified scaling key. + * * @param keyIndex the index of the position key * @return the time component */ public double getScaleKeyTime(int keyIndex) { - return mRotationKeys[keyIndex].getTime(); + return mRotInterpolator.getTime(keyIndex); } - /** * Returns the scaling factor as vector.

- * + * * @param keyIndex the index of the scale key - * + * * @return the scaling factor as vector */ - public Vector3f getScaleKeyVector(int keyIndex) { - return mScaleKeys[keyIndex].getValue(); + public void getScaleKeyVector(int keyIndex, float[] scale) { + mSclInterpolator.getKey(keyIndex, scale); } - public void setScaleKeyVector(int keyIndex, float time, final float x, final float y, final float z) { - mScaleKeys[keyIndex] = new GVRScaleKey(time, x, y, z); + public void setScaleKeyVector(int keyIndex, float time, final float[] scale) + { + mSclInterpolator.setKey(keyIndex, time, scale); } - /** + /** * Defines how the animation behaves before the first key is encountered. *

* * The default value is {@link org.gearvrf.jassimp.AiAnimBehavior#DEFAULT} (the original * transformation matrix of the affected node is used). - * + * * @return the animation behavior before the first key */ public GVRAnimationBehavior getPreState() { return mPreState; } - - /** + + /** * Defines how the animation behaves after the last key was processed.

* * The default value is {@link org.gearvrf.jassimp.AiAnimBehavior#DEFAULT} (the original * transformation matrix of the affected node is taken). - * + * * @return the animation behavior before after the last key */ public GVRAnimationBehavior getPostState() { @@ -305,58 +239,17 @@ public GVRAnimationBehavior getPostState() { /** * Obtains the transform for a specific time in animation. - * + * * @param animationTime The time in animation. - * + * * @return The transform. */ - public Matrix4f animate(float animationTime) { - Vector3f scale = getScale(animationTime); - Vector3f pos = getPosition(animationTime); - Quaternionf rot = getRotation(animationTime); - - // Allocation-free - Matrix4f mat = mCurrentTransform.set(rot); - - mat.scale(scale).setTranslation(pos); - - return mat; - } - - protected Vector3f getPosition(float time) { - Vector3f defaultPosition = new Vector3f(); - - if (mPositionKeys.length == 0) { - return defaultPosition; - } else if (mPositionKeys.length == 1) { - return mPositionKeys[0].getValue(); - } - - return mPositionInterpolator.interpolate(time); - } - - protected Vector3f getScale(float time) { - Vector3f defaultScale = new Vector3f(1f, 1f, 1f); - - if (mScaleKeys.length == 0) { - return defaultScale; - } else if (mScaleKeys.length == 1) { - return mScaleKeys[0].getValue(); - } - - return mScaleInterpolator.interpolate(time); - } - - protected Quaternionf getRotation(float time) { - Quaternionf defaultRotation = new Quaternionf(); - - if (mRotationKeys.length == 0) { - return defaultRotation; - } else if (mRotationKeys.length == 1) { - return mRotationKeys[0].getValue(); - } - - return mRotationInterpolator.interpolate(time); + public void animate(float animationTime, Matrix4f mat) + { + mRotInterpolator.animate(animationTime, mRotKey); + mPosInterpolator.animate(animationTime, mPosKey); + mSclInterpolator.animate(animationTime, mScaleKey); + mat.translationRotateScale(mPosKey[0], mPosKey[1], mPosKey[2], mRotKey[0], mRotKey[1], mRotKey[2], mRotKey[3], mScaleKey[0], mScaleKey[1], mScaleKey[2]); } @Override @@ -364,9 +257,9 @@ public void prettyPrint(StringBuffer sb, int indent) { sb.append(Log.getSpaces(indent)); sb.append(GVRAnimationChannel.class.getSimpleName()); sb.append(" [nodeName=" + m_nodeName + ", positionKeys=" - + mPositionKeys.length + ", rotationKeys=" - + mRotationKeys.length + ", scaleKeys=" - + mScaleKeys.length + ", m_preState=" + mPreState + + getNumPosKeys() + ", rotationKeys=" + + getNumRotKeys() + ", scaleKeys=" + + getNumScaleKeys() + ", m_preState=" + mPreState + ", m_postState=" + mPostState + "]"); sb.append(System.lineSeparator()); } @@ -382,16 +275,13 @@ public String toString() { * Node name. */ private final String m_nodeName; - - private final GVRPositionKey[] mPositionKeys; - private final GVRRotationKey[] mRotationKeys; - private final GVRScaleKey[] mScaleKeys; - - private final KeyFrameInterplator mPositionInterpolator; - private final KeyFrameInterplator mRotationInterpolator; - private final KeyFrameInterplator mScaleInterpolator; - - protected Matrix4f mCurrentTransform; + final private float[] mPosKey = new float[] { 0, 0, 0 }; + final private float[] mScaleKey = new float[] { 1, 1, 1 }; + final private float[] mRotKey = new float[] { 0, 0, 0, 1 }; + final private Quaternionf mTempQuat = new Quaternionf(0, 0, 0, 1); + final private GVRFloatAnimation mPosInterpolator; + final private GVRQuatAnimation mRotInterpolator; + final private GVRFloatAnimation mSclInterpolator; /** * Pre-animation behavior. @@ -402,5 +292,4 @@ public String toString() { * Post-animation behavior. */ private final GVRAnimationBehavior mPostState; -} - +} \ No newline at end of file diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRFloatAnimation.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRFloatAnimation.java index 1d883d9dd..86abfb486 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRFloatAnimation.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRFloatAnimation.java @@ -2,30 +2,27 @@ import org.gearvrf.PrettyPrint; import org.gearvrf.utility.Log; -import org.joml.Matrix4f; import org.joml.Quaternionf; import org.joml.Vector3f; -/** +/** * Describes the animation of a set of floating point values. */ -public final class GVRFloatAnimation implements PrettyPrint +public class GVRFloatAnimation implements PrettyPrint { private static final String TAG = GVRFloatAnimation.class.getSimpleName(); - public static class FloatKeyInterpolator + public static class LinearInterpolator { - private final int mFloatsPerKey; - private final float[] mKeyData; - private final float mDuration; - private int mLastKeyIndex; + protected final int mFloatsPerKey; + protected int mLastKeyIndex; + protected float[] mKeyData; - public FloatKeyInterpolator(float[] keyData, int keySize) + public LinearInterpolator(float[] keyData, int keySize) { mKeyData = keyData; mFloatsPerKey = keySize; mLastKeyIndex = -1; - mDuration = keyData[keyData.length - keySize] - keyData[0];Log.d("MORPH", "numkeys = %d floats per key = %d duration = %f ", getNumKeys(), mFloatsPerKey, mDuration); } protected float[] interpolate(float time, float[] destValues) @@ -59,9 +56,11 @@ protected float[] interpolate(float time, float[] destValues) return destValues; } - public float getDuration() + float[] getKeyData() { return mKeyData; } + + void setKeyData(float[] keyData) { - return mDuration; + mKeyData = keyData; } public int getKeyOffset(int keyIndex) @@ -95,6 +94,15 @@ public float getTime(int keyIndex) return -1.0f; } + public void setTime(int keyIndex, float time) + { + int ofs = getKeyOffset(keyIndex); + if(ofs>=0) + { + mKeyData[ofs] = time; + } + } + public boolean setValues(int keyIndex, float[] values) { int ofs = getKeyOffset(keyIndex); @@ -111,9 +119,15 @@ public boolean interpolateValues(int keyIndex, float[] values, float factor) int firstOfs = getKeyOffset(keyIndex); int lastOfs = getKeyOffset(keyIndex + 1); - for (int i = 1; i < mFloatsPerKey; ++i) + if ((firstOfs < 0) || (lastOfs < 0)) { - values[i - 1] = (1.0f - factor) * mKeyData[firstOfs + i] + factor * mKeyData[lastOfs + i]; + return false; + } + ++firstOfs; + ++lastOfs; + for (int i = 0; i < mFloatsPerKey - 1; ++i) + { + values[i] = factor * mKeyData[lastOfs + i] + (1.0f - factor) * mKeyData[firstOfs + i]; } return true; } @@ -210,7 +224,9 @@ else if (time >= getTime(mid + 1)) } }; - final private FloatKeyInterpolator mFloatInterpolator; + final protected int mFloatsPerKey; + protected float[] mKeys; + protected LinearInterpolator mFloatInterpolator; /** * Constructor. @@ -228,26 +244,128 @@ public GVRFloatAnimation(float[] keyData, int keySize) { throw new IllegalArgumentException("Not enough key data"); } - mFloatInterpolator = new FloatKeyInterpolator(keyData, keySize); + mFloatsPerKey = keySize; + mKeys = keyData; + mFloatInterpolator = new LinearInterpolator(mKeys, keySize); + } + + /** + * Constructor. + * + * @param numKeys number of keys + * @param keySize number of floats per key + */ + public GVRFloatAnimation(int numKeys, int keySize) + { + if (keySize <= 2) + { + throw new IllegalArgumentException("The number of floats per key must be > 1, the key includes time"); + } + mFloatsPerKey = keySize; + mKeys = new float[numKeys * keySize]; + mFloatInterpolator = new LinearInterpolator(mKeys, keySize); + } + + /** + * Returns the number of keys. + * + * @return the number of keys + */ + public int getNumKeys() + { + return mKeys.length / mFloatsPerKey; + } + + public float getDuration() + { + if (mKeys.length > mFloatsPerKey) + { + return mKeys[mKeys.length - mFloatsPerKey] - mKeys[0]; + } + return 0; + } + + /** + * Returns the time component of the specified key. + * + * @param keyIndex the index of the position key + * @return the time component + */ + public float getTime(int keyIndex) + { + return mKeys[keyIndex * mFloatsPerKey]; + } + + /** + * Returns the key value in the given array. + * + * @param keyIndex the index of the scale key + */ + public void getKey(int keyIndex, float[] values) + { + int index = keyIndex * mFloatsPerKey; + System.arraycopy(mKeys, index + 1, values, 0, values.length); + } + + /** + * Set the time and value of the key at the given index + * @param keyIndex 0 based index of key + * @param time key time in seconds + * @param values key values + */ + public void setKey(int keyIndex, float time, final float[] values) + { + int index = keyIndex * mFloatsPerKey; + Integer valSize = mFloatsPerKey-1; + + if (values.length != valSize) + { + + throw new IllegalArgumentException("This key needs " + valSize.toString() + " float per value"); + } + mKeys[index] = time; + System.arraycopy(values, 0, mKeys, index + 1, values.length); } /** * Obtains the transform for a specific time in animation. - * + * * @param animationTime The time in animation. - * + * * @return The transform. */ public void animate(float animationTime, float[] destValues) { - mFloatInterpolator.interpolate(animationTime * mFloatInterpolator.getDuration(), destValues); + mFloatInterpolator.interpolate(animationTime, destValues); + } + + /** + * Resize the key data area. + * This function will truncate the keys if the + * initial setting was too large. + * + * @oaran numKeys the desired number of keys + */ + public void resizeKeys(int numKeys) + { + int n = numKeys * mFloatsPerKey; + if (mKeys.length == n) + { + return; + } + float[] newKeys = new float[n]; + n = Math.min(n, mKeys.length); + + System.arraycopy(mKeys, 0, newKeys, 0, n); + mKeys = newKeys; + mFloatInterpolator.setKeyData(mKeys); } @Override public void prettyPrint(StringBuffer sb, int indent) { sb.append(Log.getSpaces(indent)); sb.append(GVRFloatAnimation.class.getSimpleName()); - sb.append(" [ Key[" + mFloatInterpolator.getNumKeys() + "]"); + sb.append(" [ Keys=" + mKeys.length + "]"); sb.append(System.lineSeparator()); } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRKeyFrameAnimation.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRKeyFrameAnimation.java index 7762e22ee..b94f30236 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRKeyFrameAnimation.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRKeyFrameAnimation.java @@ -126,7 +126,7 @@ protected void animate(GVRHybridObject target, float ratio) { protected Matrix4f[] getTransforms(float animationTime) { int i = 0; for (GVRAnimationChannel channel : mChannels) { - mTransforms[i++].set(channel.animate(animationTime)); + channel.animate(animationTime, mTransforms[i++]); } return mTransforms; } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRQuatAnimation.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRQuatAnimation.java new file mode 100644 index 000000000..d66a3ac87 --- /dev/null +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/animation/keyframe/GVRQuatAnimation.java @@ -0,0 +1,103 @@ +package org.gearvrf.animation.keyframe; + +import org.gearvrf.PrettyPrint; +import org.gearvrf.utility.Log; +import org.joml.Quaternionf; + +/** + * Describes the animation of a set of floating point values. + */ +public final class GVRQuatAnimation extends GVRFloatAnimation +{ + private static final String TAG = GVRQuatAnimation.class.getSimpleName(); + + public static class SphericalInterpolator extends LinearInterpolator + { + private Quaternionf mTempQuatA = new Quaternionf(); + private Quaternionf mTempQuatB = new Quaternionf(); + + public SphericalInterpolator(float[] keyData, int keySize) + { + super(keyData, keySize); + } + + public boolean interpolateValues(int keyIndex, float[] values, float factor) + { + int firstOfs = getKeyOffset(keyIndex); + int lastOfs = getKeyOffset(keyIndex + 1); + + if ((firstOfs < 0) || (lastOfs < 0)) + { + return false; + } + ++firstOfs; + ++lastOfs; + mTempQuatA.x = mKeyData[firstOfs + 0]; + mTempQuatA.y = mKeyData[firstOfs + 1]; + mTempQuatA.z = mKeyData[firstOfs + 2]; + mTempQuatA.w = mKeyData[firstOfs + 3]; + mTempQuatB.x = mKeyData[lastOfs + 0]; + mTempQuatB.y = mKeyData[lastOfs + 1]; + mTempQuatB.z = mKeyData[lastOfs + 2]; + mTempQuatB.w = mKeyData[lastOfs + 3]; + mTempQuatA.slerp(mTempQuatB, factor, mTempQuatA); + values[0] = mTempQuatA.x; + values[1] = mTempQuatA.y; + values[2] = mTempQuatA.z; + values[3] = mTempQuatA.w; + return true; + } + }; + + /** + * Constructor. + * + * @param keyData animation key data, must be x,y,z,w (Quaterions) + */ + public GVRQuatAnimation(float[] keyData) + { + super(keyData, 5); + mFloatInterpolator = new SphericalInterpolator(mKeys, 5); + } + + /** + * Constructor. + * + * @param numKeys expected number of animation keys + */ + public GVRQuatAnimation(int numKeys) + { + super(numKeys, 5); + mFloatInterpolator = new SphericalInterpolator(mKeys, 5); + } + + /** + * Returns the scaling factor as vector.

+ * + * @param keyIndex the index of the scale key + * + * @return the scaling factor as vector + */ + public void getKey(int keyIndex, Quaternionf q) + { + int index = keyIndex * mFloatsPerKey; + q.x = mKeys[index + 1]; + q.y = mKeys[index + 2]; + q.z = mKeys[index + 3]; + q.w = mKeys[index + 4]; + } + + public void setKey(int keyIndex, float time, final Quaternionf q) + { + int index = keyIndex * mFloatsPerKey; + + mKeys[index] = time; + mKeys[index + 1] = q.x; + mKeys[index + 2] = q.y; + mKeys[index + 3] = q.z; + mKeys[index + 4] = q.w; + } + + +} + From 9e81d108ae565e4fe9f1ab56efb4f9be9a6df061 Mon Sep 17 00:00:00 2001 From: Abhijit Date: Fri, 3 Aug 2018 14:44:41 -0700 Subject: [PATCH 2/3] Added new feature to dump screenshots to SD Card --- .../java/org/gearvrf/DaydreamViewManager.java | 1 - .../main/java/org/gearvrf/OvrViewManager.java | 4 +- .../src/main/java/org/gearvrf/GVRContext.java | 2 + .../java/org/gearvrf/GVRContextProxy.java | 4 + .../java/org/gearvrf/GVRRenderBundle.java | 11 ++ .../src/main/java/org/gearvrf/GVRSticker.java | 150 ++++++++++++++++++ .../main/java/org/gearvrf/GVRViewManager.java | 116 ++++++++++++-- .../main/jni/engine/renderer/gl_renderer.cpp | 41 +++++ .../main/jni/engine/renderer/gl_renderer.h | 7 + .../src/main/jni/engine/renderer/renderer.h | 6 + .../jni/engine/renderer/vulkan_renderer.h | 3 +- .../src/main/jni/view_manager_jni.cpp | 18 +++ .../main/res/raw/vertex_template_multitex.vsh | 9 +- 13 files changed, 342 insertions(+), 30 deletions(-) create mode 100644 GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java diff --git a/GVRf/Framework/backend_daydream/src/main/java/org/gearvrf/DaydreamViewManager.java b/GVRf/Framework/backend_daydream/src/main/java/org/gearvrf/DaydreamViewManager.java index 6a673eb43..ac837e8ba 100644 --- a/GVRf/Framework/backend_daydream/src/main/java/org/gearvrf/DaydreamViewManager.java +++ b/GVRf/Framework/backend_daydream/src/main/java/org/gearvrf/DaydreamViewManager.java @@ -146,7 +146,6 @@ void onDrawEye(int eye) { mRenderBundle.getPostEffectRenderTextureB()); captureRightEye(renderTarget,false); } - captureFinish(); } void setCameraRig(GVRCameraRig cameraRig) { diff --git a/GVRf/Framework/backend_oculus/src/main/java/org/gearvrf/OvrViewManager.java b/GVRf/Framework/backend_oculus/src/main/java/org/gearvrf/OvrViewManager.java index 6c5268fcd..bf13414a3 100644 --- a/GVRf/Framework/backend_oculus/src/main/java/org/gearvrf/OvrViewManager.java +++ b/GVRf/Framework/backend_oculus/src/main/java/org/gearvrf/OvrViewManager.java @@ -196,8 +196,6 @@ void onDrawEye(int eye, int swapChainIndex, boolean use_multiview) { captureRightEye(renderTarget, true); captureLeftEye(renderTarget, true); - captureFinish(); - if (DEBUG_STATS) { mTracerDrawEyes1.leave(); mTracerDrawEyes2.leave(); @@ -217,7 +215,6 @@ void onDrawEye(int eye, int swapChainIndex, boolean use_multiview) { mRenderBundle.getPostEffectRenderTextureB()); captureRightEye(renderTarget, false); - captureFinish(); if (DEBUG_STATS) { mTracerDrawEyes1.leave(); mTracerDrawEyes.leave(); @@ -236,6 +233,7 @@ void onDrawEye(int eye, int swapChainIndex, boolean use_multiview) { renderTarget.cullFromCamera(mMainScene, mainCameraRig.getCenterCamera(), mRenderBundle.getShaderManager()); captureCenterEye(renderTarget, false); + captureSticker(); renderTarget.render(mMainScene, leftCamera, mRenderBundle.getShaderManager(), mRenderBundle.getPostEffectRenderTextureA(), mRenderBundle.getPostEffectRenderTextureB()); captureLeftEye(renderTarget, false); diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContext.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContext.java index 2f6bf56eb..70d763cf2 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContext.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContext.java @@ -584,6 +584,8 @@ protected synchronized static void resetOnRestart() { */ public abstract void captureScreenCenter(GVRScreenshotCallback callback); + abstract boolean captureSticker(GVRScreenshotCallback callback, int pboIndex); + /** * Capture a 2D screenshot from the position of left eye. * diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContextProxy.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContextProxy.java index ca4bd4d24..33538de47 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContextProxy.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRContextProxy.java @@ -127,6 +127,10 @@ public void captureScreenCenter(GVRScreenshotCallback callback) { mContext.get().captureScreenCenter(callback); } + public boolean captureSticker(GVRScreenshotCallback callback, int pboIndex) { + return mContext.get().captureSticker(callback, pboIndex); + } + public void captureScreenLeft(GVRScreenshotCallback callback) { mContext.get().captureScreenLeft(callback); } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRRenderBundle.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRRenderBundle.java index f42afd43c..3727c7e66 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRRenderBundle.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRRenderBundle.java @@ -24,6 +24,8 @@ class GVRRenderBundle { private GVRRenderTexture mPostEffectRenderTextureA = null; private GVRRenderTexture mPostEffectRenderTextureB = null; private GVRRenderTarget mEyeCaptureRenderTarget = null; + // Three render targets for sticker generation + private GVRRenderTarget mStickerRenderTarget[] = {null, null, null}; protected int mSampleCount; protected int mWidth, mHeight; @@ -92,6 +94,15 @@ public GVRRenderTarget getEyeCaptureRenderTarget() { } return mEyeCaptureRenderTarget; } + + public GVRRenderTarget getStickerRenderTarget(int index) { + if(mStickerRenderTarget[index] == null){ + mStickerRenderTarget[index] = new GVRRenderTarget(new GVRRenderTexture(mGVRContext, mWidth, mHeight, mSampleCount), mGVRContext.getMainScene()); + mStickerRenderTarget[index].setCamera(mGVRContext.getMainScene().getMainCameraRig().getCenterCamera()); + } + return mStickerRenderTarget[index]; + } + void beginRendering(int bufferIdx, GVRViewManager.EYE eye) { getRenderTexture(bufferIdx,eye).beginRendering(); } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java new file mode 100644 index 000000000..3bb5b1d90 --- /dev/null +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java @@ -0,0 +1,150 @@ +/* Copyright 2015 Samsung Electronics Co., LTD + * + * 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 org.gearvrf; + +import android.graphics.Bitmap; +import android.os.Environment; +import android.util.Log; + +import org.gearvrf.utility.Threads; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class takes screenshots at a specified interval and writes the output PNG files to GearVRFScreenshots directory. + * The capturing can be stopped by calling {@link GVRSticker#stop()} + * The images can be used to create Stickers (a GIF animation) + */ + +public class GVRSticker { + private final GVRContext mGVRContext; + private final String mTag; + private final String mDirectory; + private final long mInterval; + private boolean mCaptureFlag = true; + private final static String TAG = "GVRSticker"; + + /** + * Constructor takes the following parameters + * @param gvrContext + * @param tag + * Name for outputting PNG's to the SD Card along with appended frame ID + * @param interval + * Time in milliseconds to take screenshot at that interval + */ + public GVRSticker(GVRContext gvrContext, String tag, long interval){ + mGVRContext = gvrContext; + mTag = tag; + mInterval = interval; + + File sdcard = Environment.getExternalStorageDirectory(); + mDirectory = sdcard.getAbsolutePath() + "/GearVRFScreenshots/"; + File d = new File(mDirectory); + d.mkdirs(); + } + + private boolean lastScreenshotFinished[] = {true, true, true}; + private int pboIndex = 0; + + /** + * Initiate's the screenshot capturing + * Captures images every {@link GVRSticker#mInterval} milliseconds + */ + public void captureScreen() + { + mCaptureFlag = true; + Threads.spawn(new Runnable() + { + public void run() + { + int frame = 0; + boolean lastStickerCall; + while(mCaptureFlag) { + if (lastScreenshotFinished[pboIndex]) { + lastStickerCall = mGVRContext + .captureSticker(newScreenshotCallback(frame, pboIndex), pboIndex); + if(lastStickerCall) { + lastScreenshotFinished[pboIndex] = false; + pboIndex = (pboIndex + 1) % 3; + frame++; + } + } + else{ + Log.e(TAG, "Sticker skipped since previous read is not completed"); + } + try { + Thread.sleep(mInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }); + } + + /** + * Stop's the screenshot thread + */ + public void stop(){ + mCaptureFlag = false; + } + + private GVRScreenshotCallback newScreenshotCallback(final int frame, final int currentPboIndex) + { + return new GVRScreenshotCallback() + { + @Override + public void onScreenCaptured(Bitmap bitmap) + { + if (bitmap != null) + { + File file = new File(mDirectory + mTag +"_"+ frame +"_" + ".png"); + FileOutputStream outputStream = null; + try + { + outputStream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + } + catch (FileNotFoundException e) + { + e.printStackTrace(); + } + finally + { + try + { + outputStream.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + else + { + Log.e(TAG, "Returned Bitmap is null for frame " + frame); + } + + // enable next screenshot + lastScreenshotFinished[currentPboIndex] = true; + } + }; + } +} diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRViewManager.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRViewManager.java index caacb98d7..d348b8c2b 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRViewManager.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRViewManager.java @@ -559,6 +559,8 @@ public GVRShaderManager getShaderManager() { protected GVRScreenshotCallback mScreenshotLeftCallback; protected GVRScreenshotCallback mScreenshotRightCallback; protected GVRScreenshot3DCallback mScreenshot3DCallback; + protected GVRScreenshotCallback mStickerCallback; + protected int mPboIndex = 0; @Override public void captureScreenCenter(GVRScreenshotCallback callback) { @@ -569,6 +571,21 @@ public void captureScreenCenter(GVRScreenshotCallback callback) { } } + @Override + public boolean captureSticker(GVRScreenshotCallback callback, int pboIndex) { + // Avoids making null of previous mStickerCallback if it is not completed + if(mStickerCallback != null) + return false; + if (callback == null) { + throw new IllegalArgumentException("callback should not be null."); + } else { + mStickerCallback = callback; + mPboIndex = pboIndex; + } + + return true; + } + @Override public void captureScreenLeft(GVRScreenshotCallback callback) { if (callback == null) { @@ -593,21 +610,41 @@ public void captureScreen3D(GVRScreenshot3DCallback callback) { } protected void readRenderResult(GVRRenderTarget renderTarget, GVRViewManager.EYE eye, boolean useMultiview) { - if (mReadbackBuffer == null) { + if (mReadbackBuffer[mPboIndex] == null) { + final VrAppSettings settings = mApplication.getAppSettings(); + final VrAppSettings.EyeBufferParams eyeBufferParams = settings.getEyeBufferParams(); + mReadbackBufferWidth = eyeBufferParams.getResolutionWidth(); + mReadbackBufferHeight = eyeBufferParams.getResolutionHeight(); + + mReadbackBuffer[mPboIndex] = ByteBuffer.allocateDirect(mReadbackBufferWidth * mReadbackBufferHeight * 4); + mReadbackBuffer[mPboIndex].order(ByteOrder.nativeOrder()); + } + readRenderResultNative(mReadbackBuffer[mPboIndex],renderTarget.getNative(), eye.ordinal(), useMultiview ); + } + + protected void readRenderResultInPBO(int pboIndex) { + if (mReadbackBuffer[mPboIndex] == null) { final VrAppSettings settings = mApplication.getAppSettings(); final VrAppSettings.EyeBufferParams eyeBufferParams = settings.getEyeBufferParams(); mReadbackBufferWidth = eyeBufferParams.getResolutionWidth(); mReadbackBufferHeight = eyeBufferParams.getResolutionHeight(); - mReadbackBuffer = ByteBuffer.allocateDirect(mReadbackBufferWidth * mReadbackBufferHeight * 4); - mReadbackBuffer.order(ByteOrder.nativeOrder()); + mReadbackBuffer[pboIndex] = ByteBuffer.allocateDirect(mReadbackBufferWidth * mReadbackBufferHeight * 4); + mReadbackBuffer[pboIndex].order(ByteOrder.nativeOrder()); } - readRenderResultNative(mReadbackBuffer,renderTarget.getNative(), eye.ordinal(), useMultiview ); + + readRenderResultInPBONative(pboIndex); + } + + protected void readRenderResultFromPBO(ByteBuffer readbackBuffer, int pboIndex) { + if(readbackBuffer == null) + return; + readRenderResultFromPBONative(readbackBuffer, pboIndex); } protected void returnScreenshotToCaller(final GVRScreenshotCallback callback, final int width, final int height) { // run the callback function in a background thread - final byte[] byteArray = Arrays.copyOf(mReadbackBuffer.array(), mReadbackBuffer.array().length); + final byte[] byteArray = Arrays.copyOf(mReadbackBuffer[mPboIndex].array(), mReadbackBuffer[mPboIndex].array().length); Threads.spawn(new Runnable() { public void run() { final Bitmap capturedBitmap = ImageUtils.generateBitmapFlipV(byteArray, width, height); @@ -690,6 +727,7 @@ private void captureEye(GVRScreenshotCallback callback, GVRRenderTarget renderTa } readRenderResult(renderTarget,eye,useMultiview); returnScreenshotToCaller(callback, mReadbackBufferWidth, mReadbackBufferHeight); + mReadbackBuffer[mPboIndex] = null; } // capture center eye @@ -727,8 +765,8 @@ protected void captureCenterEye(GVRRenderTarget renderTarget, boolean isMultivie renderTarget.endRendering(); final Bitmap bitmap = Bitmap.createBitmap(mReadbackBufferWidth, mReadbackBufferHeight, Bitmap.Config.ARGB_8888); - mReadbackBuffer.rewind(); - bitmap.copyPixelsFromBuffer(mReadbackBuffer); + mReadbackBuffer[mPboIndex].rewind(); + bitmap.copyPixelsFromBuffer(mReadbackBuffer[mPboIndex]); final GVRScreenshotCallback callback = mScreenshotCenterCallback; Threads.spawn(new Runnable() { public void run() { @@ -739,6 +777,55 @@ public void run() { mScreenshotCenterCallback = null; } + // Capture sticker + protected void captureSticker() { + if (mStickerCallback == null) { + return; + } + + // TODO: when we will use multithreading, create new camera using centercamera as we are adding posteffects into it + final GVRCamera centerCamera = mMainScene.getMainCameraRig().getCenterCamera(); + final GVRMaterial postEffect = new GVRMaterial(this, GVRMaterial.GVRShaderType.HorizontalFlip.ID); + centerCamera.addPostEffect(postEffect); + + GVRRenderTexture posteffectRenderTextureA = mRenderBundle.getPostEffectRenderTextureA(); + GVRRenderTexture posteffectRenderTextureB = mRenderBundle.getPostEffectRenderTextureB(); + + GVRRenderTarget renderTarget = mRenderBundle.getStickerRenderTarget(mPboIndex); + renderTarget.cullFromCamera(mMainScene, centerCamera ,mRenderBundle.getShaderManager()); + + renderTarget.beginRendering(centerCamera); + renderTarget.render(mMainScene,centerCamera, mRenderBundle.getShaderManager(), posteffectRenderTextureA, posteffectRenderTextureB); + centerCamera.removePostEffect(postEffect); + readRenderResultInPBO(mPboIndex); + renderTarget.endRendering(); + + // Read the previous PBO's result into readbackBuffer + int pboIndex = (mPboIndex - 1) < 0 ? 2 : mPboIndex - 1; + final ByteBuffer readbackBuffer = mReadbackBuffer[pboIndex]; + + if(readbackBuffer != null) { + readRenderResultFromPBO(readbackBuffer, pboIndex); + readbackBuffer.rewind(); + } + + final GVRScreenshotCallback callback = mStickerCallback; + + Threads.spawn(new Runnable() { + public void run() { + Bitmap bitmap = null; + // Copying asynchronously readback buffers data into bitmap + if(readbackBuffer != null) { + bitmap = Bitmap.createBitmap(mReadbackBufferWidth, mReadbackBufferHeight, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(readbackBuffer); + } + callback.onScreenCaptured(bitmap); + } + }); + + mStickerCallback = null; + } + private void renderOneCameraAndAddToList(final GVRPerspectiveCamera centerCamera, final Bitmap[] bitmaps, int index, GVRRenderTarget renderTarget, GVRRenderTexture postEffectRenderTextureA, GVRRenderTexture postEffectRenderTextureB ) { @@ -747,8 +834,8 @@ private void renderOneCameraAndAddToList(final GVRPerspectiveCamera centerCamera readRenderResult(renderTarget,EYE.CENTER, false); bitmaps[index] = Bitmap.createBitmap(mReadbackBufferWidth, mReadbackBufferHeight, Bitmap.Config.ARGB_8888); - mReadbackBuffer.rewind(); - bitmaps[index].copyPixelsFromBuffer(mReadbackBuffer); + mReadbackBuffer[mPboIndex].rewind(); + bitmaps[index].copyPixelsFromBuffer(mReadbackBuffer[mPboIndex]); } private void renderSixCamerasAndReadback(final GVRCameraRig mainCameraRig, final Bitmap[] bitmaps, GVRRenderTarget renderTarget, boolean isMultiview) { @@ -804,12 +891,6 @@ private void renderSixCamerasAndReadback(final GVRCameraRig mainCameraRig, final renderTarget.endRendering(); } - protected void captureFinish() { - if (mScreenshotLeftCallback == null && mScreenshotRightCallback == null - && mScreenshotCenterCallback == null && mScreenshot3DCallback == null) { - mReadbackBuffer = null; - } - } GVRGearCursorController.ControllerReader mControllerReader; private final GVRScriptManager mScriptManager; protected final GVRApplication mApplication; @@ -834,15 +915,16 @@ protected void captureFinish() { protected GVRMain mMain; - protected ByteBuffer mReadbackBuffer; + protected ByteBuffer mReadbackBuffer[] = {null, null, null}; protected int mReadbackBufferWidth; protected int mReadbackBufferHeight; protected native void makeShadowMaps(long scene, GVRScene javaSceneObject, long shader_manager, int width, int height); protected native void cullAndRender(long render_target, long scene, GVRScene javaSceneObject, long shader_manager, long postEffectRenderTextureA, long postEffectRenderTextureB); private native static void readRenderResultNative(Object readbackBuffer, long renderTarget, int eye, boolean useMultiview); + private native static void readRenderResultInPBONative(int pboIndex); + private native static void readRenderResultFromPBONative(Object readbackBuffer, int pboIndex); private static final String TAG = "GVRViewManager"; - } diff --git a/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.cpp b/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.cpp index c2dacf099..81bba6c6d 100644 --- a/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.cpp +++ b/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.cpp @@ -713,5 +713,46 @@ namespace gvr copy_mesh->setTriangles(faces, faces_size); } + bool GLRenderer::readRenderResultInPBO(int pboIndex) { + static GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + + GLuint* &pbos = gRenderer->getPBOs(); + if(pbos == NULL){ + pbos = new GLuint[3]; + glGenBuffers(3, pbos); + + long neededCapacity = viewport[2] * viewport[3] * 4; + for(int i = 0; i < 3; i++){ + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[i]); + glBufferData(GL_PIXEL_PACK_BUFFER, neededCapacity, NULL, GL_DYNAMIC_READ); + } + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[pboIndex]); + glReadPixels(0, 0, viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, 0); + return true; + } + + bool GLRenderer::readRenderResultFromPBO(uint8_t *readback_buffer, int pboIndex) { + static GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + long pboSize = viewport[2] * viewport[3] * 4; + + GLuint * pbos = gRenderer->getPBOs(); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[pboIndex]); + + uint8_t* ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pboSize, GL_MAP_READ_BIT); + + if (NULL != ptr) { + memcpy(readback_buffer, ptr, pboSize); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + else { + LOGE("Failed to map the buffer"); + } + return true; + } } diff --git a/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.h b/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.h index ea50e99a2..cc21a7dde 100644 --- a/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.h +++ b/GVRf/Framework/framework/src/main/jni/engine/renderer/gl_renderer.h @@ -65,6 +65,11 @@ class GLRenderer: public Renderer { if(transform_ubo_[1]) delete transform_ubo_[1]; + GLuint* &pbos = getPBOs(); + if(pbos != NULL) { + glDeleteBuffers(3, pbos); + } + } public: @@ -103,6 +108,8 @@ class GLRenderer: public Renderer { GLUniformBlock* getTransformUbo(int index) { return transform_ubo_[index]; } virtual void updatePostEffectMesh(Mesh*); virtual bool renderWithShader(RenderState& rstate, Shader* shader, RenderData* renderData, ShaderData* shaderData, int); + virtual bool readRenderResultInPBO(int); + virtual bool readRenderResultFromPBO(uint8_t *readback_buffer, int pboIndex); private: virtual void renderMesh(RenderState& rstate, RenderData* render_data); diff --git a/GVRf/Framework/framework/src/main/jni/engine/renderer/renderer.h b/GVRf/Framework/framework/src/main/jni/engine/renderer/renderer.h index b445e0ac6..975d0ed55 100644 --- a/GVRf/Framework/framework/src/main/jni/engine/renderer/renderer.h +++ b/GVRf/Framework/framework/src/main/jni/engine/renderer/renderer.h @@ -202,10 +202,13 @@ class Renderer { } return nullptr; } + virtual bool readRenderResultInPBO(int) = 0; + virtual bool readRenderResultFromPBO(uint8_t *, int) = 0; private: RenderTarget* mLeftRenderTarget[3]; RenderTarget* mRightRenderTarget[3]; RenderTarget* mMultiviewRenderTarget[3]; + GLuint * pbos = NULL; static bool isVulkan_; virtual void build_frustum(float frustum[6][4], const float *vp_matrix); virtual void frustum_cull(glm::vec3 camera_position, SceneObject *object, @@ -244,6 +247,9 @@ class Renderer { bool useStencilBuffer(){ return useStencilBuffer_; } + GLuint* & getPBOs(){ + return pbos; + } }; extern Renderer* gRenderer; } diff --git a/GVRf/Framework/framework/src/main/jni/engine/renderer/vulkan_renderer.h b/GVRf/Framework/framework/src/main/jni/engine/renderer/vulkan_renderer.h index 001f78b12..8fae5d37f 100644 --- a/GVRf/Framework/framework/src/main/jni/engine/renderer/vulkan_renderer.h +++ b/GVRf/Framework/framework/src/main/jni/engine/renderer/vulkan_renderer.h @@ -135,7 +135,8 @@ class VulkanRenderer: public Renderer { virtual bool renderWithShader(RenderState& rstate, Shader* shader, RenderData* renderData, ShaderData* shaderData, int); virtual void updatePostEffectMesh(Mesh*); - + virtual bool readRenderResultInPBO(int){} + virtual bool readRenderResultFromPBO(uint8_t *readback_buffer, int pboIndex){} private: VulkanCore* vulkanCore_; void renderMesh(RenderState& rstate, RenderData* render_data){} diff --git a/GVRf/Framework/framework/src/main/jni/view_manager_jni.cpp b/GVRf/Framework/framework/src/main/jni/view_manager_jni.cpp index fc536f3e2..cae0b1764 100644 --- a/GVRf/Framework/framework/src/main/jni/view_manager_jni.cpp +++ b/GVRf/Framework/framework/src/main/jni/view_manager_jni.cpp @@ -85,6 +85,13 @@ extern "C" { Java_org_gearvrf_GVRViewManager_readRenderResultNative(JNIEnv *env, jclass clazz, jobject jreadback_buffer, jlong jrenderTarget, jint eye, jboolean useMultiview); + JNIEXPORT void JNICALL + Java_org_gearvrf_GVRViewManager_readRenderResultInPBONative(JNIEnv *env, jclass clazz, + jint pboIndex); + + JNIEXPORT void JNICALL + Java_org_gearvrf_GVRViewManager_readRenderResultFromPBONative(JNIEnv *env, jclass clazz, + jobject jreadback_buffer, jint pboIndex); } // extern "C" @@ -98,4 +105,15 @@ JNIEXPORT void JNICALL Java_org_gearvrf_GVRViewManager_readRenderResultNative(JN } renderTexture->readRenderResult(readback_buffer); } + + JNIEXPORT void JNICALL Java_org_gearvrf_GVRViewManager_readRenderResultInPBONative(JNIEnv *env, jclass clazz, + jint pboIndex){ + gRenderer->readRenderResultInPBO(pboIndex); + } + + JNIEXPORT void JNICALL Java_org_gearvrf_GVRViewManager_readRenderResultFromPBONative(JNIEnv *env, jclass clazz, + jobject jreadback_buffer, jint pboIndex){ + uint8_t *readback_buffer = (uint8_t*) env->GetDirectBufferAddress(jreadback_buffer); + gRenderer->readRenderResultFromPBO(readback_buffer, pboIndex); + } } \ No newline at end of file diff --git a/GVRf/Framework/framework/src/main/res/raw/vertex_template_multitex.vsh b/GVRf/Framework/framework/src/main/res/raw/vertex_template_multitex.vsh index 23613a5ca..ab59022fd 100644 --- a/GVRf/Framework/framework/src/main/res/raw/vertex_template_multitex.vsh +++ b/GVRf/Framework/framework/src/main/res/raw/vertex_template_multitex.vsh @@ -143,19 +143,12 @@ void main() { viewspace_normal = vertex.viewspace_normal; view_direction = vertex.view_direction; #ifdef HAS_MULTIVIEW -<<<<<<< HEAD bool render_mask = (u_render_mask & (gl_ViewID_OVR + uint(1))) > uint(0) ? true : false; mat4 mvp = u_mvp_[gl_ViewID_OVR]; if(!render_mask) mvp = mat4(0.0); // if render_mask is not set for particular eye, dont render that object gl_Position = mvp * vertex.local_position; -======= - bool render_mask = (u_render_mask & (gl_ViewID_OVR + uint(1))) > uint(0) ? true : false; - mat4 mvp = u_mvp_[gl_ViewID_OVR]; - if(!render_mask) - mvp = mat4(0.0); // if render_mask is not set for particular eye, dont render that object - gl_Position = mvp * vertex.local_position; ->>>>>>> normalmap + #else gl_Position = u_mvp * vertex.local_position; #endif From c16837d818945537a3c1da8e264f65fdb9b7934f Mon Sep 17 00:00:00 2001 From: Abhijit Date: Fri, 3 Aug 2018 14:55:02 -0700 Subject: [PATCH 3/3] Renaming functions --- .../framework/src/main/java/org/gearvrf/GVRSticker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java index 3bb5b1d90..3d4238687 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRSticker.java @@ -66,7 +66,7 @@ public GVRSticker(GVRContext gvrContext, String tag, long interval){ * Initiate's the screenshot capturing * Captures images every {@link GVRSticker#mInterval} milliseconds */ - public void captureScreen() + public void startCapturing() { mCaptureFlag = true; Threads.spawn(new Runnable() @@ -101,7 +101,7 @@ public void run() /** * Stop's the screenshot thread */ - public void stop(){ + public void stopCapturing(){ mCaptureFlag = false; }