/// <summary>
 /// käännä kuvakulma pos:iin
 /// </summary>
 /// <param name="pos"></param>
 public new void LookAt(Vector3 pos)
 {
     GL.LoadIdentity();
     MathExt.LookAt(Position, pos, Up);
 }
        /// <summary>
        /// luo luuranko.
        /// </summary>
        /// <param name="jointInfos"></param>
        /// <param name="baseFrame"></param>
        /// <param name="animFrameData"></param>
        /// <param name="frameIndex"></param>
        /// <param name="num_joints"></param>
        /// <param name="md5anim"></param>
        void BuildFrameSkeleton(ref MD5JointInfo[] jointInfos,
                                ref MD5BaseFrameJoint[] baseFrame,
                                ref float[] animFrameData,
                                int frameIndex,
                                int num_joints, ref Animation md5anim)
        {
            int i;

            for (i = 0; i < num_joints; ++i)
            {
                MD5BaseFrameJoint baseJoint = baseFrame[i];
                Vector3           animatedPos;
                Quaternion        animatedOrient;
                int j = 0;

                animatedPos    = baseJoint.pos;
                animatedOrient = baseJoint.orient;

                if ((jointInfos[i].flags & 1) > 0) /* Tx */
                {
                    animatedPos.X = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                if ((jointInfos[i].flags & 2) > 0) /* Ty */
                {
                    animatedPos.Y = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                if ((jointInfos[i].flags & 4) > 0) /* Tz */
                {
                    animatedPos.Z = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                if ((jointInfos[i].flags & 8) > 0) /* Qx */
                {
                    animatedOrient.X = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                if ((jointInfos[i].flags & 16) > 0) /* Qy */
                {
                    animatedOrient.Y = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                if ((jointInfos[i].flags & 32) > 0) /* Qz */
                {
                    animatedOrient.Z = animFrameData[jointInfos[i].startIndex + j];
                    ++j;
                }

                /* Compute orient quaternion's w value */
                MathExt.ComputeW(ref animatedOrient);

                int parent = jointInfos[i].parent;
                md5anim.skelFrames[frameIndex, i].parent = parent;
                md5anim.skelFrames[frameIndex, i].name   = jointInfos[i].name;

                /* Has parent? */
                if (md5anim.skelFrames[frameIndex, i].parent < 0)
                {
                    md5anim.skelFrames[frameIndex, i].pos    = animatedPos;
                    md5anim.skelFrames[frameIndex, i].orient = animatedOrient;
                }
                else
                {
                    MD5Joint parentJoint = md5anim.skelFrames[frameIndex, parent];
                    Vector3  rpos; /* Rotated Position */

                    /* Add positions */
                    rpos = MathExt.RotatePoint(ref parentJoint.orient, ref animatedPos);

                    md5anim.skelFrames[frameIndex, i].pos.X = rpos.X + parentJoint.pos.X;
                    md5anim.skelFrames[frameIndex, i].pos.Y = rpos.Y + parentJoint.pos.Y;
                    md5anim.skelFrames[frameIndex, i].pos.Z = rpos.Z + parentJoint.pos.Z;

                    /* Concatenate rotations */
                    md5anim.skelFrames[frameIndex, i].orient = MathExt.Mult(ref parentJoint.orient, ref animatedOrient);
                    md5anim.skelFrames[frameIndex, i].orient = MathExt.Normalize(ref md5anim.skelFrames[frameIndex, i].orient);
                }
            }
        }
        /// <summary>
        /// lataa md5-animaatio.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="anim"></param>
        public override void LoadAnim(string animName, string fileName)
        {
            if (fileName == null || fileName == "")
            {
                return;
            }

            Animation anim = new Animation();

            anim.animName = animName;

            Buffer t = new Buffer();

            MD5JointInfo[]      jointInfos    = null;
            MD5BaseFrameJoint[] baseFrame     = null;
            float[]             animFrameData = null;
            int numAnimatedComponents         = 0;
            int frame_index;
            int i;

            using (System.IO.StreamReader file = new System.IO.StreamReader(Settings.DataDir + fileName))
            {
                string line;
                while ((line = file.ReadLine()) != null)
                {
                    if (line == "")
                    {
                        continue;
                    }

                    // Read number of joints
                    if (ParseLine(ref t, line, "numFrames %d"))
                    {
                        /* Allocate memory for skeleton frames and bounding boxes */
                        anim.numFrames = t.ibuffer[0];
                        if (anim.numFrames > 0)
                        {
                            anim.bboxes = new MD5BoundingBox[anim.numFrames];
                        }
                    }

                    if (ParseLine(ref t, line, "numJoints %d"))
                    {
                        /* Allocate memory for joints of each frame */
                        anim.numJoints = t.ibuffer[0];
                        if (anim.numJoints > 0)
                        {
                            /* Allocate temporary memory for building skeleton frames */
                            jointInfos = new MD5JointInfo[anim.numJoints];
                            baseFrame  = new MD5BaseFrameJoint[anim.numJoints];
                        }
                        anim.skelFrames = new MD5Joint[anim.numFrames, anim.numJoints];
                    }

                    if (ParseLine(ref t, line, "frameRate %d"))
                    {
                        anim.frameRate = t.ibuffer[0];
                    }

                    if (ParseLine(ref t, line, "numAnimatedComponents %d"))
                    {
                        numAnimatedComponents = t.ibuffer[0];
                        if (numAnimatedComponents > 0)
                        {
                            /* Allocate memory for animation frame data */
                            animFrameData = new float[numAnimatedComponents];
                        }
                    }

                    if (line.Equals("hierarchy {"))
                    {
                        for (i = 0; i < anim.numJoints; ++i)
                        {
                            /* Read whole line */
                            line = file.ReadLine();
                            Cleanstring(ref line);

                            /* Read joint info */
                            ParseLine(ref t, line, "%s %d %d %d");
                            jointInfos[i].name       = t.sbuffer;
                            jointInfos[i].parent     = t.ibuffer[0];
                            jointInfos[i].flags      = t.ibuffer[1];
                            jointInfos[i].startIndex = t.ibuffer[2];
                        }
                    }

                    if (line.Equals("bounds {"))
                    {
                        for (i = 0; i < anim.numFrames; ++i)
                        {
                            /* Read whole line */
                            line = file.ReadLine();
                            Cleanstring(ref line);

                            /* Read bounding box */
                            ParseLine(ref t, line, "( %f %f %f ) ( %f %f %f )");
                            anim.bboxes[i].min.X = t.fbuffer[0];
                            anim.bboxes[i].min.Y = t.fbuffer[1];
                            anim.bboxes[i].min.Z = t.fbuffer[2];
                            anim.bboxes[i].max.X = t.fbuffer[3];
                            anim.bboxes[i].max.Y = t.fbuffer[4];
                            anim.bboxes[i].max.Z = t.fbuffer[5];
                        }
                    }

                    if (line.Equals("baseframe {"))
                    {
                        for (i = 0; i < anim.numJoints; ++i)
                        {
                            /* Read whole line */
                            line = file.ReadLine();
                            Cleanstring(ref line);

                            /* Read base frame joint */
                            ParseLine(ref t, line, "( %f %f %f ) ( %f %f %f )");

                            if (t.fbuffer.Length == 6)
                            {
                                baseFrame[i].pos.X    = t.fbuffer[0];
                                baseFrame[i].pos.Y    = t.fbuffer[1];
                                baseFrame[i].pos.Z    = t.fbuffer[2];
                                baseFrame[i].orient.X = t.fbuffer[3];
                                baseFrame[i].orient.Y = t.fbuffer[4];
                                baseFrame[i].orient.Z = t.fbuffer[5];

                                /* Compute the w component */
                                MathExt.ComputeW(ref baseFrame[i].orient);
                            }
                        }
                    }

                    if (ParseLine(ref t, line, "frame %d"))
                    {
                        frame_index = t.ibuffer[0];

                        /* Read frame data */
                        for (i = 0; i < numAnimatedComponents;)
                        {
                            line = file.ReadLine();
                            if (line[0] == '}')
                            {
                                break;
                            }
                            Cleanstring(ref line);
                            string[] splt = line.Split(' ');

                            for (int ww = 0; ww < splt.Length; ww++)
                            {
                                animFrameData[i++] = float.Parse(splt[ww]);
                            }
                        }

                        /* Build frame skeleton from the collected data */
                        BuildFrameSkeleton(ref jointInfos, ref baseFrame, ref animFrameData, frame_index, anim.numJoints, ref anim);
                    }
                }

                anim.curFrame  = 0;
                anim.nextFrame = 1;

                anim.lastTime = 0;
                anim.maxTime  = 1.0f / anim.frameRate;

                /* Allocate memory for animated skeleton */
                skeleton = new MD5Joint[anim.numJoints];
                animated = true;

                Vector3 min = new Vector3(9999, 9999, 9999);
                Vector3 max = new Vector3(-9999, -9999, -9999);

                // laske bboxit
                for (int q = 0; q < anim.numFrames; q++)
                {
                    if (anim.bboxes[q].min.X < min.X)
                    {
                        min.X = anim.bboxes[q].min.X;
                    }
                    if (anim.bboxes[q].min.Y < min.Y)
                    {
                        min.Y = anim.bboxes[q].min.Y;
                    }
                    if (anim.bboxes[q].min.Z < min.Z)
                    {
                        min.Z = anim.bboxes[q].min.Z;
                    }

                    if (anim.bboxes[q].max.X > max.X)
                    {
                        max.X = anim.bboxes[q].max.X;
                    }
                    if (anim.bboxes[q].max.Y > max.Y)
                    {
                        max.Y = anim.bboxes[q].max.Y;
                    }
                    if (anim.bboxes[q].max.Z > max.Z)
                    {
                        max.Z = anim.bboxes[q].max.Z;
                    }
                }

                Boundings = new BoundingVolume();
                Boundings.CreateBoundingVolume(this, min, max);

                Update(0);
                animations.Add(anim);

                Log.WriteDebugLine("Animation: " + fileName);

                UseAnimation(animName);
            }
        }
        /// <summary>
        /// lataa md5 model
        /// </summary>
        /// <param name="fileName"></param>
        public void Load(string fileName)
        {
            // Initialize everything
            string line, textureName = "";
            int    i;
            Buffer t = new Buffer();

            meshCount = 0;
            using (System.IO.StreamReader file = new System.IO.StreamReader(Settings.DataDir + fileName))
            {
                {
                    while ((line = file.ReadLine()) != null)
                    {
                        Cleanstring(ref line);

                        // Read number of joints
                        if (ParseLine(ref t, line, "numJoints %d"))
                        {
                            numJoints = t.ibuffer[0];
                            baseSkel  = new MD5Joint[numJoints];
                        }
                        // Read number os meshes
                        if (ParseLine(ref t, line, "numMeshes %d"))
                        {
                            numMesh = t.ibuffer[0];
                            model   = new MD5Mesh[numMesh];

                            meshes = new List <Vertex[]>(numMesh);
                        }
                        // Parse model joints
                        if (line.Equals("joints {"))
                        {
                            for (i = 0; i < numJoints; i++)
                            {
                                line = file.ReadLine();
                                Cleanstring(ref line);

                                ParseLine(ref t, line, "%s %d ( %f %f %f ) ( %f %f %f )");
                                baseSkel[i].name   = t.sbuffer;
                                baseSkel[i].parent = t.ibuffer[0];
                                baseSkel[i].pos.X  = t.fbuffer[0];
                                baseSkel[i].pos.Y  = t.fbuffer[1];
                                baseSkel[i].pos.Z  = t.fbuffer[2];
                                baseSkel[i].orient = new Quaternion(t.fbuffer[3], t.fbuffer[4], t.fbuffer[5], 1);

                                /*
                                 * jotain tosi outoa..
                                 * toi orient homma ei skulaa..
                                 * vaik varaa tilaa new:llä, ei toimi..
                                 * eikä  orient.X = 1;  eikä mikään. miks??
                                 */
                                MathExt.ComputeW(ref baseSkel[i].orient);
                            }
                        }
                        // Parse model meshes
                        if (line.Equals("mesh {"))
                        {
                            while (!line.Equals("}"))
                            {
                                line = file.ReadLine();
                                Cleanstring(ref line);

                                // Read texture name
                                if (line.StartsWith("shader"))
                                {
                                    string str = line.Substring(7);
                                    int    lp  = str.LastIndexOf("/");
                                    textureName = str.Substring(lp + 1); // texturen nimi

                                    lp = fileName.LastIndexOf("/");
                                    string dir = fileName.Substring(0, lp + 1); // md5 tiedoston hakemisto

                                    textureName = Settings.DataDir + dir + textureName;
                                    textureName = textureName.Replace(",", ".");
                                }

                                // Read mesh data
                                if (ParseLine(ref t, line, "numverts %d"))
                                {
                                    model[meshCount].numVert = t.ibuffer[0];
                                    model[meshCount].verts   = new MD5Vertex[model[meshCount].numVert];

                                    model[meshCount].texture = Texture.Load(textureName, false);

                                    for (i = 0; i < model[meshCount].numVert; i++)
                                    {
                                        line = file.ReadLine();
                                        ParseLine(ref t, line, "vert %d ( %f %f ) %d %d");
                                        model[meshCount].verts[t.ibuffer[0]].uv.X   = t.fbuffer[0];
                                        model[meshCount].verts[t.ibuffer[0]].uv.Y   = 1 - t.fbuffer[1];
                                        model[meshCount].verts[t.ibuffer[0]].startw = t.ibuffer[1];
                                        model[meshCount].verts[t.ibuffer[0]].countw = t.ibuffer[2];
                                    }
                                }
                                if (ParseLine(ref t, line, "numtris %d"))
                                {
                                    model[meshCount].numTris = t.ibuffer[0];
                                    model[meshCount].faces   = new int[model[meshCount].numTris][];
                                    for (i = 0; i < model[meshCount].numTris; i++)
                                    {
                                        line = file.ReadLine();
                                        ParseLine(ref t, line, "tri %d %d %d %d");

                                        model[meshCount].faces[t.ibuffer[0]]    = new int[3];
                                        model[meshCount].faces[t.ibuffer[0]][0] = t.ibuffer[3]; // poly toisin päin
                                        model[meshCount].faces[t.ibuffer[0]][1] = t.ibuffer[2];
                                        model[meshCount].faces[t.ibuffer[0]][2] = t.ibuffer[1];
                                    }
                                }
                                if (ParseLine(ref t, line, "numweights %d"))
                                {
                                    model[meshCount].numWeights = t.ibuffer[0];
                                    model[meshCount].weights    = new MD5Weight[model[meshCount].numWeights];
                                    for (i = 0; i < model[meshCount].numWeights; i++)
                                    {
                                        line = file.ReadLine();
                                        ParseLine(ref t, line, "weight %d %d %f ( %f %f %f )");
                                        model[meshCount].weights[t.ibuffer[0]].joint = t.ibuffer[1];
                                        model[meshCount].weights[t.ibuffer[0]].bias  = t.fbuffer[0];
                                        model[meshCount].weights[t.ibuffer[0]].pos.X = t.fbuffer[1];
                                        model[meshCount].weights[t.ibuffer[0]].pos.Y = t.fbuffer[2];
                                        model[meshCount].weights[t.ibuffer[0]].pos.Z = t.fbuffer[3];
                                    }
                                }
                            }
                            meshCount++;
                        }
                    }
                }
            }

            int maxvert = 0;

            for (int k = 0; k < numMesh; k++)
            {
                if (model[k].numVert > maxvert)
                {
                    maxvert = model[k].numVert;
                }
            }
            for (int k = 0; k < numMesh; k++)
            {
                finalVert = new Vector3[maxvert];
                normals   = new Vector3[maxvert];
            }

            skeleton = baseSkel;

            updateAnimCount    = FramesBetweenAnimUpdate;
            updateNormalsCount = FramesBetweenNormalsUpdate;

            // prepare model for rendering
            PrepareMesh();

            for (int q = 0; q < numMesh; q++)
            {
                Vertex[] v   = meshes[q];
                int[]    ind = new int[v.Length];
                for (int w = 0; w < ind.Length; w++)
                {
                    ind[w] = w;
                }

                // luo vbo ja datat sinne
                model[q].vbo = new VBO(BufferUsageHint.DynamicDraw);
                model[q].vbo.DataToVBO(v, ind);
            }
            material     = new Material("default");
            MaterialName = "default";

            Log.WriteDebugLine("Model: " + Name);
        }