* AnimControl now supports abstract animation (doesn't have to be BoneAnimation)

* Misc javadocs
 * Fix for SoundHandleJme.stop()

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7988 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent 6432543395
commit 984721cc75
  1. 15
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  2. 3
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
  3. 21
      engine/src/core/com/jme3/animation/AnimChannel.java
  4. 32
      engine/src/core/com/jme3/animation/AnimControl.java
  5. 14
      engine/src/core/com/jme3/animation/Animation.java
  6. 11
      engine/src/core/com/jme3/animation/BoneAnimation.java
  7. 19
      engine/src/core/com/jme3/animation/MeshAnimation.java
  8. 1
      engine/src/core/com/jme3/app/AppTask.java
  9. 12
      engine/src/core/com/jme3/export/FormatVersion.java
  10. 29
      engine/src/core/com/jme3/export/JmeExporter.java
  11. 43
      engine/src/core/com/jme3/font/BitmapFont.java
  12. 77
      engine/src/core/com/jme3/scene/control/ControlType.java
  13. 1
      engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java
  14. 2
      engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java
  15. 15
      engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java
  16. 6
      engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
  17. 7
      engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
  18. 7
      engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
  19. 3
      engine/src/test/jme3test/TestChooser.java
  20. 2
      engine/src/test/jme3test/effect/TestEverything.java

@ -7,6 +7,7 @@ import java.util.Map.Entry;
import java.util.Set;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneAnimation;
import com.jme3.animation.BoneTrack;
@ -86,7 +87,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
String objectName = objectStructure.getName();
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
if (animationNames != null && animationNames.size() > 0) {
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
ArrayList<Animation> animations = new ArrayList<Animation>();
List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(dataRepository);
@ -112,13 +113,13 @@ import com.jme3.scene.plugins.ogre.AnimData;
return node;
}
AnimData ad = (AnimData) jmeModifierRepresentation;
ArrayList<BoneAnimation> animList = ad.anims;
ArrayList<Animation> animList = ad.anims;
Long modifierArmatureObject = (Long) additionalData;
if (animList != null && animList.size() > 0) {
List<Constraint> constraints = dataRepository.getConstraints(modifierArmatureObject);
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
HashMap<String, Animation> anims = new HashMap<String, Animation>();
for (int i = 0; i < animList.size(); ++i) {
BoneAnimation boneAnimation = animList.get(i).clone();
BoneAnimation boneAnimation = (BoneAnimation) animList.get(i).clone();
// baking constraints into animations
if (constraints != null && constraints.size() > 0) {
@ -156,13 +157,13 @@ import com.jme3.scene.plugins.ogre.AnimData;
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);
// merging animations
HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
HashMap<String, Animation> animations = new HashMap<String, Animation>();
for (String animationName : control.getAnimationNames()) {
animations.put(animationName,
control.getAnim(animationName));
}
for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) {
BoneAnimation ba = animEntry.getValue();
for (Entry<String, Animation> animEntry : anims.entrySet()) {
BoneAnimation ba = (BoneAnimation) animEntry.getValue();
for (int i = 0; i < ba.getTracks().length; ++i) {
BoneTrack bt = ba.getTracks()[i];
int newBoneIndex = bt.getTargetBoneIndex()

@ -1,5 +1,6 @@
package com.jme3.scene.plugins.blender.modifiers;
import com.jme3.animation.Animation;
import java.util.ArrayList;
import java.util.List;
@ -96,7 +97,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
BoneAnimation boneAnimation = new BoneAnimation(
objectAnimationName, stop - start);
boneAnimation.setTracks(tracks);
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(
ArrayList<Animation> animations = new ArrayList<Animation>(
1);
animations.add(boneAnimation);

@ -55,8 +55,8 @@ public final class AnimChannel {
// private ArrayList<Integer> affectedBones;
private BitSet affectedBones;
private BoneAnimation animation;
private BoneAnimation blendFrom;
private Animation animation;
private Animation blendFrom;
private float time;
private float speed;
private float timeBlendFrom;
@ -64,11 +64,13 @@ public final class AnimChannel {
private LoopMode loopMode, loopModeBlendFrom;
private float blendAmount = 1f;
private float blendRate = 0;
private static float clampWrapTime(float t, float max, LoopMode loopMode){
if (max == Float.POSITIVE_INFINITY)
return t;
if (t < 0f){
//float tMod = -(-t % max);
switch (loopMode){
@ -208,7 +210,7 @@ public final class AnimChannel {
if (blendTime < 0f)
throw new IllegalArgumentException("blendTime cannot be less than zero");
BoneAnimation anim = control.animationMap.get(name);
Animation anim = control.animationMap.get(name);
if (anim == null)
throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
@ -310,9 +312,8 @@ public final class AnimChannel {
}
}
void reset(){
animation = null;
blendFrom = null;
BitSet getAffectedBones(){
return affectedBones;
}
void update(float tpf) {
@ -320,7 +321,8 @@ public final class AnimChannel {
return;
if (blendFrom != null){
blendFrom.setTime(timeBlendFrom, control.skeleton, 1f - blendAmount, affectedBones);
blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this);
//blendFrom.setTime(timeBlendFrom, control.skeleton, 1f - blendAmount, affectedBones);
timeBlendFrom += tpf * speedBlendFrom;
timeBlendFrom = clampWrapTime(timeBlendFrom,
blendFrom.getLength(),
@ -337,7 +339,8 @@ public final class AnimChannel {
}
}
animation.setTime(time, control.skeleton, blendAmount, affectedBones);
animation.setTime(time, blendAmount, control, this);
//animation.setTime(time, control.skeleton, blendAmount, affectedBones);
time += tpf * speed;
if (animation.getLength() > 0){

@ -81,7 +81,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
/**
* List of animations
*/
HashMap<String, BoneAnimation> animationMap;
HashMap<String, Animation> animationMap;
/**
* Animation channels
@ -101,8 +101,8 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* @param skeleton The skeleton to animate
*/
public AnimControl(Skeleton skeleton) {
if (skeleton == null)
throw new IllegalArgumentException("skeleton cannot be null");
//if (skeleton == null)
// throw new IllegalArgumentException("skeleton cannot be null");
this.skeleton = skeleton;
reset();
@ -121,10 +121,14 @@ public final class AnimControl extends AbstractControl implements Cloneable {
try {
AnimControl clone = (AnimControl) super.clone();
clone.spatial = spatial;
clone.skeleton = new Skeleton(skeleton);
if (skeleton != null){
clone.skeleton = new Skeleton(skeleton);
}
clone.channels = new ArrayList<AnimChannel>();
// animationMap is reference-copied, animation data should be shared
// to reduce memory usage.
return clone;
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
@ -136,7 +140,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* will be capable of playing. The animations should be compatible
* with the skeleton given in the constructor.
*/
public void setAnimations(HashMap<String, BoneAnimation> animations) {
public void setAnimations(HashMap<String, Animation> animations) {
animationMap = animations;
}
@ -146,7 +150,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* @return The animation corresponding to the given name, or null, if no
* such named animation exists.
*/
public BoneAnimation getAnim(String name) {
public Animation getAnim(String name) {
return animationMap.get(name);
}
@ -155,7 +159,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* <code>AnimControl</code>.
* @param anim The animation to add.
*/
public void addAnim(BoneAnimation anim) {
public void addAnim(Animation anim) {
animationMap.put(anim.getName(), anim);
}
@ -163,7 +167,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* Remove an animation so that it is no longer available for playing.
* @param anim The animation to remove.
*/
public void removeAnim(BoneAnimation anim) {
public void removeAnim(Animation anim) {
if (!animationMap.containsKey(anim.getName())) {
throw new IllegalArgumentException("Given animation does not exist "
+ "in this AnimControl");
@ -305,7 +309,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
* @return The length of time, in seconds, of the named animation.
*/
public float getAnimationLength(String name) {
BoneAnimation a = animationMap.get(name);
Animation a = animationMap.get(name);
if (a == null) {
throw new IllegalArgumentException("The animation " + name
+ " does not exist in this AnimControl");
@ -319,13 +323,17 @@ public final class AnimControl extends AbstractControl implements Cloneable {
*/
@Override
protected void controlUpdate(float tpf) {
skeleton.reset(); // reset skeleton to bind pose
if (skeleton != null){
skeleton.reset(); // reset skeleton to bind pose
}
for (int i = 0; i < channels.size(); i++) {
channels.get(i).update(tpf);
}
skeleton.updateWorldVectors();
if (skeleton != null){
skeleton.updateWorldVectors();
}
}
/**
@ -348,7 +356,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
super.read(im);
InputCapsule in = im.getCapsule(this);
skeleton = (Skeleton) in.readSavable("skeleton", null);
animationMap = (HashMap<String, BoneAnimation>) in.readStringSavableMap("animations", null);
animationMap = (HashMap<String, Animation>) in.readStringSavableMap("animations", null);
if (im.getFormatVersion() == 0){
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split

@ -0,0 +1,14 @@
package com.jme3.animation;
import com.jme3.export.Savable;
public interface Animation extends Savable, Cloneable {
public String getName();
public float getLength();
public void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel);
public Animation clone();
}

@ -48,7 +48,7 @@ import java.util.BitSet;
*
* @author Kirill Vainer
*/
public final class BoneAnimation implements Savable, Cloneable {
public final class BoneAnimation implements Animation, Savable, Cloneable {
private String name;
private float length;
@ -109,11 +109,14 @@ public final class BoneAnimation implements Savable, Cloneable {
return tracks;
}
void setTime(float time, Skeleton skeleton, float weight, BitSet affectedBones) {
public void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel){
BitSet affectedBones = channel.getAffectedBones();
Skeleton skeleton = control.getSkeleton();
for (int i = 0; i < tracks.length; i++) {
if (affectedBones == null
|| affectedBones.get(tracks[i].getTargetBoneIndex())) {
tracks[i].setTime(time, skeleton, weight);
tracks[i].setTime(time, skeleton, blendAmount);
}
}
}
@ -129,7 +132,7 @@ public final class BoneAnimation implements Savable, Cloneable {
try {
result = (BoneAnimation) super.clone();
} catch (CloneNotSupportedException e) {
result = new BoneAnimation(name, length);
throw new AssertionError();
}
if (result.tracks == null) {
result.tracks = new BoneTrack[tracks.length];

@ -39,8 +39,10 @@ import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.scene.Mesh;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MeshAnimation implements Savable {
public class MeshAnimation implements Animation, Savable {
private String name;
private float length;
@ -73,6 +75,10 @@ public class MeshAnimation implements Savable {
}
}
@Override
public void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel) {
// TODO: ...
}
public void write(JmeExporter e) throws IOException {
OutputCapsule out = e.getCapsule(this);
@ -89,4 +95,15 @@ public class MeshAnimation implements Savable {
tracks = (Track[]) in.readSavableArray("tracks", null);
}
@Override
public Animation clone() {
try {
return (Animation) super.clone();
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
}
}
}

@ -71,7 +71,6 @@ public class AppTask<V> implements Future<V> {
}
public boolean cancel(boolean mayInterruptIfRunning) {
// TODO mayInterruptIfRunning was ignored in previous code, should this param be removed?
stateLock.lock();
try {
if (result != null) {

@ -1,8 +1,20 @@
package com.jme3.export;
/**
* Specifies the version of the format for jME3 object (j3o) files.
*
* @author Kirill Vainer
*/
public final class FormatVersion {
/**
* Version number of the format
*/
public static final int VERSION = 1;
/**
* Signature of the format. Currently "JME3" as ASCII
*/
public static final int SIGNATURE = 0x4A4D4533;
private FormatVersion(){

@ -36,10 +36,39 @@ import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* <code>JmeExporter</code> specifies an export implementation for jME3
* data.
*/
public interface JmeExporter {
/**
* Export the {@link Savable} to an OutputStream.
*
* @param object The savable to export
* @param f The output stream
* @return Always returns true. If an error occurs during export,
* an exception is thrown
* @throws IOException If an io exception occurs during export
*/
public boolean save(Savable object, OutputStream f) throws IOException;
/**
* Export the {@link Savable} to a file.
*
* @param object The savable to export
* @param f The file to export to
* @return Always returns true. If an error occurs during export,
* an exception is thrown
* @throws IOException If an io exception occurs during export
*/
public boolean save(Savable object, File f) throws IOException;
/**
* Returns the {@link OutputCapsule} for the given savable object.
*
* @param object The object to retrieve an output capsule for.
* @return
*/
public OutputCapsule getCapsule(Savable object);
}

@ -33,7 +33,6 @@
package com.jme3.font;
import java.io.IOException;
import java.util.Arrays;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
@ -48,11 +47,49 @@ import com.jme3.material.Material;
*/
public class BitmapFont implements Savable {
/**
* Specifies horizontal alignment for text.
*
* @see BitmapText#setAlignment(com.jme3.font.BitmapFont.Align)
*/
public enum Align {
Left, Center, Right
/**
* Align text on the left of the text block
*/
Left,
/**
* Align text in the center of the text block
*/
Center,
/**
* Align text on the right of the text block
*/
Right
}
/**
* Specifies vertical alignment for text.
*
* @see BitmapText#setVerticalAlignment(com.jme3.font.BitmapFont.VAlign)
*/
public enum VAlign {
Top, Center, Bottom
/**
* Align text on the top of the text block
*/
Top,
/**
* Align text in the center of the text block
*/
Center,
/**
* Align text at the bottom of the text block
*/
Bottom
}
private BitmapCharacterSet charSet;

@ -1,77 +0,0 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.control;
/**
* The type of control.
*
* @author Kirill Vainer.
*/
public enum ControlType {
/**
* Manages the level of detail for the model.
*/
LevelOfDetail,
/**
* Provides methods to manipulate the skeleton and bones.
*/
BoneControl,
/**
* Handles the bone animation and skeleton updates.
*/
BoneAnimation,
/**
* Handles attachments to bones
*/
Attachment,
/**
* Handles vertex/morph animation.
*/
VertexAnimation,
/**
* Handles poses or morph keys.
*/
Pose,
/**
* Handles particle updates
*/
Particle
}

@ -3,7 +3,6 @@ package com.jme3.scene.mesh;
import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
import java.nio.Buffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;

@ -50,7 +50,7 @@ public class SoundDeviceJme implements SoundDevice {
}
public SoundHandle loadSound(SoundSystem soundSystem, String filename) {
AudioNode an = new AudioNode(ar, assetManager, filename, false);
AudioNode an = new AudioNode(assetManager, filename, false);
an.setPositional(false);
return new SoundHandleJme(ar, an);
}

@ -41,7 +41,6 @@ import de.lessvoid.nifty.spi.sound.SoundHandle;
public class SoundHandleJme implements SoundHandle {
private AudioNode node;
private AudioRenderer ar;
private AssetManager am;
private String fileName;
private float volume = 1;
@ -50,7 +49,6 @@ public class SoundHandleJme implements SoundHandle {
if (ar == null || node == null)
throw new NullPointerException();
this.ar = ar;
this.node = node;
}
@ -64,7 +62,6 @@ public class SoundHandleJme implements SoundHandle {
if (ar == null || am == null)
throw new NullPointerException();
this.ar = ar;
this.am = am;
if (fileName == null)
throw new NullPointerException();
@ -75,21 +72,21 @@ public class SoundHandleJme implements SoundHandle {
public void play() {
if (fileName != null){
if (node != null){
ar.stopSource(node);
node.stop();
}
node = new AudioNode(ar, am, fileName, true);
node = new AudioNode(am, fileName, true);
node.setPositional(false);
node.setVolume(volume);
ar.playSource(node);
node.play();
}else{
ar.playSourceInstance(node);
node.playInstance();
}
}
public void stop() {
if (fileName != null){
ar.stopSource(node);
if (node != null){
node.stop();
node = null;
}
}

@ -32,16 +32,16 @@
package com.jme3.scene.plugins.ogre;
import com.jme3.animation.BoneAnimation;
import com.jme3.animation.Animation;
import com.jme3.animation.Skeleton;
import java.util.ArrayList;
public class AnimData {
public final Skeleton skeleton;
public final ArrayList<BoneAnimation> anims;
public final ArrayList<Animation> anims;
public AnimData(Skeleton skeleton, ArrayList<BoneAnimation> anims) {
public AnimData(Skeleton skeleton, ArrayList<Animation> anims) {
this.skeleton = skeleton;
this.anims = anims;
}

@ -31,6 +31,7 @@
*/
package com.jme3.scene.plugins.ogre;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
import com.jme3.animation.AnimControl;
import com.jme3.animation.BoneAnimation;
@ -750,10 +751,10 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
}
// Put the animations in the AnimControl
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
ArrayList<BoneAnimation> animList = animData.anims;
HashMap<String, Animation> anims = new HashMap<String, Animation>();
ArrayList<Animation> animList = animData.anims;
for (int i = 0; i < animList.size(); i++) {
BoneAnimation anim = animList.get(i);
Animation anim = animList.get(i);
anims.put(anim.getName(), anim);
}

@ -31,6 +31,7 @@
*/
package com.jme3.scene.plugins.ogre;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneAnimation;
import com.jme3.animation.BoneTrack;
@ -73,7 +74,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader {
private BoneTrack track;
private ArrayList<BoneTrack> tracks = new ArrayList<BoneTrack>();
private BoneAnimation animation;
private ArrayList<BoneAnimation> animations;
private ArrayList<Animation> animations;
private Bone bone;
private Skeleton skeleton;
private ArrayList<Float> times = new ArrayList<Float>();
@ -136,7 +137,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader {
assert elementStack.peek().equals("skeleton");
} else if (qName.equals("animations")) {
assert elementStack.peek().equals("skeleton");
animations = new ArrayList<BoneAnimation>();
animations = new ArrayList<Animation>();
} else if (qName.equals("bones")) {
assert elementStack.peek().equals("skeleton");
} else if (qName.equals("skeleton")) {
@ -273,7 +274,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader {
InputStreamReader r = new InputStreamReader(in);
xr.parse(new InputSource(r));
if (animations == null) {
animations = new ArrayList<BoneAnimation>();
animations = new ArrayList<Animation>();
}
AnimData data = new AnimData(skeleton, animations);
skeleton = null;

@ -196,6 +196,9 @@ public class TestChooser extends JDialog {
} catch (NoSuchMethodException e) {
// class does not have a main method
return null;
} catch (UnsupportedClassVersionError e){
// unsupported version
return null;
}
}
return null;

@ -42,14 +42,12 @@ import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.HDRRenderer;
import com.jme3.renderer.Caps;
import com.jme3.renderer.queue.RenderQueue.Bucket;
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.Spatial.CullHint;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.BasicShadowRenderer;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;

Loading…
Cancel
Save