commit
754c256a66
@ -0,0 +1,81 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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.util.clone; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Provides custom cloning for a particular object type. Once |
||||||
|
* registered with the Cloner, this function object will be called twice |
||||||
|
* for any cloned object that matches the class for which it was registered. |
||||||
|
* It will first call cloneObject() to shallow clone the object and then call |
||||||
|
* cloneFields() to deep clone the object's values. |
||||||
|
* |
||||||
|
* <p>This two step process is important because this is what allows |
||||||
|
* circular references in the cloned object graph.</p> |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public interface CloneFunction<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a shallow clone of the specified object. This is similar |
||||||
|
* to the JmeCloneable.clone() method in semantics and is the first part |
||||||
|
* of a two part cloning process. Once the shallow clone is created, it |
||||||
|
* is cached and CloneFunction.cloneFields() is called. In this way, |
||||||
|
* the CloneFunction interface can completely take over the JmeCloneable |
||||||
|
* style cloning for an object that doesn't otherwise implement that interface. |
||||||
|
* |
||||||
|
* @param cloner The cloner performing the cloning operation. |
||||||
|
* @param original The original object that needs to be cloned. |
||||||
|
*/ |
||||||
|
public T cloneObject( Cloner cloner, T original ); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a deep clone of the specified clone's fields. This is similar |
||||||
|
* to the JmeCloneable.cloneFields() method in semantics and is the second part |
||||||
|
* of a two part cloning process. Once the shallow clone is created, it |
||||||
|
* is cached and CloneFunction.cloneFields() is called. In this way, |
||||||
|
* the CloneFunction interface can completely take over the JmeCloneable |
||||||
|
* style cloning for an object that doesn't otherwise implement that interface. |
||||||
|
* |
||||||
|
* @param cloner The cloner performing the cloning operation. |
||||||
|
* @param clone The clone previously returned from cloneObject(). |
||||||
|
* @param original The original object that was cloned. This is provided for |
||||||
|
* the very special case where field cloning needs to refer to |
||||||
|
* the original object. Mostly the necessary fields should already |
||||||
|
* be on the clone. |
||||||
|
*/ |
||||||
|
public void cloneFields( Cloner cloner, T clone, T original ); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,348 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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.util.clone; |
||||||
|
|
||||||
|
import java.lang.reflect.Array; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.IdentityHashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* A deep clone utility that provides similar object-graph-preserving |
||||||
|
* qualities to typical serialization schemes. An internal registry |
||||||
|
* of cloned objects is kept to be used by other objects in the deep |
||||||
|
* clone process that implement JmeCloneable. |
||||||
|
* |
||||||
|
* <p>By default, objects that do not implement JmeCloneable will |
||||||
|
* be treated like normal Java Cloneable objects. If the object does |
||||||
|
* not implement the JmeCloneable or the regular JDK Cloneable interfaces |
||||||
|
* AND has no special handling defined then an IllegalArgumentException |
||||||
|
* will be thrown.</p> |
||||||
|
* |
||||||
|
* <p>Enhanced object cloning is done in a two step process. First, |
||||||
|
* the object is cloned using the normal Java clone() method and stored |
||||||
|
* in the clone registry. After that, if it implements JmeCloneable then |
||||||
|
* its cloneFields() method is called to deep clone any of the fields. |
||||||
|
* This two step process has a few benefits. First, it means that objects |
||||||
|
* can easily have a regular shallow clone implementation just like any |
||||||
|
* normal Java objects. Second, the deep cloning of fields happens after |
||||||
|
* creation wich means that the clone is available to future field cloning |
||||||
|
* to resolve circular references.</p> |
||||||
|
* |
||||||
|
* <p>Similar to Java serialization, the handling of specific object |
||||||
|
* types can be customized. This allows certain objects to be cloned gracefully |
||||||
|
* even if they aren't normally Cloneable. This can also be used as a |
||||||
|
* sort of filter to keep certain types of objects from being cloned. |
||||||
|
* (For example, adding the IdentityCloneFunction for Mesh.class would cause |
||||||
|
* all mesh instances to be shared with the original object graph.)</p> |
||||||
|
* |
||||||
|
* <p>By default, the Cloner registers serveral default clone functions |
||||||
|
* as follows:</p> |
||||||
|
* <ul> |
||||||
|
* <li>java.util.ArrayList: ListCloneFunction |
||||||
|
* <li>java.util.LinkedList: ListCloneFunction |
||||||
|
* <li>java.util.concurrent.CopyOnWriteArrayList: ListCloneFunction |
||||||
|
* <li>java.util.Vector: ListCloneFunction |
||||||
|
* <li>java.util.Stack: ListCloneFunction |
||||||
|
* <li>com.jme3.util.SafeArrayList: ListCloneFunction |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* <p>Usage:</p> |
||||||
|
* <pre> |
||||||
|
* // Example 1: using an instantiated, reusable cloner.
|
||||||
|
* Cloner cloner = new Cloner(); |
||||||
|
* Foo fooClone = cloner.clone(foo); |
||||||
|
* cloner.clearIndex(); // prepare it for reuse
|
||||||
|
* Foo fooClone2 = cloner.clone(foo); |
||||||
|
* |
||||||
|
* // Example 2: using the utility method that self-instantiates a temporary cloner.
|
||||||
|
* Foo fooClone = Cloner.deepClone(foo); |
||||||
|
* |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public class Cloner { |
||||||
|
|
||||||
|
/** |
||||||
|
* Keeps track of the objects that have been cloned so far. |
||||||
|
*/ |
||||||
|
private IdentityHashMap<Object, Object> index = new IdentityHashMap<Object, Object>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Custom functions for cloning objects. |
||||||
|
*/ |
||||||
|
private Map<Class, CloneFunction> functions = new HashMap<Class, CloneFunction>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Cache the clone methods once for all cloners. |
||||||
|
*/ |
||||||
|
private static final Map<Class, Method> methodCache = new ConcurrentHashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new cloner with only default clone functions and an empty |
||||||
|
* object index. |
||||||
|
*/ |
||||||
|
public Cloner() { |
||||||
|
// Register some standard types
|
||||||
|
ListCloneFunction listFunction = new ListCloneFunction(); |
||||||
|
functions.put(java.util.ArrayList.class, listFunction); |
||||||
|
functions.put(java.util.LinkedList.class, listFunction); |
||||||
|
functions.put(java.util.concurrent.CopyOnWriteArrayList.class, listFunction); |
||||||
|
functions.put(java.util.Vector.class, listFunction); |
||||||
|
functions.put(java.util.Stack.class, listFunction); |
||||||
|
functions.put(com.jme3.util.SafeArrayList.class, listFunction); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience utility function that creates a new Cloner, uses it to |
||||||
|
* deep clone the object, and then returns the result. |
||||||
|
*/ |
||||||
|
public static <T> T deepClone( T object ) { |
||||||
|
return new Cloner().clone(object); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deeps clones the specified object, reusing previous clones when possible. |
||||||
|
* |
||||||
|
* <p>Object cloning priority works as follows:</p> |
||||||
|
* <ul> |
||||||
|
* <li>If the object has already been cloned then its clone is returned. |
||||||
|
* <li>If there is a custom CloneFunction then it is called to clone the object. |
||||||
|
* <li>If the object implements Cloneable then its clone() method is called, arrays are |
||||||
|
* deep cloned with entries passing through clone(). |
||||||
|
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the |
||||||
|
* clone. |
||||||
|
* <li>Else an IllegalArgumentException is thrown. |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* Note: objects returned by this method may not have yet had their cloneField() |
||||||
|
* method called. |
||||||
|
*/ |
||||||
|
public <T> T clone( T object ) { |
||||||
|
return clone(object, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Internal method to work around a Java generics typing issue by |
||||||
|
* isolating the 'bad' case into a method with suppressed warnings. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
private <T> Class<T> objectClass( T object ) { |
||||||
|
// This should be 100% allowed without a cast but Java generics
|
||||||
|
// is not that smart sometimes.
|
||||||
|
// Wrapping it in a method at least isolates the warning suppression
|
||||||
|
return (Class<T>)object.getClass(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deeps clones the specified object, reusing previous clones when possible. |
||||||
|
* |
||||||
|
* <p>Object cloning priority works as follows:</p> |
||||||
|
* <ul> |
||||||
|
* <li>If the object has already been cloned then its clone is returned. |
||||||
|
* <li>If useFunctions is true and there is a custom CloneFunction then it is |
||||||
|
* called to clone the object. |
||||||
|
* <li>If the object implements Cloneable then its clone() method is called, arrays are |
||||||
|
* deep cloned with entries passing through clone(). |
||||||
|
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the |
||||||
|
* clone. |
||||||
|
* <li>Else an IllegalArgumentException is thrown. |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* <p>The abililty to selectively use clone functions is useful when |
||||||
|
* being called from a clone function.</p> |
||||||
|
* |
||||||
|
* Note: objects returned by this method may not have yet had their cloneField() |
||||||
|
* method called. |
||||||
|
*/ |
||||||
|
public <T> T clone( T object, boolean useFunctions ) { |
||||||
|
if( object == null ) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
Class<T> type = objectClass(object); |
||||||
|
|
||||||
|
// Check the index to see if we already have it
|
||||||
|
Object clone = index.get(object); |
||||||
|
if( clone != null ) { |
||||||
|
return type.cast(clone); |
||||||
|
} |
||||||
|
|
||||||
|
// See if there is a custom function... that trumps everything.
|
||||||
|
CloneFunction<T> f = getCloneFunction(type); |
||||||
|
if( f != null ) { |
||||||
|
T result = f.cloneObject(this, object); |
||||||
|
|
||||||
|
// Store the object in the identity map so that any circular references
|
||||||
|
// are resolvable.
|
||||||
|
index.put(object, result); |
||||||
|
|
||||||
|
// Now call the function again to deep clone the fields
|
||||||
|
f.cloneFields(this, result, object); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
if( object.getClass().isArray() ) { |
||||||
|
// Perform an array clone
|
||||||
|
clone = arrayClone(object); |
||||||
|
|
||||||
|
// Array clone already indexes the clone
|
||||||
|
} else if( object instanceof JmeCloneable ) { |
||||||
|
// Use the two-step cloning semantics
|
||||||
|
clone = ((JmeCloneable)object).jmeClone(); |
||||||
|
|
||||||
|
// Store the object in the identity map so that any circular references
|
||||||
|
// are resolvable
|
||||||
|
index.put(object, clone); |
||||||
|
|
||||||
|
((JmeCloneable)clone).cloneFields(this, object); |
||||||
|
} else if( object instanceof Cloneable ) { |
||||||
|
|
||||||
|
// Perform a regular Java shallow clone
|
||||||
|
try { |
||||||
|
clone = javaClone(object); |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new IllegalArgumentException("Object is not cloneable, type:" + type, e); |
||||||
|
} |
||||||
|
|
||||||
|
// Store the object in the identity map so that any circular references
|
||||||
|
// are resolvable
|
||||||
|
index.put(object, clone); |
||||||
|
} else { |
||||||
|
throw new IllegalArgumentException("Object is not cloneable, type:" + type); |
||||||
|
} |
||||||
|
|
||||||
|
return type.cast(clone); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets a custom CloneFunction for the exact Java type. Note: no inheritence |
||||||
|
* checks are performed so a function must be registered for each specific type |
||||||
|
* that it handles. By default ListCloneFunction is registered for |
||||||
|
* ArrayList, LinkedList, CopyOnWriteArrayList, Vector, Stack, and JME's SafeArrayList. |
||||||
|
*/ |
||||||
|
public <T> void setCloneFunction( Class<T> type, CloneFunction<T> function ) { |
||||||
|
if( function == null ) { |
||||||
|
functions.remove(type); |
||||||
|
} else { |
||||||
|
functions.put(type, function); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a previously registered clone function for the specified type or null |
||||||
|
* if there is no custom clone function for the type. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public <T> CloneFunction<T> getCloneFunction( Class<T> type ) { |
||||||
|
return (CloneFunction<T>)functions.get(type); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears the object index allowing the cloner to be reused for a brand new |
||||||
|
* cloning operation. |
||||||
|
*/ |
||||||
|
public void clearIndex() { |
||||||
|
index.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a raw shallow Java clone using reflection. This call does NOT |
||||||
|
* check against the clone index and so will return new objects every time |
||||||
|
* it is called. That's because these are shallow clones and have not (and may |
||||||
|
* not ever, depending on the caller) get resolved. |
||||||
|
* |
||||||
|
* <p>This method is provided as a convenient way for CloneFunctions to call |
||||||
|
* clone() and objects without necessarily knowing their real type.</p> |
||||||
|
*/ |
||||||
|
public <T> T javaClone( T object ) throws CloneNotSupportedException { |
||||||
|
Method m = methodCache.get(object.getClass()); |
||||||
|
if( m == null ) { |
||||||
|
try { |
||||||
|
// Lookup the method and cache it
|
||||||
|
m = object.getClass().getMethod("clone"); |
||||||
|
} catch( NoSuchMethodException e ) { |
||||||
|
throw new CloneNotSupportedException("No public clone method found for:" + object.getClass()); |
||||||
|
} |
||||||
|
methodCache.put(object.getClass(), m); |
||||||
|
|
||||||
|
// Note: yes we might cache the method twice... but so what?
|
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
Class<? extends T> type = objectClass(object); |
||||||
|
return type.cast(m.invoke(object)); |
||||||
|
} catch( IllegalAccessException | InvocationTargetException e ) { |
||||||
|
throw new RuntimeException("Error cloning object of type:" + object.getClass(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clones a primitive array by coping it and clones an object |
||||||
|
* array by coping it and then running each of its values through |
||||||
|
* Cloner.clone(). |
||||||
|
*/ |
||||||
|
protected <T> T arrayClone( T object ) { |
||||||
|
|
||||||
|
// Java doesn't support the cloning of arrays through reflection unless
|
||||||
|
// you open access to Object's protected clone array... which requires
|
||||||
|
// elevated privileges. So we will do a work-around that is slightly less
|
||||||
|
// elegant.
|
||||||
|
// This should be 100% allowed without a case but Java generics
|
||||||
|
// is not that smart
|
||||||
|
Class<T> type = objectClass(object); |
||||||
|
Class elementType = type.getComponentType(); |
||||||
|
int size = Array.getLength(object); |
||||||
|
Object clone = Array.newInstance(elementType, size); |
||||||
|
|
||||||
|
// Store the clone for later lookups
|
||||||
|
index.put(object, clone); |
||||||
|
|
||||||
|
if( elementType.isPrimitive() ) { |
||||||
|
// Then our job is a bit easier
|
||||||
|
System.arraycopy(object, 0, clone, 0, size); |
||||||
|
} else { |
||||||
|
// Else it's an object array so we'll clone it and its children
|
||||||
|
for( int i = 0; i < size; i++ ) { |
||||||
|
Object element = clone(Array.get(object, i)); |
||||||
|
Array.set(clone, i, element); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return type.cast(clone); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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.util.clone; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* A CloneFunction implementation that simply returns the |
||||||
|
* the passed object without cloning it. This is useful for |
||||||
|
* forcing some object types (like Meshes) to be shared between |
||||||
|
* the original and cloned object graph. |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public class IdentityCloneFunction<T> implements CloneFunction<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the object directly. |
||||||
|
*/ |
||||||
|
public T cloneObject( Cloner cloner, T object ) { |
||||||
|
return object; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Does nothing. |
||||||
|
*/ |
||||||
|
public void cloneFields( Cloner cloner, T clone, T object ) { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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.util.clone; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates an object that wishes to more actively participate in the |
||||||
|
* two-part deep copying process provided by the Cloner. Objects implementing |
||||||
|
* this interface can access the already cloned object graph to resolve |
||||||
|
* their local dependencies in a way that will be equivalent to the |
||||||
|
* original object graph. In other words, if two objects in the graph |
||||||
|
* share the same target reference then the cloned version will share |
||||||
|
* the cloned reference. |
||||||
|
* |
||||||
|
* <p>For example, if an object wishes to deep clone one of its fields |
||||||
|
* then it will call cloner.clone(object) instead of object.clone(). |
||||||
|
* The cloner will keep track of any clones already created for 'object' |
||||||
|
* and return that instead of a new clone.</p> |
||||||
|
* |
||||||
|
* <p>Cloning of a JmeCloneable object is done in two parts. First, |
||||||
|
* the standard Java clone() method is called to create a shallow clone |
||||||
|
* of the object. Second, the cloner wil lcall the cloneFields() method |
||||||
|
* to let the object deep clone any of its fields that should be cloned.</p> |
||||||
|
* |
||||||
|
* <p>This two part process is necessary to facilitate circular references. |
||||||
|
* When an object calls cloner.clone() during its cloneFields() method, it |
||||||
|
* may get only a shallow copy that will be filled in later.</p> |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public interface JmeCloneable extends Cloneable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a regular shallow clone of the object. Some fields |
||||||
|
* may also be cloned but generally only if they will never be |
||||||
|
* shared with other objects. (For example, local Vector3fs and so on.) |
||||||
|
* |
||||||
|
* <p>This method is separate from the regular clone() method |
||||||
|
* so that objects might still maintain their own regular java clone() |
||||||
|
* semantics (perhaps even using Cloner for those methods). However, |
||||||
|
* because Java's clone() has specific features in the sense of Object's |
||||||
|
* clone() implementation, it's usually best to have some path for |
||||||
|
* subclasses to bypass the public clone() method that might be cloning |
||||||
|
* fields and instead get at the superclass protected clone() methods. |
||||||
|
* For example, through super.jmeClone() or another protected clone |
||||||
|
* method that some base class eventually calls super.clone() in.</p> |
||||||
|
*/ |
||||||
|
public Object jmeClone(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Implemented to perform deep cloning for this object, resolving |
||||||
|
* local cloned references using the specified cloner. The object |
||||||
|
* can call cloner.clone(fieldValue) to deep clone any of its fields. |
||||||
|
* |
||||||
|
* <p>Note: during normal clone operations the original object |
||||||
|
* will not be needed as the clone has already had all of the fields |
||||||
|
* shallow copied.</p> |
||||||
|
* |
||||||
|
* @param cloner The cloner that is performing the cloning operation. The |
||||||
|
* cloneFields method can call back into the cloner to make |
||||||
|
* clones if its subordinate fields. |
||||||
|
* @param original The original object from which this object was cloned. |
||||||
|
* This is provided for the very rare case that this object needs |
||||||
|
* to refer to its original for some reason. In general, all of |
||||||
|
* the relevant values should have been transferred during the |
||||||
|
* shallow clone and this object need merely clone what it wants. |
||||||
|
*/ |
||||||
|
public void cloneFields( Cloner cloner, Object original ); |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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.util.clone; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* A CloneFunction implementation that deep clones a list by |
||||||
|
* creating a new list and cloning its values using the cloner. |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public class ListCloneFunction<T extends List> implements CloneFunction<T> { |
||||||
|
|
||||||
|
public T cloneObject( Cloner cloner, T object ) { |
||||||
|
try { |
||||||
|
T clone = cloner.javaClone(object); |
||||||
|
return clone; |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new IllegalArgumentException("Clone not supported for type:" + object.getClass(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clones the elements of the list. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public void cloneFields( Cloner cloner, T clone, T object ) { |
||||||
|
for( int i = 0; i < clone.size(); i++ ) { |
||||||
|
// Need to clone the clones... because T might
|
||||||
|
// have done something special in its clone method that
|
||||||
|
// we will have to adhere to. For example, clone may have nulled
|
||||||
|
// out some things or whatever that might be implementation specific.
|
||||||
|
// At any rate, if it's a proper clone then the clone will already
|
||||||
|
// have shallow versions of the elements that we can clone.
|
||||||
|
clone.set(i, cloner.clone(clone.get(i))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,80 +0,0 @@ |
|||||||
#import "Common/ShaderLib/Shadows15.glsllib" |
|
||||||
|
|
||||||
out vec4 outFragColor; |
|
||||||
|
|
||||||
#if defined(PSSM) || defined(FADE) |
|
||||||
in float shadowPosition; |
|
||||||
#endif |
|
||||||
|
|
||||||
in vec4 projCoord0; |
|
||||||
in vec4 projCoord1; |
|
||||||
in vec4 projCoord2; |
|
||||||
in vec4 projCoord3; |
|
||||||
|
|
||||||
#ifdef POINTLIGHT |
|
||||||
in vec4 projCoord4; |
|
||||||
in vec4 projCoord5; |
|
||||||
in vec4 worldPos; |
|
||||||
uniform vec3 m_LightPos; |
|
||||||
#else |
|
||||||
#ifndef PSSM |
|
||||||
in float lightDot; |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA |
|
||||||
#ifdef COLOR_MAP |
|
||||||
uniform sampler2D m_ColorMap; |
|
||||||
#else |
|
||||||
uniform sampler2D m_DiffuseMap; |
|
||||||
#endif |
|
||||||
uniform float m_AlphaDiscardThreshold; |
|
||||||
varying vec2 texCoord; |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef FADE |
|
||||||
uniform vec2 m_FadeInfo; |
|
||||||
#endif |
|
||||||
|
|
||||||
void main(){ |
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA |
|
||||||
#ifdef COLOR_MAP |
|
||||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
|
||||||
#else |
|
||||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
|
||||||
#endif |
|
||||||
|
|
||||||
if(alpha < m_AlphaDiscardThreshold){ |
|
||||||
discard; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
float shadow = 1.0; |
|
||||||
#ifdef POINTLIGHT |
|
||||||
shadow = getPointLightShadows(worldPos, m_LightPos, |
|
||||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, |
|
||||||
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); |
|
||||||
#else |
|
||||||
#ifdef PSSM |
|
||||||
shadow = getDirectionalLightShadows(m_Splits, shadowPosition, |
|
||||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, |
|
||||||
projCoord0, projCoord1, projCoord2, projCoord3); |
|
||||||
#else |
|
||||||
//spotlight |
|
||||||
if(lightDot < 0){ |
|
||||||
outFragColor = vec4(1.0); |
|
||||||
return; |
|
||||||
} |
|
||||||
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef FADE |
|
||||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
|
||||||
#endif |
|
||||||
|
|
||||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
|
||||||
outFragColor = vec4(shadow, shadow, shadow, 1.0); |
|
||||||
} |
|
||||||
|
|
@ -1,82 +0,0 @@ |
|||||||
#import "Common/ShaderLib/Instancing.glsllib" |
|
||||||
#import "Common/ShaderLib/Skinning.glsllib" |
|
||||||
uniform mat4 m_LightViewProjectionMatrix0; |
|
||||||
uniform mat4 m_LightViewProjectionMatrix1; |
|
||||||
uniform mat4 m_LightViewProjectionMatrix2; |
|
||||||
uniform mat4 m_LightViewProjectionMatrix3; |
|
||||||
|
|
||||||
|
|
||||||
out vec4 projCoord0; |
|
||||||
out vec4 projCoord1; |
|
||||||
out vec4 projCoord2; |
|
||||||
out vec4 projCoord3; |
|
||||||
|
|
||||||
#ifdef POINTLIGHT |
|
||||||
uniform mat4 m_LightViewProjectionMatrix4; |
|
||||||
uniform mat4 m_LightViewProjectionMatrix5; |
|
||||||
out vec4 projCoord4; |
|
||||||
out vec4 projCoord5; |
|
||||||
out vec4 worldPos; |
|
||||||
#else |
|
||||||
#ifndef PSSM |
|
||||||
uniform vec3 m_LightPos; |
|
||||||
uniform vec3 m_LightDir; |
|
||||||
out float lightDot; |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
|
|
||||||
#if defined(PSSM) || defined(FADE) |
|
||||||
out float shadowPosition; |
|
||||||
#endif |
|
||||||
out vec3 lightVec; |
|
||||||
|
|
||||||
out vec2 texCoord; |
|
||||||
|
|
||||||
in vec3 inPosition; |
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA |
|
||||||
in vec2 inTexCoord; |
|
||||||
#endif |
|
||||||
|
|
||||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, |
|
||||||
0.0, 0.5, 0.0, 0.0, |
|
||||||
0.0, 0.0, 0.5, 0.0, |
|
||||||
0.5, 0.5, 0.5, 1.0); |
|
||||||
|
|
||||||
|
|
||||||
void main(){ |
|
||||||
vec4 modelSpacePos = vec4(inPosition, 1.0); |
|
||||||
|
|
||||||
#ifdef NUM_BONES |
|
||||||
Skinning_Compute(modelSpacePos); |
|
||||||
#endif |
|
||||||
gl_Position = TransformWorldViewProjection(modelSpacePos); |
|
||||||
|
|
||||||
#if defined(PSSM) || defined(FADE) |
|
||||||
shadowPosition = gl_Position.z; |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifndef POINTLIGHT |
|
||||||
vec4 worldPos=vec4(0.0); |
|
||||||
#endif |
|
||||||
// get the vertex in world space |
|
||||||
worldPos = TransformWorld(modelSpacePos); |
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA |
|
||||||
texCoord = inTexCoord; |
|
||||||
#endif |
|
||||||
// populate the light view matrices array and convert vertex to light viewProj space |
|
||||||
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; |
|
||||||
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; |
|
||||||
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; |
|
||||||
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; |
|
||||||
#ifdef POINTLIGHT |
|
||||||
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; |
|
||||||
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; |
|
||||||
#else |
|
||||||
#ifndef PSSM |
|
||||||
vec3 lightDir = worldPos.xyz - m_LightPos; |
|
||||||
lightDot = dot(m_LightDir,lightDir); |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
} |
|
@ -1,242 +0,0 @@ |
|||||||
// Because gpu_shader5 is actually where those |
|
||||||
// gather functions are declared to work on shadowmaps |
|
||||||
#extension GL_ARB_gpu_shader5 : enable |
|
||||||
|
|
||||||
#ifdef HARDWARE_SHADOWS |
|
||||||
#define SHADOWMAP sampler2DShadow |
|
||||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset) |
|
||||||
#define SHADOWCOMPARE(tex,coord) textureProj(tex, coord) |
|
||||||
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) |
|
||||||
#else |
|
||||||
#define SHADOWMAP sampler2D |
|
||||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r) |
|
||||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r) |
|
||||||
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy)) |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
#if FILTER_MODE == 0 |
|
||||||
#define GETSHADOW Shadow_Nearest |
|
||||||
#define KERNEL 1.0 |
|
||||||
#elif FILTER_MODE == 1 |
|
||||||
#ifdef HARDWARE_SHADOWS |
|
||||||
#define GETSHADOW Shadow_Nearest |
|
||||||
#else |
|
||||||
#define GETSHADOW Shadow_DoBilinear_2x2 |
|
||||||
#endif |
|
||||||
#define KERNEL 1.0 |
|
||||||
#elif FILTER_MODE == 2 |
|
||||||
#define GETSHADOW Shadow_DoDither_2x2 |
|
||||||
#define KERNEL 1.0 |
|
||||||
#elif FILTER_MODE == 3 |
|
||||||
#define GETSHADOW Shadow_DoPCF |
|
||||||
#define KERNEL 4.0 |
|
||||||
#elif FILTER_MODE == 4 |
|
||||||
#define GETSHADOW Shadow_DoPCFPoisson |
|
||||||
#define KERNEL 4.0 |
|
||||||
#elif FILTER_MODE == 5 |
|
||||||
#define GETSHADOW Shadow_DoPCF |
|
||||||
#define KERNEL 8.0 |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uniform SHADOWMAP m_ShadowMap0; |
|
||||||
uniform SHADOWMAP m_ShadowMap1; |
|
||||||
uniform SHADOWMAP m_ShadowMap2; |
|
||||||
uniform SHADOWMAP m_ShadowMap3; |
|
||||||
#ifdef POINTLIGHT |
|
||||||
uniform SHADOWMAP m_ShadowMap4; |
|
||||||
uniform SHADOWMAP m_ShadowMap5; |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef PSSM |
|
||||||
uniform vec4 m_Splits; |
|
||||||
#endif |
|
||||||
uniform float m_ShadowIntensity; |
|
||||||
|
|
||||||
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); |
|
||||||
float shadowBorderScale = 1.0; |
|
||||||
|
|
||||||
float Shadow_BorderCheck(in vec2 coord){ |
|
||||||
// Fastest, "hack" method (uses 4-5 instructions) |
|
||||||
vec4 t = vec4(coord.xy, 0.0, 1.0); |
|
||||||
t = step(t.wwxy, t.xyzz); |
|
||||||
return dot(t,t); |
|
||||||
} |
|
||||||
|
|
||||||
float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){ |
|
||||||
float border = Shadow_BorderCheck(projCoord.xy); |
|
||||||
if (border > 0.0){ |
|
||||||
return 1.0; |
|
||||||
} |
|
||||||
return SHADOWCOMPARE(tex,projCoord); |
|
||||||
} |
|
||||||
|
|
||||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ |
|
||||||
float border = Shadow_BorderCheck(projCoord.xy); |
|
||||||
if (border > 0.0) |
|
||||||
return 1.0; |
|
||||||
|
|
||||||
vec2 pixSize = pixSize2 * shadowBorderScale; |
|
||||||
|
|
||||||
float shadow = 0.0; |
|
||||||
ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw)); |
|
||||||
shadow *= 0.25; |
|
||||||
return shadow; |
|
||||||
} |
|
||||||
|
|
||||||
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ |
|
||||||
float border = Shadow_BorderCheck(projCoord.xy); |
|
||||||
if (border > 0.0) |
|
||||||
return 1.0; |
|
||||||
|
|
||||||
#ifdef GL_ARB_gpu_shader5 |
|
||||||
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); |
|
||||||
vec4 gather = SHADOWGATHER(tex, coord); |
|
||||||
#else |
|
||||||
vec4 gather = vec4(0.0); |
|
||||||
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1)); |
|
||||||
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1)); |
|
||||||
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0)); |
|
||||||
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0)); |
|
||||||
#endif |
|
||||||
|
|
||||||
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE ); |
|
||||||
vec2 mx = mix( gather.wx, gather.zy, f.x ); |
|
||||||
return mix( mx.x, mx.y, f.y ); |
|
||||||
} |
|
||||||
|
|
||||||
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ |
|
||||||
|
|
||||||
vec2 pixSize = pixSize2 * shadowBorderScale; |
|
||||||
float shadow = 0.0; |
|
||||||
float border = Shadow_BorderCheck(projCoord.xy); |
|
||||||
if (border > 0.0) |
|
||||||
return 1.0; |
|
||||||
|
|
||||||
float bound = KERNEL * 0.5 - 0.5; |
|
||||||
bound *= PCFEDGE; |
|
||||||
for (float y = -bound; y <= bound; y += PCFEDGE){ |
|
||||||
for (float x = -bound; x <= bound; x += PCFEDGE){ |
|
||||||
vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw); |
|
||||||
shadow += SHADOWCOMPARE(tex, coord); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
shadow = shadow / (KERNEL * KERNEL); |
|
||||||
return shadow; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
//12 tap poisson disk |
|
||||||
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); |
|
||||||
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); |
|
||||||
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); |
|
||||||
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); |
|
||||||
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); |
|
||||||
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); |
|
||||||
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); |
|
||||||
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); |
|
||||||
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); |
|
||||||
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); |
|
||||||
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); |
|
||||||
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419); |
|
||||||
|
|
||||||
|
|
||||||
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ |
|
||||||
|
|
||||||
float shadow = 0.0; |
|
||||||
float border = Shadow_BorderCheck(projCoord.xy); |
|
||||||
if (border > 0.0){ |
|
||||||
return 1.0; |
|
||||||
} |
|
||||||
|
|
||||||
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale; |
|
||||||
|
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw)); |
|
||||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw)); |
|
||||||
|
|
||||||
//this is divided by 12 |
|
||||||
return shadow * 0.08333333333; |
|
||||||
} |
|
||||||
|
|
||||||
#ifdef POINTLIGHT |
|
||||||
float getPointLightShadows(in vec4 worldPos,in vec3 lightPos, |
|
||||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5, |
|
||||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){ |
|
||||||
float shadow = 1.0; |
|
||||||
vec3 vect = worldPos.xyz - lightPos; |
|
||||||
vec3 absv= abs(vect); |
|
||||||
float maxComp = max(absv.x,max(absv.y,absv.z)); |
|
||||||
if(maxComp == absv.y){ |
|
||||||
if(vect.y < 0.0){ |
|
||||||
shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w); |
|
||||||
}else{ |
|
||||||
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w); |
|
||||||
} |
|
||||||
}else if(maxComp == absv.z){ |
|
||||||
if(vect.z < 0.0){ |
|
||||||
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w); |
|
||||||
}else{ |
|
||||||
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w); |
|
||||||
} |
|
||||||
}else if(maxComp == absv.x){ |
|
||||||
if(vect.x < 0.0){ |
|
||||||
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w); |
|
||||||
}else{ |
|
||||||
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w); |
|
||||||
} |
|
||||||
} |
|
||||||
return shadow; |
|
||||||
} |
|
||||||
#else |
|
||||||
#ifdef PSSM |
|
||||||
float getDirectionalLightShadows(in vec4 splits,in float shadowPosition, |
|
||||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3, |
|
||||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){ |
|
||||||
float shadow = 1.0; |
|
||||||
if(shadowPosition < splits.x){ |
|
||||||
shadow = GETSHADOW(shadowMap0, projCoord0 ); |
|
||||||
}else if( shadowPosition < splits.y){ |
|
||||||
shadowBorderScale = 0.5; |
|
||||||
shadow = GETSHADOW(shadowMap1, projCoord1); |
|
||||||
}else if( shadowPosition < splits.z){ |
|
||||||
shadowBorderScale = 0.25; |
|
||||||
shadow = GETSHADOW(shadowMap2, projCoord2); |
|
||||||
}else if( shadowPosition < splits.w){ |
|
||||||
shadowBorderScale = 0.125; |
|
||||||
shadow = GETSHADOW(shadowMap3, projCoord3); |
|
||||||
} |
|
||||||
return shadow; |
|
||||||
} |
|
||||||
#else |
|
||||||
float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){ |
|
||||||
float shadow = 1.0; |
|
||||||
projCoord /= projCoord.w; |
|
||||||
shadow = GETSHADOW(shadowMap,projCoord); |
|
||||||
|
|
||||||
//a small falloff to make the shadow blend nicely into the not lighten |
|
||||||
//we translate the texture coordinate value to a -1,1 range so the length |
|
||||||
//of the texture coordinate vector is actually the radius of the lighten area on the ground |
|
||||||
projCoord = projCoord * 2.0 - 1.0; |
|
||||||
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1; |
|
||||||
return mix(shadow,1.0,clamp(fallOff,0.0,1.0)); |
|
||||||
|
|
||||||
} |
|
||||||
#endif |
|
||||||
#endif |
|
@ -1,11 +1,12 @@ |
|||||||
# THIS IS AN AUTO-GENERATED FILE.. |
# THIS IS AN AUTO-GENERATED FILE.. |
||||||
# DO NOT MODIFY! |
# DO NOT MODIFY! |
||||||
build.date=1900-01-01 |
build.date=2016-03-25 |
||||||
git.revision=0 |
git.revision=0 |
||||||
git.branch=unknown |
git.branch=unknown |
||||||
git.hash= |
git.hash= |
||||||
git.hash.short= |
git.hash.short= |
||||||
git.tag= |
git.tag= |
||||||
name.full=jMonkeyEngine 3.1.0-UNKNOWN |
name.full=jMonkeyEngine 3.1.0-UNKNOWN |
||||||
|
version.full=3.1.0-UNKNOWN |
||||||
version.number=3.1.0 |
version.number=3.1.0 |
||||||
version.tag=SNAPSHOT |
version.tag=SNAPSHOT |
@ -0,0 +1,367 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2016 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 jme3test.app; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import com.jme3.util.clone.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public class TestCloner { |
||||||
|
|
||||||
|
public static void main( String... args ) { |
||||||
|
|
||||||
|
System.out.println("Clone test:"); |
||||||
|
|
||||||
|
Cloner cloner = new Cloner(); |
||||||
|
|
||||||
|
RegularObject ro = new RegularObject(42); |
||||||
|
System.out.println("Regular Object:" + ro); |
||||||
|
RegularObject roCloneLegacy = ro.clone(); |
||||||
|
System.out.println("Regular Object Clone:" + roCloneLegacy); |
||||||
|
RegularObject roClone = cloner.clone(ro); |
||||||
|
System.out.println("cloner: Regular Object Clone:" + roClone); |
||||||
|
|
||||||
|
System.out.println("------------------------------------"); |
||||||
|
System.out.println(); |
||||||
|
|
||||||
|
cloner = new Cloner(); |
||||||
|
RegularSubclass rsc = new RegularSubclass(69, "test"); |
||||||
|
System.out.println("Regular subclass:" + rsc); |
||||||
|
RegularSubclass rscCloneLegacy = (RegularSubclass)rsc.clone(); |
||||||
|
System.out.println("Regular subclass Clone:" + rscCloneLegacy); |
||||||
|
RegularSubclass rscClone = cloner.clone(rsc); |
||||||
|
System.out.println("cloner: Regular subclass Clone:" + rscClone); |
||||||
|
|
||||||
|
System.out.println("------------------------------------"); |
||||||
|
System.out.println(); |
||||||
|
|
||||||
|
cloner = new Cloner(); |
||||||
|
Parent parent = new Parent("Foo", 34); |
||||||
|
System.out.println("Parent:" + parent); |
||||||
|
Parent parentCloneLegacy = parent.clone(); |
||||||
|
System.out.println("Parent Clone:" + parentCloneLegacy); |
||||||
|
Parent parentClone = cloner.clone(parent); |
||||||
|
System.out.println("cloner: Parent Clone:" + parentClone); |
||||||
|
|
||||||
|
System.out.println("------------------------------------"); |
||||||
|
System.out.println(); |
||||||
|
|
||||||
|
cloner = new Cloner(); |
||||||
|
GraphNode root = new GraphNode("root"); |
||||||
|
GraphNode child1 = root.addLink("child1"); |
||||||
|
GraphNode child2 = root.addLink("child2"); |
||||||
|
GraphNode shared = child1.addLink("shared"); |
||||||
|
child2.addLink(shared); |
||||||
|
|
||||||
|
// Add a circular reference to get fancy
|
||||||
|
shared.addLink(root); |
||||||
|
|
||||||
|
System.out.println("Simple graph:"); |
||||||
|
root.dump(" "); |
||||||
|
|
||||||
|
GraphNode rootClone = cloner.clone(root); |
||||||
|
System.out.println("clone:"); |
||||||
|
rootClone.dump(" "); |
||||||
|
|
||||||
|
System.out.println("original:"); |
||||||
|
root.dump(" "); |
||||||
|
|
||||||
|
GraphNode reclone = Cloner.deepClone(root); |
||||||
|
System.out.println("reclone:"); |
||||||
|
reclone.dump(" "); |
||||||
|
|
||||||
|
System.out.println("------------------------------------"); |
||||||
|
System.out.println(); |
||||||
|
cloner = new Cloner(); |
||||||
|
|
||||||
|
ArrayHolder arrays = new ArrayHolder(5, 3, 7, 3, 7, 2, 1, 4); |
||||||
|
System.out.println("Array holder:" + arrays); |
||||||
|
ArrayHolder arraysClone = cloner.clone(arrays); |
||||||
|
System.out.println("Array holder clone:" + arraysClone); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static class RegularObject implements Cloneable { |
||||||
|
protected int i; |
||||||
|
|
||||||
|
public RegularObject( int i ) { |
||||||
|
this.i = i; |
||||||
|
} |
||||||
|
|
||||||
|
public RegularObject clone() { |
||||||
|
try { |
||||||
|
return (RegularObject)super.clone(); |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getSimpleName() + "@" + System.identityHashCode(this) |
||||||
|
+ "[i=" + i + "]"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class RegularSubclass extends RegularObject { |
||||||
|
protected String name; |
||||||
|
|
||||||
|
public RegularSubclass( int i, String name ) { |
||||||
|
super(i); |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getSimpleName() + "@" + System.identityHashCode(this) |
||||||
|
+ "[i=" + i + ", name=" + name + "]"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class Parent implements Cloneable, JmeCloneable { |
||||||
|
|
||||||
|
private RegularObject ro; |
||||||
|
private RegularSubclass rsc; |
||||||
|
|
||||||
|
public Parent( String name, int age ) { |
||||||
|
this.ro = new RegularObject(age); |
||||||
|
this.rsc = new RegularSubclass(age, name); |
||||||
|
} |
||||||
|
|
||||||
|
public Parent clone() { |
||||||
|
try { |
||||||
|
return (Parent)super.clone(); |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Parent jmeClone() { |
||||||
|
// Ok to delegate to clone() in this case because no deep
|
||||||
|
// cloning is done there.
|
||||||
|
return clone(); |
||||||
|
} |
||||||
|
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) { |
||||||
|
this.ro = cloner.clone(ro); |
||||||
|
this.rsc = cloner.clone(rsc); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getSimpleName() + "@" + System.identityHashCode(this) |
||||||
|
+ "[ro=" + ro + ", rsc=" + rsc + "]"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class GraphNode implements Cloneable, JmeCloneable { |
||||||
|
|
||||||
|
private String name; |
||||||
|
private List<GraphNode> links = new ArrayList<>(); |
||||||
|
|
||||||
|
public GraphNode( String name ) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public void dump( String indent ) { |
||||||
|
dump(indent, new HashSet<GraphNode>()); |
||||||
|
} |
||||||
|
|
||||||
|
private void dump( String indent, Set<GraphNode> visited ) { |
||||||
|
if( visited.contains(this) ) { |
||||||
|
// already been here
|
||||||
|
System.out.println(indent + this + " ** circular."); |
||||||
|
return; |
||||||
|
} |
||||||
|
System.out.println(indent + this); |
||||||
|
visited.add(this); |
||||||
|
for( GraphNode n : links ) { |
||||||
|
n.dump(indent + " ", visited); |
||||||
|
} |
||||||
|
visited.remove(this); |
||||||
|
} |
||||||
|
|
||||||
|
public GraphNode addLink( String name ) { |
||||||
|
GraphNode node = new GraphNode(name); |
||||||
|
links.add(node); |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
public GraphNode addLink( GraphNode node ) { |
||||||
|
links.add(node); |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
public List<GraphNode> getLinks() { |
||||||
|
return links; |
||||||
|
} |
||||||
|
|
||||||
|
public GraphNode jmeClone() { |
||||||
|
try { |
||||||
|
return (GraphNode)super.clone(); |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) { |
||||||
|
this.links = cloner.clone(links); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getSimpleName() + "@" + System.identityHashCode(this) |
||||||
|
+ "[name=" + name + "]"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class ArrayHolder implements JmeCloneable { |
||||||
|
|
||||||
|
private int[] intArray; |
||||||
|
private int[][] intArray2D; |
||||||
|
private Object[] objects; |
||||||
|
private RegularObject[] regularObjects; |
||||||
|
private String[] strings; |
||||||
|
|
||||||
|
public ArrayHolder( int... values ) { |
||||||
|
this.intArray = values; |
||||||
|
this.intArray2D = new int[values.length][2]; |
||||||
|
for( int i = 0; i < values.length; i++ ) { |
||||||
|
intArray2D[i][0] = values[i] + 1; |
||||||
|
intArray2D[i][1] = values[i] * 2; |
||||||
|
} |
||||||
|
this.objects = new Object[values.length]; |
||||||
|
this.regularObjects = new RegularObject[values.length]; |
||||||
|
this.strings = new String[values.length]; |
||||||
|
for( int i = 0; i < values.length; i++ ) { |
||||||
|
objects[i] = values[i]; |
||||||
|
regularObjects[i] = new RegularObject(values[i]); |
||||||
|
strings[i] = String.valueOf(values[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ArrayHolder jmeClone() { |
||||||
|
try { |
||||||
|
return (ArrayHolder)super.clone(); |
||||||
|
} catch( CloneNotSupportedException e ) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) { |
||||||
|
intArray = cloner.clone(intArray); |
||||||
|
intArray2D = cloner.clone(intArray2D); |
||||||
|
|
||||||
|
// Boxed types are not cloneable so this will fail
|
||||||
|
//objects = cloner.clone(objects);
|
||||||
|
|
||||||
|
regularObjects = cloner.clone(regularObjects); |
||||||
|
|
||||||
|
// Strings are also not cloneable
|
||||||
|
//strings = cloner.clone(strings);
|
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append("intArray=" + intArray); |
||||||
|
for( int i = 0; i < intArray.length; i++ ) { |
||||||
|
if( i == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(intArray[i]); |
||||||
|
} |
||||||
|
sb.append("], "); |
||||||
|
|
||||||
|
sb.append("intArray2D=" + intArray2D); |
||||||
|
for( int i = 0; i < intArray2D.length; i++ ) { |
||||||
|
if( i == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append("intArray2D[" + i + "]=" + intArray2D[i]); |
||||||
|
for( int j = 0; j < 2; j++ ) { |
||||||
|
if( j == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(intArray2D[i][j]); |
||||||
|
} |
||||||
|
sb.append("], "); |
||||||
|
} |
||||||
|
sb.append("], "); |
||||||
|
|
||||||
|
sb.append("objectArray=" + objects); |
||||||
|
for( int i = 0; i < objects.length; i++ ) { |
||||||
|
if( i == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(objects[i]); |
||||||
|
} |
||||||
|
sb.append("], "); |
||||||
|
|
||||||
|
sb.append("objectArray=" + regularObjects); |
||||||
|
for( int i = 0; i < regularObjects.length; i++ ) { |
||||||
|
if( i == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(regularObjects[i]); |
||||||
|
} |
||||||
|
sb.append("], "); |
||||||
|
|
||||||
|
sb.append("stringArray=" + strings); |
||||||
|
for( int i = 0; i < strings.length; i++ ) { |
||||||
|
if( i == 0 ) { |
||||||
|
sb.append("["); |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(strings[i]); |
||||||
|
} |
||||||
|
sb.append("]"); |
||||||
|
|
||||||
|
return getClass().getSimpleName() + "@" + System.identityHashCode(this) |
||||||
|
+ "[" + sb + "]"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
package jme3test.light; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.*; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.util.TangentBinormalGenerator; |
||||||
|
import com.jme3.util.mikktspace.MikktspaceTangentGenerator; |
||||||
|
|
||||||
|
/** |
||||||
|
* test |
||||||
|
* |
||||||
|
* @author normenhansen |
||||||
|
*/ |
||||||
|
public class TestTangentSpace extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestTangentSpace app = new TestTangentSpace(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
private Node debugNode = new Node("debug"); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
renderManager.setSinglePassLightBatchSize(2); |
||||||
|
renderManager.setPreferredLightMode(TechniqueDef.LightMode.SinglePass); |
||||||
|
initView(); |
||||||
|
|
||||||
|
Spatial s = assetManager.loadModel("Models/Test/BasicCubeLow.obj"); |
||||||
|
rootNode.attachChild(s); |
||||||
|
|
||||||
|
Material m = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
m.setTexture("NormalMap", assetManager.loadTexture("Models/Test/Normal_pixel.png")); |
||||||
|
|
||||||
|
Geometry g = (Geometry)s; |
||||||
|
Geometry g2 = (Geometry) g.deepClone(); |
||||||
|
g2.move(5, 0, 0); |
||||||
|
g.getParent().attachChild(g2); |
||||||
|
|
||||||
|
g.setMaterial(m); |
||||||
|
g2.setMaterial(m); |
||||||
|
|
||||||
|
//Regular tangent generation (left geom)
|
||||||
|
TangentBinormalGenerator.generate(g2.getMesh(), true); |
||||||
|
|
||||||
|
//MikkTSPace Tangent generation (right geom)
|
||||||
|
|
||||||
|
MikktspaceTangentGenerator.generate(g); |
||||||
|
|
||||||
|
createDebugTangents(g2); |
||||||
|
createDebugTangents(g); |
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (name.equals("toggleDebug") && isPressed) { |
||||||
|
if (debugNode.getParent() == null) { |
||||||
|
rootNode.attachChild(debugNode); |
||||||
|
} else { |
||||||
|
debugNode.removeFromParent(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, "toggleDebug"); |
||||||
|
|
||||||
|
inputManager.addMapping("toggleDebug", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
|
||||||
|
|
||||||
|
DirectionalLight dl = new DirectionalLight(new Vector3f(-1, -1, -1).normalizeLocal()); |
||||||
|
rootNode.addLight(dl); |
||||||
|
} |
||||||
|
|
||||||
|
private void initView() { |
||||||
|
viewPort.setBackgroundColor(ColorRGBA.DarkGray); |
||||||
|
cam.setLocation(new Vector3f(8.569681f, 3.335546f, 5.4372444f)); |
||||||
|
cam.setRotation(new Quaternion(-0.07608022f, 0.9086564f, -0.18992864f, -0.3639813f)); |
||||||
|
flyCam.setMoveSpeed(10); |
||||||
|
} |
||||||
|
|
||||||
|
private void createDebugTangents(Geometry geom) { |
||||||
|
Geometry debug = new Geometry( |
||||||
|
"Debug " + geom.getName(), |
||||||
|
TangentBinormalGenerator.genTbnLines(geom.getMesh(), 0.8f) |
||||||
|
); |
||||||
|
Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m"); |
||||||
|
debug.setMaterial(debugMat); |
||||||
|
debug.setCullHint(Spatial.CullHint.Never); |
||||||
|
debug.getLocalTranslation().set(geom.getWorldTranslation()); |
||||||
|
debugNode.attachChild(debug); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 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 jme3test.scene; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
|
||||||
|
public class TestLineWidthRenderState extends SimpleApplication { |
||||||
|
|
||||||
|
private Material mat; |
||||||
|
|
||||||
|
public static void main(String[] args){ |
||||||
|
TestLineWidthRenderState app = new TestLineWidthRenderState(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
setDisplayFps(false); |
||||||
|
setDisplayStatView(false); |
||||||
|
cam.setLocation(new Vector3f(5.5826545f, 3.6192513f, 8.016988f)); |
||||||
|
cam.setRotation(new Quaternion(-0.04787097f, 0.9463123f, -0.16569641f, -0.27339742f)); |
||||||
|
|
||||||
|
Box b = new Box(1, 1, 1); |
||||||
|
Geometry geom = new Geometry("Box", b); |
||||||
|
mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.setColor("Color", ColorRGBA.Blue); |
||||||
|
mat.getAdditionalRenderState().setWireframe(true); |
||||||
|
mat.getAdditionalRenderState().setLineWidth(2); |
||||||
|
geom.setMaterial(mat); |
||||||
|
rootNode.attachChild(geom); |
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if(name.equals("up") && isPressed){ |
||||||
|
mat.getAdditionalRenderState().setLineWidth(mat.getAdditionalRenderState().getLineWidth() + 1); |
||||||
|
} |
||||||
|
if(name.equals("down") && isPressed){ |
||||||
|
mat.getAdditionalRenderState().setLineWidth(Math.max(mat.getAdditionalRenderState().getLineWidth() - 1, 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
}, "up", "down"); |
||||||
|
inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_U)); |
||||||
|
inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_J)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.material.plugin.export.material; |
||||||
|
|
||||||
|
import com.jme3.export.JmeExporter; |
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.material.MaterialDef; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
|
||||||
|
/** |
||||||
|
* Saves a Material to a j3m file with proper formatting. |
||||||
|
* |
||||||
|
* usage is : |
||||||
|
* <pre> |
||||||
|
* J3MExporter exporter = new J3MExporter(); |
||||||
|
* exporter.save(material, myFile); |
||||||
|
* //or
|
||||||
|
* exporter.save(material, myOutputStream); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author tsr |
||||||
|
* @author nehon (documentation and safety check) |
||||||
|
*/ |
||||||
|
public class J3MExporter implements JmeExporter { |
||||||
|
|
||||||
|
private final J3MRootOutputCapsule rootCapsule; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a J3MExporter |
||||||
|
*/ |
||||||
|
public J3MExporter() { |
||||||
|
rootCapsule = new J3MRootOutputCapsule(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void save(Savable object, OutputStream f) throws IOException { |
||||||
|
|
||||||
|
if (!(object instanceof Material)) { |
||||||
|
throw new IllegalArgumentException("J3MExporter can only save com.jme3.material.Material class"); |
||||||
|
} |
||||||
|
|
||||||
|
OutputStreamWriter out = new OutputStreamWriter(f, Charset.forName("UTF-8")); |
||||||
|
|
||||||
|
rootCapsule.clear(); |
||||||
|
object.write(this); |
||||||
|
rootCapsule.writeToStream(out); |
||||||
|
|
||||||
|
out.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void save(Savable object, File f) throws IOException { |
||||||
|
try (FileOutputStream fos = new FileOutputStream(f)) { |
||||||
|
save(object, fos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public OutputCapsule getCapsule(Savable object) { |
||||||
|
if ((object instanceof Material) || (object instanceof MaterialDef)) { |
||||||
|
return rootCapsule; |
||||||
|
} |
||||||
|
|
||||||
|
return rootCapsule.getCapsule(object); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,383 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.material.plugin.export.material; |
||||||
|
|
||||||
|
import com.jme3.asset.TextureKey; |
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
import com.jme3.material.MatParam; |
||||||
|
import com.jme3.material.MatParamTexture; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.shader.VarType; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
import com.jme3.util.IntMap; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.nio.FloatBuffer; |
||||||
|
import java.nio.IntBuffer; |
||||||
|
import java.nio.ShortBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.BitSet; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author tsr |
||||||
|
*/ |
||||||
|
public class J3MOutputCapsule implements OutputCapsule { |
||||||
|
|
||||||
|
private final HashMap<String, String> parameters; |
||||||
|
protected final J3MExporter exporter; |
||||||
|
|
||||||
|
public J3MOutputCapsule(J3MExporter exporter) { |
||||||
|
this.exporter = exporter; |
||||||
|
parameters = new HashMap<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public void writeToStream(OutputStreamWriter out) throws IOException { |
||||||
|
for (String key : parameters.keySet()) { |
||||||
|
out.write(" "); |
||||||
|
writeParameter(out, key, parameters.get(key)); |
||||||
|
out.write("\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void writeParameter(OutputStreamWriter out, String name, String value) throws IOException { |
||||||
|
out.write(name); |
||||||
|
out.write(" : "); |
||||||
|
out.write(value); |
||||||
|
} |
||||||
|
|
||||||
|
public void clear() { |
||||||
|
parameters.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void putParameter(String name, String value) { |
||||||
|
parameters.put(name, value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(boolean value, String name, boolean defVal) throws IOException { |
||||||
|
if (value == defVal) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
putParameter(name, ((value) ? "On" : "Off")); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeStringSavableMap(Map<String, ? extends Savable> map, String name, Map<String, ? extends Savable> defVal) throws IOException { |
||||||
|
for (String key : map.keySet()) { |
||||||
|
Savable value = map.get(key); |
||||||
|
if (defVal == null || !defVal.containsKey(key) || !defVal.get(key).equals(value)) { |
||||||
|
putParameter(key, format(value)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected String format(Savable value) { |
||||||
|
if (value instanceof MatParamTexture) { |
||||||
|
return formatMatParamTexture((MatParamTexture) value); |
||||||
|
} |
||||||
|
if (value instanceof MatParam) { |
||||||
|
return formatMatParam((MatParam) value); |
||||||
|
} |
||||||
|
|
||||||
|
throw new UnsupportedOperationException(value.getClass() + ": Not supported yet."); |
||||||
|
} |
||||||
|
|
||||||
|
private String formatMatParam(MatParam param){ |
||||||
|
VarType type = param.getVarType(); |
||||||
|
Object val = param.getValue(); |
||||||
|
switch (type) { |
||||||
|
case Boolean: |
||||||
|
case Float: |
||||||
|
case Int: |
||||||
|
return val.toString(); |
||||||
|
case Vector2: |
||||||
|
Vector2f v2 = (Vector2f) val; |
||||||
|
return v2.getX() + " " + v2.getY(); |
||||||
|
case Vector3: |
||||||
|
Vector3f v3 = (Vector3f) val; |
||||||
|
return v3.getX() + " " + v3.getY() + " " + v3.getZ(); |
||||||
|
case Vector4: |
||||||
|
// can be either ColorRGBA, Vector4f or Quaternion
|
||||||
|
if (val instanceof Vector4f) { |
||||||
|
Vector4f v4 = (Vector4f) val; |
||||||
|
return v4.getX() + " " + v4.getY() + " " |
||||||
|
+ v4.getZ() + " " + v4.getW(); |
||||||
|
} else if (val instanceof ColorRGBA) { |
||||||
|
ColorRGBA color = (ColorRGBA) val; |
||||||
|
return color.getRed() + " " + color.getGreen() + " " |
||||||
|
+ color.getBlue() + " " + color.getAlpha(); |
||||||
|
} else if (val instanceof Quaternion) { |
||||||
|
Quaternion quat = (Quaternion) val; |
||||||
|
return quat.getX() + " " + quat.getY() + " " |
||||||
|
+ quat.getZ() + " " + quat.getW(); |
||||||
|
} else { |
||||||
|
throw new UnsupportedOperationException("Unexpected Vector4 type: " + val); |
||||||
|
} |
||||||
|
|
||||||
|
default: |
||||||
|
return null; // parameter type not supported in J3M
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected static String formatMatParamTexture(MatParamTexture param) { |
||||||
|
StringBuilder ret = new StringBuilder(); |
||||||
|
Texture tex = (Texture) param.getValue(); |
||||||
|
TextureKey key; |
||||||
|
if (tex != null) { |
||||||
|
key = (TextureKey) tex.getKey(); |
||||||
|
|
||||||
|
if (key != null && key.isFlipY()) { |
||||||
|
ret.append("Flip "); |
||||||
|
} |
||||||
|
|
||||||
|
ret.append(formatWrapMode(tex, Texture.WrapAxis.S)); |
||||||
|
ret.append(formatWrapMode(tex, Texture.WrapAxis.T)); |
||||||
|
ret.append(formatWrapMode(tex, Texture.WrapAxis.R)); |
||||||
|
|
||||||
|
//Min and Mag filter
|
||||||
|
Texture.MinFilter def = Texture.MinFilter.BilinearNoMipMaps; |
||||||
|
if (tex.getImage().hasMipmaps() || (key != null && key.isGenerateMips())) { |
||||||
|
def = Texture.MinFilter.Trilinear; |
||||||
|
} |
||||||
|
if (tex.getMinFilter() != def) { |
||||||
|
ret.append("Min").append(tex.getMinFilter().name()).append(" "); |
||||||
|
} |
||||||
|
|
||||||
|
if (tex.getMagFilter() != Texture.MagFilter.Bilinear) { |
||||||
|
ret.append("Mag").append(tex.getMagFilter().name()).append(" "); |
||||||
|
} |
||||||
|
|
||||||
|
ret.append("\"").append(key.getName()).append("\""); |
||||||
|
} |
||||||
|
|
||||||
|
return ret.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
protected static String formatWrapMode(Texture texVal, Texture.WrapAxis axis) { |
||||||
|
WrapMode mode; |
||||||
|
try { |
||||||
|
mode = texVal.getWrap(axis); |
||||||
|
} catch (IllegalArgumentException e) { |
||||||
|
//this axis doesn't exist on the texture
|
||||||
|
return ""; |
||||||
|
} |
||||||
|
if (mode != WrapMode.EdgeClamp) { |
||||||
|
return "Wrap" + mode.name() + "_" + axis.name() + " "; |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(Enum value, String name, Enum defVal) throws IOException { |
||||||
|
if (value == defVal) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
putParameter(name, value.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(float value, String name, float defVal) throws IOException { |
||||||
|
if (value == defVal) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
putParameter(name, Float.toString(value)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(float[] value, String name, float[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(float[][] value, String name, float[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(double value, String name, double defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(double[] value, String name, double[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(double[][] value, String name, double[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(long value, String name, long defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(long[] value, String name, long[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(long[][] value, String name, long[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(short value, String name, short defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(short[] value, String name, short[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(short[][] value, String name, short[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(boolean[] value, String name, boolean[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(boolean[][] value, String name, boolean[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String value, String name, String defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String[] value, String name, String[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String[][] value, String name, String[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(BitSet value, String name, BitSet defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(Savable object, String name, Savable defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(Savable[] objects, String name, Savable[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(Savable[][] objects, String name, Savable[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeSavableArrayList(ArrayList array, String name, ArrayList defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeSavableArrayListArray(ArrayList[] array, String name, ArrayList[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeSavableArrayListArray2D(ArrayList[][] array, String name, ArrayList[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array, String name, ArrayList<FloatBuffer> defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeByteBufferArrayList(ArrayList<ByteBuffer> array, String name, ArrayList<ByteBuffer> defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeSavableMap(Map<? extends Savable, ? extends Savable> map, String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeIntSavableMap(IntMap<? extends Savable> map, String name, IntMap<? extends Savable> defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(FloatBuffer value, String name, FloatBuffer defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(IntBuffer value, String name, IntBuffer defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(ByteBuffer value, String name, ByteBuffer defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(ShortBuffer value, String name, ShortBuffer defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(byte value, String name, byte defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(byte[] value, String name, byte[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(byte[][] value, String name, byte[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int value, String name, int defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int[] value, String name, int[] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int[][] value, String name, int[][] defVal) throws IOException { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.material.plugin.export.material; |
||||||
|
|
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.util.HashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author tsr |
||||||
|
*/ |
||||||
|
public class J3MRenderStateOutputCapsule extends J3MOutputCapsule { |
||||||
|
protected final static HashMap<String, String> NAME_MAP; |
||||||
|
protected String offsetUnit; |
||||||
|
|
||||||
|
static { |
||||||
|
NAME_MAP = new HashMap<>(); |
||||||
|
NAME_MAP.put( "wireframe", "Wireframe"); |
||||||
|
NAME_MAP.put( "cullMode", "FaceCull"); |
||||||
|
NAME_MAP.put( "depthWrite", "DepthWrite"); |
||||||
|
NAME_MAP.put( "depthTest", "DepthTest"); |
||||||
|
NAME_MAP.put( "blendMode", "Blend"); |
||||||
|
NAME_MAP.put( "alphaFallOff", "AlphaTestFalloff"); |
||||||
|
NAME_MAP.put( "offsetFactor", "PolyOffset"); |
||||||
|
NAME_MAP.put( "colorWrite", "ColorWrite"); |
||||||
|
NAME_MAP.put( "pointSprite", "PointSprite"); |
||||||
|
NAME_MAP.put( "depthFunc", "DepthFunc"); |
||||||
|
NAME_MAP.put( "alphaFunc", "AlphaFunc"); |
||||||
|
NAME_MAP.put( "lineWidth", "LineWidth"); |
||||||
|
} |
||||||
|
public J3MRenderStateOutputCapsule(J3MExporter exporter) { |
||||||
|
super(exporter); |
||||||
|
} |
||||||
|
|
||||||
|
public OutputCapsule getCapsule(Savable object) { |
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
super.clear(); |
||||||
|
offsetUnit = ""; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeToStream(OutputStreamWriter out) throws IOException { |
||||||
|
out.write(" AdditionalRenderState {\n"); |
||||||
|
super.writeToStream(out); |
||||||
|
out.write(" }\n"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void writeParameter(OutputStreamWriter out, String name, String value) throws IOException { |
||||||
|
out.write(name); |
||||||
|
out.write(" "); |
||||||
|
out.write(value); |
||||||
|
|
||||||
|
if( "PolyOffset".equals(name) ) { |
||||||
|
out.write(" "); |
||||||
|
out.write(offsetUnit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void putParameter(String name, String value ) { |
||||||
|
if( "offsetUnits".equals(name) ) { |
||||||
|
offsetUnit = value; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if( !NAME_MAP.containsKey(name) ) |
||||||
|
return; |
||||||
|
|
||||||
|
super.putParameter(NAME_MAP.get(name), value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.material.plugin.export.material; |
||||||
|
|
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.util.HashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tsr |
||||||
|
*/ |
||||||
|
public class J3MRootOutputCapsule extends J3MOutputCapsule { |
||||||
|
|
||||||
|
private final HashMap<Savable, J3MOutputCapsule> outCapsules; |
||||||
|
private String name; |
||||||
|
private String materialDefinition; |
||||||
|
private Boolean isTransparent; |
||||||
|
|
||||||
|
public J3MRootOutputCapsule(J3MExporter exporter) { |
||||||
|
super(exporter); |
||||||
|
outCapsules = new HashMap<>(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
super.clear(); |
||||||
|
isTransparent = null; |
||||||
|
name = ""; |
||||||
|
materialDefinition = ""; |
||||||
|
outCapsules.clear(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public OutputCapsule getCapsule(Savable object) { |
||||||
|
if (!outCapsules.containsKey(object)) { |
||||||
|
outCapsules.put(object, new J3MRenderStateOutputCapsule(exporter)); |
||||||
|
} |
||||||
|
|
||||||
|
return outCapsules.get(object); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeToStream(OutputStreamWriter out) throws IOException { |
||||||
|
out.write("Material " + name + " : " + materialDefinition + " {\n\n"); |
||||||
|
if (isTransparent != null) |
||||||
|
out.write(" Transparent " + ((isTransparent) ? "On" : "Off") + "\n\n"); |
||||||
|
|
||||||
|
out.write(" MaterialParameters {\n"); |
||||||
|
super.writeToStream(out); |
||||||
|
out.write(" }\n\n"); |
||||||
|
|
||||||
|
for (J3MOutputCapsule c : outCapsules.values()) { |
||||||
|
c.writeToStream(out); |
||||||
|
} |
||||||
|
out.write("}\n"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String value, String name, String defVal) throws IOException { |
||||||
|
switch (name) { |
||||||
|
case "material_def": |
||||||
|
materialDefinition = value; |
||||||
|
break; |
||||||
|
case "name": |
||||||
|
this.name = value; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new UnsupportedOperationException(name + " string material parameter not supported yet"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(boolean value, String name, boolean defVal) throws IOException { |
||||||
|
if( value == defVal) |
||||||
|
return; |
||||||
|
|
||||||
|
switch (name) { |
||||||
|
case "is_transparent": |
||||||
|
isTransparent = value; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new UnsupportedOperationException(name + " boolean material parameter not supported yet"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(Savable object, String name, Savable defVal) throws IOException { |
||||||
|
if(object != null && !object.equals(defVal)) { |
||||||
|
object.write(exporter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue