Esempio n. 1
0
void mtxScaleRotateTranslate(float* _result
							, const float _scaleX
							, const float _scaleY
							, const float _scaleZ
							, const float _rotX
							, const float _rotY
							, const float _rotZ
							, const float _translateX
							, const float _translateY
							, const float _translateZ
							)
{
	float mtxRotateTranslate[16];
	float mtxScale[16];

	mtxRotateXYZ(mtxRotateTranslate, _rotX, _rotY, _rotZ);
	mtxRotateTranslate[12] = _translateX;
	mtxRotateTranslate[13] = _translateY;
	mtxRotateTranslate[14] = _translateZ;

	memset(mtxScale, 0, sizeof(float)*16);
	mtxScale[0]  = _scaleX;
	mtxScale[5]  = _scaleY;
	mtxScale[10] = _scaleZ;
	mtxScale[15] = 1.0f;

	mtxMul(_result, mtxScale, mtxRotateTranslate);
}
Esempio n. 2
0
void aabbTransformToObb(Obb& _obb, const Aabb& _aabb, const float* _mtx)
{
	aabbToObb(_obb, _aabb);
	float result[16];
	mtxMul(result, _obb.m_mtx, _mtx);
	memcpy(_obb.m_mtx, result, sizeof(result) );
}
Esempio n. 3
0
static MTXMatrix _getEigenvaluesIteration (MTXMatrix matrix) {
    int i, j, n = 0;
    int rotation_m_len = ((int) pow(matrix->rows, 2) - matrix->rows) / 2;
    MTXMatrix aux = NULL, Q = NULL, R = NULL, result = NULL;
    MTXMatrix rotation_matrices[rotation_m_len];

    /* Calculate rotations matrices */
    for (i = 0; i < matrix->rows; i ++)
        for (j = 0; j < matrix->columns; j ++)
            if (j < i) {
                rotation_matrices[n] = mtxGetRotationMatrix(matrix, i, j);
                n ++;
            }

    /* Begin calculate R matrix */
    R = mtxNew(matrix->rows, matrix->columns);
    for (i = n - 1; i >= 0; i --) {
        aux = R;
        R = mtxMul(R, rotation_matrices[i]);
        mtxDestroy(&aux);
    }
    aux = R;
    R = mtxMul(R, matrix);
    mtxDestroy(&aux);

    /* Begin calculate Q matrix */
    Q = mtxNew(matrix->rows, matrix->columns);
    for (i = 0; i < n; i ++) {
        aux = Q;
        Q = mtxMul(aux, mtxTranspose(rotation_matrices[i]));
        mtxDestroy(&aux);
    }

    /* Destroy rotations matrices */
    for (i = 0; i < rotation_m_len; i ++) {
        aux = rotation_matrices[i];
        mtxDestroy(&aux);
    }

    result = mtxMul(R, Q);
    mtxDestroy(&R);
    mtxDestroy(&Q);

    return result;
}
Esempio n. 4
0
void cameraUpdate(Camera * camera) {

	mtxMakeLookAtRH(camera->viewMtx, camera->position, camera->target, camera->up);
	mtxMakePerspectiveRH(camera->projMtx, D_DEG2RAD(camera->fov), camera->aspect, camera->znear, camera->zfar);

	mtxMul(camera->viewProjMtx, camera->viewMtx, camera->projMtx);
	mtxInvertFull(camera->worldMtx, camera->viewMtx);

	mtxCopy(camera->normalMtx, camera->viewMtx);

	vecZero4(camera->normalMtx[3]);
	camera->normalMtx[0][3] = 0.0f;
	camera->normalMtx[1][3] = 0.0f;
	camera->normalMtx[2][3] = 0.0f;

	camera->up[0] = camera->viewMtx[0][1];
	camera->up[1] = camera->viewMtx[1][1];
	camera->up[2] = camera->viewMtx[2][1];
	camera->up[3] = 1.0f;
}
Esempio n. 5
0
int _main_(int _argc, char** _argv)
{
	uint32_t width = 1280;
	uint32_t height = 720;
	uint32_t debug = BGFX_DEBUG_TEXT;
	uint32_t reset = BGFX_RESET_NONE;

	bgfx::init();
	bgfx::reset(width, height);

	// Enable debug text.
	bgfx::setDebug(debug);

	// Set view 0 clear state.
	bgfx::setViewClear(0
		, BGFX_CLEAR_COLOR_BIT|BGFX_CLEAR_DEPTH_BIT
		, 0x303030ff
		, 1.0f
		, 0
		);

	// Setup root path for binary shaders. Shader binaries are different 
	// for each renderer.
	switch (bgfx::getRendererType() )
	{
	default:
	case bgfx::RendererType::Direct3D9:
		s_shaderPath = "shaders/dx9/";
		break;

	case bgfx::RendererType::Direct3D11:
		s_shaderPath = "shaders/dx11/";
		break;

	case bgfx::RendererType::OpenGL:
		s_shaderPath = "shaders/glsl/";
		s_flipV = true;
		break;

	case bgfx::RendererType::OpenGLES2:
	case bgfx::RendererType::OpenGLES3:
		s_shaderPath = "shaders/gles/";
		s_flipV = true;
		break;
	}

	// Create vertex stream declaration.
	s_PosColorTexCoord0Decl.begin();
	s_PosColorTexCoord0Decl.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);
	s_PosColorTexCoord0Decl.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
	s_PosColorTexCoord0Decl.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
	s_PosColorTexCoord0Decl.end();  

	bgfx::UniformHandle u_time = bgfx::createUniform("u_time", bgfx::UniformType::Uniform1f);
	bgfx::UniformHandle u_mtx = bgfx::createUniform("u_mtx", bgfx::UniformType::Uniform4x4fv);
	bgfx::UniformHandle u_lightDir = bgfx::createUniform("u_lightDir", bgfx::UniformType::Uniform3fv);

	bgfx::ProgramHandle raymarching = loadProgram("vs_raymarching", "fs_raymarching");

	while (!processEvents(width, height, debug, reset) )
	{
		// Set view 0 default viewport.
		bgfx::setViewRect(0, 0, 0, width, height);

		// Set view 1 default viewport.
		bgfx::setViewRect(1, 0, 0, width, height);

		// This dummy draw call is here to make sure that view 0 is cleared
		// if no other draw calls are submitted to viewZ 0.
		bgfx::submit(0);

		int64_t now = bx::getHPCounter();
		static int64_t last = now;
		const int64_t frameTime = now - last;
		last = now;
		const double freq = double(bx::getHPFrequency() );
		const double toMs = 1000.0/freq;

		// Use debug font to print information about this example.
		bgfx::dbgTextClear();
		bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/03-raymarch");
		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Updating shader uniforms.");
		bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);

		float at[3] = { 0.0f, 0.0f, 0.0f };
		float eye[3] = { 0.0f, 0.0f, -15.0f };
		
		float view[16];
		float proj[16];
		mtxLookAt(view, eye, at);
		mtxProj(proj, 60.0f, 16.0f/9.0f, 0.1f, 100.0f);

		// Set view and projection matrix for view 1.
		bgfx::setViewTransform(0, view, proj);

		float ortho[16];
		mtxOrtho(ortho, 0.0f, 1280.0f, 720.0f, 0.0f, 0.0f, 100.0f);

		// Set view and projection matrix for view 0.
		bgfx::setViewTransform(1, NULL, ortho);

		float time = (float)(bx::getHPCounter()/double(bx::getHPFrequency() ) );

		float vp[16];
		mtxMul(vp, view, proj);

		float mtx[16];
		mtxRotateXY(mtx
			, time
			, time*0.37f
			); 

		float mtxInv[16];
		mtxInverse(mtxInv, mtx);
		float lightDirModel[4] = { -0.4f, -0.5f, -1.0f, 0.0f };
		float lightDirModelN[4];
		vec3Norm(lightDirModelN, lightDirModel);
		float lightDir[4];
		vec4MulMtx(lightDir, lightDirModelN, mtxInv);
		bgfx::setUniform(u_lightDir, lightDir);

		float mvp[16];
		mtxMul(mvp, mtx, vp);

		float invMvp[16];
		mtxInverse(invMvp, mvp);
		bgfx::setUniform(u_mtx, invMvp);

		bgfx::setUniform(u_time, &time);

		renderScreenSpaceQuad(1, raymarching, 0.0f, 0.0f, 1280.0f, 720.0f);

		// Advance to next frame. Rendering thread will be kicked to 
		// process submitted rendering primitives.
		bgfx::frame();
	}

	// Cleanup.
	bgfx::destroyProgram(raymarching);

	bgfx::destroyUniform(u_time);
	bgfx::destroyUniform(u_mtx);
	bgfx::destroyUniform(u_lightDir);

	// Shutdown bgfx.
	bgfx::shutdown();

	return 0;
}
void SurfaceTexture::computeCurrentTransformMatrixLocked() {
    ST_LOGV("computeCurrentTransformMatrixLocked");

    float xform[16];
    for (int i = 0; i < 16; i++) {
        xform[i] = mtxIdentity[i];
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
        float result[16];
        mtxMul(result, xform, mtxFlipH);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
        float result[16];
        mtxMul(result, xform, mtxFlipV);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
        float result[16];
        mtxMul(result, xform, mtxRot90);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }

    sp<GraphicBuffer>& buf(mCurrentTextureBuf);

    if (buf == NULL) {
        ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
    }

    Rect cropRect = mCurrentCrop;
    float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
    float bufferWidth = buf->getWidth();
    float bufferHeight = buf->getHeight();
    if (!cropRect.isEmpty()) {
        float shrinkAmount = 0.0f;
        if (mFilteringEnabled) {
            // In order to prevent bilinear sampling beyond the edge of the
            // crop rectangle we may need to shrink it by 2 texels in each
            // dimension.  Normally this would just need to take 1/2 a texel
            // off each end, but because the chroma channels of YUV420 images
            // are subsampled we may need to shrink the crop region by a whole
            // texel on each side.
            switch (buf->getPixelFormat()) {
                case PIXEL_FORMAT_RGBA_8888:
                case PIXEL_FORMAT_RGBX_8888:
                case PIXEL_FORMAT_RGB_888:
                case PIXEL_FORMAT_RGB_565:
                case PIXEL_FORMAT_BGRA_8888:
                case PIXEL_FORMAT_RGBA_5551:
                case PIXEL_FORMAT_RGBA_4444:
                    // We know there's no subsampling of any channels, so we
                    // only need to shrink by a half a pixel.
                    shrinkAmount = 0.5;
                    break;

                default:
                    // If we don't recognize the format, we must assume the
                    // worst case (that we care about), which is YUV420.
                    shrinkAmount = 1.0;
                    break;
            }
        }

        // Only shrink the dimensions that are not the size of the buffer.
        if (cropRect.width() < bufferWidth) {
            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
                    bufferWidth;
        }
        if (cropRect.height() < bufferHeight) {
            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
                    bufferHeight;
            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
                    bufferHeight;
        }
    }
    float crop[16] = {
        sx, 0, 0, 0,
        0, sy, 0, 0,
        0, 0, 1, 0,
        tx, ty, 0, 1,
    };

    float mtxBeforeFlipV[16];
    mtxMul(mtxBeforeFlipV, crop, xform);

    // SurfaceFlinger expects the top of its window textures to be at a Y
    // coordinate of 0, so SurfaceTexture must behave the same way.  We don't
    // want to expose this to applications, however, so we must add an
    // additional vertical flip to the transform after all the other transforms.
    mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
void SurfaceTexture::computeCurrentTransformMatrix() {
    ST_LOGV("computeCurrentTransformMatrix");

    float xform[16];
    for (int i = 0; i < 16; i++) {
        xform[i] = mtxIdentity[i];
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
        float result[16];
        mtxMul(result, xform, mtxFlipH);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
        float result[16];
        mtxMul(result, xform, mtxFlipV);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }
    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
        float result[16];
        mtxMul(result, xform, mtxRot90);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }

    sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
    float tx, ty, sx, sy;
    if (!mCurrentCrop.isEmpty()) {
        // In order to prevent bilinear sampling at the of the crop rectangle we
        // may need to shrink it by 2 texels in each direction.  Normally this
        // would just need to take 1/2 a texel off each end, but because the
        // chroma channels will likely be subsampled we need to chop off a whole
        // texel.  This will cause artifacts if someone does nearest sampling
        // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
        // accomodate the bilinear and nearest sampling uses.
        //
        // If nearest sampling turns out to be a desirable usage of these
        // textures then we could add the ability to switch a SurfaceTexture to
        // nearest-mode.  Preferably, however, the image producers (video
        // decoder, camera, etc.) would simply not use a crop rectangle (or at
        // least not tell the framework about it) so that the GPU can do the
        // correct edge behavior.
        int xshrink = 0, yshrink = 0;
        if (mCurrentCrop.left > 0) {
            tx = float(mCurrentCrop.left + 1) / float(buf->getWidth());
            xshrink++;
        } else {
            tx = 0.0f;
        }
        if (mCurrentCrop.right < int32_t(buf->getWidth())) {
            xshrink++;
        }
        if (mCurrentCrop.bottom < int32_t(buf->getHeight())) {
            ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
                    float(buf->getHeight());
            yshrink++;
        } else {
            ty = 0.0f;
        }
        if (mCurrentCrop.top > 0) {
            yshrink++;
        }
        sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth());
        sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight());
    } else {
        tx = 0.0f;
        ty = 0.0f;
        sx = 1.0f;
        sy = 1.0f;
    }
    float crop[16] = {
        sx, 0, 0, 0,
        0, sy, 0, 0,
        0, 0, 1, 0,
        tx, ty, 0, 1,
    };

    float mtxBeforeFlipV[16];
    mtxMul(mtxBeforeFlipV, crop, xform);

    // SurfaceFlinger expects the top of its window textures to be at a Y
    // coordinate of 0, so SurfaceTexture must behave the same way.  We don't
    // want to expose this to applications, however, so we must add an
    // additional vertical flip to the transform after all the other transforms.
    mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
Esempio n. 8
0
int _main_(int /*_argc*/, char** /*_argv*/)
{
	uint32_t width = 1280;
	uint32_t height = 720;
	uint32_t debug = BGFX_DEBUG_TEXT;
	uint32_t reset = BGFX_RESET_VSYNC;

	bgfx::init();
	bgfx::reset(width, height, reset);

	// Enable debug text.
	bgfx::setDebug(debug);

	// Setup root path for binary shaders. Shader binaries are different
	// for each renderer.
	switch (bgfx::getRendererType() )
	{
	default:
	case bgfx::RendererType::Direct3D9:
		s_shaderPath = "shaders/dx9/";
		s_texelHalf = 0.5f;
		break;

	case bgfx::RendererType::Direct3D11:
		s_shaderPath = "shaders/dx11/";
		break;

	case bgfx::RendererType::OpenGL:
		s_shaderPath = "shaders/glsl/";
		s_flipV = true;
		break;

	case bgfx::RendererType::OpenGLES2:
	case bgfx::RendererType::OpenGLES3:
		s_shaderPath = "shaders/gles/";
		s_flipV = true;
		break;
	}

	// Uniforms.
	u_shadowMap = bgfx::createUniform("u_shadowMap", bgfx::UniformType::Uniform1iv);

	bgfx::UniformHandle u_lightPos = bgfx::createUniform("u_lightPos", bgfx::UniformType::Uniform4fv);
	bgfx::UniformHandle u_lightMtx = bgfx::createUniform("u_lightMtx", bgfx::UniformType::Uniform4x4fv);

	// Programs.
	bgfx::ProgramHandle progPackDepth = loadProgram("vs_smsimple_packdepth", "fs_smsimple_packdepth");
	bgfx::ProgramHandle progDraw      = loadProgram("vs_smsimple_draw",      "fs_smsimple_draw");

	// Vertex declarations.
	bgfx::VertexDecl PosNormalDecl;
	PosNormalDecl.begin();
	PosNormalDecl.add(bgfx::Attrib::Position,  3, bgfx::AttribType::Float);
	PosNormalDecl.add(bgfx::Attrib::Normal,    4, bgfx::AttribType::Uint8, true, true);
	PosNormalDecl.end();

	// Meshes.
	Mesh bunnyMesh;
	Mesh cubeMesh;
	Mesh hollowcubeMesh;
	Mesh hplaneMesh;
	bunnyMesh.load("meshes/bunny.bin");
	cubeMesh.load("meshes/cube.bin");
	hollowcubeMesh.load("meshes/hollowcube.bin");
	hplaneMesh.load(s_hplaneVertices, s_numHPlaneVertices, PosNormalDecl, s_planeIndices, s_numPlaneIndices);

	// Render targets.
	uint16_t shadowMapSize = 512;

	bgfx::TextureHandle fbtextures[] =
	{
		bgfx::createTexture2D(shadowMapSize, shadowMapSize, 1, bgfx::TextureFormat::BGRA8, BGFX_TEXTURE_RT),
		bgfx::createTexture2D(shadowMapSize, shadowMapSize, 1, bgfx::TextureFormat::D16, BGFX_TEXTURE_RT_BUFFER_ONLY),
	};
	s_shadowMapFB = bgfx::createFrameBuffer(BX_COUNTOF(fbtextures), fbtextures, true);

	// Set view and projection matrices.
	float view[16];
	float proj[16];

	const float eye[3] = { 0.0f, 30.0f, -60.0f };
	const float at[3]  = { 0.0f, 5.0f, 0.0f };
	mtxLookAt(view, eye, at);

	const float aspect = float(int32_t(width) ) / float(int32_t(height) );
	mtxProj(proj, 60.0f, aspect, 0.1f, 1000.0f);

	// Time acumulators.
	float timeAccumulatorLight = 0.0f;
	float timeAccumulatorScene = 0.0f;

	entry::MouseState mouseState;
	while (!entry::processEvents(width, height, debug, reset, &mouseState) )
	{
		// Time.
		int64_t now = bx::getHPCounter();
		static int64_t last = now;
		const int64_t frameTime = now - last;
		last = now;
		const double freq = double(bx::getHPFrequency() );
		const double toMs = 1000.0/freq;
		const float deltaTime = float(frameTime/freq);

		// Update time accumulators.
		timeAccumulatorLight += deltaTime;
		timeAccumulatorScene += deltaTime;

		// Use debug font to print information about this example.
		bgfx::dbgTextClear();
		bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/15-shadowmaps-simple");
		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Shadow maps example.");
		bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);

		// Setup lights.
		float lightPos[4];
		lightPos[0] = -cos(timeAccumulatorLight);
		lightPos[1] = -1.0f;
		lightPos[2] = -sin(timeAccumulatorLight);
		lightPos[3] = 0.0f;

		bgfx::setUniform(u_lightPos, lightPos);

		// Setup instance matrices.
		float mtxFloor[16];
		mtxScaleRotateTranslate(mtxFloor
			, 30.0f //scaleX
			, 30.0f //scaleY
			, 30.0f //scaleZ
			, 0.0f  //rotX
			, 0.0f  //rotY
			, 0.0f  //rotZ
			, 0.0f  //translateX
			, 0.0f  //translateY
			, 0.0f  //translateZ
			);

		float mtxBunny[16];
		mtxScaleRotateTranslate(mtxBunny
			, 5.0f
			, 5.0f
			, 5.0f
			, 0.0f
			, float(M_PI) - timeAccumulatorScene
			, 0.0f
			, 15.0f
			, 5.0f
			, 0.0f
			);

		float mtxHollowcube[16];
		mtxScaleRotateTranslate(mtxHollowcube
			, 2.5f
			, 2.5f
			, 2.5f
			, 0.0f
			, 1.56f - timeAccumulatorScene
			, 0.0f
			, 0.0f
			, 10.0f
			, 0.0f
			);

		float mtxCube[16];
		mtxScaleRotateTranslate(mtxCube
			, 2.5f
			, 2.5f
			, 2.5f
			, 0.0f
			, 1.56f - timeAccumulatorScene
			, 0.0f
			, -15.0f
			, 5.0f
			, 0.0f
			);

		// Define matrices.
		float screenView[16];
		float screenProj[16];
		mtxIdentity(screenView);
		mtxOrtho(screenProj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f);

		float lightView[16];
		float lightProj[16];

		const float eye[3] =
		{
			-lightPos[0],
			-lightPos[1],
			-lightPos[2],
		};
		const float at[3] = { 0.0f, 0.0f, 0.0f };
		mtxLookAt(lightView, eye, at);

		const float area = 30.0f;
		mtxOrtho(lightProj, -area, area, -area, area, -100.0f, 100.0f);

		bgfx::setViewRect(RENDER_SHADOW_PASS_ID, 0, 0, shadowMapSize, shadowMapSize);
		bgfx::setViewRect(RENDER_SCENE_PASS_ID, 0, 0, width, height);

		bgfx::setViewTransform(RENDER_SHADOW_PASS_ID, lightView, lightProj);
		bgfx::setViewTransform(RENDER_SCENE_PASS_ID, view, proj);

		bgfx::setViewFrameBuffer(RENDER_SHADOW_PASS_ID, s_shadowMapFB);

		// Clear backbuffer and shadowmap framebuffer at beginning.
		bgfx::setViewClearMask(0x3, BGFX_CLEAR_COLOR_BIT | BGFX_CLEAR_DEPTH_BIT, 0x303030ff, 1.0f, 0);
		bgfx::submitMask(0x3);

		// Render.

		{ // Craft shadow map.

			hplaneMesh.submit(RENDER_SHADOW_PASS_ID, mtxFloor, progPackDepth);
			bunnyMesh.submit(RENDER_SHADOW_PASS_ID, mtxBunny, progPackDepth);
			hollowcubeMesh.submit(RENDER_SHADOW_PASS_ID, mtxHollowcube, progPackDepth);
			cubeMesh.submit(RENDER_SHADOW_PASS_ID, mtxCube, progPackDepth);
		}

		{ // Draw Scene.

			float mtxShadow[16]; //lightviewProjCrop
			float lightMtx[16];  //modelLightviewProjCrop

			const float s = (s_flipV) ? 1.0f : -1.0f; //sign

			const float mtxCrop[16] =
			{
				0.5f,   0.0f, 0.0f, 0.0f,
				0.0f, s*0.5f, 0.0f, 0.0f,
				0.0f,   0.0f, 0.5f, 0.0f,
				0.5f,   0.5f, 0.5f, 1.0f,
			};

			float mtxTmp[16];
			mtxMul(mtxTmp, lightProj, mtxCrop);
			mtxMul(mtxShadow, lightView, mtxTmp);

			// Floor.
			mtxMul(lightMtx, mtxFloor, mtxShadow);
			bgfx::setUniform(u_lightMtx, lightMtx);
			hplaneMesh.submit(RENDER_SCENE_PASS_ID, mtxFloor, progDraw);

			// Bunny.
			mtxMul(lightMtx, mtxBunny, mtxShadow);
			bgfx::setUniform(u_lightMtx, lightMtx);
			bunnyMesh.submit(RENDER_SCENE_PASS_ID, mtxBunny, progDraw);

			// Hollow cube.
			mtxMul(lightMtx, mtxHollowcube, mtxShadow);
			bgfx::setUniform(u_lightMtx, lightMtx);
			hollowcubeMesh.submit(RENDER_SCENE_PASS_ID, mtxHollowcube, progDraw);

			// Cube.
			mtxMul(lightMtx, mtxCube, mtxShadow);
			bgfx::setUniform(u_lightMtx, lightMtx);
			cubeMesh.submit(RENDER_SCENE_PASS_ID, mtxCube, progDraw);
		}

		// Advance to next frame. Rendering thread will be kicked to
		// process submitted rendering primitives.
		bgfx::frame();

	}

	bunnyMesh.unload();
	cubeMesh.unload();
	hollowcubeMesh.unload();
	hplaneMesh.unload();

	bgfx::destroyProgram(progPackDepth);
	bgfx::destroyProgram(progDraw);

	bgfx::destroyFrameBuffer(s_shadowMapFB);

	bgfx::destroyUniform(u_shadowMap);
	bgfx::destroyUniform(u_lightPos);
	bgfx::destroyUniform(u_lightMtx);

	// Shutdown bgfx.
	bgfx::shutdown();

	return 0;
}