/** This setlive simply concatinates it's transform onto all the ones passed in. */
  void setLive(SetLiveState s) {
    int i, j;
    Transform3D trans = null;
    Targets[] newTargets = null;
    Targets[] savedTransformTargets = null;
    int oldTraverseFlags = 0;
    int len;
    Object obj;

    // XXXX - optimization for targetThreads computation, require
    // cleanup in GroupRetained.doSetLive()
    // int savedTargetThreads = 0;
    // savedTargetThreads = s.transformTargetThreads;
    // s.transformTargetThreads = 0;

    oldTraverseFlags = s.traverseFlags;

    savedTransformTargets = s.transformTargets;

    int numPaths = (s.inSharedGroup) ? s.keys.length : 1;
    newTargets = new Targets[numPaths];
    for (i = 0; i < numPaths; i++) {
      newTargets[i] = new Targets();
    }

    s.transformTargets = newTargets;
    s.traverseFlags = 0;

    // This is needed b/c super.setlive is called after inSharedGroup check.
    inSharedGroup = s.inSharedGroup;

    trans = new Transform3D();
    transform.getWithLock(trans);
    currentTransform.set(trans);

    ArrayList savedChildTransformLinks = s.childTransformLinks;
    GroupRetained savedParentTransformLink = s.parentTransformLink;
    Transform3D[][] oldCurrentList = s.currentTransforms;
    int[][] oldCurrentIndexList = s.currentTransformsIndex;

    super.doSetLive(s);

    if (!inSharedGroup) {
      if (s.transformTargets[0] != null) {
        cachedTargets[0] = s.transformTargets[0].snapShotInit();
      }
      if (s.switchTargets != null && s.switchTargets[0] != null) {
        s.switchTargets[0].addNode(this, Targets.GRP_TARGETS);
      }
    } else {
      int hkIndex;
      for (i = 0; i < numPaths; i++) {
        if (s.transformTargets[i] != null) {
          hkIndex = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
          cachedTargets[hkIndex] = s.transformTargets[i].snapShotInit();
        }
        if (s.switchTargets != null && s.switchTargets[i] != null) {
          s.switchTargets[i].addNode(this, Targets.GRP_TARGETS);
        }
      }
    }

    // Assign data in cachedTargets to j3dCTs.
    j3dCTs = new CachedTargets[cachedTargets.length];
    copyCachedTargets(TargetsInterface.TRANSFORM_TARGETS, j3dCTs);

    computeTargetThreads(TargetsInterface.TRANSFORM_TARGETS, cachedTargets);

    // restore setLiveState from it's local variables.
    // setNodeData did keep a reference to these variables.
    s.localToVworld = localToVworld;
    s.localToVworldIndex = localToVworldIndex;
    s.currentTransforms = oldCurrentList;
    s.currentTransformsIndex = oldCurrentIndexList;

    s.childTransformLinks = savedChildTransformLinks;
    s.parentTransformLink = savedParentTransformLink;

    s.transformTargets = savedTransformTargets;

    if (!s.inSharedGroup) {
      s.transformLevels[0] -= 1;
    } else {
      for (i = 0; i < s.keys.length; i++) {
        s.transformLevels[i] -= 1;
      }
    }

    if ((s.traverseFlags & NodeRetained.CONTAINS_VIEWPLATFORM) != 0) {
      aboveAViewPlatform = true;
    }
    s.traverseFlags |= oldTraverseFlags;

    if (aboveAViewPlatform && !trans.isCongruent()) {
      throw new BadTransformException(J3dI18N.getString("ViewPlatformRetained0"));
    }

    super.markAsLive();
  }