/*
* Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
package javax.media.j3d;
import javax.vecmath.Point2f;
import javax.vecmath.Point3f;
/**
* The PointSoundRetained node (a sub-class of the SoundRetained node) defines
* a spatially-located sound source whose waves radiate uniformly in all
* directions from a given location in space.
*/
class PointSoundRetained extends SoundRetained {
/**
* Origin of Sound source in Listener's space.
*/
Point3f position = new Point3f(0.0f, 0.0f, 0.0f);
/**
* The transformed position of this sound
*/
Point3f xformPosition = new Point3f();
Transform3D trans = new Transform3D();
// Pairs of distances and gain scale factors that define piecewise linear
// gain attenuation between each pair.
float[] attenuationDistance;
float[] attenuationGain;
PointSoundRetained() {
this.nodeType = NodeRetained.POINTSOUND;
}
/**
* Sets this sound's location from the vector provided.
* @param position the new location
*/
void setPosition(Point3f position) {
if (staticTransform != null) {
staticTransform.transform.transform(position, this.position);
} else {
this.position.set(position);
}
getLastLocalToVworld().transform(position, xformPosition);
dispatchAttribChange(POSITION_DIRTY_BIT, (new Point3f(this.position)));
if (source != null && source.isLive()) {
notifySceneGraphChanged(false);
}
}
/**
* Sets this sound's position from the three values provided.
* @param x the new x position
* @param y the new y position
* @param z the new z position
*/
void setPosition(float x, float y, float z) {
position.x = x;
position.y = y;
position.z = z;
if (staticTransform != null) {
staticTransform.transform.transform(this.position);
}
getLastLocalToVworld().transform(position, xformPosition);
dispatchAttribChange(POSITION_DIRTY_BIT, (new Point3f(this.position)));
if (source != null && source.isLive()) {
notifySceneGraphChanged(false);
}
}
/**
* Retrieves this sound's location and places it in the vector provided.
* @param position the variable to receive the location vector
*/
void getPosition(Point3f position) {
if (staticTransform != null) {
Transform3D invTransform = staticTransform.getInvTransform();
invTransform.transform(this.position, position);
} else {
position.set(this.position);
}
}
void getXformPosition(Point3f position) {
position.set(this.xformPosition);
}
/**
* Sets this sound's distance gain attenuation - where gain scale factor
* is applied to sound based on distance listener is from sound source.
* @param distance attenuation pairs of (distance,gain-scale-factor)
*/
void setDistanceGain(Point2f[] attenuation) {
// if attenuation array null set both attenuation components to null
if (attenuation == null) {
this.attenuationDistance = null;
this.attenuationGain = null;
// QUESTION: is this needed so that dispatch***() doesn't
// fail with null?
return;
}
int attenuationLength = attenuation.length;
this.attenuationDistance = new float[attenuationLength];
this.attenuationGain = new float[attenuationLength];
for (int i = 0; i < attenuationLength; i++) {
this.attenuationDistance[i] = attenuation[i].x;
this.attenuationGain[i] = attenuation[i].y;
}
dispatchAttribChange(DISTANCE_GAIN_DIRTY_BIT, attenuation);
if (source != null && source.isLive()) {
notifySceneGraphChanged(false);
}
}
/**
* Sets this sound's distance gain given separate arrays.
* applied to sound based on distance listener is from sound source.
* @param distance array of monotonically-increasing floats.
* @param gain array of amplitude scale factors associated with distances.
*/
void setDistanceGain(float[] distance, float[] gain) {
// if distance or gain arrays are null then treat both as null
if (distance == null) {
this.attenuationDistance = null;
this.attenuationGain = null;
// QUESTION: is this needed so that dispatch***() doesn't
// fail with null?
return;
}
int gainLength = gain.length;
int distanceLength = distance.length;
this.attenuationDistance = new float[distanceLength];
this.attenuationGain = new float[distanceLength];
// Copy the distance array into nodes field
System.arraycopy(distance, 0, this.attenuationDistance, 0, distanceLength);
// Copy the gain array an array of same length as the distance array
if (distanceLength <= gainLength) {
System.arraycopy(gain, 0, this.attenuationGain, 0, distanceLength);
}
else {
System.arraycopy(gain, 0, this.attenuationGain, 0, gainLength);
// Extend gain array to length of distance array
// replicate last gain values.
for (int i=gainLength; i< distanceLength; i++) {
this.attenuationGain[i] = gain[gainLength - 1];
}
}
Point2f [] attenuation = new Point2f[distanceLength];
for (int i=0; i<distanceLength; i++) {
attenuation[i] = new Point2f(this.attenuationDistance[i],
this.attenuationGain[i]);
}
dispatchAttribChange(DISTANCE_GAIN_DIRTY_BIT, attenuation);
if (source != null && source.isLive()) {
notifySceneGraphChanged(false);
}
}
/**
* Gets this sound's distance attenuation array length
* @return distance gain attenuation array length
*/
int getDistanceGainLength() {
if (attenuationDistance == null)
return 0;
else
return this.attenuationDistance.length;
}
/**
* Retieves sound's distance attenuation
* Put the contents of the two separate distance and gain arrays into
* an array of Point2f.
* @param attenuation containing distance attenuation pairs
*/
void getDistanceGain(Point2f[] attenuation) {
// write into arrays passed in, don't do a new
if (attenuation == null)
return;
if (this.attenuationDistance == null ||
this.attenuationGain == null)
return;
int attenuationLength = attenuation.length;
// attenuationDistance and Gain array lengths should be the same
int distanceLength = this.attenuationDistance.length;
if (distanceLength > attenuationLength)
distanceLength = attenuationLength;
for (int i=0; i< distanceLength; i++) {
attenuation[i].x = attenuationDistance[i];
attenuation[i].y = attenuationGain[i];
}
}
/**
* Retieves this sound's attenuation distance and gain arrays, returned in
* separate arrays.
* @param distance array of monotonically-increasing floats.
* @param gain array of amplitude scale factors associated with distances.
*/
void getDistanceGain(float[] distance, float[] gain) {
// write into arrays passed in, don't do a new
if (distance == null || gain == null)
return;
if (this.attenuationDistance == null || this.attenuationGain == null)
return;
// These two array length should be the same
int attenuationLength = this.attenuationDistance.length;
int distanceLength = distance.length;
if (distanceLength > attenuationLength)
distanceLength = attenuationLength;
System.arraycopy(this.attenuationDistance, 0, distance, 0, distanceLength);
attenuationLength = this.attenuationDistance.length;
int gainLength = gain.length;
if (gainLength > attenuationLength)
gainLength = attenuationLength;
System.arraycopy(this.attenuationGain, 0, gain, 0, gainLength);
}
/**
* This updates the positional fields of point sound.
*
* Distance gain attenuation field not maintained in mirror object.
*/
@Override
void updateMirrorObject(Object[] objs) {
if (debugFlag)
debugPrint("PointSoundRetained:updateMirrorObj()");
int component = ((Integer)objs[1]).intValue();
int numSnds = ((Integer)objs[2]).intValue();
SoundRetained[] mSnds = (SoundRetained[]) objs[3];
if (component == -1) {
// update every field
initMirrorObject(((PointSoundRetained)objs[2]));
return;
}
if ((component & POSITION_DIRTY_BIT) != 0) {
for (int i = 0; i < numSnds; i++) {
PointSoundRetained point = (PointSoundRetained) mSnds[i];
Object o = objs[4];
if (o instanceof Point3f) {
point.position = (Point3f) objs[4];
point.getLastLocalToVworld().transform(point.position,
point.xformPosition);
}
}
}
// call the parent's mirror object update routine
super.updateMirrorObject(objs);
}
synchronized void initMirrorObject(PointSoundRetained ms) {
super.initMirrorObject(ms);
ms.position.set(this.position);
ms.xformPosition.set(this.xformPosition);
}
// Called on the mirror object
@Override
void updateTransformChange() {
super.updateTransformChange();
getLastLocalToVworld().transform(position, xformPosition);
// set flag looked at by Scheduler to denote Transform change
// this flag will force resneding transformed position to AudioDevice
if (debugFlag)
debugPrint("PointSoundRetained xformPosition is (" + xformPosition.x +
", " + xformPosition.y + ", "+ xformPosition.z + ")");
}
@Override
void mergeTransform(TransformGroupRetained xform) {
super.mergeTransform(xform);
xform.transform.transform(position, position);
}
}