Hello!
I'm fairly new to both Android and Java (pretty experienced in C/C++),
and (stupidly, perhaps) took on a big project. In it, I need to load
information about 3D models and their respective animations from a
text file (proprietary file format based on PSK/PSA).
The loading process on a sample (1000+ line) file is abysmally slow
(more than 4 minutes). The good news is that there are lots and lots
of ways I can improve it. What I'm hoping to find out here is if
there's anything in *particular* in my code that is very inefficient
that I can change. Don't worry about offending me, I know this is
amateur code here.
Some possible causes for the slowness:
- Extensive use of parseFloat()
- Creating new StringTokenizer for every line
- Inefficiencies in for() loops (not caching as necessary?)
- Repeated use of the acos() and sqrt() functions
I'd like to find out from you gentlemen (ladies) what you think is the
major thing eating up my time here. If there's some tool I can use to
find out what lines of code are taking up the most time that'd be
ideal. I'm open to any possible solutions. The file format can
change (and probably will). I could even do something as drastic as
performing this load in a separate app (before distribution) and
somehow exporting the resulting object (KdfAnimation) that could more
easily be picked back up (this would work in C, but I'm doubtful Java
would allow such low-level memory copying).
One final note is that the "action" loading section of this code
occupies about 3/4 of the files being loaded (so this section may be
more important to optimize).
====Loader Code====
public class KdfLoader {
public static KdfAnimation load(String file) throws IOException {
KdfAnimation anim = new KdfAnimation();
FileInputStream fis = new FileInputStream(file);
String line = "[start]";
LineNumberReader lnr = new LineNumberReader(new
InputStreamReader
(fis));
int boneNum = 0;
try {
for (line = lnr.readLine(); line != null; line =
lnr.readLine()) {
if (line.length() > 0) {
if (line.startsWith("numbones")) {
// Ignore it for now
}
else if (line.startsWith("bone")) {
// Bone define
StringTokenizer tok = new
StringTokenizer(line);
KdfBone bone = new KdfBone();
// Toss out "bone"
tok.nextToken();
// Set name
bone.boneName = tok.nextToken();
if (boneNum == 0) {
// Root bone. Ignore children
and parent id in file
bone.parentId = 0;
bone.parent = null;
// ignore "numchildren x"
// ignore "parentid x"
}
else
{
// ignore "numchildren x"
tok.nextToken();
tok.nextToken();
// ignore "parentid" text
tok.nextToken();
bone.parentId =
Integer.parseInt(tok.nextToken());
bone.parent =
anim.allBones.get(bone.parentId);
}
// get loc and rot
line = lnr.readLine();
bone.loc = parseLocLine(line);
line = lnr.readLine();
bone.rot = parseRotLine(line);
// Handle explicit linkage
while(true) {
lnr.mark(1000);
line = lnr.readLine();
if (line.startsWith("obj")) {
tok = new
StringTokenizer(line);
// ignore "obj"
tok.nextToken();
bone.partLinks.add(tok.nextToken());
anim.explicitPartLinkage = true;
}
else {
// Reset to previous
line number
lnr.reset();
break;
}
}
// Add bone to animation
if (boneNum == 0) {
anim.rootBone = bone;
}
else {
// Add this bone to its
parent's list of children
bone.parent.children.add(bone);
}
anim.allBones.add(bone);
boneNum++;
}
else if (line.startsWith("action")) {
// Action define
StringTokenizer tok = new
StringTokenizer(line);
KdfAction action = new KdfAction();
// ignore "action"
tok.nextToken();
// Set name
action.actionName = tok.nextToken();
// ignore "numframes" text
tok.nextToken();
// Get number of frames
action.numberOfFrames =
Integer.parseInt(tok.nextToken());
// Get all bone actions
while(true) {
//Mark the current line
lnr.mark(1000);
line = lnr.readLine();
if (line == null) {
break;
}
if (!line.startsWith("bone")) {
// Go back to the
previous line so the for() loop we're in
doesn't skip one
lnr.reset();
break;
}
KdfBoneAction bact = new
KdfBoneAction();
tok = new StringTokenizer(line);
// ignore "bone"
tok.nextToken();
// Figure out the bone this
using
String boneName =
tok.nextToken();
for (int i = 0; i <
anim.allBones.size(); i++) {
if
(anim.allBones.get(i).boneName.equalsIgnoreCase(boneName)) {
bact.bone =
anim.allBones.get(i);
break;
}
}
// Get [numberOfFrames] locs
for (int i = 0; i <
action.numberOfFrames; i++) {
line = lnr.readLine();
bact.locs.add(parseLocLine(line));
}
// Get [numberOfFrames] rots
for (int i = 0; i <
action.numberOfFrames; i++) {
line = lnr.readLine();
bact.rots.add(parseRotLine(line));
}
// Add the BoneAction to the
action
action.boneActions.add(bact);
}
// Add the action to the animation
anim.actions.add(action);
}
}
}
} catch (Exception e) {
System.err.println("Error parsing file:");
System.err.println(lnr.getLineNumber()+" : "+line);
}
return anim;
}
private static float[] parseLocLine(String line) {
float[] retFloat = new float[3];
StringTokenizer tok = new StringTokenizer(line);
// Ignore "loc"
tok.nextToken();
// x
tok.nextToken();
retFloat[0] = Float.parseFloat(tok.nextToken());
// y
tok.nextToken();
retFloat[1] = Float.parseFloat(tok.nextToken());
// z
tok.nextToken();
retFloat[2] = Float.parseFloat(tok.nextToken());
return retFloat;
}
private static float[] parseRotLine(String line) {
float[] retFloat = new float[4];
StringTokenizer tok = new StringTokenizer(line);
// Ignore "rot"
tok.nextToken();
// theta
tok.nextToken();
retFloat[0] = wToTheta(Float.parseFloat(tok.nextToken()));
// x
tok.nextToken();
retFloat[1] = Float.parseFloat(tok.nextToken());
// y
tok.nextToken();
retFloat[2] = Float.parseFloat(tok.nextToken());
// z
tok.nextToken();
retFloat[3] = Float.parseFloat(tok.nextToken());
// Need to scale this vector to make glRotate happy
retFloat = scaleAxisAngleVector(retFloat);
return retFloat;
}
private static float wToTheta(float w) {
// w = cos(theta/2)
float theta = (float)Math.acos((double)w) * 2;
// convert theta to degrees
theta = 180 * theta / (float)Math.PI;
// The above calculation only gave us a value from 0 to 180
because
of arccosine
// TODO: Determine if this is a problem. Note that w can be
negative, and this implies 180 degrees
if (w < 0) {
theta += 180;
}
return theta;
}
private static float[] scaleAxisAngleVector(float[] unscaled) {
float[] scaled = new float[4];
// scale = sqrt(x^2 + y^2 + z^2);
float scale = (float)Math.sqrt(unscaled[1] * unscaled[1] +
unscaled
[2] * unscaled[2] + unscaled[3] * unscaled[3]);
if (scale != 0) {
scaled[0] = unscaled[0];
scaled[1] = unscaled[1] / scale;
scaled[2] = unscaled[2] / scale;
scaled[3] = unscaled[3] / scale;
}
else
{
// Scale can be 0, which means we're not rotating
// but dividing by zero is bad, and glRotate still
// needs a normalized vector.
scaled[0] = 0;
scaled[1] = 1;
scaled[2] = 0;
scaled[3] = 0;
}
return scaled;
}
}
====End of Loader Code====
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Android Beginners" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-beginners?hl=en
-~----------~----~----~----~------~----~------~--~---