1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.scene.plugins.blender; 33 34 import java.io.IOException; 35 import java.util.ArrayList; 36 import java.util.EmptyStackException; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Stack; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 44 import com.jme3.animation.Skeleton; 45 import com.jme3.asset.AssetManager; 46 import com.jme3.asset.BlenderKey; 47 import com.jme3.material.Material; 48 import com.jme3.math.ColorRGBA; 49 import com.jme3.scene.plugins.blender.animations.BoneContext; 50 import com.jme3.scene.plugins.blender.animations.Ipo; 51 import com.jme3.scene.plugins.blender.constraints.Constraint; 52 import com.jme3.scene.plugins.blender.file.BlenderInputStream; 53 import com.jme3.scene.plugins.blender.file.DnaBlockData; 54 import com.jme3.scene.plugins.blender.file.FileBlockHeader; 55 import com.jme3.scene.plugins.blender.file.Structure; 56 import com.jme3.scene.plugins.blender.materials.MaterialContext; 57 import com.jme3.scene.plugins.blender.meshes.MeshContext; 58 import com.jme3.scene.plugins.blender.modifiers.Modifier; 59 import com.jme3.scene.plugins.ogre.AnimData; 60 61 /** 62 * The class that stores temporary data and manages it during loading the belnd 63 * file. This class is intended to be used in a single loading thread. It holds 64 * the state of loading operations. 65 * 66 * @author Marcin Roguski (Kaelthas) 67 */ 68 public class BlenderContext { 69 private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName()); 70 71 /** The blender file version. */ 72 private int blenderVersion; 73 /** The blender key. */ 74 private BlenderKey blenderKey; 75 /** The header of the file block. */ 76 private DnaBlockData dnaBlockData; 77 /** The input stream of the blend file. */ 78 private BlenderInputStream inputStream; 79 /** The asset manager. */ 80 private AssetManager assetManager; 81 /** 82 * A map containing the file block headers. The key is the old pointer 83 * address. 84 */ 85 private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>(); 86 /** A map containing the file block headers. The key is the block code. */ 87 private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>(); 88 /** 89 * This map stores the loaded features by their old memory address. The 90 * first object in the value table is the loaded structure and the second - 91 * the structure already converted into proper data. 92 */ 93 private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>(); 94 /** 95 * This map stores the loaded features by their name. Only features with ID 96 * structure can be stored here. The first object in the value table is the 97 * loaded structure and the second - the structure already converted into 98 * proper data. 99 */ 100 private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>(); 101 /** A stack that hold the parent structure of currently loaded feature. */ 102 private Stack<Structure> parentStack = new Stack<Structure>(); 103 /** 104 * A map storing loaded ipos. The key is the ipo's owner old memory address 105 * and the value is the ipo. 106 */ 107 private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>(); 108 /** A list of modifiers for the specified object. */ 109 protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>(); 110 /** A list of constraints for the specified object. */ 111 protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>(); 112 /** Anim data loaded for features. */ 113 private Map<Long, AnimData> animData = new HashMap<Long, AnimData>(); 114 /** Loaded skeletons. */ 115 private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>(); 116 /** A map of mesh contexts. */ 117 protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>(); 118 /** A map of bone contexts. */ 119 protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>(); 120 /** A map of material contexts. */ 121 protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>(); 122 /** A map og helpers that perform loading. */ 123 private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>(); 124 125 /** 126 * This method sets the blender file version. 127 * 128 * @param blenderVersion 129 * the blender file version 130 */ setBlenderVersion(String blenderVersion)131 public void setBlenderVersion(String blenderVersion) { 132 this.blenderVersion = Integer.parseInt(blenderVersion); 133 } 134 135 /** 136 * @return the blender file version 137 */ getBlenderVersion()138 public int getBlenderVersion() { 139 return blenderVersion; 140 } 141 142 /** 143 * This method sets the blender key. 144 * 145 * @param blenderKey 146 * the blender key 147 */ setBlenderKey(BlenderKey blenderKey)148 public void setBlenderKey(BlenderKey blenderKey) { 149 this.blenderKey = blenderKey; 150 } 151 152 /** 153 * This method returns the blender key. 154 * 155 * @return the blender key 156 */ getBlenderKey()157 public BlenderKey getBlenderKey() { 158 return blenderKey; 159 } 160 161 /** 162 * This method sets the dna block data. 163 * 164 * @param dnaBlockData 165 * the dna block data 166 */ setBlockData(DnaBlockData dnaBlockData)167 public void setBlockData(DnaBlockData dnaBlockData) { 168 this.dnaBlockData = dnaBlockData; 169 } 170 171 /** 172 * This method returns the dna block data. 173 * 174 * @return the dna block data 175 */ getDnaBlockData()176 public DnaBlockData getDnaBlockData() { 177 return dnaBlockData; 178 } 179 180 /** 181 * This method returns the asset manager. 182 * 183 * @return the asset manager 184 */ getAssetManager()185 public AssetManager getAssetManager() { 186 return assetManager; 187 } 188 189 /** 190 * This method sets the asset manager. 191 * 192 * @param assetManager 193 * the asset manager 194 */ setAssetManager(AssetManager assetManager)195 public void setAssetManager(AssetManager assetManager) { 196 this.assetManager = assetManager; 197 } 198 199 /** 200 * This method returns the input stream of the blend file. 201 * 202 * @return the input stream of the blend file 203 */ getInputStream()204 public BlenderInputStream getInputStream() { 205 return inputStream; 206 } 207 208 /** 209 * This method sets the input stream of the blend file. 210 * 211 * @param inputStream 212 * the input stream of the blend file 213 */ setInputStream(BlenderInputStream inputStream)214 public void setInputStream(BlenderInputStream inputStream) { 215 this.inputStream = inputStream; 216 } 217 218 /** 219 * This method adds a file block header to the map. Its old memory address 220 * is the key. 221 * 222 * @param oldMemoryAddress 223 * the address of the block header 224 * @param fileBlockHeader 225 * the block header to store 226 */ addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader)227 public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { 228 fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); 229 List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); 230 if (headers == null) { 231 headers = new ArrayList<FileBlockHeader>(); 232 fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); 233 } 234 headers.add(fileBlockHeader); 235 } 236 237 /** 238 * This method returns the block header of a given memory address. If the 239 * header is not present then null is returned. 240 * 241 * @param oldMemoryAddress 242 * the address of the block header 243 * @return loaded header or null if it was not yet loaded 244 */ getFileBlock(Long oldMemoryAddress)245 public FileBlockHeader getFileBlock(Long oldMemoryAddress) { 246 return fileBlockHeadersByOma.get(oldMemoryAddress); 247 } 248 249 /** 250 * This method returns a list of file blocks' headers of a specified code. 251 * 252 * @param code 253 * the code of file blocks 254 * @return a list of file blocks' headers of a specified code 255 */ getFileBlocks(Integer code)256 public List<FileBlockHeader> getFileBlocks(Integer code) { 257 return fileBlockHeadersByCode.get(code); 258 } 259 260 /** 261 * This method clears the saved block headers stored in the features map. 262 */ clearFileBlocks()263 public void clearFileBlocks() { 264 fileBlockHeadersByOma.clear(); 265 fileBlockHeadersByCode.clear(); 266 } 267 268 /** 269 * This method adds a helper instance to the helpers' map. 270 * 271 * @param <T> 272 * the type of the helper 273 * @param clazz 274 * helper's class definition 275 * @param helper 276 * the helper instance 277 */ putHelper(Class<T> clazz, AbstractBlenderHelper helper)278 public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) { 279 helpers.put(clazz.getSimpleName(), helper); 280 } 281 282 @SuppressWarnings("unchecked") getHelper(Class<?> clazz)283 public <T> T getHelper(Class<?> clazz) { 284 return (T) helpers.get(clazz.getSimpleName()); 285 } 286 287 /** 288 * This method adds a loaded feature to the map. The key is its unique old 289 * memory address. 290 * 291 * @param oldMemoryAddress 292 * the address of the feature 293 * @param featureName 294 * the name of the feature 295 * @param structure 296 * the filled structure of the feature 297 * @param feature 298 * the feature we want to store 299 */ addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature)300 public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) { 301 if (oldMemoryAddress == null || structure == null || feature == null) { 302 throw new IllegalArgumentException("One of the given arguments is null!"); 303 } 304 Object[] storedData = new Object[] { structure, feature }; 305 loadedFeatures.put(oldMemoryAddress, storedData); 306 if (featureName != null) { 307 loadedFeaturesByName.put(featureName, storedData); 308 } 309 } 310 311 /** 312 * This method returns the feature of a given memory address. If the feature 313 * is not yet loaded then null is returned. 314 * 315 * @param oldMemoryAddress 316 * the address of the feature 317 * @param loadedFeatureDataType 318 * the type of data we want to retreive it can be either filled 319 * structure or already converted feature 320 * @return loaded feature or null if it was not yet loaded 321 */ getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType)322 public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) { 323 Object[] result = loadedFeatures.get(oldMemoryAddress); 324 if (result != null) { 325 return result[loadedFeatureDataType.getIndex()]; 326 } 327 return null; 328 } 329 330 /** 331 * This method returns the feature of a given name. If the feature is not 332 * yet loaded then null is returned. 333 * 334 * @param featureName 335 * the name of the feature 336 * @param loadedFeatureDataType 337 * the type of data we want to retreive it can be either filled 338 * structure or already converted feature 339 * @return loaded feature or null if it was not yet loaded 340 */ getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType)341 public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) { 342 Object[] result = loadedFeaturesByName.get(featureName); 343 if (result != null) { 344 return result[loadedFeatureDataType.getIndex()]; 345 } 346 return null; 347 } 348 349 /** 350 * This method clears the saved features stored in the features map. 351 */ clearLoadedFeatures()352 public void clearLoadedFeatures() { 353 loadedFeatures.clear(); 354 } 355 356 /** 357 * This method adds the structure to the parent stack. 358 * 359 * @param parent 360 * the structure to be added to the stack 361 */ pushParent(Structure parent)362 public void pushParent(Structure parent) { 363 parentStack.push(parent); 364 } 365 366 /** 367 * This method removes the structure from the top of the parent's stack. 368 * 369 * @return the structure that was removed from the stack 370 */ popParent()371 public Structure popParent() { 372 try { 373 return parentStack.pop(); 374 } catch (EmptyStackException e) { 375 return null; 376 } 377 } 378 379 /** 380 * This method retreives the structure at the top of the parent's stack but 381 * does not remove it. 382 * 383 * @return the structure from the top of the stack 384 */ peekParent()385 public Structure peekParent() { 386 try { 387 return parentStack.peek(); 388 } catch (EmptyStackException e) { 389 return null; 390 } 391 } 392 393 /** 394 * This method adds new ipo curve for the feature. 395 * 396 * @param ownerOMA 397 * the OMA of blender feature that owns the ipo 398 * @param ipo 399 * the ipo to be added 400 */ addIpo(Long ownerOMA, Ipo ipo)401 public void addIpo(Long ownerOMA, Ipo ipo) { 402 loadedIpos.put(ownerOMA, ipo); 403 } 404 405 /** 406 * This method removes the ipo curve from the feature. 407 * 408 * @param ownerOma 409 * the OMA of blender feature that owns the ipo 410 */ removeIpo(Long ownerOma)411 public Ipo removeIpo(Long ownerOma) { 412 return loadedIpos.remove(ownerOma); 413 } 414 415 /** 416 * This method returns the ipo curve of the feature. 417 * 418 * @param ownerOMA 419 * the OMA of blender feature that owns the ipo 420 */ getIpo(Long ownerOMA)421 public Ipo getIpo(Long ownerOMA) { 422 return loadedIpos.get(ownerOMA); 423 } 424 425 /** 426 * This method adds a new modifier to the list. 427 * 428 * @param ownerOMA 429 * the owner's old memory address 430 * @param modifier 431 * the object's modifier 432 */ addModifier(Long ownerOMA, Modifier modifier)433 public void addModifier(Long ownerOMA, Modifier modifier) { 434 List<Modifier> objectModifiers = this.modifiers.get(ownerOMA); 435 if (objectModifiers == null) { 436 objectModifiers = new ArrayList<Modifier>(); 437 this.modifiers.put(ownerOMA, objectModifiers); 438 } 439 objectModifiers.add(modifier); 440 } 441 442 /** 443 * This method returns modifiers for the object specified by its old memory 444 * address and the modifier type. If no modifiers are found - empty list is 445 * returned. If the type is null - all modifiers for the object are 446 * returned. 447 * 448 * @param objectOMA 449 * object's old memory address 450 * @param type 451 * the type of the modifier 452 * @return the list of object's modifiers 453 */ getModifiers(Long objectOMA, String type)454 public List<Modifier> getModifiers(Long objectOMA, String type) { 455 List<Modifier> result = new ArrayList<Modifier>(); 456 List<Modifier> readModifiers = modifiers.get(objectOMA); 457 if (readModifiers != null && readModifiers.size() > 0) { 458 for (Modifier modifier : readModifiers) { 459 if (type == null || type.isEmpty() || modifier.getType().equals(type)) { 460 result.add(modifier); 461 } 462 } 463 } 464 return result; 465 } 466 467 /** 468 * This method adds a new modifier to the list. 469 * 470 * @param ownerOMA 471 * the owner's old memory address 472 * @param constraints 473 * the object's constraints 474 */ addConstraints(Long ownerOMA, List<Constraint> constraints)475 public void addConstraints(Long ownerOMA, List<Constraint> constraints) { 476 List<Constraint> objectConstraints = this.constraints.get(ownerOMA); 477 if (objectConstraints == null) { 478 objectConstraints = new ArrayList<Constraint>(); 479 this.constraints.put(ownerOMA, objectConstraints); 480 } 481 objectConstraints.addAll(constraints); 482 } 483 484 /** 485 * This method returns constraints for the object specified by its old 486 * memory address. If no modifiers are found - <b>null</b> is returned. 487 * 488 * @param objectOMA 489 * object's old memory address 490 * @return the list of object's modifiers or null 491 */ getConstraints(Long objectOMA)492 public List<Constraint> getConstraints(Long objectOMA) { 493 return objectOMA == null ? null : constraints.get(objectOMA); 494 } 495 496 /** 497 * This method sets the anim data for the specified OMA of its owner. 498 * 499 * @param ownerOMA 500 * the owner's old memory address 501 * @param animData 502 * the animation data for the feature specified by ownerOMA 503 */ setAnimData(Long ownerOMA, AnimData animData)504 public void setAnimData(Long ownerOMA, AnimData animData) { 505 this.animData.put(ownerOMA, animData); 506 } 507 508 /** 509 * This method returns the animation data for the specified owner. 510 * 511 * @param ownerOMA 512 * the old memory address of the animation data owner 513 * @return the animation data or null if none exists 514 */ getAnimData(Long ownerOMA)515 public AnimData getAnimData(Long ownerOMA) { 516 return this.animData.get(ownerOMA); 517 } 518 519 /** 520 * This method sets the skeleton for the specified OMA of its owner. 521 * 522 * @param skeletonOMA 523 * the skeleton's old memory address 524 * @param skeleton 525 * the skeleton specified by the given OMA 526 */ setSkeleton(Long skeletonOMA, Skeleton skeleton)527 public void setSkeleton(Long skeletonOMA, Skeleton skeleton) { 528 this.skeletons.put(skeletonOMA, skeleton); 529 } 530 531 /** 532 * This method returns the skeleton for the specified OMA of its owner. 533 * 534 * @param skeletonOMA 535 * the skeleton's old memory address 536 * @return the skeleton specified by the given OMA 537 */ getSkeleton(Long skeletonOMA)538 public Skeleton getSkeleton(Long skeletonOMA) { 539 return this.skeletons.get(skeletonOMA); 540 } 541 542 /** 543 * This method sets the mesh context for the given mesh old memory address. 544 * If the context is already set it will be replaced. 545 * 546 * @param meshOMA 547 * the mesh's old memory address 548 * @param meshContext 549 * the mesh's context 550 */ setMeshContext(Long meshOMA, MeshContext meshContext)551 public void setMeshContext(Long meshOMA, MeshContext meshContext) { 552 this.meshContexts.put(meshOMA, meshContext); 553 } 554 555 /** 556 * This method returns the mesh context for the given mesh old memory 557 * address. If no context exists then <b>null</b> is returned. 558 * 559 * @param meshOMA 560 * the mesh's old memory address 561 * @return mesh's context 562 */ getMeshContext(Long meshOMA)563 public MeshContext getMeshContext(Long meshOMA) { 564 return this.meshContexts.get(meshOMA); 565 } 566 567 /** 568 * This method sets the bone context for the given bone old memory address. 569 * If the context is already set it will be replaced. 570 * 571 * @param boneOMA 572 * the bone's old memory address 573 * @param boneContext 574 * the bones's context 575 */ setBoneContext(Long boneOMA, BoneContext boneContext)576 public void setBoneContext(Long boneOMA, BoneContext boneContext) { 577 this.boneContexts.put(boneOMA, boneContext); 578 } 579 580 /** 581 * This method returns the bone context for the given bone old memory 582 * address. If no context exists then <b>null</b> is returned. 583 * 584 * @param boneOMA 585 * the bone's old memory address 586 * @return bone's context 587 */ getBoneContext(Long boneOMA)588 public BoneContext getBoneContext(Long boneOMA) { 589 return boneContexts.get(boneOMA); 590 } 591 592 /** 593 * This method sets the material context for the given material. If the 594 * context is already set it will be replaced. 595 * 596 * @param material 597 * the material 598 * @param materialContext 599 * the material's context 600 */ setMaterialContext(Material material, MaterialContext materialContext)601 public void setMaterialContext(Material material, MaterialContext materialContext) { 602 this.materialContexts.put(material, materialContext); 603 } 604 605 /** 606 * This method returns the material context for the given material. If no 607 * context exists then <b>null</b> is returned. 608 * 609 * @param material 610 * the material 611 * @return material's context 612 */ getMaterialContext(Material material)613 public MaterialContext getMaterialContext(Material material) { 614 return materialContexts.get(material); 615 } 616 617 /** 618 * This metod returns the default material. 619 * 620 * @return the default material 621 */ getDefaultMaterial()622 public synchronized Material getDefaultMaterial() { 623 if (blenderKey.getDefaultMaterial() == null) { 624 Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 625 defaultMaterial.setColor("Color", ColorRGBA.DarkGray); 626 blenderKey.setDefaultMaterial(defaultMaterial); 627 } 628 return blenderKey.getDefaultMaterial(); 629 } 630 dispose()631 public void dispose() { 632 try { 633 inputStream.close(); 634 } catch (IOException e) { 635 LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); 636 } 637 loadedFeatures.clear(); 638 loadedFeaturesByName.clear(); 639 } 640 641 /** 642 * This enum defines what loaded data type user wants to retreive. It can be 643 * either filled structure or already converted data. 644 * 645 * @author Marcin Roguski 646 */ 647 public static enum LoadedFeatureDataType { 648 649 LOADED_STRUCTURE(0), LOADED_FEATURE(1); 650 private int index; 651 LoadedFeatureDataType(int index)652 private LoadedFeatureDataType(int index) { 653 this.index = index; 654 } 655 getIndex()656 public int getIndex() { 657 return index; 658 } 659 } 660 } 661