RenderModelExternal & DRAWABLE::generateRenderModelData(StringIdMap & stringMap)
{
	// copy data over to the GL3V renderModel object
	// eventually this should only be done when we update the values, but for now
	// we call this every time we draw the drawable

	// cache off the stringId values
	static StringId diffuseId = stringMap.addStringId("diffuseTexture");
	static StringId misc1Id = stringMap.addStringId("misc1Texture");
	static StringId misc2Id = stringMap.addStringId("normalMapTexture");
	static StringId transformId = stringMap.addStringId("modelMatrix");
	static StringId colorId = stringMap.addStringId("colorTint");

	// textures
	if (texturesChanged)
	{
		renderModel.clearTextureCache();
		renderModel.textures.clear();
		if (diffuse_map && !diffuse_map->IsCube()) // for right now, restrict the diffuse map to 2d textures
		{
			renderModel.textures.push_back(RenderTextureEntry(diffuseId, diffuse_map->GetID(), GL_TEXTURE_2D));
		}
		if (misc_map1 && !misc_map1->IsCube())
		{
			renderModel.textures.push_back(RenderTextureEntry(misc1Id, misc_map1->GetID(), GL_TEXTURE_2D));
		}
		if (misc_map2 && !misc_map2->IsCube())
		{
			renderModel.textures.push_back(RenderTextureEntry(misc2Id, misc_map2->GetID(), GL_TEXTURE_2D));
		}

		texturesChanged = false;
	}

	// uniforms
	if (uniformsChanged)
	{
		renderModel.clearUniformCache();
		renderModel.uniforms.clear();
		if (transform != MATRIX4<float>()) // only add it if it's not the identity matrix
			renderModel.uniforms.push_back(RenderUniformEntry(transformId, transform.GetArray(), 16));
		if (r != 1 || g != 1 || b != 1 || a != 1) // only add it if it's not the default
		{
			float srgb_alpha[4];
			srgb_alpha[0] = r < 1 ? pow(r, 2.2f) : r;
			srgb_alpha[1] = g < 1 ? pow(g, 2.2f) : g;
			srgb_alpha[2] = b < 1 ? pow(b, 2.2f) : b;
			srgb_alpha[3] = a;
			renderModel.uniforms.push_back(RenderUniformEntry(colorId, srgb_alpha, 4));
		}

		uniformsChanged = false;
	}

	return renderModel;
}
void GraphicsGL3::SetupScene(
    float fov, float new_view_distance,
    const Vec3 cam_position,
    const Quat & cam_rotation,
    const Vec3 & /*dynamic_reflection_sample_pos*/,
    std::ostream & error_output)
{
    lastCameraPosition = cam_position;

    const float nearDistance = 0.1;

    setCameraPerspective("default",
                         cam_position,
                         cam_rotation,
                         fov,
                         nearDistance,
                         new_view_distance,
                         w,
                         h);

    Vec3 skyboxCamPosition(0,0,0);
    if (fixed_skybox)
        skyboxCamPosition[2] = cam_position[2];

    setCameraPerspective("skybox",
                         skyboxCamPosition,
                         cam_rotation,
                         fov,
                         nearDistance,
                         10000.f,
                         w,
                         h);

    // derive light rotation quaternion from light direction vector
    Quat light_rotation;
    Vec3 up(0, 0, 1);
    float cosa = up.dot(light_direction);
    if (cosa * cosa < 1.0f)
    {
        float a = -acosf(cosa);
        Vec3 x = up.cross(light_direction).Normalize();
        light_rotation.SetAxisAngle(a, x[0], x[1], x[2]);
    }

    // shadow cameras
    for (int i = 0; i < 3; i++)
    {
        //float shadow_radius = (1<<i)*closeshadow+(i)*20.0; //5,30,60
        float shadow_radius = (1<<(2-i))*closeshadow+(2-i)*20.0;

        Vec3 shadowbox(1,1,1);
        //shadowbox = shadowbox * (shadow_radius*sqrt(2.0));
        shadowbox = shadowbox * (shadow_radius*1.5);
        Vec3 shadowoffset(0,0,-1);
        shadowoffset = shadowoffset * shadow_radius;
        (-cam_rotation).RotateVector(shadowoffset);
        if (i == 2)
            shadowbox[2] += 25.0;
        Vec3 shadowPosition = cam_position+shadowoffset;

        // snap the shadow camera's location to shadow map texels
        // this can be commented out to minimize car aliasing at the expense of scenery aliasing
        const float shadowMapResolution = 512;
        float snapToGridSize = 2.f*shadowbox[0]/shadowMapResolution;
        Vec3 cameraSpaceShadowPosition = shadowPosition;
        light_rotation.RotateVector(cameraSpaceShadowPosition);
        for (int n = 0; n < 3; n++)
        {
            float pos = cameraSpaceShadowPosition[n];
            float gridpos = pos / snapToGridSize;
            gridpos = floor(gridpos);
            cameraSpaceShadowPosition[n] = gridpos*snapToGridSize;
        }
        (-light_rotation).RotateVector(cameraSpaceShadowPosition);
        shadowPosition = cameraSpaceShadowPosition;

        std::string suffix = Utils::tostr(i+1);

        CameraMatrices & shadowcam = setCameraOrthographic("shadow"+suffix,
                                     shadowPosition,
                                     light_rotation,
                                     -shadowbox,
                                     shadowbox);

        std::string matrixName = "shadowMatrix";

        // create and send shadow reconstruction matrices
        // the reconstruction matrix should transform from view to world, then from world to shadow view, then from shadow view to shadow clip space
        const CameraMatrices & defaultcam = cameras.find("default")->second;
        Mat4 shadowReconstruction = defaultcam.inverseViewMatrix.Multiply(shadowcam.viewMatrix).Multiply(shadowcam.projectionMatrix);
        /*//Mat4 shadowReconstruction = shadowcam.projectionMatrix.Multiply(shadowcam.viewMatrix.Multiply(defaultcam.inverseViewMatrix));
        std::cout << "shadowcam.projectionMatrix: " << std::endl;
        shadowcam.projectionMatrix.DebugPrint(std::cout);
        std::cout << "defaultcam.inverseViewMatrix: " << std::endl;
        defaultcam.inverseViewMatrix.DebugPrint(std::cout);
        std::cout << "shadowcam.viewMatrix: " << std::endl;
        shadowcam.viewMatrix.DebugPrint(std::cout);
        std::cout << "defaultcam.inverseViewMatrix.Multiply(shadowcam.viewMatrix): " << std::endl;
        defaultcam.inverseViewMatrix.Multiply(shadowcam.viewMatrix).DebugPrint(std::cout);
        std::cout << matrixName << ":" << std::endl;
        shadowReconstruction.DebugPrint(std::cout);*/

        //renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId(matrixName), shadowReconstruction.GetArray(),16));

        // examine the user-defined fields to find out which shadow matrix to send to a pass
        std::vector <StringId> passes = renderer.getPassNames();
        for (std::vector <StringId>::const_iterator i = passes.begin(); i != passes.end(); i++)
        {
            std::map <std::string, std::string> fields = renderer.getUserDefinedFields(*i);
            std::map <std::string, std::string>::const_iterator field = fields.find(matrixName);
            if (field != fields.end() && field->second == suffix)
            {
                renderer.setPassUniform(*i, RenderUniformEntry(stringMap.addStringId(matrixName), shadowReconstruction.GetArray(),16));
            }
        }
    }

    // send cameras to passes
    for (std::map <std::string, std::string>::const_iterator i = passNameToCameraName.begin(); i != passNameToCameraName.end(); i++)
    {
        renderer.setPassUniform(stringMap.addStringId(i->first), RenderUniformEntry(stringMap.addStringId("viewMatrix"), cameras[i->second].viewMatrix.GetArray(),16));
        renderer.setPassUniform(stringMap.addStringId(i->first), RenderUniformEntry(stringMap.addStringId("projectionMatrix"), cameras[i->second].projectionMatrix.GetArray(),16));
    }

    // send matrices for the default camera
    const CameraMatrices & defaultCamera = cameras.find("default")->second;
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("invProjectionMatrix"), defaultCamera.inverseProjectionMatrix.GetArray(),16));
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("invViewMatrix"), defaultCamera.inverseViewMatrix.GetArray(),16));
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("defaultViewMatrix"), defaultCamera.viewMatrix.GetArray(),16));
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("defaultProjectionMatrix"), defaultCamera.projectionMatrix.GetArray(),16));

    // send sun light direction for the default camera

    // transform to eyespace (view space)
    MathVector <float, 4> lightDirection4;
    for (int i = 0; i < 3; i++)
        lightDirection4[i] = light_direction[i];
    lightDirection4[3] = 0;
    defaultCamera.viewMatrix.MultiplyVector4(&lightDirection4[0]);

    // upload to the shaders
    RenderUniformEntry lightDirectionUniform(stringMap.addStringId("eyespaceLightDirection"), &lightDirection4[0], 3);
    renderer.setGlobalUniform(lightDirectionUniform);

    // set the reflection strength
    // TODO: read this from the track definition
    float reflectedLightColor[4];
    for (int i = 0; i < 3; i++)
        reflectedLightColor[i] = 0.5;
    reflectedLightColor[3] = 1.;
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("reflectedLightColor"), reflectedLightColor, 4));

    // set the ambient strength
    // TODO: read this from the track definition
    float ambientLightColor[4];
    for (int i = 0; i < 3; i++)
        ambientLightColor[i] = 1.56;
    ambientLightColor[3] = 1.;
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("ambientLightColor"), ambientLightColor, 4));

    // set the sun strength
    // TODO: read this from the track definition
    float directionalLightColor[4];
    for (int i = 0; i < 3; i++)
        directionalLightColor[i] = 8.3;
    directionalLightColor[3] = 1.;
    renderer.setGlobalUniform(RenderUniformEntry(stringMap.addStringId("directionalLightColor"), directionalLightColor, 4));

    AssembleDrawMap(error_output);
}