void SWDynamicTree2D::query( tarray<tuint>& result, const taabb2d& aabb ) const
{
	result.clear();

	tlist<tuint> suspects;
	suspects.push_back( m_rootID );

	while ( suspects.size() > 0 )
	{
		tuint nodeID = suspects.front();
		suspects.pop_front();

		if ( nodeID == nullID ) continue;

		const TreeNode& node = m_nodes[ nodeID ];
		if ( node.aabb.collide( aabb ) )
		{
			if ( node.isLeaf() )
			{
				result.push_back( nodeID );
			}
			else
			{
				suspects.push_back( node.childID[0] );
				suspects.push_back( node.childID[1] );
			}
		}
	}
}
int PreprocessOccluders(const tmatrix &invCameraMat)
{
	if (!GetInstancesCount(ZOccluderBox))
		return 0;

	PROFILER_START(PreprocessOccluders);

	tvector3 campos = invCameraMat.V4.position;
	tvector3 camdir = invCameraMat.V4.dir;

	FActiveOccluder* pActiveOccluder = &gActiveOccluders[0]; 
    tvector4 viewPoint = vector4(campos.x, campos.y, campos.z, 0);
    tvector4 viewDir = vector4(camdir.x, camdir.y, camdir.z, 0);
	float sqrFar = 1000.0f * 1000.0f;
	float sqrDist;
	int i;

	gNbActiveOccluders = 0;
	gOccluderBoxes.clear();

	ZOccluderBox *pocc = (ZOccluderBox*)FirstInstanceOf(ZOccluderBox);
	while (pocc)
	{
		addDynamicOccluder(pocc->GetTransform());

		pocc = (ZOccluderBox*)NI(pocc);
	}

    
    for (unsigned int ju = 0;ju<gOccluderBoxes.size(); ju++)
	{
		// todo : frustum culling of the  occluder (except far plane)
		// todo : compute solid angle to reorder occluder accordingly
		FOccluderBox *obox = &gOccluderBoxes[ju];

         //= oboxiter.Get();
		// check for far plane
		const tvector3 &oboxcenter = obox->mCenter;//pocc->GetTransform()->GetWorldMatrix().position;
		sqrDist= SquaredDistance(viewPoint, oboxcenter);
		if (sqrDist > sqrFar)
		{
			continue;
		}

		// check for near plane
		if (DotProduct(tvector3(viewDir), tvector3(oboxcenter - viewPoint)) < 0.0f)
		{
			continue;
		}

		// select planes of the occluder box that lies on the viewing direction
		// todo : reduce to 3 planes instead of 6 (due to box symetry)
		float invSqrDist = 1.0f/sqrDist;//Rcp(sqrDist);

		pActiveOccluder->mSolidAngle = 0.0f;

		BoxSilhouette	silhouette;

		silhouette.vertices = &obox->mVertex[0];

		for (i=0; i<6; i++)
		{
			tvector4 dir= obox->mVertex[FaceVertexIndex[i][0]];
			dir -= viewPoint;
			float vdotp = silhouette.dots[i] = DotProduct(obox->mPlanes[i], dir);

			// compute the maximum solidAngle of the box : -area * v.p/d.d
			pActiveOccluder->mSolidAngle = Max(-obox->mVertex[i].w * vdotp * invSqrDist, pActiveOccluder->mSolidAngle);

		}

		// exit if the occluder is not relevant enough
		if (pActiveOccluder->mSolidAngle < gMinSolidAngle)
			continue;


		int	  nPlanes = 0;
		tvector4*	pPlanes = &pActiveOccluder->mPlanes[0];

		// find silhouette
		tvector4		vertices[12];
        int			nVertices = silhouette.findSilhouette(vertices);

		// create a plane with a edge of the occluder and the viewpoint
        
		for (i=0; i<nVertices; i+=2)
		{
            //tplane plan(campos, vertices[i], vertices[i+1]);
            
			tvector3	v1 = vertices[i];
			v1 -= viewPoint;
			tvector3	v2 = vertices[i+1];
			v2 -= viewPoint;

            v1.Normalize();
            v2.Normalize();
			*pPlanes = CrossProduct(v1, v2);
			pPlanes->Normalize();
			
			pPlanes->w = - DotProduct(*pPlanes, vertices[i]);

			pPlanes++;
			nPlanes ++;

		}
    
		if (gAddNearPlane)
		{
			for (int i=0; i<6; i++)
			{
				if (silhouette.dots[i] < 0.0f)
				{
					pActiveOccluder->mPlanes[nPlanes] = obox->mPlanes[i];
					nPlanes++;
				}
			}
		}
        
		pActiveOccluder->mNbPlanes = nPlanes;

		pActiveOccluder++;
		gNbActiveOccluders++;

		if (gNbActiveOccluders >= gMaxCandidateOccluders)
			break;
		
	}
	

	if (gNbActiveOccluders)
	{
		qsort(gActiveOccluders, gNbActiveOccluders, sizeof(FActiveOccluder), compareOccluder);
		if (gNbActiveOccluders > gMaxActiveOccluders)
			gNbActiveOccluders = gMaxActiveOccluders;
	}

	PROFILER_END();
	return gNbActiveOccluders;
}