void model_collide_sortnorm(ubyte * p)
{
	int frontlist = w(p+36);
	int backlist = w(p+40);
	int prelist = w(p+44);
	int postlist = w(p+48);
	int onlist = w(p+52);
	vec3d hitpos;

	if ( Mc_pm->version >= 2000 )	{
		if ( mc_ray_boundingbox( vp(p+56), vp(p+68), &Mc_p0, &Mc_direction, &hitpos) )	{
			if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
				return;
			}
		} else {
			return;
		}
	}

	if (prelist) model_collide_sub(p+prelist);
	if (backlist) model_collide_sub(p+backlist);
	if (onlist) model_collide_sub(p+onlist);
	if (frontlist) model_collide_sub(p+frontlist);
	if (postlist) model_collide_sub(p+postlist);
}
// checks a vector collision against a ships shield (if it has shield points defined).
void mc_check_shield()
{
	int i;


	if ( Mc_pm->shield.ntris < 1 )
		return;
	if (Mc_pm->shield_collision_tree)
	{
		mc_check_sldc(0); // see if we hit the SLDC
	}
	else
	{
		int o;
		for (o=0; o<8; o++ )	{
			model_octant * poct1 = &Mc_pm->octants[o];

			if (!mc_ray_boundingbox( &poct1->min, &poct1->max, &Mc_p0, &Mc_direction, NULL ))	{
				continue;
			}
			
			for (i = 0; i < poct1->nshield_tris; i++) {
				shield_tri	* tri = poct1->shield_tris[i];
				mc_shield_check_common(tri);
			}
		}
	}//model has shield_collsion_tree
}
bool mc_check_sldc(int offset)
{
	if (offset > Mc_pm->sldc_size-5) //no way is this big enough
		return false;
	char *type_p = (char *)(Mc_pm->shield_collision_tree+offset);
	
	// not used
	//int *size_p = (int *)(Mc_pm->shield_collision_tree+offset+1);
	// split and polygons
	vec3d *minbox_p = (vec3d*)(Mc_pm->shield_collision_tree+offset+5);
	vec3d *maxbox_p = (vec3d*)(Mc_pm->shield_collision_tree+offset+17);

	// split
	unsigned int *front_offset_p = (unsigned int*)(Mc_pm->shield_collision_tree+offset+29);
	unsigned int *back_offset_p = (unsigned int*)(Mc_pm->shield_collision_tree+offset+33);

	// polygons
	unsigned int *num_polygons_p = (unsigned int*)(Mc_pm->shield_collision_tree+offset+29);

	unsigned int *shld_polys = (unsigned int*)(Mc_pm->shield_collision_tree+offset+33);



	// see if it fits inside our bbox
	if (!mc_ray_boundingbox( minbox_p, maxbox_p, &Mc_p0, &Mc_direction, NULL ))	{
		return false;
	}

	if (*type_p == 0) // SPLIT
	{
			return mc_check_sldc(offset+*front_offset_p) || mc_check_sldc(offset+*back_offset_p);
	}
	else
	{
		// poly list
		shield_tri	* tri;
		for (unsigned int i = 0; i < *num_polygons_p; i++)
		{
			tri = &Mc_pm->shield.tris[shld_polys[i]];
						
			mc_shield_check_common(tri);

		} // for (unsigned int i = 0; i < leaf->num_polygons; i++)
	}

	// shouldn't be reached
	return false;
}
//calls the object interpreter to render an object.  The object renderer
//is really a seperate pipeline. returns true if drew
int model_collide_sub(void *model_ptr )
{
    ubyte *p = (ubyte *)model_ptr;
    int chunk_type, chunk_size;
    vec3d hitpos;

    chunk_type = w(p);
    chunk_size = w(p+4);

    while (chunk_type != OP_EOF)	{

//		mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size ));

        switch (chunk_type) {
        case OP_DEFPOINTS:
            model_collide_defpoints(p);
            break;
        case OP_FLATPOLY:
            model_collide_flatpoly(p);
            break;
        case OP_TMAPPOLY:
            model_collide_tmappoly(p);
            break;
        case OP_SORTNORM:
            model_collide_sortnorm(p);
            break;
        case OP_BOUNDBOX:
            if ( mc_ray_boundingbox( vp(p+8), vp(p+20), &Mc_p0, &Mc_direction, &hitpos ) )	{
                if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
                    // The ray isn't long enough to intersect the bounding box
                    return 1;
                }
            } else {
                return 1;
            }
            break;
        default:
            mprintf(( "Bad chunk type %d, len=%d in model_collide_sub\n", chunk_type, chunk_size ));
            Int3();		// Bad chunk type!
            return 0;
        }
        p += chunk_size;
        chunk_type = w(p);
        chunk_size = w(p+4);
    }
    return 1;
}
void model_collide_bsp(bsp_collision_tree *tree, int node_index)
{
	if ( tree->node_list == NULL || tree->n_verts <= 0) {
		return;
	}

	bsp_collision_node *node = &tree->node_list[node_index];
	vec3d hitpos;

	// check the bounding box of this node. if it passes, check left and right children
	if ( mc_ray_boundingbox( &node->min, &node->max, &Mc_p0, &Mc_direction, &hitpos ) ) {
		if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
			// The ray isn't long enough to intersect the bounding box
			return;
		}

		if ( node->leaf >= 0 ) {
			model_collide_bsp_poly(tree, node->leaf);
		} else {
			if ( node->back >= 0 ) model_collide_bsp(tree, node->back);
			if ( node->front >= 0 ) model_collide_bsp(tree, node->front);
		}
	}
}
// This function recursively checks a submodel and its children
// for a collision with a vector.
void mc_check_subobj( int mn )
{
	vec3d tempv;
	vec3d hitpt;		// used in bounding box check
	bsp_info * sm;
	int i;

	Assert( mn >= 0 );
	Assert( mn < Mc_pm->n_models );
	if ( (mn < 0) || (mn>=Mc_pm->n_models) ) return;
	
	sm = &Mc_pm->submodel[mn];
	if (sm->no_collisions) return; // don't do collisions
	if (sm->nocollide_this_only) goto NoHit; // Don't collide for this model, but keep checking others

	// Rotate the world check points into the current subobject's 
	// frame of reference.
	// After this block, Mc_p0, Mc_p1, Mc_direction, and Mc_mag are correct
	// and relative to this subobjects' frame of reference.
	vm_vec_sub(&tempv, Mc->p0, &Mc_base);
	vm_vec_rotate(&Mc_p0, &tempv, &Mc_orient);

	vm_vec_sub(&tempv, Mc->p1, &Mc_base);
	vm_vec_rotate(&Mc_p1, &tempv, &Mc_orient);
	vm_vec_sub(&Mc_direction, &Mc_p1, &Mc_p0);

	// bail early if no ray exists
	if ( IS_VEC_NULL(&Mc_direction) ) {
		return;
	}

	if (Mc_pm->detail[0] == mn)	{
		// Quickly bail if we aren't inside the full model bbox
		if (!mc_ray_boundingbox( &Mc_pm->mins, &Mc_pm->maxs, &Mc_p0, &Mc_direction, NULL))	{
			return;
		}

		// If we are checking the root submodel, then we might want to check	
		// the shield at this point
		if ((Mc->flags & MC_CHECK_SHIELD) && (Mc_pm->shield.ntris > 0 )) {
			mc_check_shield();
			return;
		}
	}

	if (!(Mc->flags & MC_CHECK_MODEL)) {
		return;
	}
	
	Mc_submodel = mn;

	// Check if the ray intersects this subobject's bounding box
	if ( mc_ray_boundingbox(&sm->min, &sm->max, &Mc_p0, &Mc_direction, &hitpt) ) {
		if (Mc->flags & MC_ONLY_BOUND_BOX) {
			float dist = vm_vec_dist( &Mc_p0, &hitpt );

			// If the ray is behind the plane there is no collision
			if (dist < 0.0f) {
				goto NoHit;
			}

			// The ray isn't long enough to intersect the plane
			if ( !(Mc->flags & MC_CHECK_RAY) && (dist > Mc_mag) ) {
				goto NoHit;
			}

			// If the ray hits, but a closer intersection has already been found, return
			if ( Mc->num_hits && (dist >= Mc->hit_dist) ) {
				goto NoHit;
			}

			Mc->hit_dist = dist;
			Mc->hit_point = hitpt;
			Mc->hit_submodel = Mc_submodel;
			Mc->hit_bitmap = -1;
			Mc->num_hits++;
		} else {
			// The ray intersects this bounding box, so we have to check all the
			// polygons in this submodel.
			if ( Cmdline_old_collision_sys ) {
				model_collide_sub(sm->bsp_data);
			} else {
				if (Mc->lod > 0 && sm->num_details > 0) {
					bsp_info *lod_sm = sm;

					for (i = Mc->lod - 1; i >= 0; i--) {
						if (sm->details[i] != -1) {
							lod_sm = &Mc_pm->submodel[sm->details[i]];

							//mprintf(("Checking %s collision for %s using %s instead\n", Mc_pm->filename, sm->name, lod_sm->name));
							break;
						}
					}

					model_collide_bsp(model_get_bsp_collision_tree(lod_sm->collision_tree_index), 0);
				} else {
					model_collide_bsp(model_get_bsp_collision_tree(sm->collision_tree_index), 0);
				}
			}
		}
	}

NoHit:

	// If we're only checking one submodel, return
	if (Mc->flags & MC_SUBMODEL)	{
		return;
	}

	
	// If this subobject doesn't have any children, we're done checking it.
	if ( sm->num_children < 1 ) return;
	
	// Save instance (Mc_orient, Mc_base, Mc_point_base)
	matrix saved_orient = Mc_orient;
	vec3d saved_base = Mc_base;
	
	// Check all of this subobject's children
	i = sm->first_child;
	while ( i >= 0 )	{
		angles angs;
		bool blown_off;
		bool collision_checked;
		bsp_info * csm = &Mc_pm->submodel[i];
		
		if ( Mc_pmi ) {
			angs = Mc_pmi->submodel[i].angs;
			blown_off = Mc_pmi->submodel[i].blown_off;
			collision_checked = Mc_pmi->submodel[i].collision_checked;
		} else {
			angs = csm->angs;
			blown_off = csm->blown_off ? true : false;
			collision_checked = false;
		}

		// Don't check it or its children if it is destroyed
		// or if it's set to no collision
		if ( !blown_off && !collision_checked && !csm->no_collisions )	{
			if ( Mc_pmi ) {
				Mc_orient = Mc_pmi->submodel[i].mc_orient;
				Mc_base = Mc_pmi->submodel[i].mc_base;
				vm_vec_add2(&Mc_base, Mc->pos);
			} else {
				//instance for this subobject
				matrix tm = IDENTITY_MATRIX;

				vm_vec_unrotate(&Mc_base, &csm->offset, &saved_orient );
				vm_vec_add2(&Mc_base, &saved_base );

				if( vm_matrix_same(&tm, &csm->orientation)) {
					// if submodel orientation matrix is identity matrix then don't bother with matrix ops
					vm_angles_2_matrix(&tm, &angs);
				} else {
					matrix rotation_matrix = csm->orientation;
					vm_rotate_matrix_by_angles(&rotation_matrix, &angs);

					matrix inv_orientation;
					vm_copy_transpose(&inv_orientation, &csm->orientation);

					vm_matrix_x_matrix(&tm, &rotation_matrix, &inv_orientation);
				}

				vm_matrix_x_matrix(&Mc_orient, &saved_orient, &tm);
			}

			mc_check_subobj( i );
		}

		i = csm->next_sibling;
	}

}