@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender;
import java.io.IOException ;
import java.util.ArrayList ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.logging.Level ;
import java.util.logging.Logger ;
import com.jme3.animation.Animation ;
import com.jme3.asset.AssetInfo ;
import com.jme3.asset.AssetLoader ;
import com.jme3.asset.BlenderKey ;
import com.jme3.asset.BlenderKey.FeaturesToLoad ;
import com.jme3.asset.BlenderKey.LoadingResults ;
import com.jme3.asset.ModelKey ;
import com.jme3.light.Light ;
import com.jme3.math.ColorRGBA ;
import com.jme3.post.Filter ;
import com.jme3.renderer.Camera ;
import com.jme3.scene.CameraNode ;
import com.jme3.scene.LightNode ;
import com.jme3.scene.Node ;
@ -55,16 +59,20 @@ import com.jme3.scene.plugins.blender.curves.CurvesHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException ;
import com.jme3.scene.plugins.blender.file.BlenderInputStream ;
import com.jme3.scene.plugins.blender.file.FileBlockHeader ;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode ;
import com.jme3.scene.plugins.blender.file.Pointer ;
import com.jme3.scene.plugins.blender.file.Structure ;
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper ;
import com.jme3.scene.plugins.blender.lights.LightHelper ;
import com.jme3.scene.plugins.blender.materials.MaterialContext ;
import com.jme3.scene.plugins.blender.materials.MaterialHelper ;
import com.jme3.scene.plugins.blender.meshes.MeshHelper ;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh ;
import com.jme3.scene.plugins.blender.modifiers.ModifierHelper ;
import com.jme3.scene.plugins.blender.objects.ObjectHelper ;
import com.jme3.scene.plugins.blender.particles.ParticlesHelper ;
import com.jme3.scene.plugins.blender.textures.TextureHelper ;
import com.jme3.texture.Texture ;
/ * *
* This is the main loading class . Have in notice that asset manager needs to have loaders for resources like textures .
@ -83,72 +91,130 @@ public class BlenderLoader implements AssetLoader {
try {
this . setup ( assetInfo ) ;
List < FileBlockHeader > sceneBlocks = new ArrayList < FileBlockHeader > ( ) ;
BlenderKey blenderKey = blenderContext . getBlenderKey ( ) ;
LoadingResults loadingResults = blenderKey . prepareLoadingResults ( ) ;
AnimationHelper animationHelper = blenderContext . getHelper ( AnimationHelper . class ) ;
animationHelper . loadAnimations ( ) ;
BlenderKey blenderKey = blenderContext . getBlenderKey ( ) ;
LoadedFeatures loadedFeatures = new LoadedFeatures ( ) ;
for ( FileBlockHeader block : blocks ) {
switch ( block . getCode ( ) ) {
case FileBlockHeader . BLOCK_OB00 : // Object
case BLOCK_OB00 :
ObjectHelper objectHelper = blenderContext . getHelper ( ObjectHelper . class ) ;
Object object = objectHelper . toObject ( block . getStructure ( blenderContext ) , blenderContext ) ;
if ( object instanceof LightNode ) {
loadingResults . addLight ( ( LightNode ) object ) ;
} else if ( object instanceof CameraNode ) {
loadingResults . addCamera ( ( CameraNode ) object ) ;
} else if ( object instanceof Node ) {
if ( LOGGER . isLoggable ( Level . FINE ) ) {
LOGGER . log ( Level . FINE , "{0}: {1}--> {2}" , new Object [ ] { ( ( Node ) object ) . getName ( ) , ( ( Node ) object ) . getLocalTranslation ( ) . toString ( ) , ( ( Node ) object ) . getParent ( ) = = null ? "null" : ( ( Node ) object ) . getParent ( ) . getName ( ) } ) ;
}
if ( this . isRootObject ( loadingResults , ( Node ) object ) ) {
loadingResults . addObject ( ( Node ) object ) ;
}
Node object = ( Node ) objectHelper . toObject ( block . getStructure ( blenderContext ) , blenderContext ) ;
if ( LOGGER . isLoggable ( Level . FINE ) ) {
LOGGER . log ( Level . FINE , "{0}: {1}--> {2}" , new Object [ ] { object . getName ( ) , object . getLocalTranslation ( ) . toString ( ) , object . getParent ( ) = = null ? "null" : object . getParent ( ) . getName ( ) } ) ;
}
if ( object . getParent ( ) = = null ) {
loadedFeatures . objects . add ( object ) ;
}
if ( object instanceof LightNode & & ( ( LightNode ) object ) . getLight ( ) ! = null ) {
loadedFeatures . lights . add ( ( ( LightNode ) object ) . getLight ( ) ) ;
} else if ( object instanceof CameraNode & & ( ( CameraNode ) object ) . getCamera ( ) ! = null ) {
loadedFeatures . cameras . add ( ( ( CameraNode ) object ) . getCamera ( ) ) ;
}
break ;
case BLOCK_SC00 : // Scene
loadedFeatures . sceneBlocks . add ( block ) ;
break ;
case BLOCK_MA00 : // Material
MaterialHelper materialHelper = blenderContext . getHelper ( MaterialHelper . class ) ;
MaterialContext materialContext = materialHelper . toMaterialContext ( block . getStructure ( blenderContext ) , blenderContext ) ;
loadedFeatures . materials . add ( materialContext ) ;
break ;
case BLOCK_ME00 : // Mesh
MeshHelper meshHelper = blenderContext . getHelper ( MeshHelper . class ) ;
TemporalMesh temporalMesh = meshHelper . toTemporalMesh ( block . getStructure ( blenderContext ) , blenderContext ) ;
loadedFeatures . meshes . add ( temporalMesh ) ;
break ;
case BLOCK_IM00 : // Image
TextureHelper textureHelper = blenderContext . getHelper ( TextureHelper . class ) ;
Texture image = textureHelper . loadImageAsTexture ( block . getStructure ( blenderContext ) , 0 , blenderContext ) ;
if ( image ! = null & & image . getImage ( ) ! = null ) { // render results are stored as images but are not being loaded
loadedFeatures . images . add ( image ) ;
}
break ;
// case FileBlockHeader.BLOCK_MA00:// Material
// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) {
// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
// }
// break;
case FileBlockHeader . BLOCK_SC00 : // Scene
if ( blenderKey . shouldLoad ( FeaturesToLoad . SCENES ) ) {
sceneBlocks . add ( block ) ;
case BLOCK_TE00 :
Structure textureStructure = block . getStructure ( blenderContext ) ;
int type = ( ( Number ) textureStructure . getFieldValue ( "type" ) ) . intValue ( ) ;
if ( type = = TextureHelper . TEX_IMAGE ) {
TextureHelper texHelper = blenderContext . getHelper ( TextureHelper . class ) ;
Texture texture = texHelper . getTexture ( textureStructure , null , blenderContext ) ;
if ( texture ! = null ) { // null is returned when texture has no image
loadedFeatures . textures . add ( texture ) ;
}
} else {
LOGGER . fine ( "Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object." ) ;
}
break ;
case FileBlockHeader . BLOCK_WO00 : // World
if ( blenderKey . shouldLoad ( FeaturesToLoad . WORLD ) ) {
Structure worldStructure = block . getStructure ( blenderContext ) ;
String worldName = worldStructure . getName ( ) ;
if ( blenderKey . getUsedWorld ( ) = = null | | blenderKey . getUsedWorld ( ) . equals ( worldName ) ) {
LandscapeHelper landscapeHelper = blenderContext . getHelper ( LandscapeHelper . class ) ;
Light ambientLight = landscapeHelper . toAmbientLight ( worldStructure ) ;
if ( ambientLight ! = null ) {
loadingResults . addLight ( new LightNode ( null , ambientLight ) ) ;
}
loadingResults . setSky ( landscapeHelper . toSky ( worldStructure ) ) ;
loadingResults . addFilter ( landscapeHelper . toFog ( worldStructure ) ) ;
loadingResults . setBackgroundColor ( landscapeHelper . toBackgroundColor ( worldStructure ) ) ;
case BLOCK_WO00 : // World
LandscapeHelper landscapeHelper = blenderContext . getHelper ( LandscapeHelper . class ) ;
Structure worldStructure = block . getStructure ( blenderContext ) ;
String worldName = worldStructure . getName ( ) ;
if ( blenderKey . getUsedWorld ( ) = = null | | blenderKey . getUsedWorld ( ) . equals ( worldName ) ) {
Light ambientLight = landscapeHelper . toAmbientLight ( worldStructure ) ;
if ( ambientLight ! = null ) {
loadedFeatures . objects . add ( new LightNode ( null , ambientLight ) ) ;
loadedFeatures . lights . add ( ambientLight ) ;
}
loadedFeatures . sky = landscapeHelper . toSky ( worldStructure ) ;
loadedFeatures . backgroundColor = landscapeHelper . toBackgroundColor ( worldStructure ) ;
Filter fogFilter = landscapeHelper . toFog ( worldStructure ) ;
if ( fogFilter ! = null ) {
loadedFeatures . filters . add ( landscapeHelper . toFog ( worldStructure ) ) ;
}
}
break ;
case BLOCK_AC00 :
LOGGER . fine ( "Loading unlinked animations is not yet supported!" ) ;
break ;
default :
LOGGER . log ( Level . FINEST , "Ommiting the block: {0}." , block . getCode ( ) ) ;
}
}
// bake constraints after everything is loaded
LOGGER . fine ( "Baking constraints after every feature is loaded." ) ;
ConstraintHelper constraintHelper = blenderContext . getHelper ( ConstraintHelper . class ) ;
constraintHelper . bakeConstraints ( blenderContext ) ;
// load the scene at the very end so that the root nodes have no parent during loading or constraints applying
for ( FileBlockHeader sceneBlock : sceneBlocks ) {
loadingResults . addScene ( this . toScene ( sceneBlock . getStructure ( blenderContext ) ) ) ;
LOGGER . fine ( "Loading scenes and attaching them to the root object." ) ;
for ( FileBlockHeader sceneBlock : loadedFeatures . sceneBlocks ) {
loadedFeatures . scenes . add ( this . toScene ( sceneBlock . getStructure ( blenderContext ) ) ) ;
}
LOGGER . fine ( "Creating the root node of the model and applying loaded nodes of the scene and loaded features to it." ) ;
Node modelRoot = new Node ( blenderKey . getName ( ) ) ;
for ( Node scene : loadedFeatures . scenes ) {
modelRoot . attachChild ( scene ) ;
}
if ( blenderKey . isLoadUnlinkedAssets ( ) ) {
LOGGER . fine ( "Setting loaded content as user data in resulting sptaial." ) ;
Map < String , Map < String , Object > > linkedData = new HashMap < String , Map < String , Object > > ( ) ;
Map < String , Object > thisFileData = new HashMap < String , Object > ( ) ;
thisFileData . put ( "scenes" , loadedFeatures . scenes = = null ? new ArrayList < Object > ( ) : loadedFeatures . scenes ) ;
thisFileData . put ( "objects" , loadedFeatures . objects = = null ? new ArrayList < Object > ( ) : loadedFeatures . objects ) ;
thisFileData . put ( "meshes" , loadedFeatures . meshes = = null ? new ArrayList < Object > ( ) : loadedFeatures . meshes ) ;
thisFileData . put ( "materials" , loadedFeatures . materials = = null ? new ArrayList < Object > ( ) : loadedFeatures . materials ) ;
thisFileData . put ( "textures" , loadedFeatures . textures = = null ? new ArrayList < Object > ( ) : loadedFeatures . textures ) ;
thisFileData . put ( "images" , loadedFeatures . images = = null ? new ArrayList < Object > ( ) : loadedFeatures . images ) ;
thisFileData . put ( "animations" , loadedFeatures . animations = = null ? new ArrayList < Object > ( ) : loadedFeatures . animations ) ;
thisFileData . put ( "cameras" , loadedFeatures . cameras = = null ? new ArrayList < Object > ( ) : loadedFeatures . cameras ) ;
thisFileData . put ( "lights" , loadedFeatures . lights = = null ? new ArrayList < Object > ( ) : loadedFeatures . lights ) ;
thisFileData . put ( "filters" , loadedFeatures . filters = = null ? new ArrayList < Object > ( ) : loadedFeatures . filters ) ;
thisFileData . put ( "backgroundColor" , loadedFeatures . backgroundColor ) ;
thisFileData . put ( "sky" , loadedFeatures . sky ) ;
linkedData . put ( "this" , thisFileData ) ;
linkedData . putAll ( blenderContext . getLinkedFeatures ( ) ) ;
modelRoot . setUserData ( "linkedData" , linkedData ) ;
}
return loadingResults ;
return modelRoot ;
} catch ( BlenderFileException e ) {
throw new IOException ( e . getLocalizedMessage ( ) , e ) ;
} catch ( Exception e ) {
@ -158,62 +224,36 @@ public class BlenderLoader implements AssetLoader {
}
}
/ * *
* This method indicates if the given spatial is a root object . It means it
* has no parent or is directly attached to one of the already loaded scene
* nodes .
*
* @param loadingResults
* loading results containing the scene nodes
* @param spatial
* spatial object
* @return < b > true < / b > if the given spatial is a root object and
* < b > false < / b > otherwise
* /
protected boolean isRootObject ( LoadingResults loadingResults , Spatial spatial ) {
if ( spatial . getParent ( ) = = null ) {
return true ;
}
for ( Node scene : loadingResults . getScenes ( ) ) {
if ( spatial . getParent ( ) . equals ( scene ) ) {
return true ;
}
}
return false ;
}
/ * *
* This method converts the given structure to a scene node .
* @param structure
* structure of a scene
* @return scene ' s node
* @throws BlenderFileException
* an exception throw when problems with blender file occur
* /
private Node toScene ( Structure structure ) {
private Node toScene ( Structure structure ) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext . getHelper ( ObjectHelper . class ) ;
Node result = new Node ( structure . getName ( ) ) ;
try {
List < Structure > base = ( ( Structure ) structure . getFieldValue ( "base" ) ) . evaluateListBase ( ) ;
for ( Structure b : base ) {
Pointer pObject = ( Pointer ) b . getFieldValue ( "object" ) ;
if ( pObject . isNotNull ( ) ) {
Structure objectStructure = pObject . fetchData ( ) . get ( 0 ) ;
List < Structure > base = ( ( Structure ) structure . getFieldValue ( "base" ) ) . evaluateListBase ( ) ;
for ( Structure b : base ) {
Pointer pObject = ( Pointer ) b . getFieldValue ( "object" ) ;
if ( pObject . isNotNull ( ) ) {
Structure objectStructure = pObject . fetchData ( ) . get ( 0 ) ;
Object object = objectHelper . toObject ( objectStructure , blenderContext ) ;
if ( object instanceof LightNode ) {
result . addLight ( ( ( LightNode ) object ) . getLight ( ) ) ;
result . attachChild ( ( LightNode ) object ) ;
} else if ( object instanceof Node ) {
if ( LOGGER . isLoggable ( Level . FINE ) ) {
LOGGER . log ( Level . FINE , "{0}: {1}--> {2}" , new Object [ ] { ( ( Node ) object ) . getName ( ) , ( ( Node ) object ) . getLocalTranslation ( ) . toString ( ) , ( ( Node ) object ) . getParent ( ) = = null ? "null" : ( ( Node ) object ) . getParent ( ) . getName ( ) } ) ;
}
if ( ( ( Node ) object ) . getParent ( ) = = null ) {
result . attachChild ( ( Spatial ) object ) ;
}
Object object = objectHelper . toObject ( objectStructure , blenderContext ) ;
if ( object instanceof LightNode ) {
result . addLight ( ( ( LightNode ) object ) . getLight ( ) ) ; // FIXME: check if this is needed !!!
result . attachChild ( ( LightNode ) object ) ;
} else if ( object instanceof Node ) {
if ( LOGGER . isLoggable ( Level . FINE ) ) {
LOGGER . log ( Level . FINE , "{0}: {1}--> {2}" , new Object [ ] { ( ( Node ) object ) . getName ( ) , ( ( Node ) object ) . getLocalTranslation ( ) . toString ( ) , ( ( Node ) object ) . getParent ( ) = = null ? "null" : ( ( Node ) object ) . getParent ( ) . getName ( ) } ) ;
}
if ( ( ( Node ) object ) . getParent ( ) = = null ) {
result . attachChild ( ( Spatial ) object ) ;
}
}
}
} catch ( BlenderFileException e ) {
LOGGER . log ( Level . SEVERE , e . getLocalizedMessage ( ) , e ) ;
}
return result ;
}
@ -269,7 +309,7 @@ public class BlenderLoader implements AssetLoader {
if ( ! fileBlock . isDnaBlock ( ) ) {
blocks . add ( fileBlock ) ;
// save the scene's file block
if ( fileBlock . getCode ( ) = = FileBlockHeader . BLOCK_SC00 ) {
if ( fileBlock . getCode ( ) = = BlockCode . BLOCK_SC00 ) {
sceneFileBlock = fileBlock ;
}
}
@ -287,4 +327,39 @@ public class BlenderLoader implements AssetLoader {
blenderContext = null ;
blocks = null ;
}
/ * *
* This class holds the loading results according to the given loading flag .
* @author Marcin Roguski ( Kaelthas )
* /
private static class LoadedFeatures {
private List < FileBlockHeader > sceneBlocks = new ArrayList < FileBlockHeader > ( ) ;
/** The scenes from the file. */
private List < Node > scenes = new ArrayList < Node > ( ) ;
/** Objects from all scenes. */
private List < Node > objects = new ArrayList < Node > ( ) ;
/** All meshes. */
private List < TemporalMesh > meshes = new ArrayList < TemporalMesh > ( ) ;
/** Materials from all objects. */
private List < MaterialContext > materials = new ArrayList < MaterialContext > ( ) ;
/** Textures from all objects. */
private List < Texture > textures = new ArrayList < Texture > ( ) ;
/** The images stored in the blender file. */
private List < Texture > images = new ArrayList < Texture > ( ) ;
/** Animations of all objects. */
private List < Animation > animations = new ArrayList < Animation > ( ) ;
/** All cameras from the file. */
private List < Camera > cameras = new ArrayList < Camera > ( ) ;
/** All lights from the file. */
private List < Light > lights = new ArrayList < Light > ( ) ;
/** Loaded sky. */
private Spatial sky ;
/** Scene filters (ie. FOG). */
private List < Filter > filters = new ArrayList < Filter > ( ) ;
/ * *
* The background color of the render loaded from the horizon color of the world . If no world is used than the gray color
* is set to default ( as in blender editor .
* /
private ColorRGBA backgroundColor = ColorRGBA . Gray ;
}
}