Index: engine/vertmodel.h
===================================================================
--- engine/vertmodel.h	(revision 299)
+++ engine/vertmodel.h	(working copy)
@@ -41,33 +41,6 @@
             DELETEA(tris);
         }
 
-        virtual mesh *allocate() { return new vertmesh; }
-
-        mesh *copy()
-        {
-            vertmesh &m = *(vertmesh *)mesh::copy();
-            m.numverts = numverts;
-            m.verts = new vert[numverts*((vertmeshgroup *)group)->numframes];
-            memcpy(m.verts, verts, numverts*((vertmeshgroup *)group)->numframes*sizeof(vert));
-            m.tcverts = new tcvert[numverts];
-            memcpy(m.tcverts, tcverts, numverts*sizeof(tcvert));
-            m.numtris = numtris;
-            m.tris = new tri[numtris];
-            memcpy(m.tris, tris, numtris*sizeof(tri));
-            if(bumpverts)
-            {
-                m.bumpverts = new bumpvert[numverts];
-                memcpy(m.bumpverts, bumpverts, numverts*sizeof(bumpvert));
-            }
-            else m.bumpverts = NULL;
-            return &m;
-        }
-
-        void scaleverts(const vec &transdiff, float scalediff)
-        {
-            loopi(((vertmeshgroup *)group)->numframes*numverts) verts[i].pos.add(transdiff).mul(scalediff);
-        }
-
         void buildnorms(bool areaweight = true)
         {
             loopk(((vertmeshgroup *)group)->numframes)
@@ -445,38 +418,15 @@
             return -1;
         }
 
-        virtual meshgroup *allocate() { return new vertmeshgroup; }
-
-        meshgroup *copy()
-        {
-            vertmeshgroup &group = *(vertmeshgroup *)meshgroup::copy();
-            group.numframes = numframes;
-            group.numtags = numtags;
-            group.tags = new tag[numframes*numtags];
-            memcpy(group.tags, tags, numframes*numtags*sizeof(tag));
-            loopi(numframes*numtags) if(group.tags[i].name) group.tags[i].name = newstring(group.tags[i].name);
-            return &group;
-        }
-        
         int totalframes() const { return numframes; }
 
-        void scaletags(const vec &transdiff, float scalediff)
+        void concattagtransform(part *p, int frame, int i, const matrix3x4 &m, matrix3x4 &n)
         {
-            loopi(numframes*numtags) 
-            {
-                matrix3x4 &m = tags[i].transform;
-                m.a.w = (m.a.w+transdiff.x)*scalediff;
-                m.b.w = (m.b.w+transdiff.y)*scalediff;
-                m.c.w = (m.c.w+transdiff.z)*scalediff;
-            }
-        }
-
-        void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n)
-        {
             n.mul(m, tags[frame*numtags + i].transform);
+            n.translate(m.transformnormal(p->translate).mul(p->model->scale));
         }
 
-        void calctagmatrix(int i, const animstate &as, glmatrixf &matrix)
+        void calctagmatrix(part *p, int i, const animstate &as, glmatrixf &matrix)
         {
             const matrix3x4 &tag1 = tags[as.cur.fr1*numtags + i].transform, 
                             &tag2 = tags[as.cur.fr2*numtags + i].transform;
@@ -501,6 +451,9 @@
             } 
             #undef ip_ai_tag
             #undef ip 
+            matrix[12] = (matrix[12] + p->translate.x) * p->model->scale;
+            matrix[13] = (matrix[13] + p->translate.y) * p->model->scale;
+            matrix[14] = (matrix[14] + p->translate.z) * p->model->scale;
             matrix[3] = matrix[7] = matrix[11] = 0.0f;
             matrix[15] = 1.0f;
         }
@@ -683,7 +636,7 @@
             bindvbo(as, *vc);
             loopv(meshes) ((vertmesh *)meshes[i])->render(as, p->skins[i], *vc);
             
-            loopv(p->links) calctagmatrix(p->links[i].tag, *as, p->links[i].matrix);
+            loopv(p->links) calctagmatrix(p, p->links[i].tag, *as, p->links[i].matrix);
         }
     };
 
Index: engine/obj.h
===================================================================
--- engine/obj.h	(revision 299)
+++ engine/obj.h	(working copy)
@@ -209,7 +209,10 @@
             loadingobj = NULL;
             if(!loaddefaultparts()) return false;
         }
-        loopv(parts) parts[i]->meshes = parts[i]->meshes->scaleverts(scale/4.0f, i ? vec(0, 0, 0) : vec(translate.x, -translate.y, translate.z));
+        scale /= 4;
+        translate.y = -translate.y;
+        parts[0]->translate = translate;
+        loopv(parts) parts[i]->meshes->shared++;
         preloadshaders();
         return loaded = true;
     }
Index: engine/md2.h
===================================================================
--- engine/md2.h	(revision 299)
+++ engine/md2.h	(working copy)
@@ -262,7 +262,10 @@
         }
         persistidents = true;
         loadingmd2 = 0;
-        loopv(parts) parts[i]->meshes = parts[i]->meshes->scaleverts(scale/4.0f, i ? vec(0, 0, 0) : vec(translate.x, -translate.y, translate.z));
+        scale /= 4;
+        translate.y = -translate.y;
+        parts[0]->translate = translate;
+        loopv(parts) parts[i]->meshes->shared++;
         preloadshaders();
         return loaded = true;
     }
Index: engine/md3.h
===================================================================
--- engine/md3.h	(revision 299)
+++ engine/md3.h	(working copy)
@@ -219,7 +219,10 @@
             loadingmd3 = NULL;
             if(!loaddefaultparts()) return false;
         }
-        loopv(parts) parts[i]->meshes = parts[i]->meshes->scaleverts(scale/4.0f, i ? vec(0, 0, 0) : vec(translate.x, -translate.y, translate.z));
+        scale /= 4;
+        translate.y = -translate.y;
+        parts[0]->translate = translate;
+        loopv(parts) parts[i]->meshes->shared++;
         preloadshaders();
         return loaded = true;
     }
Index: engine/ragdoll.h
===================================================================
--- engine/ragdoll.h	(revision 299)
+++ engine/ragdoll.h	(working copy)
@@ -46,24 +46,6 @@
 
     ragdollskel() : eye(-1) {}
 
-    ragdollskel *copy()
-    {
-        return new ragdollskel(*this);
-    }
-
-    void scaletags(const vec &transdiff, float scalediff)
-    {
-        loopv(verts) verts[i].pos.mul(scalediff);
-        loopv(distlimits) distlimits[i].dist *= scalediff;
-        loopv(joints)
-        {
-            joint &j = joints[i];
-            j.orient.a.w *= scalediff;
-            j.orient.b.w *= scalediff;
-            j.orient.c.w *= scalediff;
-        }
-    }
-
     void setup()
     {
         loopv(verts) verts[i].weight = 0;
@@ -123,17 +105,18 @@
     ragdollskel *skel;
     int millis, collidemillis, lastmove;
     vec offset, center;
-    float radius, timestep;
+    float radius, timestep, scale;
     vert *verts;
     matrix3x3 *tris;
     matrix3x4 *reljoints;
 
-    ragdolldata(ragdollskel *skel)
+    ragdolldata(ragdollskel *skel, float scale = 1)
         : skel(skel),
           millis(lastmillis),
           collidemillis(0),
           lastmove(lastmillis),
           timestep(0),
+          scale(scale),
           verts(new vert[skel->verts.length()]), 
           tris(new matrix3x3[skel->tris.length()]),
           reljoints(skel->reljoints.empty() ? NULL : new matrix3x4[skel->reljoints.length()])
@@ -205,8 +188,8 @@
         vec dir = vec(v2.pos).sub(v1.pos),
             center = vec(v1.pos).add(v2.pos).mul(0.5f);
         float dist = dir.magnitude();
-        if(dist < 1e-4f) dir = vec(0, 0, d.dist*0.5f);
-        else dir.mul(d.dist*0.5f/dist);
+        if(dist < 1e-4f) dir = vec(0, 0, d.dist*scale*0.5f);
+        else dir.mul(d.dist*scale*0.5f/dist);
         v1.newpos.add(vec(center).sub(dir));
         v1.weight++;
         v2.newpos.add(vec(center).add(dir));
Index: engine/md5.h
===================================================================
--- engine/md5.h	(revision 299)
+++ engine/md5.h	(working copy)
@@ -455,18 +455,19 @@
             loadingmd5 = NULL;
             loopv(parts) if(!parts[i]->meshes) return false;
         }
-        else if(!loaddefaultparts()) // md5 without configuration, try default tris and skin
+        else // md5 without configuration, try default tris and skin 
         {
             persistidents = true;
             loadingmd5 = NULL;
-            return false;
+            if(!loaddefaultparts()) return false;
         }
-        loadingmd5 = NULL;
+        scale /= 4;
+        parts[0]->translate = translate;
         loopv(parts) 
         {
             skelpart *p = (skelpart *)parts[i];
             p->endanimparts();
-            p->meshes = p->meshes->scaleverts(scale/4.0f, i ? vec(0, 0, 0) : vec(translate.x, translate.y, translate.z));
+            p->meshes->shared++;
         }
         preloadshaders();
         return loaded = true;
Index: engine/animmodel.h
===================================================================
--- engine/animmodel.h	(revision 299)
+++ engine/animmodel.h	(working copy)
@@ -382,16 +382,6 @@
             DELETEA(name);
         }
 
-        virtual mesh *allocate() = 0;
-        virtual mesh *copy()
-        {
-            mesh &m = *allocate();
-            if(name) m.name = newstring(name);
-            m.noclip = noclip;
-            return &m;
-        }
-            
-        virtual void scaleverts(const vec &transdiff, float scalediff) {}        
         virtual void calcbb(int frame, vec &bbmin, vec &bbmax, const matrix3x4 &m) {}
         virtual void gentris(int frame, Texture *tex, vector<BIH::tri> *out, const matrix3x4 &m) {}
 
@@ -423,7 +413,7 @@
         }            
 
         virtual int findtag(const char *name) { return -1; }
-        virtual void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n) {}
+        virtual void concattagtransform(part *p, int frame, int i, const matrix3x4 &m, matrix3x4 &n) {}
 
         void calcbb(int frame, vec &bbmin, vec &bbmax, const matrix3x4 &m)
         {
@@ -440,39 +430,6 @@
         bool hasframes(int i, int n) const { return i>=0 && i+n<=totalframes(); }
         int clipframes(int i, int n) const { return min(n, totalframes() - i); }
 
-        virtual meshgroup *allocate() = 0;
-        virtual meshgroup *copy()
-        {
-            meshgroup &group = *allocate();
-            group.name = newstring(name);
-            loopv(meshes) group.meshes.add(meshes[i]->copy())->group = &group;
-            group.scale = scale;
-            group.translate = translate;
-            return &group;
-        }
-       
-        virtual void scaletags(const vec &transdiff, float scalediff) {}
- 
-        meshgroup *scaleverts(float nscale, const vec &ntranslate)
-        {
-            if(nscale==scale && ntranslate==translate) { shared++; return this; }
-            else if(next || shared)
-            {
-                if(!next) next = copy();
-                return next->scaleverts(nscale, ntranslate);
-            }
-            float scalediff = nscale/scale;
-            vec transdiff(ntranslate);
-            transdiff.sub(translate);
-            transdiff.mul(scale);
-            loopv(meshes) meshes[i]->scaleverts(transdiff, scalediff);
-            scaletags(transdiff, scalediff);
-            scale = nscale;
-            translate = ntranslate;
-            shared++;
-            return this;
-        }
-
         virtual void cleanup() {}
         virtual void render(const animstate *as, float pitch, const vec &axis, dynent *d, part *p) {}
     };
@@ -513,8 +470,9 @@
         vector<animspec> *anims[MAXANIMPARTS];
         int numanimparts;
         float pitchscale, pitchoffset, pitchmin, pitchmax;
+        vec translate;
 
-        part() : meshes(NULL), numanimparts(1), pitchscale(1), pitchoffset(0), pitchmin(0), pitchmax(0) 
+        part() : meshes(NULL), numanimparts(1), pitchscale(1), pitchoffset(0), pitchmin(0), pitchmax(0), translate(0, 0, 0)
         {
             loopk(MAXANIMPARTS) anims[k] = NULL;
         }
@@ -530,22 +488,28 @@
 
         void calcbb(int frame, vec &bbmin, vec &bbmax, const matrix3x4 &m)
         {
-            meshes->calcbb(frame, bbmin, bbmax, m);
+            matrix3x4 t = m;
+            t.translate(translate);
+            t.scale(model->scale);
+            meshes->calcbb(frame, bbmin, bbmax, t);
             loopv(links)
             {
                 matrix3x4 n;
-                meshes->concattagtransform(frame, links[i].tag, m, n);
+                meshes->concattagtransform(this, frame, links[i].tag, m, n);
                 links[i].p->calcbb(frame, bbmin, bbmax, n);
             }
         }
 
         void gentris(int frame, vector<BIH::tri> *tris, const matrix3x4 &m)
         {
-            meshes->gentris(frame, skins, tris, m);
+            matrix3x4 t = m;
+            t.translate(translate);
+            t.scale(model->scale);
+            meshes->gentris(frame, skins, tris, t);
             loopv(links)
             {
                 matrix3x4 n;
-                meshes->concattagtransform(frame, links[i].tag, m, n);
+                meshes->concattagtransform(this, frame, links[i].tag, m, n);
                 links[i].p->gentris(frame, tris, n);
             }
         }
@@ -755,6 +719,8 @@
 
             glPushMatrix();
             glMultMatrixf(matrixstack[matrixpos].v);
+            if(model->scale!=1) glScalef(model->scale, model->scale, model->scale);
+            if(!translate.iszero()) glTranslatef(translate.x, translate.y, translate.z);
             if(renderpath!=R_FIXEDFUNCTION && anim&ANIM_ENVMAP)
             {
                 glMatrixMode(GL_TEXTURE);
@@ -764,15 +730,18 @@
 
             if(!(anim&ANIM_NOSKIN))
             {
+                rfogplane.translate(vec(translate).mul(model->scale));
                 if(renderpath!=R_FIXEDFUNCTION)
                 {
-                    if(fogging) setfogplane(rfogplane);
+                    if(fogging) setfogplane(plane(rfogplane).scale(model->scale));
                     setenvparamf("direction", SHPARAM_VERTEX, 0, rdir.x, rdir.y, rdir.z);
-                    setenvparamf("camera", SHPARAM_VERTEX, 1, rcampos.x, rcampos.y, rcampos.z, 1);
+                    vec ocampos(rcampos);
+                    ocampos.div(model->scale).sub(translate);
+                    setenvparamf("camera", SHPARAM_VERTEX, 1, ocampos.x, ocampos.y, ocampos.z, 1);
                 }
                 else
                 {
-                    if(fogging) refractfogplane = rfogplane;
+                    if(fogging) refractfogplane = plane(rfogplane).scale(model->scale);
                 }
             }
 
Index: engine/skelmodel.h
===================================================================
--- engine/skelmodel.h	(revision 299)
+++ engine/skelmodel.h	(working copy)
@@ -184,39 +184,12 @@
             DELETEA(tris);
         }
 
-        virtual mesh *allocate() { return new skelmesh; }
-
-        mesh *copy()
-        {
-            skelmesh &m = *(skelmesh *)mesh::copy();
-            m.numverts = numverts;
-            m.verts = new vert[numverts];
-            memcpy(m.verts, verts, numverts*sizeof(vert));
-            m.numtris = numtris;
-            m.tris = new tri[numtris];
-            memcpy(m.tris, tris, numtris*sizeof(tri));
-            m.maxweights = maxweights;
-            if(bumpverts)
-            {
-                m.bumpverts = new bumpvert[numverts];
-                memcpy(m.bumpverts, bumpverts, numverts*sizeof(bumpvert));
-            }
-            else m.bumpverts = NULL;
-            return &m;
-        }
-
         int addblendcombo(const blendcombo &c)
         {
             maxweights = max(maxweights, c.size());
             return ((skelmeshgroup *)group)->addblendcombo(c);
         }
 
-        void scaleverts(const vec &transdiff, float scalediff)
-        {
-            if(((skelmeshgroup *)group)->skel->numframes) loopi(numverts) verts[i].pos.mul(scalediff);
-            else loopi(numverts) verts[i].pos.add(transdiff).mul(scalediff);
-        }
-
         void buildnorms(bool areaweight = true)
         {
             loopi(numverts) verts[i].norm = vec(0, 0, 0);
@@ -732,38 +705,6 @@
             return true;
         }
 
-        skeleton *copy()
-        {
-            skeleton &s = *new skeleton;
-            s.numbones = numbones;
-            s.numinterpbones = numinterpbones;
-            s.numgpubones = numgpubones;
-            s.numframes = numframes;
-            s.optimizedframes = optimizedframes;
-            s.bones = new boneinfo[numbones];
-            memcpy(s.bones, bones, numbones*sizeof(boneinfo));
-            loopi(numbones) if(bones[i].name) s.bones[i].name = newstring(bones[i].name);
-            if(numframes)
-            {
-                s.framebones = new dualquat[numframes*numbones];
-                memcpy(s.framebones, framebones, numframes*numbones*sizeof(dualquat));
-            }
-            loopv(skelanims)
-            {
-                skelanimspec &sa = s.addskelanim(skelanims[i].name);
-                sa.frame = skelanims[i].frame;
-                sa.range = skelanims[i].range;
-            }
-            loopv(tags)
-            {
-                tag &t = s.tags.add();
-                t.name = newstring(tags[i].name);
-                t.bone = tags[i].bone;
-            }
-            if(ragdoll) s.ragdoll = ragdoll->copy();
-            return &s;
-        }
-
         void remapbones()
         {
             loopi(numbones) 
@@ -918,27 +859,6 @@
         int availgpubones() const { return (min(maxvpenvparams - reservevpparams, 256) - 10) / (matskel ? 3 : 2); }
         bool gpuaccelerate() const { return renderpath!=R_FIXEDFUNCTION && numframes && gpuskel && numgpubones<=availgpubones(); }
 
-        void scaletags(const vec &transdiff, float scalediff)
-        {
-            DELETEA(invbones);
-            DELETEA(matinvbones);
-            DELETEA(matframebones);
-            if(shared <= 1) 
-            {
-                loopi(numbones) bones[i].base.scale(scalediff);
-                if(ragdoll) ragdoll->scaletags(transdiff, scalediff);
-            }
-            for(; scaledframes < numframes; scaledframes++)
-            {
-                dualquat *frame = &framebones[scaledframes*numbones];
-                loopj(numbones) if(bones[j].interpindex >= 0)
-                {
-                    if(bones[j].interpparent < 0) frame[j].translate(transdiff);
-                    frame[j].scale(scalediff);
-                }
-            }
-        }
-
         void geninvbones()
         {
             if(invbones) return;
@@ -1064,7 +984,7 @@
             }
         }
 
-        void initmatragdoll(ragdolldata &d, skelcacheentry &sc)
+        void initmatragdoll(ragdolldata &d, skelcacheentry &sc, part *p)
         {
             const matrix3x4 *mdata = sc.mdata;
             loopv(ragdoll->joints)
@@ -1076,7 +996,7 @@
                 {
                     int vert = j.vert[k];
                     vec pos;
-                    matrixstack[matrixpos].transform(m.transform(ragdoll->verts[vert].pos), pos);
+                    matrixstack[matrixpos].transform(m.transform(ragdoll->verts[vert].pos).add(p->translate).mul(p->model->scale), pos);
                     d.verts[vert].pos.add(pos);
                 }
             }
@@ -1091,7 +1011,7 @@
             loopv(ragdoll->verts) d.verts[i].pos.mul(ragdoll->verts[i].weight);
         }
 
-        void initragdoll(ragdolldata &d, skelcacheentry &sc)
+        void initragdoll(ragdolldata &d, skelcacheentry &sc, part *p)
         {
             const dualquat *bdata = sc.bdata;
             loopv(ragdoll->joints)
@@ -1103,7 +1023,7 @@
                 {
                     int vert = j.vert[k];
                     vec pos;
-                    matrixstack[matrixpos].transform(q.transform(ragdoll->verts[vert].pos), pos);
+                    matrixstack[matrixpos].transform(q.transform(ragdoll->verts[vert].pos).add(p->translate).mul(p->model->scale), pos);
                     d.verts[vert].pos.add(pos);
                 }
             }
@@ -1119,7 +1039,7 @@
             loopv(ragdoll->verts) d.verts[i].pos.mul(ragdoll->verts[i].weight);
         }
 
-        void genmatragdollbones(ragdolldata &d, skelcacheentry &sc)
+        void genmatragdollbones(ragdolldata &d, skelcacheentry &sc, part *p)
         {
             if(!sc.mdata) sc.mdata = new matrix3x4[numinterpbones];
             loopv(ragdoll->joints)
@@ -1128,7 +1048,7 @@
                 const boneinfo &b = bones[j.bone];
                 vec pos(0, 0, 0);
                 loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos);
-                pos.mul(j.weight);
+                pos.mul(j.weight/p->model->scale).sub(p->translate);
                 sc.mdata[b.interpindex].transposemul(d.tris[j.tri], pos, j.orient);
             }
             loopv(ragdoll->reljoints)
@@ -1140,7 +1060,7 @@
             }
         }
 
-        void genragdollbones(ragdolldata &d, skelcacheentry &sc)
+        void genragdollbones(ragdolldata &d, skelcacheentry &sc, part *p)
         {
             if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
             loopv(ragdoll->joints)
@@ -1149,7 +1069,7 @@
                 const boneinfo &b = bones[j.bone];
                 vec pos(0, 0, 0);
                 loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos);
-                pos.mul(j.weight);
+                pos.mul(j.weight/p->model->scale).sub(p->translate);
                 matrix3x4 m;
                 m.transposemul(d.tris[j.tri], pos, j.orient);
                 sc.bdata[b.interpindex] = dualquat(m);
@@ -1168,21 +1088,25 @@
             }
         }
 
-        void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n)
+        void concattagtransform(part *p, int frame, int i, const matrix3x4 &m, matrix3x4 &n)
         {
             matrix3x4 t = bones[tags[i].bone].base;
+            t.translate(vec(p->translate).mul(p->model->scale));
             n.mul(m, t);
         }
 
-        void calctagmatrix(int bone, const matrix3x4 &m, linkedpart &l)
+        void calctagmatrix(part *p, int bone, const matrix3x4 &m, linkedpart &l)
         {
             if(numframes) 
             {
                 matrix3x4 t;
-                t.mul(m, bones[bone].base);
+                t.mul(m, bones[bone].base); 
                 l.matrix = t;
             }
             else l.matrix = m;
+            l.matrix[12] = (l.matrix[12] + p->translate.x) * p->model->scale;
+            l.matrix[13] = (l.matrix[13] + p->translate.y) * p->model->scale;
+            l.matrix[14] = (l.matrix[14] + p->translate.z) * p->model->scale;
         }
 
         void calctags(skelcacheentry &sc, part *p)
@@ -1190,7 +1114,7 @@
             loopv(p->links)
             {
                 int tagbone = tags[p->links[i].tag].bone, interpindex = bones[tagbone].interpindex;
-                calctagmatrix(tagbone, usematskel ? sc.mdata[interpindex] : sc.bdata[interpindex], p->links[i]);
+                calctagmatrix(p, tagbone, usematskel ? sc.mdata[interpindex] : sc.bdata[interpindex], p->links[i]);
             }
         }
 
@@ -1199,7 +1123,7 @@
             loopv(p->links)
             {
                int tagbone = tags[p->links[i].tag].bone;
-               calctagmatrix(tagbone, bones[tagbone].base, p->links[i]);
+               calctagmatrix(p, tagbone, bones[tagbone].base, p->links[i]);
             }
         }
 
@@ -1217,7 +1141,7 @@
             loopv(users) users[i]->cleanup();
         }
 
-        skelcacheentry &checkskelcache(const animstate *as, float pitch, const vec &axis, ragdolldata *rdata)
+        skelcacheentry &checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, ragdolldata *rdata)
         {
             if(skelcache.empty()) 
             {
@@ -1249,8 +1173,8 @@
                 sc->ragdoll = rdata;
                 if(rdata)
                 {
-                    if(matskel) genmatragdollbones(*rdata, *sc);
-                    else genragdollbones(*rdata, *sc);
+                    if(matskel) genmatragdollbones(*rdata, *sc, p);
+                    else genragdollbones(*rdata, *sc, p);
                 }
                 else if(matskel) interpmatbones(as, pitch, axis, numanimparts, partmask, *sc);
                 else interpbones(as, pitch, axis, numanimparts, partmask, *sc);
@@ -1361,26 +1285,8 @@
             return skel->findtag(name);
         }
 
-        virtual meshgroup *allocate() { return new skelmeshgroup; }
-
-        meshgroup *copy()
-        {
-            skelmeshgroup &group = *(skelmeshgroup *)meshgroup::copy();
-            group.skel = skel->shared ? skel : skel->copy();
-            group.skel->users.add(&group);
-            if(skel->shared) skel->shared++;
-            loopv(blendcombos) group.blendcombos.add(blendcombos[i]);
-            memcpy(group.numblends, numblends, sizeof(numblends));
-            return &group;
-        }
-
         int totalframes() const { return max(skel->numframes, 1); }
 
-        void scaletags(const vec &transdiff, float scalediff)
-        {
-            skel->scaletags(transdiff, scalediff);
-        }
- 
         void genvbo(bool norms, bool tangents, vbocacheentry &vc)
         {
             if(hasVBO)
@@ -1555,9 +1461,9 @@
             if(bc && vblends) skel->setgpubones(*bc, vblends);
         }
 
-        void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n)
+        void concattagtransform(part *p, int frame, int i, const matrix3x4 &m, matrix3x4 &n)
         {
-            skel->concattagtransform(frame, i, m, n);
+            skel->concattagtransform(p, frame, i, m, n);
         }
 
         int addblendcombo(const blendcombo &c)
@@ -1706,7 +1612,7 @@
                 return;
             }
 
-            skelcacheentry &sc = skel->checkskelcache(as, pitch, axis, as->anim&ANIM_RAGDOLL ? NULL : d->ragdoll);
+            skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, as->anim&ANIM_RAGDOLL ? NULL : d->ragdoll);
             int owner = &sc-&skel->skelcache[0];
             vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner);
             vc.millis = lastmillis;
@@ -1748,9 +1654,9 @@
 
             if(as->anim&ANIM_RAGDOLL && skel->ragdoll && !d->ragdoll)
             {
-                d->ragdoll = new ragdolldata(skel->ragdoll);
-                if(matskel) skel->initmatragdoll(*d->ragdoll, sc);
-                else skel->initragdoll(*d->ragdoll, sc);
+                d->ragdoll = new ragdolldata(skel->ragdoll, p->model->scale);
+                if(matskel) skel->initmatragdoll(*d->ragdoll, sc, p);
+                else skel->initragdoll(*d->ragdoll, sc, p);
                 d->ragdoll->init(d);
             }
         }
Index: shared/geom.h
===================================================================
--- shared/geom.h	(revision 301)
+++ shared/geom.h	(working copy)
@@ -658,6 +658,9 @@
                    a.y*o.x + b.y*o.y + c.y*o.z,
                    a.z*o.x + b.z*o.y + c.z*o.z);
     }
+
+    float getscale() const { return a.magnitude3(); }
+    vec gettranslation() const { return vec(a.w, b.w, c.w); }
 };
 
 inline dualquat::dualquat(const matrix3x4 &m) : real(m)
@@ -711,6 +714,18 @@
         return true;
     }
 
+    plane &scale(float k)
+    {
+        mul(k);
+        return *this;
+    }
+
+    plane &translate(const vec &p)
+    {
+        offset += dot(p);
+        return *this;
+    }
+
     float zintersect(const vec &p) const { return -(x*p.x+y*p.y+offset)/z; }
     float zdist(const vec &p) const { return p.z-zintersect(p); }
 };
