/** * Stops the current animation and poses the skins to the static pose of the specified and frame. */ public void pose(String frameName) { AnimationController ac = (skeleton == null) ? null : skeleton.getAnimationController(); if (ac == null) throw new IllegalStateException("Can't pose without an AnimationController set"); BoneAnimation animation = ac.getActiveAnimation(); if (animation == null) throw new IllegalStateException("Controller has no active animation"); ac.setActive(false); animation.setCurrentFrame(frameName); // updateSkin(); needsRefresh = true; }
/** * Assimilates the specified skin mesh Geometries from the specified 'otherSkinNode' into this * one, removing them from 'otherSkinNode'. If otherSkinNode is a non-null-skinRegion * SkinTransferNode and no narrowing regex is supplied, we will REPLACE all current Geometries * with that same skinRegion. * * <p>For this first implementation, we have some rather stringent requirements. If use cases * justify accommodating other states, these requirements can be relaxed with further development * work. * * <p>This method will not succeed if the rest animation and rest pose frame have not been set in * the AnimationController (unless no AnimationController has been assigned for this SkinNode * yet). <b>IMPORTANT: The calling signature is tentative. It's likely that checked exceptions * will be added, so expect to need to update your exception-handling until this method has * stabilized.</b> * * @param otherSkinNode Node from which the Geometries and BoneInfluences will be taken. * @param geoNameRegex To specify which Geometries to assimilate. Null to assimilate all skin * Geometries from 'otherSkinNode'. * @throws RuntimeException for various state validation failures. This is easier to use, but not * as robust. This is likely to change soon. */ public void assimilate(SkinNode otherSkinNode, String geoNameRegex) { /* URGENT TODOs: * Find out if need to stop, clear, reset animations or the * AnimationController before doing this stuff. * Consider how to valiate inported SkinNodes' * BI.nOffset, BI.vOffset, which should always be derived, as they * should never be persisted for hot skins. */ Node otherSkins = otherSkinNode.getSkins(); if (otherSkins == null || otherSkins.getQuantity() < 1) { logger.log( Level.WARNING, "Not merging SkinNode ''{0}'' into ''{1}'' " + "since no skin meshes for former", new String[] {otherSkinNode.getName(), getName()}); return; } BoneAnimation origAnim = null; int origFrame = -1; boolean origActive = false; AnimationController ac = (skeleton == null) ? null : skeleton.getAnimationController(); if (ac == null) { logger.fine("No Animation Controller in place"); } else { origActive = ac.isActive(); origAnim = ac.getActiveAnimation(); if (origAnim != null) origFrame = origAnim.getCurrentFrame(); ac.rest(); updateSkin(); } ArrayList<BoneInfluence>[][] otherCache = otherSkinNode.getCache(); if (otherCache == null) throw new IllegalArgumentException( "Other skin has skin mesh(es), but no influence cache: " + otherSkinNode.getName()); if (otherSkins.getQuantity() != otherSkinNode.getCache().length) throw new IllegalArgumentException( "SkinNode '" + otherSkinNode.getName() + "' has skin geo vs. cache count mismatch: " + otherSkins.getQuantity() + " vs. " + otherSkinNode.getCache().length); logger.log( Level.FINE, "Merging SkinNode ''{0}'' into ''{1}''", new String[] {otherSkinNode.getName(), getName()}); if (otherSkinNode.bindMatrix != null) throw new IllegalArgumentException("Skin bindMatrixes are not supported for assimilation"); ArrayList<BoneInfluence>[] transferredInfluences; String skinRegion = (otherSkinNode instanceof SkinTransferNode) ? ((SkinTransferNode) otherSkinNode).getSkinRegion() : null; Geometry g; validateSkins(); otherSkinNode.validateSkins(); if (geoNameRegex == null && skinRegion != null && skins != null && skins.getQuantity() > 0) { // This block culls old Geometries with the incoming skinRegion. int rmCount = 0; cullRegionMappings(); int childCount = skins.getQuantity(); for (int i = childCount - 1; i >= 0; i--) { g = getSkin(i); if (!geometryRegions.containsKey(g.getName())) continue; if (!geometryRegions.get(g.getName()).equals(skinRegion)) continue; if (otherSkinNode.hasSkinGeometry(g.getName(), skinRegion)) { logger.log(Level.INFO, "Retaining geometry ''{0}''", g.getName()); continue; } removeSkinGeometry(i); rmCount++; } logger.log(Level.INFO, "Purged {0} Geos to be replaced", rmCount); } for (int i = otherSkins.getQuantity() - 1; i >= 0; i--) { g = otherSkinNode.getSkin(i); if (geoNameRegex == null) { if (skinRegion != null && hasSkinGeometry(g.getName(), skinRegion)) continue; // We retained the old skin of same name and region above } else { if (!g.getName().matches(geoNameRegex)) continue; } transferredInfluences = otherCache[i]; assimilate(otherSkinNode.removeSkinGeometry(g.getName()), transferredInfluences, skinRegion); } if (ac != null) { ac.setActive(origActive); if (origAnim == null) { ac.clearActiveAnimation(); } else { ac.setActiveAnimation(origAnim); if (origFrame > -1) { origAnim.setCurrentFrame(origFrame); updateSkin(); /* There are cases where this updateSkin is unnecessary, * but it is very easy to miss cases where it is * necessary, so just do it! * The only case where this is undesirable is if an * animation is the activeAnimation but the Controller * itself has never been active while the animation has * been active. In this case, we will switch the active * pose. Unfortunately, it's impossible to detect this * border case. */ } } } }