diff --git a/.travis.yml b/.travis.yml index 8a29bd1f8..71c3b576f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,11 +14,28 @@ branches: notifications: slack: - secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s=" on_success: change on_failure: always + rooms: + secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s=" -before_install: +install: + - ./gradlew assemble + +script: + - ./gradlew check + - ./gradlew createZipDistribution + +deploy: + provider: releases + api_key: + secure: "KbFiMt0a8FxUKvCJUYwikLYaqqGMn1p6k4OsXnGqwptQZEUIayabNLHeaD2kTNT3e6AY1ETwQLff/lB2LttmIo4g5NWW63g1K3A/HwgnhJwETengiProZ/Udl+ugPeDL/+ar43HUhFq4knBnzFKnEcHAThTPVqH/RMDvZf1UUYI=" + file: build/distributions/jME3.1.0_snapshot-github_2015-06-20.zip + skip_cleanup: true + on: + tags: true + +# before_install: # required libs for android build tools # sudo apt-get update # sudo apt-get install -qq p7zip-full diff --git a/jme3-blender/build.gradle b/jme3-blender/build.gradle index 1b42c7109..a556d7a65 100644 --- a/jme3-blender/build.gradle +++ b/jme3-blender/build.gradle @@ -6,4 +6,7 @@ dependencies { compile project(':jme3-core') compile project(':jme3-desktop') compile project(':jme3-effects') -} + compile ('org.ejml:core:0.27') + compile ('org.ejml:dense64:0.27') + compile ('org.ejml:simple:0.27') +} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java index 91cfd2f3f..f2268199c 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java @@ -64,7 +64,7 @@ public abstract class Constraint { Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); if (pData.isNotNull()) { Structure data = pData.fetchData().get(0); - constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, ownerOMA, blenderContext); + constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, name, ownerOMA, blenderContext); Pointer pTar = (Pointer) data.getFieldValue("tar"); if (pTar != null && pTar.isNotNull()) { targetOMA = pTar.getOldMemoryAddress(); @@ -77,7 +77,7 @@ public abstract class Constraint { } } else { // Null constraint has no data, so create it here - constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext); + constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, name, null, blenderContext); } ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue()); ipo = influenceIpo; diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java index 031676b94..711e0a338 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; import java.util.logging.Logger; import com.jme3.animation.AnimChannel; @@ -38,9 +39,9 @@ import com.jme3.util.TempVars; * @author Marcin Roguski (Kaelthas) */ public class SimulationNode { - private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName()); + private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName()); - private Long featureOMA; + private Long featureOMA; /** The blender context. */ private BlenderContext blenderContext; /** The name of the node (for debugging purposes). */ @@ -51,11 +52,11 @@ public class SimulationNode { private List animations; /** The nodes spatial (if null then the boneContext should be set). */ - private Spatial spatial; + private Spatial spatial; /** The skeleton of the bone (not null if the node simulated the bone). */ - private Skeleton skeleton; + private Skeleton skeleton; /** Animation controller for the node's feature. */ - private AnimControl animControl; + private AnimControl animControl; /** * The star transform of a spatial. Needed to properly reset the spatial to @@ -64,7 +65,7 @@ public class SimulationNode { private Transform spatialStartTransform; /** Star transformations for bones. Needed to properly reset the bones. */ private Map boneStartTransforms; - + /** * Builds the nodes tree for the given feature. The feature (bone or * spatial) is found by its OMA. The feature must be a root bone or a root @@ -208,8 +209,7 @@ public class SimulationNode { if (animations != null) { TempVars vars = TempVars.get(); AnimChannel animChannel = animControl.createChannel(); - - // List bonesWithConstraints = this.collectBonesWithConstraints(skeleton); + for (Animation animation : animations) { float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation); int maxFrame = (int) animationTimeBoundaries[0]; @@ -233,7 +233,7 @@ public class SimulationNode { for (Bone rootBone : skeleton.getRoots()) { // ignore the 0-indexed bone if (skeleton.getBoneIndex(rootBone) > 0) { - this.applyConstraints(rootBone, alteredOmas, applied, frame); + this.applyConstraints(rootBone, alteredOmas, applied, frame, new Stack()); } } @@ -294,34 +294,39 @@ public class SimulationNode { * the set of OMAS of the altered bones (is populated if necessary) * @param frame * the current frame of the animation + * @param bonesStack + * the stack of bones used to avoid infinite loops while applying constraints */ - private void applyConstraints(Bone bone, Set alteredOmas, Set applied, int frame) { - BoneContext boneContext = blenderContext.getBoneContext(bone); - if(!applied.contains(boneContext.getBoneOma())) { - List constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext); - if (constraints != null && constraints.size() > 0) { - // TODO: BEWARE OF INFINITE LOOPS !!!!!!!!!!!!!!!!!!!!!!!!!! - for (Constraint constraint : constraints) { - if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) { - // first apply constraints of the target bone - BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA()); - this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame); - } - constraint.apply(frame); - if (constraint.getAlteredOmas() != null) { - alteredOmas.addAll(constraint.getAlteredOmas()); + private void applyConstraints(Bone bone, Set alteredOmas, Set applied, int frame, Stack bonesStack) { + if (!bonesStack.contains(bone)) { + bonesStack.push(bone); + BoneContext boneContext = blenderContext.getBoneContext(bone); + if (!applied.contains(boneContext.getBoneOma())) { + List constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext); + if (constraints != null && constraints.size() > 0) { + for (Constraint constraint : constraints) { + if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) { + // first apply constraints of the target bone + BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA()); + this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame, bonesStack); + } + constraint.apply(frame); + if (constraint.getAlteredOmas() != null) { + alteredOmas.addAll(constraint.getAlteredOmas()); + } + alteredOmas.add(boneContext.getBoneOma()); } - alteredOmas.add(boneContext.getBoneOma()); } + applied.add(boneContext.getBoneOma()); } - applied.add(boneContext.getBoneOma()); - } - - List children = bone.getChildren(); - if (children != null && children.size() > 0) { - for (Bone child : bone.getChildren()) { - this.applyConstraints(child, alteredOmas, applied, frame); + + List children = bone.getChildren(); + if (children != null && children.size() > 0) { + for (Bone child : bone.getChildren()) { + this.applyConstraints(child, alteredOmas, applied, frame, bonesStack); + } } + bonesStack.pop(); } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java index 346554052..a1f3a7316 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java @@ -30,6 +30,8 @@ public abstract class ConstraintDefinition { protected Set alteredOmas; /** The variable that determines if the constraint will alter the track in any way. */ protected boolean trackToBeChanged = true; + /** The name of the constraint. */ + protected String constraintName; /** * Loads a constraint definition based on the constraint definition @@ -53,6 +55,10 @@ public abstract class ConstraintDefinition { constraintHelper = (ConstraintHelper) (blenderContext == null ? null : blenderContext.getHelper(ConstraintHelper.class)); this.ownerOMA = ownerOMA; } + + public void setConstraintName(String constraintName) { + this.constraintName = constraintName; + } /** * @return determines if the definition of the constraint will change the bone in any way; in most cases diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java index cbf727290..c1d69fe06 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java @@ -92,7 +92,7 @@ public class ConstraintDefinitionFactory { * this exception is thrown when the blender file is somehow * corrupted */ - public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException { + public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, String constraintName, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException { if (constraintStructure == null) { return new ConstraintDefinitionNull(null, ownerOMA, blenderContext); } @@ -100,7 +100,9 @@ public class ConstraintDefinitionFactory { Class constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName); if (constraintDefinitionClass != null) { try { - return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext); + ConstraintDefinition def = (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext); + def.setConstraintName(constraintName); + return def; } catch (IllegalArgumentException e) { throw new BlenderFileException(e.getLocalizedMessage(), e); } catch (SecurityException e) { @@ -113,9 +115,9 @@ public class ConstraintDefinitionFactory { throw new BlenderFileException(e.getLocalizedMessage(), e); } } else { - String constraintName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName); - if (constraintName != null) { - return new UnsupportedConstraintDefinition(constraintName); + String unsupportedConstraintClassName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName); + if (unsupportedConstraintClassName != null) { + return new UnsupportedConstraintDefinition(unsupportedConstraintClassName); } else { throw new BlenderFileException("Unknown constraint type: " + constraintClassName); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java index 35a4e5618..69e087a28 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java @@ -1,40 +1,44 @@ package com.jme3.scene.plugins.blender.constraints.definitions; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.HashSet; import java.util.List; +import org.ejml.simple.SimpleMatrix; + import com.jme3.animation.Bone; import com.jme3.math.Transform; -import com.jme3.math.Vector3f; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.animations.BoneContext; +import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.math.DQuaternion; import com.jme3.scene.plugins.blender.math.DTransform; +import com.jme3.scene.plugins.blender.math.Matrix; import com.jme3.scene.plugins.blender.math.Vector3d; /** - * The Inverse Kinematics constraint. + * A definiotion of a Inverse Kinematics constraint. This implementation uses Jacobian pseudoinverse algorithm. * - * @author Wesley Shillingford (wezrule) * @author Marcin Roguski (Kaelthas) */ public class ConstraintDefinitionIK extends ConstraintDefinition { - private static final float MIN_DISTANCE = 0.0001f; - private static final int FLAG_USE_TAIL = 0x01; - private static final int FLAG_POSITION = 0x20; + private static final float MIN_DISTANCE = 0.001f; + private static final float MIN_ANGLE_CHANGE = 0.001f; + private static final int FLAG_USE_TAIL = 0x01; + private static final int FLAG_POSITION = 0x20; + private BonesChain bones; /** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */ - private int bonesAffected; - /** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */ - private double chainLength; + private int bonesAffected; /** Indicates if the tail of the bone should be used or not. */ - private boolean useTail; + private boolean useTail; /** The amount of iterations of the algorithm. */ - private int iterations; + private int iterations; + /** The count of bones' chain. */ + private int bonesCount = -1; public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { super(constraintData, ownerOMA, blenderContext); @@ -51,122 +55,104 @@ public class ConstraintDefinitionIK extends ConstraintDefinition { } } + /** + * Below are the variables that only need to be allocated once for IK constraint instance. + */ + /** Temporal quaternion. */ + private DQuaternion tempDQuaternion = new DQuaternion(); + /** Temporal matrix column. */ + private Vector3d col = new Vector3d(); + /** Effector's position change. */ + private Matrix deltaP = new Matrix(3, 1); + /** The current target position. */ + private Vector3d target = new Vector3d(); + /** Rotation vectors for each joint (allocated when we know the size of a bones' chain. */ + private Vector3d[] rotationVectors; + /** The Jacobian matrix. Allocated when the bones' chain size is known. */ + private Matrix J; + @Override public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || !trackToBeChanged || targetTransform == null) { + if (influence == 0 || !trackToBeChanged || targetTransform == null || bonesCount == 0) { return;// no need to do anything } - DQuaternion q = new DQuaternion(); - Vector3d t = new Vector3d(targetTransform.getTranslation()); - List bones = this.loadBones(); + + if (bones == null) { + bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, alteredOmas, blenderContext); + } if (bones.size() == 0) { + bonesCount = 0; return;// no need to do anything } double distanceFromTarget = Double.MAX_VALUE; + target.set(targetTransform.getTranslation().x, targetTransform.getTranslation().y, targetTransform.getTranslation().z); - int iterations = this.iterations; - if (bones.size() == 1) { - iterations = 1;// if only one bone is in the chain then only one iteration that will properly rotate it will be needed - } else { - // if the target cannot be rached by the bones' chain then the chain will become straight and point towards the target - // in this case only one iteration will be needed, computed from the root to top bone - BoneContext rootBone = bones.get(bones.size() - 1); - Transform rootBoneTransform = constraintHelper.getTransform(rootBone.getArmatureObjectOMA(), rootBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD); - if (t.distance(new Vector3d(rootBoneTransform.getTranslation())) >= chainLength) { - Collections.reverse(bones); - - for (BoneContext boneContext : bones) { - Bone bone = boneContext.getBone(); - DTransform boneTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD)); - - Vector3d e = boneTransform.getTranslation().add(boneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(boneContext.getLength()));// effector - Vector3d j = boneTransform.getTranslation(); // current join position - - Vector3d currentDir = e.subtractLocal(j).normalizeLocal(); - Vector3d target = t.subtract(j).normalizeLocal(); - double angle = currentDir.angleBetween(target); - if (angle != 0) { - Vector3d cross = currentDir.crossLocal(target).normalizeLocal(); - q.fromAngleAxis(angle, cross); - - if(bone.equals(this.getOwner())) { - if (boneContext.isLockX()) { - q.set(0, q.getY(), q.getZ(), q.getW()); - } - if (boneContext.isLockY()) { - q.set(q.getX(), 0, q.getZ(), q.getW()); - } - if (boneContext.isLockZ()) { - q.set(q.getX(), q.getY(), 0, q.getW()); - } - } - - boneTransform.getRotation().set(q.multLocal(boneTransform.getRotation())); - constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform.toTransform()); - } - } - - iterations = 0; + if (bonesCount < 0) { + bonesCount = bones.size(); + rotationVectors = new Vector3d[bonesCount]; + for (int i = 0; i < bonesCount; ++i) { + rotationVectors[i] = new Vector3d(); } + J = new Matrix(3, bonesCount); } - List bestSolution = new ArrayList(bones.size()); - double bestSolutionDistance = Double.MAX_VALUE; BoneContext topBone = bones.get(0); - for (int i = 0; i < iterations && distanceFromTarget > MIN_DISTANCE; ++i) { - for (BoneContext boneContext : bones) { - Bone bone = boneContext.getBone(); - DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD)); - DTransform boneWorldTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD)); + for (int i = 0; i < iterations; ++i) { + DTransform topBoneTransform = bones.getWorldTransform(topBone); + Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector + distanceFromTarget = e.distance(target); + if (distanceFromTarget <= MIN_DISTANCE) { + break; + } - Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector + deltaP.setColumn(0, 0, target.x - e.x, target.y - e.y, target.z - e.z); + int column = 0; + for (BoneContext boneContext : bones) { + DTransform boneWorldTransform = bones.getWorldTransform(boneContext); Vector3d j = boneWorldTransform.getTranslation(); // current join position + Vector3d vectorFromJointToEffector = e.subtract(j); + vectorFromJointToEffector.cross(target.subtract(j), rotationVectors[column]).normalizeLocal(); + rotationVectors[column].cross(vectorFromJointToEffector, col); + J.setColumn(col, column++); + } + Matrix J_1 = J.pseudoinverse(); - Vector3d currentDir = e.subtractLocal(j).normalizeLocal(); - Vector3d target = t.subtract(j).normalizeLocal(); - double angle = currentDir.angleBetween(target); - if (angle != 0) { - Vector3d cross = currentDir.crossLocal(target).normalizeLocal(); - q.fromAngleAxis(angle, cross); - - if(bone.equals(this.getOwner())) { - if (boneContext.isLockX()) { - q.set(0, q.getY(), q.getZ(), q.getW()); - } - if (boneContext.isLockY()) { - q.set(q.getX(), 0, q.getZ(), q.getW()); - } - if (boneContext.isLockZ()) { - q.set(q.getX(), q.getY(), 0, q.getW()); - } - } - - boneWorldTransform.getRotation().set(q.multLocal(boneWorldTransform.getRotation())); - constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform.toTransform()); - } else { - iterations = 0; - break; - } + SimpleMatrix deltaThetas = J_1.mult(deltaP); + if (deltaThetas.elementMaxAbs() < MIN_ANGLE_CHANGE) { + break; } + for (int j = 0; j < deltaThetas.numRows(); ++j) { + double angle = deltaThetas.get(j, 0); + Vector3d rotationVector = rotationVectors[j]; - DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD)); - Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector - distanceFromTarget = e.distance(t); - - if(distanceFromTarget < bestSolutionDistance) { - bestSolutionDistance = distanceFromTarget; - bestSolution.clear(); - for(BoneContext boneContext : bones) { - bestSolution.add(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD)); + tempDQuaternion.fromAngleAxis(angle, rotationVector); + BoneContext boneContext = bones.get(j); + Bone bone = boneContext.getBone(); + if (bone.equals(this.getOwner())) { + if (boneContext.isLockX()) { + tempDQuaternion.set(0, tempDQuaternion.getY(), tempDQuaternion.getZ(), tempDQuaternion.getW()); + } + if (boneContext.isLockY()) { + tempDQuaternion.set(tempDQuaternion.getX(), 0, tempDQuaternion.getZ(), tempDQuaternion.getW()); + } + if (boneContext.isLockZ()) { + tempDQuaternion.set(tempDQuaternion.getX(), tempDQuaternion.getY(), 0, tempDQuaternion.getW()); + } } + + DTransform boneTransform = bones.getWorldTransform(boneContext); + boneTransform.getRotation().set(tempDQuaternion.mult(boneTransform.getRotation())); + bones.setWorldTransform(boneContext, boneTransform); } } - - // applying best solution - for(int i=0;i= 0; --i) { BoneContext boneContext = bones.get(i); - constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, bestSolution.get(i)); + DTransform transform = bones.getWorldTransform(boneContext); + constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform()); } + bones = null;// need to reload them again } @Override @@ -174,56 +160,68 @@ public class ConstraintDefinitionIK extends ConstraintDefinition { return "Inverse kinematics"; } + @Override + public boolean isTargetRequired() { + return true; + } + /** - * @return the bone contexts of all bones that will be used in this constraint computations + * Loaded bones' chain. This class allows to operate on transform matrices that use double precision in computations. + * Only the final result is being transformed to single precision numbers. + * + * @author Marcin Roguski (Kaelthas) */ - private List loadBones() { - List bones = new ArrayList(); - Bone bone = (Bone) this.getOwner(); - if (bone == null) { - return bones; - } - if (!useTail) { - bone = bone.getParent(); - } - chainLength = 0; - while (bone != null) { - BoneContext boneContext = blenderContext.getBoneContext(bone); - chainLength += boneContext.getLength(); - bones.add(boneContext); - alteredOmas.add(boneContext.getBoneOma()); - if (bonesAffected != 0 && bones.size() >= bonesAffected) { - break; - } - // need to add spaces between bones to the chain length - Transform boneWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD); - Vector3f boneWorldTranslation = boneWorldTransform.getTranslation(); + private static class BonesChain extends ArrayList { + private static final long serialVersionUID = -1850524345643600718L; - bone = bone.getParent(); + private List bonesMatrices = new ArrayList(); + public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection alteredOmas, BlenderContext blenderContext) { if (bone != null) { - boneContext = blenderContext.getBoneContext(bone); - Transform parentWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD); - Vector3f parentWorldTranslation = parentWorldTransform.getTranslation(); - chainLength += boneWorldTranslation.distance(parentWorldTranslation); + ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); + if (!useTail) { + bone = bone.getParent(); + } + while (bone != null && (bonesAffected <= 0 || this.size() < bonesAffected)) { + BoneContext boneContext = blenderContext.getBoneContext(bone); + this.add(boneContext); + alteredOmas.add(boneContext.getBoneOma()); + + Space space = this.size() < bonesAffected ? Space.CONSTRAINT_SPACE_LOCAL : Space.CONSTRAINT_SPACE_WORLD; + Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), space); + bonesMatrices.add(new DTransform(transform).toMatrix()); + + bone = bone.getParent(); + } } } - return bones; - } - @Override - public boolean isTrackToBeChanged() { - if (trackToBeChanged) { - // need to check the bone structure too (when constructor was called not all of the bones might have been loaded yet) - // that is why it is also checked here - List bones = this.loadBones(); - trackToBeChanged = bones.size() > 0; + public DTransform getWorldTransform(BoneContext bone) { + int index = this.indexOf(bone); + return this.getWorldMatrix(index).toTransform(); } - return trackToBeChanged; - } - @Override - public boolean isTargetRequired() { - return true; + public void setWorldTransform(BoneContext bone, DTransform transform) { + int index = this.indexOf(bone); + Matrix boneMatrix = transform.toMatrix(); + + if (index < this.size() - 1) { + // computing the current bone local transform + Matrix parentWorldMatrix = this.getWorldMatrix(index + 1); + SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix); + boneMatrix = new Matrix(m); + } + bonesMatrices.set(index, boneMatrix); + } + + public Matrix getWorldMatrix(int index) { + if (index == this.size() - 1) { + return new Matrix(bonesMatrices.get(this.size() - 1)); + } + + SimpleMatrix result = this.getWorldMatrix(index + 1); + result = result.mult(bonesMatrices.get(index)); + return new Matrix(result); + } } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java index 359abf057..9739ccd4b 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java @@ -164,6 +164,134 @@ public final class DQuaternion implements Savable, Cloneable, java.io.Serializab w = 1; } + /** + * norm returns the norm of this quaternion. This is the dot + * product of this quaternion with itself. + * + * @return the norm of the quaternion. + */ + public double norm() { + return w * w + x * x + y * y + z * z; + } + + public DQuaternion fromRotationMatrix(double m00, double m01, double m02, + double m10, double m11, double m12, double m20, double m21, double m22) { + // first normalize the forward (F), up (U) and side (S) vectors of the rotation matrix + // so that the scale does not affect the rotation + double lengthSquared = m00 * m00 + m10 * m10 + m20 * m20; + if (lengthSquared != 1f && lengthSquared != 0f) { + lengthSquared = 1.0 / Math.sqrt(lengthSquared); + m00 *= lengthSquared; + m10 *= lengthSquared; + m20 *= lengthSquared; + } + lengthSquared = m01 * m01 + m11 * m11 + m21 * m21; + if (lengthSquared != 1 && lengthSquared != 0f) { + lengthSquared = 1.0 / Math.sqrt(lengthSquared); + m01 *= lengthSquared; + m11 *= lengthSquared; + m21 *= lengthSquared; + } + lengthSquared = m02 * m02 + m12 * m12 + m22 * m22; + if (lengthSquared != 1f && lengthSquared != 0f) { + lengthSquared = 1.0 / Math.sqrt(lengthSquared); + m02 *= lengthSquared; + m12 *= lengthSquared; + m22 *= lengthSquared; + } + + // Use the Graphics Gems code, from + // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z + // *NOT* the "Matrix and Quaternions FAQ", which has errors! + + // the trace is the sum of the diagonal elements; see + // http://mathworld.wolfram.com/MatrixTrace.html + double t = m00 + m11 + m22; + + // we protect the division by s by ensuring that s>=1 + if (t >= 0) { // |w| >= .5 + double s = Math.sqrt(t + 1); // |s|>=1 ... + w = 0.5f * s; + s = 0.5f / s; // so this division isn't bad + x = (m21 - m12) * s; + y = (m02 - m20) * s; + z = (m10 - m01) * s; + } else if (m00 > m11 && m00 > m22) { + double s = Math.sqrt(1.0 + m00 - m11 - m22); // |s|>=1 + x = s * 0.5f; // |x| >= .5 + s = 0.5f / s; + y = (m10 + m01) * s; + z = (m02 + m20) * s; + w = (m21 - m12) * s; + } else if (m11 > m22) { + double s = Math.sqrt(1.0 + m11 - m00 - m22); // |s|>=1 + y = s * 0.5f; // |y| >= .5 + s = 0.5f / s; + x = (m10 + m01) * s; + z = (m21 + m12) * s; + w = (m02 - m20) * s; + } else { + double s = Math.sqrt(1.0 + m22 - m00 - m11); // |s|>=1 + z = s * 0.5f; // |z| >= .5 + s = 0.5f / s; + x = (m02 + m20) * s; + y = (m21 + m12) * s; + w = (m10 - m01) * s; + } + + return this; + } + + /** + * toRotationMatrix converts this quaternion to a rotational + * matrix. The result is stored in result. 4th row and 4th column values are + * untouched. Note: the result is created from a normalized version of this quat. + * + * @param result + * The Matrix4f to store the result in. + * @return the rotation matrix representation of this quaternion. + */ + public Matrix toRotationMatrix(Matrix result) { + Vector3d originalScale = new Vector3d(); + + result.toScaleVector(originalScale); + result.setScale(1, 1, 1); + double norm = this.norm(); + // we explicitly test norm against one here, saving a division + // at the cost of a test and branch. Is it worth it? + double s = norm == 1f ? 2f : norm > 0f ? 2f / norm : 0; + + // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs + // will be used 2-4 times each. + double xs = x * s; + double ys = y * s; + double zs = z * s; + double xx = x * xs; + double xy = x * ys; + double xz = x * zs; + double xw = w * xs; + double yy = y * ys; + double yz = y * zs; + double yw = w * ys; + double zz = z * zs; + double zw = w * zs; + + // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here + result.set(0, 0, 1 - (yy + zz)); + result.set(0, 1, xy - zw); + result.set(0, 2, xz + yw); + result.set(1, 0, xy + zw); + result.set(1, 1, 1 - (xx + zz)); + result.set(1, 2, yz - xw); + result.set(2, 0, xz - yw); + result.set(2, 1, yz + xw); + result.set(2, 2, 1 - (xx + yy)); + + result.setScale(originalScale); + + return result; + } + /** * fromAngleAxis sets this quaternion to the values specified * by an angle and an axis of rotation. This method creates an object, so diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java index ed31a4c98..28fcda0c7 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java @@ -31,11 +31,15 @@ */ package com.jme3.scene.plugins.blender.math; -import com.jme3.export.*; -import com.jme3.math.Transform; - import java.io.IOException; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; +import com.jme3.math.Transform; + /** * Started Date: Jul 16, 2004
*
@@ -57,6 +61,12 @@ public final class DTransform implements Savable, Cloneable, java.io.Serializabl private Vector3d translation; private Vector3d scale; + public DTransform() { + translation = new Vector3d(); + rotation = new DQuaternion(); + scale = new Vector3d(); + } + public DTransform(Transform transform) { translation = new Vector3d(transform.getTranslation()); rotation = new DQuaternion(transform.getRotation()); @@ -66,7 +76,15 @@ public final class DTransform implements Savable, Cloneable, java.io.Serializabl public Transform toTransform() { return new Transform(translation.toVector3f(), rotation.toQuaternion(), scale.toVector3f()); } - + + public Matrix toMatrix() { + Matrix m = Matrix.identity(4); + m.setTranslation(translation); + m.setRotationQuaternion(rotation); + m.setScale(scale); + return m; + } + /** * Sets this translation to the given value. * @param trans diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java new file mode 100644 index 000000000..29550f23b --- /dev/null +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java @@ -0,0 +1,214 @@ +package com.jme3.scene.plugins.blender.math; + +import java.text.DecimalFormat; + +import org.ejml.ops.CommonOps; +import org.ejml.simple.SimpleMatrix; +import org.ejml.simple.SimpleSVD; + +import com.jme3.math.FastMath; + +/** + * Encapsulates a 4x4 matrix + * + * + */ +public class Matrix extends SimpleMatrix { + private static final long serialVersionUID = 2396600537315902559L; + + public Matrix(int rows, int cols) { + super(rows, cols); + } + + /** + * Copy constructor + */ + public Matrix(SimpleMatrix m) { + super(m); + } + + public Matrix(double[][] data) { + super(data); + } + + public static Matrix identity(int size) { + Matrix result = new Matrix(size, size); + CommonOps.setIdentity(result.mat); + return result; + } + + public Matrix pseudoinverse() { + return this.pseudoinverse(1); + } + + @SuppressWarnings("unchecked") + public Matrix pseudoinverse(double lambda) { + SimpleSVD simpleSVD = this.svd(); + + SimpleMatrix U = simpleSVD.getU(); + SimpleMatrix S = simpleSVD.getW(); + SimpleMatrix V = simpleSVD.getV(); + + int N = Math.min(this.numRows(),this.numCols()); + double maxSingular = 0; + for( int i = 0; i < N; ++i ) { + if( S.get(i, i) > maxSingular ) { + maxSingular = S.get(i, i); + } + } + + double tolerance = FastMath.DBL_EPSILON * Math.max(this.numRows(),this.numCols()) * maxSingular; + for(int i=0;isetRotationQuaternion builds a rotation from a + * Quaternion. + * + * @param quat + * the quaternion to build the rotation from. + * @throws NullPointerException + * if quat is null. + */ + public void setRotationQuaternion(DQuaternion quat) { + quat.toRotationMatrix(this); + } + + public DTransform toTransform() { + DTransform result = new DTransform(); + result.setTranslation(this.toTranslationVector()); + result.setRotation(this.toRotationQuat()); + result.setScale(this.toScaleVector()); + return result; + } + + public Vector3d toTranslationVector() { + return new Vector3d(this.get(0, 3), this.get(1, 3), this.get(2, 3)); + } + + public DQuaternion toRotationQuat() { + DQuaternion quat = new DQuaternion(); + quat.fromRotationMatrix(this.get(0, 0), this.get(0, 1), this.get(0, 2), this.get(1, 0), this.get(1, 1), this.get(1, 2), this.get(2, 0), this.get(2, 1), this.get(2, 2)); + return quat; + } + + /** + * Retreives the scale vector from the matrix and stores it into a given + * vector. + * + * @param the + * vector where the scale will be stored + */ + public Vector3d toScaleVector() { + Vector3d result = new Vector3d(); + this.toScaleVector(result); + return result; + } + + /** + * Retreives the scale vector from the matrix and stores it into a given + * vector. + * + * @param the + * vector where the scale will be stored + */ + public void toScaleVector(Vector3d vector) { + double scaleX = Math.sqrt(this.get(0, 0) * this.get(0, 0) + this.get(1, 0) * this.get(1, 0) + this.get(2, 0) * this.get(2, 0)); + double scaleY = Math.sqrt(this.get(0, 1) * this.get(0, 1) + this.get(1, 1) * this.get(1, 1) + this.get(2, 1) * this.get(2, 1)); + double scaleZ = Math.sqrt(this.get(0, 2) * this.get(0, 2) + this.get(1, 2) * this.get(1, 2) + this.get(2, 2) * this.get(2, 2)); + vector.set(scaleX, scaleY, scaleZ); + } +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/Limits.java b/jme3-core/src/main/java/com/jme3/renderer/Limits.java index b10c539b8..86cddb178 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Limits.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Limits.java @@ -75,4 +75,6 @@ public enum Limits { ColorTextureSamples, DepthTextureSamples, + + VertexUniformVectors, } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java index 76eedb521..4a9380cf4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java @@ -74,6 +74,7 @@ public interface GL { public static final int GL_FRONT_AND_BACK = 0x408; public static final int GL_GEQUAL = 0x206; public static final int GL_GREATER = 0x204; + public static final int GL_GREEN = 0x1904; public static final int GL_INCR = 0x1E02; public static final int GL_INCR_WRAP = 0x8507; public static final int GL_INFO_LOG_LENGTH = 0x8B84; @@ -99,6 +100,8 @@ public interface GL { public static final int GL_MAX_TEXTURE_SIZE = 0xD33; public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869; public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; + public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A; + public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; public static final int GL_MIRRORED_REPEAT = 0x8370; public static final int GL_NEAREST = 0x2600; public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702; @@ -114,6 +117,7 @@ public interface GL { public static final int GL_OUT_OF_MEMORY = 0x505; public static final int GL_POINTS = 0x0; public static final int GL_POLYGON_OFFSET_FILL = 0x8037; + public static final int GL_RED = 0x1903; public static final int GL_RENDERER = 0x1F01; public static final int GL_REPEAT = 0x2901; public static final int GL_REPLACE = 0x1E01; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java index 190ed4547..2a7c38bb9 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java @@ -34,7 +34,7 @@ package com.jme3.renderer.opengl; import java.nio.IntBuffer; /** - * GL functions only available on vanilla desktop OpenGL 3.0. + * GL functions only available on vanilla desktop OpenGL 3.0+. * * @author Kirill Vainer */ @@ -43,6 +43,17 @@ public interface GL3 extends GL2 { public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A; public static final int GL_GEOMETRY_SHADER = 0x8DD9; public static final int GL_NUM_EXTENSIONS = 0x821D; + public static final int GL_R8 = 0x8229; + public static final int GL_R16F = 0x822D; + public static final int GL_R32F = 0x822E; + public static final int GL_RG16F = 0x822F; + public static final int GL_RG32F = 0x8230; + public static final int GL_RG = 0x8227; + public static final int GL_RG8 = 0x822B; + public static final int GL_TEXTURE_SWIZZLE_A = 0x8E45; + public static final int GL_TEXTURE_SWIZZLE_B = 0x8E44; + public static final int GL_TEXTURE_SWIZZLE_G = 0x8E43; + public static final int GL_TEXTURE_SWIZZLE_R = 0x8E42; public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+ public void glBindVertexArray(int param1); /// GL3+ diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java index ce982eebf..058bc00ec 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java @@ -34,7 +34,7 @@ package com.jme3.renderer.opengl; import java.nio.IntBuffer; /** - * GL functions only available on vanilla desktop OpenGL 3.0. + * GL functions only available on vanilla desktop OpenGL 4.0. * * @author Kirill Vainer */ diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java index 71d95f59b..8e65fbdef 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java @@ -42,6 +42,7 @@ public final class GLImageFormat { public final int format; public final int dataType; public final boolean compressed; + public final boolean swizzleRequired; /** * Constructor for formats. @@ -55,6 +56,7 @@ public final class GLImageFormat { this.format = format; this.dataType = dataType; this.compressed = false; + this.swizzleRequired = false; } /** @@ -63,11 +65,30 @@ public final class GLImageFormat { * @param internalFormat OpenGL internal format * @param format OpenGL format * @param dataType OpenGL datatype + * @param compressed Format is compressed */ public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) { this.internalFormat = internalFormat; this.format = format; this.dataType = dataType; this.compressed = compressed; + this.swizzleRequired = false; + } + + /** + * Constructor for formats. + * + * @param internalFormat OpenGL internal format + * @param format OpenGL format + * @param dataType OpenGL datatype + * @param compressed Format is compressed + * @param swizzleRequired Need to use texture swizzle to upload texture + */ + public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed, boolean swizzleRequired) { + this.internalFormat = internalFormat; + this.format = format; + this.dataType = dataType; + this.compressed = compressed; + this.swizzleRequired = swizzleRequired; } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java index a7ef9f52d..ab80b9e2a 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java @@ -52,6 +52,13 @@ public final class GLImageFormats { formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType); } + private static void formatSwiz(GLImageFormat[][] formatToGL, Image.Format format, + int glInternalFormat, + int glFormat, + int glDataType){ + formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true); + } + private static void formatSrgb(GLImageFormat[][] formatToGL, Image.Format format, int glInternalFormat, int glFormat, @@ -60,6 +67,14 @@ public final class GLImageFormats { formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType); } + private static void formatSrgbSwiz(GLImageFormat[][] formatToGL, Image.Format format, + int glInternalFormat, + int glFormat, + int glDataType) + { + formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true); + } + private static void formatComp(GLImageFormat[][] formatToGL, Image.Format format, int glCompressedFormat, int glFormat, @@ -88,6 +103,19 @@ public final class GLImageFormats { public static GLImageFormat[][] getFormatsForCaps(EnumSet caps) { GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length]; + // Core Profile Formats (supported by both OpenGL Core 3.3 and OpenGL ES 3.0+) + if (caps.contains(Caps.CoreProfile)) { + formatSwiz(formatToGL, Format.Alpha8, GL3.GL_R8, GL.GL_RED, GL.GL_UNSIGNED_BYTE); + formatSwiz(formatToGL, Format.Luminance8, GL3.GL_R8, GL.GL_RED, GL.GL_UNSIGNED_BYTE); + formatSwiz(formatToGL, Format.Luminance8Alpha8, GL3.GL_RG8, GL3.GL_RG, GL.GL_UNSIGNED_BYTE); + formatSwiz(formatToGL, Format.Luminance16F, GL3.GL_R16F, GL.GL_RED, GLExt.GL_HALF_FLOAT_ARB); + formatSwiz(formatToGL, Format.Luminance32F, GL3.GL_R32F, GL.GL_RED, GL.GL_FLOAT); + formatSwiz(formatToGL, Format.Luminance16FAlpha16F, GL3.GL_RG16F, GL3.GL_RG, GLExt.GL_HALF_FLOAT_ARB); + + formatSrgbSwiz(formatToGL, Format.Luminance8, GLExt.GL_SRGB8_EXT, GL.GL_RED, GL.GL_UNSIGNED_BYTE); + formatSrgbSwiz(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SRGB8_ALPHA8_EXT, GL3.GL_RG, GL.GL_UNSIGNED_BYTE); + } + if (caps.contains(Caps.OpenGL20)) { if (!caps.contains(Caps.CoreProfile)) { format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 3f14bdb10..729f4e203 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -73,7 +73,7 @@ public class GLRenderer implements Renderer { private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); private static final boolean VALIDATE_SHADER = false; private static final Pattern GLVERSION_PATTERN = Pattern.compile(".*?(\\d+)\\.(\\d+).*"); - + private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); private final StringBuilder stringBuf = new StringBuilder(250); private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1); @@ -83,7 +83,7 @@ public class GLRenderer implements Renderer { private final NativeObjectManager objManager = new NativeObjectManager(); private final EnumSet caps = EnumSet.noneOf(Caps.class); private final EnumMap limits = new EnumMap(Limits.class); - + private FrameBuffer mainFbOverride = null; private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; @@ -98,7 +98,7 @@ public class GLRenderer implements Renderer { private final GLExt glext; private final GLFbo glfbo; private final TextureUtil texUtil; - + public GLRenderer(GL gl, GLExt glext, GLFbo glfbo) { this.gl = gl; this.gl2 = gl instanceof GL2 ? (GL2)gl : null; @@ -106,7 +106,7 @@ public class GLRenderer implements Renderer { this.gl4 = gl instanceof GL4 ? (GL4)gl : null; this.glfbo = glfbo; this.glext = glext; - this.texUtil = new TextureUtil(gl, gl2, glext, context); + this.texUtil = new TextureUtil(gl, gl2, glext); } @Override @@ -118,7 +118,7 @@ public class GLRenderer implements Renderer { public EnumSet getCaps() { return caps; } - + // Not making public yet ... public EnumMap getLimits() { return limits; @@ -140,7 +140,7 @@ public class GLRenderer implements Renderer { } return extensionSet; } - + public static int extractVersion(String version) { Matcher m = GLVERSION_PATTERN.matcher(version); if (m.matches()) { @@ -160,17 +160,17 @@ public class GLRenderer implements Renderer { private boolean hasExtension(String extensionName) { return extensions.contains(extensionName); } - + private void loadCapabilitiesES() { caps.add(Caps.GLSL100); caps.add(Caps.OpenGLES20); - + // Important: Do not add OpenGL20 - that's the desktop capability! } - + private void loadCapabilitiesGL2() { int oglVer = extractVersion(gl.glGetString(GL.GL_VERSION)); - + if (oglVer >= 200) { caps.add(Caps.OpenGL20); if (oglVer >= 210) { @@ -194,9 +194,9 @@ public class GLRenderer implements Renderer { } } } - + int glslVer = extractVersion(gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)); - + switch (glslVer) { default: if (glslVer < 400) { @@ -222,34 +222,34 @@ public class GLRenderer implements Renderer { caps.add(Caps.GLSL100); break; } - + // Workaround, always assume we support GLSL100 & GLSL110 // Supporting OpenGL 2.0 means supporting GLSL 1.10. caps.add(Caps.GLSL110); caps.add(Caps.GLSL100); - + // Fix issue in TestRenderToMemory when GL.GL_FRONT is the main // buffer being used. context.initialDrawBuf = getInteger(GL2.GL_DRAW_BUFFER); context.initialReadBuf = getInteger(GL2.GL_READ_BUFFER); - + // XXX: This has to be GL.GL_BACK for canvas on Mac // Since initialDrawBuf is GL.GL_FRONT for pbuffer, gotta // change this value later on ... // initialDrawBuf = GL.GL_BACK; // initialReadBuf = GL.GL_BACK; } - + private void loadCapabilitiesCommon() { extensions = loadExtensions(); - + limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)); if (limits.get(Limits.VertexTextureUnits) > 0) { caps.add(Caps.VertexTextureFetch); } limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS)); - + // gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16); // vertexUniforms = intBuf16.get(0); // logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); @@ -257,62 +257,66 @@ public class GLRenderer implements Renderer { // gl.glGetInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16); // fragUniforms = intBuf16.get(0); // logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); - + if (caps.contains(Caps.OpenGLES20)) { + limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS)); + } else { + limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4); + } limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS)); limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE)); limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE)); - if (hasExtension("GL_ARB_draw_instanced") && - hasExtension("GL_ARB_instanced_arrays")) { + if (hasExtension("GL_ARB_draw_instanced") && + hasExtension("GL_ARB_instanced_arrays")) { caps.add(Caps.MeshInstancing); } if (hasExtension("GL_OES_element_index_uint") || gl2 != null) { caps.add(Caps.IntegerIndexBuffer); } - + if (hasExtension("GL_ARB_texture_buffer_object")) { caps.add(Caps.TextureBuffer); } - + // == texture format extensions == - + boolean hasFloatTexture; hasFloatTexture = hasExtension("GL_OES_texture_half_float") && - hasExtension("GL_OES_texture_float"); - + hasExtension("GL_OES_texture_float"); + if (!hasFloatTexture) { hasFloatTexture = hasExtension("GL_ARB_texture_float") && - hasExtension("GL_ARB_half_float_pixel"); - + hasExtension("GL_ARB_half_float_pixel"); + if (!hasFloatTexture) { hasFloatTexture = caps.contains(Caps.OpenGL30); } } - + if (hasFloatTexture) { caps.add(Caps.FloatTexture); } - + if (hasExtension("GL_OES_depth_texture") || gl2 != null) { caps.add(Caps.DepthTexture); - + // TODO: GL_OES_depth24 } - - if (hasExtension("GL_OES_rgb8_rgba8") || - hasExtension("GL_ARM_rgba8") || - hasExtension("GL_EXT_texture_format_BGRA8888")) { + + if (hasExtension("GL_OES_rgb8_rgba8") || + hasExtension("GL_ARM_rgba8") || + hasExtension("GL_EXT_texture_format_BGRA8888")) { caps.add(Caps.Rgba8); } - + if (caps.contains(Caps.OpenGL30) || hasExtension("GL_OES_packed_depth_stencil")) { caps.add(Caps.PackedDepthStencilBuffer); } if (hasExtension("GL_ARB_color_buffer_float") && - hasExtension("GL_ARB_half_float_pixel")) { + hasExtension("GL_ARB_half_float_pixel")) { // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. caps.add(Caps.FloatColorBuffer); } @@ -321,44 +325,44 @@ public class GLRenderer implements Renderer { caps.add(Caps.FloatDepthBuffer); } - if ((hasExtension("GL_EXT_packed_float") && hasFloatTexture) || - caps.contains(Caps.OpenGL30)) { + if ((hasExtension("GL_EXT_packed_float") && hasFloatTexture) || + caps.contains(Caps.OpenGL30)) { // Either OpenGL3 is available or both packed_float & half_float_pixel. caps.add(Caps.PackedFloatColorBuffer); caps.add(Caps.PackedFloatTexture); } - + if (hasExtension("GL_EXT_texture_shared_exponent") || caps.contains(Caps.OpenGL30)) { caps.add(Caps.SharedExponentTexture); } - + if (hasExtension("GL_EXT_texture_compression_s3tc")) { caps.add(Caps.TextureCompressionS3TC); } - + if (hasExtension("GL_ARB_ES3_compatibility")) { caps.add(Caps.TextureCompressionETC2); caps.add(Caps.TextureCompressionETC1); } else if (hasExtension("GL_OES_compressed_ETC1_RGB8_texture")) { caps.add(Caps.TextureCompressionETC1); } - + // == end texture format extensions == - + if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) { caps.add(Caps.VertexBufferArray); } - if (hasExtension("GL_ARB_texture_non_power_of_two") || - hasExtension("GL_OES_texture_npot") || - caps.contains(Caps.OpenGL30)) { + if (hasExtension("GL_ARB_texture_non_power_of_two") || + hasExtension("GL_OES_texture_npot") || + caps.contains(Caps.OpenGL30)) { caps.add(Caps.NonPowerOfTwoTextures); } else { logger.log(Level.WARNING, "Your graphics card does not " - + "support non-power-of-2 textures. " - + "Some features might not work."); + + "support non-power-of-2 textures. " + + "Some features might not work."); } - + if (caps.contains(Caps.OpenGLES20)) { // OpenGL ES 2 has some limited support for NPOT textures caps.add(Caps.PartialNonPowerOfTwoTextures); @@ -374,14 +378,14 @@ public class GLRenderer implements Renderer { if (hasExtension("GL_EXT_framebuffer_object") || gl3 != null) { caps.add(Caps.FrameBuffer); - + limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT)); limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT)); - + if (hasExtension("GL_EXT_framebuffer_blit")) { caps.add(Caps.FrameBufferBlit); } - + if (hasExtension("GL_EXT_framebuffer_multisample")) { caps.add(Caps.FrameBufferMultisample); limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT)); @@ -419,10 +423,10 @@ public class GLRenderer implements Renderer { } caps.add(Caps.Multisample); } - + // Supports sRGB pipeline. - if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB")) - || caps.contains(Caps.OpenGL30) ) { + if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB")) + || caps.contains(Caps.OpenGL30) ) { caps.add(Caps.Srgb); } @@ -430,33 +434,33 @@ public class GLRenderer implements Renderer { if (hasExtension("GL_ARB_seamless_cube_map") || caps.contains(Caps.OpenGL32)) { caps.add(Caps.SeamlessCubemap); } - + if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) { caps.add(Caps.CoreProfile); } - + if (hasExtension("GL_ARB_get_program_binary")) { int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS); if (binaryFormats > 0) { caps.add(Caps.BinaryShader); } } - + // Print context information logger.log(Level.INFO, "OpenGL Renderer Information\n" + - " * Vendor: {0}\n" + - " * Renderer: {1}\n" + - " * OpenGL Version: {2}\n" + - " * GLSL Version: {3}\n" + - " * Profile: {4}", - new Object[]{ - gl.glGetString(GL.GL_VENDOR), - gl.glGetString(GL.GL_RENDERER), - gl.glGetString(GL.GL_VERSION), - gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), - caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility" - }); - + " * Vendor: {0}\n" + + " * Renderer: {1}\n" + + " * OpenGL Version: {2}\n" + + " * GLSL Version: {3}\n" + + " * Profile: {4}", + new Object[]{ + gl.glGetString(GL.GL_VENDOR), + gl.glGetString(GL.GL_RENDERER), + gl.glGetString(GL.GL_VERSION), + gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), + caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility" + }); + // Print capabilities (if fine logging is enabled) if (logger.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder(); @@ -467,10 +471,10 @@ public class GLRenderer implements Renderer { } logger.log(Level.FINE, sb.toString()); } - + texUtil.initialize(caps); } - + private void loadCapabilities() { if (gl2 != null) { loadCapabilitiesGL2(); @@ -479,31 +483,31 @@ public class GLRenderer implements Renderer { } loadCapabilitiesCommon(); } - + private int getInteger(int en) { intBuf16.clear(); gl.glGetInteger(en, intBuf16); return intBuf16.get(0); } - + private boolean getBoolean(int en) { gl.glGetBoolean(en, nameBuf); return nameBuf.get(0) != (byte)0; } - + @SuppressWarnings("fallthrough") public void initialize() { loadCapabilities(); - + // Initialize default state.. gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); - + if (caps.contains(Caps.CoreProfile)) { // Core Profile requires VAO to be bound. gl3.glGenVertexArrays(intBuf16); int vaoId = intBuf16.get(0); gl3.glBindVertexArray(vaoId); - } + } if (gl2 != null) { gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); if (!caps.contains(Caps.CoreProfile)) { @@ -536,8 +540,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Render State *| - \*********************************************************************/ + |* Render State *| + \*********************************************************************/ public void setDepthRange(float start, float end) { gl.glDepthRange(start, end); } @@ -602,7 +606,7 @@ public class GLRenderer implements Renderer { } if (state.isDepthTest() && !context.depthTestEnabled) { - gl.glEnable(GL.GL_DEPTH_TEST); + gl.glEnable(GL.GL_DEPTH_TEST); gl.glDepthFunc(convertTestFunction(context.depthFunc)); context.depthTestEnabled = true; } else if (!state.isDepthTest() && context.depthTestEnabled) { @@ -714,7 +718,7 @@ public class GLRenderer implements Renderer { case Color: case Screen: gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR); - break; + break; case Exclusion: gl.glBlendFunc(GL.GL_ONE_MINUS_DST_COLOR, GL.GL_ONE_MINUS_SRC_COLOR); break; @@ -815,8 +819,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Camera and World transforms *| - \*********************************************************************/ + |* Camera and World transforms *| + \*********************************************************************/ public void setViewPort(int x, int y, int w, int h) { if (x != vpX || vpY != y || vpW != w || vpH != h) { gl.glViewport(x, y, w, h); @@ -859,8 +863,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Shaders *| - \*********************************************************************/ + |* Shaders *| + \*********************************************************************/ protected void updateUniformLocation(Shader shader, Uniform uniform) { int loc = gl.glGetUniformLocation(shader.getId(), uniform.getName()); if (loc < 0) { @@ -1040,12 +1044,12 @@ public class GLRenderer implements Renderer { boolean gles2 = caps.contains(Caps.OpenGLES20); String language = source.getLanguage(); - + if (gles2 && !language.equals("GLSL100")) { throw new RendererException("This shader cannot run in OpenGL ES 2. " - + "Only GLSL 1.00 shaders are supported."); + + "Only GLSL 1.00 shaders are supported."); } - + // Upload shader source. // Merge the defines and source code. stringBuf.setLength(0); @@ -1072,14 +1076,14 @@ public class GLRenderer implements Renderer { } } } - + if (linearizeSrgbImages) { stringBuf.append("#define SRGB 1\n"); } - + stringBuf.append(source.getDefines()); stringBuf.append(source.getSource()); - + intBuf1.clear(); intBuf1.put(0, stringBuf.length()); gl.glShaderSource(id, new String[]{ stringBuf.toString() }, intBuf1); @@ -1137,7 +1141,7 @@ public class GLRenderer implements Renderer { // If using GLSL 1.5, we bind the outputs for the user // For versions 3.3 and up, user should use layout qualifiers instead. boolean bindFragDataRequired = false; - + for (ShaderSource source : shader.getSources()) { if (source.isUpdateNeeded()) { updateShaderSourceData(source); @@ -1246,8 +1250,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Framebuffers *| - \*********************************************************************/ + |* Framebuffers *| + \*********************************************************************/ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { copyFrameBuffer(src, dst, true); } @@ -1402,7 +1406,7 @@ public class GLRenderer implements Renderer { } else if (attachmentSlot < 0 || attachmentSlot >= 16) { throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot); } - + return GLFbo.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot; } @@ -1412,7 +1416,7 @@ public class GLRenderer implements Renderer { if (image.isUpdateNeeded()) { // Check NPOT requirements checkNonPowerOfTwo(tex); - + updateTexImageData(image, tex.getType(), 0, false); // NOTE: For depth textures, sets nearest/no-mips mode @@ -1476,7 +1480,7 @@ public class GLRenderer implements Renderer { } checkFrameBufferError(); - + fb.clearUpdateNeeded(); } @@ -1569,7 +1573,7 @@ public class GLRenderer implements Renderer { // update viewport to reflect framebuffer's resolution setViewPort(0, 0, fb.getWidth(), fb.getHeight()); - + if (context.boundFBO != fb.getId()) { glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId()); statistics.onFrameBufferUse(fb, true); @@ -1640,7 +1644,7 @@ public class GLRenderer implements Renderer { public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { readFrameBufferWithGLFormat(fb, byteBuf, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); } - + private void readFrameBufferWithGLFormat(FrameBuffer fb, ByteBuffer byteBuf, int glFormat, int dataType) { if (fb != null) { RenderBuffer rb = fb.getColorBuffer(); @@ -1662,8 +1666,8 @@ public class GLRenderer implements Renderer { gl.glReadPixels(vpX, vpY, vpW, vpH, glFormat, dataType, byteBuf); } - - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { + + public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { GLImageFormat glFormat = texUtil.getImageFormatWithError(format, false); readFrameBufferWithGLFormat(fb, byteBuf, glFormat.format, glFormat.dataType); } @@ -1696,14 +1700,14 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Textures *| - \*********************************************************************/ + |* Textures *| + \*********************************************************************/ private int convertTextureType(Texture.Type type, int samples, int face) { if (samples > 1 && !caps.contains(Caps.TextureMultisample)) { - throw new RendererException("Multisample textures are not supported" + - " by the video hardware."); + throw new RendererException("Multisample textures are not supported" + + " by the video hardware."); } - + switch (type) { case TwoDimensional: if (samples > 1) { @@ -1723,8 +1727,8 @@ public class GLRenderer implements Renderer { } case ThreeDimensional: if (!caps.contains(Caps.OpenGL20)) { - throw new RendererException("3D textures are not supported" + - " by the video hardware."); + throw new RendererException("3D textures are not supported" + + " by the video hardware."); } return GL2.GL_TEXTURE_3D; case CubeMap: @@ -1807,11 +1811,11 @@ public class GLRenderer implements Renderer { int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); boolean haveMips = true; - + if (image != null) { haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps(); } - + // filter things if (image.getLastTextureState().magFilter != tex.getMagFilter()) { int magFilter = convertMagFilter(tex.getMagFilter()); @@ -1834,7 +1838,7 @@ public class GLRenderer implements Renderer { context.seamlessCubemap = false; } } - + if (tex.getAnisotropicFilter() > 1) { if (caps.contains(Caps.TextureFilterAnisotropic)) { gl.glTexParameterf(target, @@ -1871,15 +1875,15 @@ public class GLRenderer implements Renderer { // R to Texture compare mode if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); - gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); + gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); } else { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); } }else{ - //restoring default value - gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); + //restoring default value + gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); } tex.compareModeUpdated(); } @@ -1891,7 +1895,7 @@ public class GLRenderer implements Renderer { * Textures with power-of-2 dimensions are supported on all hardware, however * non-power-of-2 textures may or may not be supported depending on which * texturing features are used. - * + * * @param tex The texture to validate. * @throws RendererException If the texture is not supported by the hardware */ @@ -1900,23 +1904,23 @@ public class GLRenderer implements Renderer { // Texture is power-of-2, safe to use. return; } - + if (caps.contains(Caps.NonPowerOfTwoTextures)) { // Texture is NPOT but it is supported by video hardware. return; } - + // Maybe we have some / partial support for NPOT? if (!caps.contains(Caps.PartialNonPowerOfTwoTextures)) { // Cannot use any type of NPOT texture (uncommon) throw new RendererException("non-power-of-2 textures are not " - + "supported by the video hardware"); + + "supported by the video hardware"); } - + // Partial NPOT supported.. if (tex.getMinFilter().usesMipMapLevels()) { throw new RendererException("non-power-of-2 textures with mip-maps " - + "are not supported by the video hardware"); + + "are not supported by the video hardware"); } switch (tex.getType()) { @@ -1924,7 +1928,7 @@ public class GLRenderer implements Renderer { case ThreeDimensional: if (tex.getWrap(WrapAxis.R) != Texture.WrapMode.EdgeClamp) { throw new RendererException("repeating non-power-of-2 textures " - + "are not supported by the video hardware"); + + "are not supported by the video hardware"); } // fallthrough intentional!!! case TwoDimensionalArray: @@ -1932,17 +1936,17 @@ public class GLRenderer implements Renderer { if (tex.getWrap(WrapAxis.S) != Texture.WrapMode.EdgeClamp || tex.getWrap(WrapAxis.T) != Texture.WrapMode.EdgeClamp) { throw new RendererException("repeating non-power-of-2 textures " - + "are not supported by the video hardware"); + + "are not supported by the video hardware"); } break; default: throw new UnsupportedOperationException("unrecongized texture type"); } } - + /** * Uploads the given image to the GL driver. - * + * * @param img The image to upload * @param type How the data in the image argument should be interpreted. * @param unit The texture slot to be used to upload the image, not important @@ -1968,7 +1972,7 @@ public class GLRenderer implements Renderer { gl.glActiveTexture(GL.GL_TEXTURE0 + unit); context.boundTextureUnit = unit; } - + gl.glBindTexture(target, texId); context.boundTextures[unit] = img; @@ -2011,12 +2015,12 @@ public class GLRenderer implements Renderer { throw new RendererException("Multisample textures are not supported by the video hardware"); } } - + // Check if graphics card doesn't support depth textures if (img.getFormat().isDepthFormat() && !caps.contains(Caps.DepthTexture)) { throw new RendererException("Depth textures are not supported by the video hardware"); } - + if (target == GL.GL_TEXTURE_CUBE_MAP) { // Check max texture size before upload int cubeSize = limits.get(Limits.CubemapSize); @@ -2053,12 +2057,12 @@ public class GLRenderer implements Renderer { if (!caps.contains(Caps.TextureArray)) { throw new RendererException("Texture arrays not supported by graphics hardware"); } - + List data = imageForUpload.getData(); - + // -1 index specifies prepare data for 2D Array texUtil.uploadTexture(imageForUpload, target, -1, linearizeSrgbImages); - + for (int i = 0; i < data.size(); i++) { // upload each slice of 2D array in turn // this time with the appropriate index @@ -2087,21 +2091,21 @@ public class GLRenderer implements Renderer { if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { // Check NPOT requirements boolean scaleToPot = false; - + try { checkNonPowerOfTwo(tex); } catch (RendererException ex) { if (logger.isLoggable(Level.WARNING)) { int nextWidth = FastMath.nearestPowerOfTwo(tex.getImage().getWidth()); int nextHeight = FastMath.nearestPowerOfTwo(tex.getImage().getHeight()); - logger.log(Level.WARNING, - "Non-power-of-2 textures are not supported! Scaling texture '" + tex.getName() + - "' of size " + tex.getImage().getWidth() + "x" + tex.getImage().getHeight() + - " to " + nextWidth + "x" + nextHeight); + logger.log(Level.WARNING, + "Non-power-of-2 textures are not supported! Scaling texture '" + tex.getName() + + "' of size " + tex.getImage().getWidth() + "x" + tex.getImage().getHeight() + + " to " + nextWidth + "x" + nextHeight); } scaleToPot = true; } - + updateTexImageData(image, tex.getType(), unit, scaleToPot); } @@ -2147,8 +2151,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Vertex Buffers and Attributes *| - \*********************************************************************/ + |* Vertex Buffers and Attributes *| + \*********************************************************************/ private int convertUsage(Usage usage) { switch (usage) { case Static: @@ -2222,7 +2226,7 @@ public class GLRenderer implements Renderer { //statistics.onVertexBufferUse(vb, false); } } - + int usage = convertUsage(vb.getUsage()); vb.getData().rewind(); @@ -2283,7 +2287,7 @@ public class GLRenderer implements Renderer { if (context.boundShaderProgram <= 0) { throw new IllegalStateException("Cannot render mesh without shader bound"); } - + Attribute attrib = context.boundShader.getAttribute(vb.getBufferType()); int loc = attrib.getLocation(); if (loc == -1) { @@ -2413,7 +2417,7 @@ public class GLRenderer implements Renderer { // What is this? throw new RendererException("Unexpected format for index buffer: " + indexBuf.getFormat()); } - + if (indexBuf.isUpdateNeeded()) { updateBufferData(indexBuf); } @@ -2487,8 +2491,8 @@ public class GLRenderer implements Renderer { } /*********************************************************************\ - |* Render Calls *| - \*********************************************************************/ + |* Render Calls *| + \*********************************************************************/ public int convertElementMode(Mesh.Mode mode) { switch (mode) { case Points: @@ -2530,7 +2534,7 @@ public class GLRenderer implements Renderer { if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData); } - + if (instanceData != null) { setVertexAttrib(instanceData, null); } @@ -2580,11 +2584,11 @@ public class GLRenderer implements Renderer { } private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { - + // Here while count is still passed in. Can be removed when/if // the method is collapsed again. -pspeed count = Math.max(mesh.getInstanceCount(), count); - + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData); @@ -2602,7 +2606,7 @@ public class GLRenderer implements Renderer { setVertexAttrib(vb, null); } } - + for (VertexBuffer vb : mesh.getBufferList().getArray()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers @@ -2637,7 +2641,7 @@ public class GLRenderer implements Renderer { gl.glLineWidth(mesh.getLineWidth()); context.lineWidth = mesh.getLineWidth(); } - + if (gl4 != null && mesh.getMode().equals(Mode.Patch)) { gl4.glPatchParameter(mesh.getPatchVertexCount()); } @@ -2653,12 +2657,12 @@ public class GLRenderer implements Renderer { // Gamma correction if (!caps.contains(Caps.Srgb) && enableSrgb) { // Not supported, sorry. - logger.warning("sRGB framebuffer is not supported " + - "by video hardware, but was requested."); - + logger.warning("sRGB framebuffer is not supported " + + "by video hardware, but was requested."); + return; } - + setFrameBuffer(null); if (enableSrgb) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java index 37ffe5ed3..a2f21ceaa 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java @@ -54,14 +54,12 @@ final class TextureUtil { private final GL gl; private final GL2 gl2; private final GLExt glext; - private final RenderContext context; private GLImageFormat[][] formats; - - public TextureUtil(GL gl, GL2 gl2, GLExt glext, RenderContext context) { + + public TextureUtil(GL gl, GL2 gl2, GLExt glext) { this.gl = gl; this.gl2 = gl2; this.glext = glext; - this.context = context; } public void initialize(EnumSet caps) { @@ -103,6 +101,33 @@ final class TextureUtil { return glFmt; } + private void setupTextureSwizzle(int target, Format format) { + // Needed for OpenGL 3.3 to support luminance / alpha formats + switch (format) { + case Alpha8: + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_ZERO); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_ZERO); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_ZERO); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_RED); + break; + case Luminance8: + case Luminance16F: + case Luminance32F: + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_ONE); + break; + case Luminance8Alpha8: + case Luminance16FAlpha16F: + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED); + gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_GREEN); + break; + } + } + private void uploadTextureLevel(GLImageFormat format, int target, int level, int slice, int sliceCount, int width, int height, int depth, int samples, ByteBuffer data) { if (format.compressed && data != null) { if (target == GL2.GL_TEXTURE_3D) { @@ -242,6 +267,11 @@ final class TextureUtil { } int samples = image.getMultiSamples(); + + // For OGL3 core: setup texture swizzle. + if (oglFormat.swizzleRequired) { + setupTextureSwizzle(target, jmeFormat); + } for (int i = 0; i < mipSizes.length; i++) { int mipWidth = Math.max(1, width >> i); diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag index 107597047..d2ade5199 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag @@ -37,6 +37,7 @@ varying vec3 SpecularSum; #endif #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) uniform float m_ParallaxHeight; + varying vec3 vViewDirPrlx; #endif #ifdef LIGHTMAP @@ -78,18 +79,18 @@ void main(){ #ifdef STEEP_PARALLAX #ifdef NORMALMAP_PARALLAX //parallax map is stored in the alpha channel of the normal map - newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); + newTexCoord = steepParallaxOffset(m_NormalMap, vViewDirPrlx, texCoord, m_ParallaxHeight); #else //parallax map is a texture - newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); + newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDirPrlx, texCoord, m_ParallaxHeight); #endif #else #ifdef NORMALMAP_PARALLAX //parallax map is stored in the alpha channel of the normal map - newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); + newTexCoord = classicParallaxOffset(m_NormalMap, vViewDirPrlx, texCoord, m_ParallaxHeight); #else //parallax map is a texture - newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); + newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDirPrlx, texCoord, m_ParallaxHeight); #endif #endif #else diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert index f92b91fff..df441ed59 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert @@ -48,6 +48,10 @@ varying vec3 lightVec; uniform vec4 g_LightDirection; #endif +#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) + varying vec3 vViewDirPrlx; +#endif + #ifdef USE_REFLECTION uniform vec3 g_CameraPosition; @@ -107,17 +111,25 @@ void main(){ wvLightPos.w = g_LightPosition.w; vec4 lightColor = g_LightColor; - #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) + #if (defined(NORMALMAP) || defined(PARALLAXMAP)) && !defined(VERTEX_LIGHTING) vec3 wvTangent = normalize(TransformNormal(modelSpaceTan)); vec3 wvBinormal = cross(wvNormal, wvTangent); mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal); + #endif - vViewDir = -wvPosition * tbnMat; + #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) + vViewDir = -wvPosition * tbnMat; + #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) + vViewDirPrlx = vViewDir; + #endif lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec); vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz; #elif !defined(VERTEX_LIGHTING) vNormal = wvNormal; vViewDir = viewDir; + #if defined(PARALLAXMAP) + vViewDirPrlx = -wvPosition * tbnMat; + #endif lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec); #endif diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn index b4ccd3640..69fdec638 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn @@ -2,6 +2,7 @@ ShaderNodeDefinitions{ ShaderNodeDefinition TextureFetch { Type: Fragment Shader GLSL100: Common/MatDefs/ShaderNodes/Basic/texture.frag + Shader GLSL150: Common/MatDefs/ShaderNodes/Basic/texture15.frag Documentation{ Fetches a color value in the given texture acording to given texture coordinates @input texture the texture to read diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/texture15.frag b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/texture15.frag new file mode 100644 index 000000000..b716c3e89 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/texture15.frag @@ -0,0 +1,3 @@ +void main(){ + outColor = texture(texture,texCoord); +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib new file mode 100644 index 000000000..336490696 --- /dev/null +++ b/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib @@ -0,0 +1,14 @@ +#if _VERSION_ >= 150 +out vec4 outFragColor; +# define texture1D texture +# define texture2D texture +# define texture3D texture +# define texture2DLod texture +# if defined VERTEX_SHADER +# define varying out +# define attribute in +# elif defined FRAGMENT_SHADER +# define varying in +# define gl_FragColor outFragColor +# endif +#endif \ No newline at end of file diff --git a/jme3-core/src/plugins/java/com/jme3/asset/plugins/ZipLocator.java b/jme3-core/src/plugins/java/com/jme3/asset/plugins/ZipLocator.java index d20816f34..22e7cc244 100644 --- a/jme3-core/src/plugins/java/com/jme3/asset/plugins/ZipLocator.java +++ b/jme3-core/src/plugins/java/com/jme3/asset/plugins/ZipLocator.java @@ -82,6 +82,7 @@ public class ZipLocator implements AssetLocator { public AssetInfo locate(AssetManager manager, AssetKey key) { String name = key.getName(); + if(name.startsWith("/"))name=name.substring(1); ZipEntry entry = zipfile.getEntry(name); if (entry == null) return null; diff --git a/jme3-examples/src/main/java/jme3test/material/TestParallax.java b/jme3-examples/src/main/java/jme3test/material/TestParallax.java index 37b5e09b0..f5af57f1f 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestParallax.java +++ b/jme3-examples/src/main/java/jme3test/material/TestParallax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2015 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,20 +39,17 @@ import com.jme3.input.controls.KeyTrigger; import com.jme3.light.DirectionalLight; import com.jme3.material.Material; import com.jme3.math.*; -import com.jme3.post.FilterPostProcessor; -import com.jme3.post.filters.FXAAFilter; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Texture.WrapMode; import com.jme3.util.SkyFactory; import com.jme3.util.TangentBinormalGenerator; public class TestParallax extends SimpleApplication { - private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal(); + private final Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal(); public static void main(String[] args) { TestParallax app = new TestParallax(); @@ -60,7 +57,7 @@ public class TestParallax extends SimpleApplication { } public void setupSkyBox() { - rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false)); + rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", SkyFactory.EnvMapType.CubeMap)); } DirectionalLight dl; @@ -75,12 +72,7 @@ public class TestParallax extends SimpleApplication { public void setupFloor() { mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall2.j3m"); - mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat); - mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat); - - // Node floorGeom = (Node) assetManager.loadAsset("Models/WaterTest/WaterTest.mesh.xml"); - //Geometry g = ((Geometry) floorGeom.getChild(0)); - //g.getMesh().scaleTextureCoordinates(new Vector2f(10, 10)); + //mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"); Node floorGeom = new Node("floorGeom"); Quad q = new Quad(100, 100); @@ -100,9 +92,9 @@ public class TestParallax extends SimpleApplication { public void setupSignpost() { Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml"); - Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); + Material matSp = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); TangentBinormalGenerator.generate(signpost); - signpost.setMaterial(mat); + signpost.setMaterial(matSp); signpost.rotate(0, FastMath.HALF_PI, 0); signpost.setLocalTranslation(12, 23.5f, 30); signpost.setLocalScale(4); @@ -116,7 +108,6 @@ public class TestParallax extends SimpleApplication { cam.setRotation(new Quaternion(0.05173137f, 0.92363626f, -0.13454558f, 0.35513034f)); flyCam.setMoveSpeed(30); - setupLighting(); setupSkyBox(); setupFloor(); @@ -124,13 +115,14 @@ public class TestParallax extends SimpleApplication { inputManager.addListener(new AnalogListener() { + @Override public void onAnalog(String name, float value, float tpf) { if ("heightUP".equals(name)) { - parallaxHeigh += 0.0001; + parallaxHeigh += 0.01; mat.setFloat("ParallaxHeight", parallaxHeigh); } if ("heightDown".equals(name)) { - parallaxHeigh -= 0.0001; + parallaxHeigh -= 0.01; parallaxHeigh = Math.max(parallaxHeigh, 0); mat.setFloat("ParallaxHeight", parallaxHeigh); } @@ -142,6 +134,7 @@ public class TestParallax extends SimpleApplication { inputManager.addListener(new ActionListener() { + @Override public void onAction(String name, boolean isPressed, float tpf) { if (isPressed && "toggleSteep".equals(name)) { steep = !steep; diff --git a/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/GZIPSerializer.java b/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/GZIPSerializer.java index 01dbd1d41..7579e84c5 100644 --- a/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/GZIPSerializer.java +++ b/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/GZIPSerializer.java @@ -87,7 +87,8 @@ public class GZIPSerializer extends Serializer { ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream(); GZIPOutputStream gzipOutput = new GZIPOutputStream(byteArrayOutput); - gzipOutput.write(tempBuffer.array()); + tempBuffer.flip(); + gzipOutput.write(tempBuffer.array(), 0, tempBuffer.limit()); gzipOutput.flush(); gzipOutput.finish(); gzipOutput.close(); diff --git a/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/ZIPSerializer.java b/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/ZIPSerializer.java index 1241c1341..ad7d7162d 100644 --- a/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/ZIPSerializer.java +++ b/jme3-networking/src/main/java/com/jme3/network/serializing/serializers/ZIPSerializer.java @@ -98,7 +98,8 @@ public class ZIPSerializer extends Serializer { ZipEntry zipEntry = new ZipEntry("zip"); zipOutput.putNextEntry(zipEntry); - zipOutput.write(tempBuffer.array()); + tempBuffer.flip(); + zipOutput.write(tempBuffer.array(), 0, tempBuffer.limit()); zipOutput.flush(); zipOutput.closeEntry(); zipOutput.close(); diff --git a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java index 2f5d788b4..f832b4266 100644 --- a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java +++ b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java @@ -38,6 +38,8 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import de.lessvoid.nifty.Nifty; import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -45,6 +47,8 @@ import java.io.IOException; */ public class GuiEvent extends AbstractCinematicEvent { + static final Logger log = Logger.getLogger(GuiEvent.class.getName()); + protected String screen; protected Nifty nifty; @@ -76,7 +80,7 @@ public class GuiEvent extends AbstractCinematicEvent { @Override public void onPlay() { - System.out.println("screen should be " + screen); + log.log(Level.FINEST, "screen should be {0}", screen); nifty.gotoScreen(screen); } diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.j3m b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.j3m index 8b54f9e39..41af10431 100644 --- a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.j3m +++ b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.j3m @@ -1,8 +1,7 @@ Material Pong Rock : Common/MatDefs/Light/Lighting.j3md { MaterialParameters { Shininess: 2.0 - DiffuseMap : Textures/Terrain/BrickWall/BrickWall.jpg - NormalMap : Textures/Terrain/BrickWall/BrickWall_normal.jpg - ParallaxMap : Textures/Terrain/BrickWall/BrickWall_height.jpg + DiffuseMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg + ParallaxMap : Repeat Textures/Terrain/BrickWall/BrickWall_height.jpg } } \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall2.j3m b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall2.j3m index 7db3ab893..8f90a9454 100644 --- a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall2.j3m +++ b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall2.j3m @@ -1,8 +1,8 @@ Material Pong Rock : Common/MatDefs/Light/Lighting.j3md { MaterialParameters { Shininess: 2.0 - DiffuseMap : Textures/Terrain/BrickWall/BrickWall.jpg - NormalMap : Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds + DiffuseMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg + NormalMap : Repeat Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds PackedNormalParallax: true } } \ No newline at end of file