Example #1
0
CVector3 CSplineInterpolatorVector3::getAt(F32 key){
    S32 i;

    if(mDirty)
        calcTangents();

	if(keys.size() == 0)
		return CVector3(0.0, 0.0, 0.0);

	if(key < keys[0])
		return nodes[0];
		
	for(i = 0 ; i < keys.size() - 1 ; i++){
		if(key >= keys[i] && key < keys[i+1]){
			F32 s = keys[i];
			F32 e = keys[i+1];
			
			if(s == e)return nodes[i];
			
			F32 f = (key - s) / (e - s);
			return intepolate(i, f);
		}
	}
	
	return nodes[nodes.size() - 1];
}
Example #2
0
  sF32 fCurve::evalDerivative(sF32 frame) const
  {
    if (frame < m_firstFrame || m_keys.size() == 1 || m_type == constant)
      return 0;
    else if (frame >= m_lastFrame)
      return 0;
    else
    {
      keyVectorCIt key0, key1;

      key0 = findKeyByFrame(frame);
      key1 = key0 + 1;
      frame -= key0->frame;

      const sInt tLength = key1->frame - key0->frame;

      if (m_type == linear)
        return (key1->value - key0->value) / tLength;

      const sF32 t = (tLength > 1) ? frame / tLength : 0.0f, tt = t * t;
      const sF32 h1 = 6.0f * tt - 6.0f * t, h3=3.0f * tt - 4.0f * t + 1.0f, h4=3.0f * tt - 2.0f * t;
      // h2' is just -h1', so not evaluated here

      sF32 dd0, ds1;
      calcTangents(key0, dd0, ds1);

      return key0->value * h1 - key1->value * h1 + dd0 * h3 + ds1 * h4;
    }
  }
Example #3
0
  sF32 fCurve::evaluate(sF32 frame) const
  {
    if (frame <= m_firstFrame || m_keys.size() == 1)
      return m_keys.front().value;
    else if (frame >= m_lastFrame)
      return m_keys.back().value;
    else
    {
      keyVectorCIt key0, key1;
      
      key0 = findKeyByFrame(frame);

      if (m_type == constant)
        return key0->value;

      key1 = key0 + 1;
      frame -= key0->frame;

      const sInt tLength = key1->frame - key0->frame;
      const sF32 t = (tLength > 1) ? frame / tLength : 0.0f;

      if (m_type == linear)
        return key0->value + t * (key1->value - key0->value);

      const sF32 tt = t * t, ttt = tt * t;
      const sF32 h2 = 3.0f * tt - 2.0f * ttt, h1 = 1.0f - h2, h3 = ttt - 2.0f * tt + t, h4 = ttt - tt;
      
      sF32 dd0, ds1;
      calcTangents(key0, dd0, ds1);
      
      return key0->value * h1 + key1->value * h2 + dd0 * h3 + ds1 * h4;
    }
  }
Example #4
0
//-----------------------------------------------------------------------------
//! SLMesh::shapeInit sets the transparency flag of the AABB
void SLMesh::shapeInit(SLSceneView* sv)
{  
   (void)sv; // avoid unused parameter warning
   
   if (P && N)
   {  // loop through material faces and check for opaqueness & transparency
      for (SLuint m = 0; m < numM; ++m)
      {  
         // check if the mesh has it own material
         if (M[m].mat) SLMaterial::current = M[m].mat;
         else M[m].mat = SLMaterial::current;
         
         // be sure that we have a material at this point
         assert(SLMaterial::current && "No current material set!");
                  
         // set transparent flag of the mesh
         if (!_aabb.hasAlpha() && M[m].mat->hasAlpha()) 
            _aabb.hasAlpha(true);
         
         // build tangents for bump mapping
         if (M[m].mat->needsTangents() && Tc && T==0)
            calcTangents();
      }
   }
}
Example #5
0
 void calcTangents(VertexArray& vertices) {
     assert(!(vertices.size() % 3));
     for (GLuint i = 0; i < vertices.size(); i += 3)
         calcTangents(
                 std::array<Vertex4f*, 3> {
                         &vertices[i],
                         &vertices[i + 1],
                         &vertices[i + 2] });
 }
Example #6
0
  void fCurve::convertToBezier(cubicBezierCurve2D& t) const
  {
    if (getNKeys()>1)
    {
      t.resize(getNKeys()-1);

      cubicBezierSegment2D *seg = t.segment;

      for (keyVectorCIt it = m_keys.begin(); (it + 1) != m_keys.end(); ++it)
      {
        const sF32 v0 = it->value, v1 = (m_type != constant) ? (it + 1)->value : it->value;
        const sInt f0 = it->frame, f1 = (it + 1) -> frame;

        sF32 dd0, ds1;
        calcTangents(it, dd0, ds1);

        seg->A=vector2(f0, v0);
        seg->B=vector2((2.0f * f0 + f1) / 3.0f, v0 + dd0 / 3.0f);
        seg->C=vector2((f0 + 2.0f * f1) / 3.0f, v1 - ds1 / 3.0f);
        seg->D=vector2(f1, v1);
        seg++;
      }
    }
  }
Example #7
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);

	// Set clear color palette for index 0
	bgfx::setClearColor(0, UINT32_C(0x00000000) );

	// Set clear color palette for index 1
	bgfx::setClearColor(1, UINT32_C(0x303030ff) );

	// Set geometry pass view clear state.
	bgfx::setViewClear(RENDER_PASS_GEOMETRY_ID
		, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
		, 1.0f
		, 0
		, 1
		);

	// Set light pass view clear state.
	bgfx::setViewClear(RENDER_PASS_LIGHT_ID
		, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
		, 1.0f
		, 0
		, 0
		);

	// Create vertex stream declaration.
	PosNormalTangentTexcoordVertex::init();
	PosTexCoord0Vertex::init();
	DebugVertex::init();

	calcTangents(s_cubeVertices
		, BX_COUNTOF(s_cubeVertices)
		, PosNormalTangentTexcoordVertex::ms_decl
		, s_cubeIndices
		, BX_COUNTOF(s_cubeIndices)
		);

	// Create static vertex buffer.
	bgfx::VertexBufferHandle vbh = bgfx::createVertexBuffer(
		  bgfx::makeRef(s_cubeVertices, sizeof(s_cubeVertices) )
		, PosNormalTangentTexcoordVertex::ms_decl
		);

	// Create static index buffer.
	bgfx::IndexBufferHandle ibh = bgfx::createIndexBuffer(bgfx::makeRef(s_cubeIndices, sizeof(s_cubeIndices) ) );

	// Create texture sampler uniforms.
	bgfx::UniformHandle s_texColor  = bgfx::createUniform("s_texColor",  bgfx::UniformType::Uniform1iv);
	bgfx::UniformHandle s_texNormal = bgfx::createUniform("s_texNormal", bgfx::UniformType::Uniform1iv);

	bgfx::UniformHandle s_albedo = bgfx::createUniform("s_albedo", bgfx::UniformType::Uniform1iv);
	bgfx::UniformHandle s_normal = bgfx::createUniform("s_normal", bgfx::UniformType::Uniform1iv);
	bgfx::UniformHandle s_depth  = bgfx::createUniform("s_depth",  bgfx::UniformType::Uniform1iv);
	bgfx::UniformHandle s_light  = bgfx::createUniform("s_light",  bgfx::UniformType::Uniform1iv);

	bgfx::UniformHandle u_mtx            = bgfx::createUniform("u_mtx",            bgfx::UniformType::Uniform4x4fv);
	bgfx::UniformHandle u_lightPosRadius = bgfx::createUniform("u_lightPosRadius", bgfx::UniformType::Uniform4fv);
	bgfx::UniformHandle u_lightRgbInnerR = bgfx::createUniform("u_lightRgbInnerR", bgfx::UniformType::Uniform4fv);

	// Create program from shaders.
	bgfx::ProgramHandle geomProgram    = loadProgram("vs_deferred_geom",       "fs_deferred_geom");
	bgfx::ProgramHandle lightProgram   = loadProgram("vs_deferred_light",      "fs_deferred_light");
	bgfx::ProgramHandle combineProgram = loadProgram("vs_deferred_combine",    "fs_deferred_combine");
	bgfx::ProgramHandle debugProgram   = loadProgram("vs_deferred_debug",      "fs_deferred_debug");
	bgfx::ProgramHandle lineProgram    = loadProgram("vs_deferred_debug_line", "fs_deferred_debug_line");

	// Load diffuse texture.
	bgfx::TextureHandle textureColor  = loadTexture("fieldstone-rgba.dds");

	// Load normal texture.
	bgfx::TextureHandle textureNormal = loadTexture("fieldstone-n.dds");

	bgfx::TextureHandle gbufferTex[3] = { BGFX_INVALID_HANDLE, BGFX_INVALID_HANDLE, BGFX_INVALID_HANDLE };
	bgfx::FrameBufferHandle gbuffer = BGFX_INVALID_HANDLE;
	bgfx::FrameBufferHandle lightBuffer = BGFX_INVALID_HANDLE;

	// Imgui.
	imguiCreate();

	const int64_t timeOffset = bx::getHPCounter();
	const bgfx::RendererType::Enum renderer = bgfx::getRendererType();
	const float texelHalf = bgfx::RendererType::Direct3D9 == renderer ? 0.5f : 0.0f;
	s_originBottomLeft = bgfx::RendererType::OpenGL == renderer || bgfx::RendererType::OpenGLES == renderer;

	// Get renderer capabilities info.
	const bgfx::Caps* caps = bgfx::getCaps();

	uint32_t oldWidth  = 0;
	uint32_t oldHeight = 0;
	uint32_t oldReset  = reset;

	int32_t scrollArea = 0;
	int32_t numLights = 512;
	float lightAnimationSpeed = 0.3f;
	bool animateMesh = true;
	bool showScissorRects = false;
	bool showGBuffer = true;

	float view[16];
	float initialPos[3] = { 0.0f, 0.0f, -15.0f };
	cameraCreate();
	cameraSetPosition(initialPos);
	cameraSetVerticalAngle(0.0f);
	cameraGetViewMtx(view);

	entry::MouseState mouseState;
	while (!entry::processEvents(width, height, debug, reset, &mouseState) )
	{
		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);

		float time = (float)( (now-timeOffset)/freq);

		// Use debug font to print information about this example.
		bgfx::dbgTextClear();
		bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/21-deferred");
		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: MRT rendering and deferred shading.");
		bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);

		if (2 > caps->maxFBAttachments)
		{
			// When multiple render targets (MRT) is not supported by GPU,
			// implement alternative code path that doesn't use MRT.
			bool blink = uint32_t(time*3.0f)&1;
			bgfx::dbgTextPrintf(0, 5, blink ? 0x1f : 0x01, " MRT not supported by GPU. ");

			// Set view 0 default viewport.
			bgfx::setViewRect(0, 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 view 0.
			bgfx::submit(0);
		}
		else
		{
			if (oldWidth  != width
			||  oldHeight != height
			||  oldReset  != reset
			||  !bgfx::isValid(gbuffer) )
			{
				// Recreate variable size render targets when resolution changes.
				oldWidth  = width;
				oldHeight = height;
				oldReset  = reset;

				if (bgfx::isValid(gbuffer) )
				{
					bgfx::destroyFrameBuffer(gbuffer);
				}

				const uint32_t samplerFlags = 0
					| BGFX_TEXTURE_RT
					| BGFX_TEXTURE_MIN_POINT
					| BGFX_TEXTURE_MAG_POINT
					| BGFX_TEXTURE_MIP_POINT
					| BGFX_TEXTURE_U_CLAMP
					| BGFX_TEXTURE_V_CLAMP
					;
				gbufferTex[0] = bgfx::createTexture2D(width, height, 1, bgfx::TextureFormat::BGRA8, samplerFlags);
				gbufferTex[1] = bgfx::createTexture2D(width, height, 1, bgfx::TextureFormat::BGRA8, samplerFlags);
				gbufferTex[2] = bgfx::createTexture2D(width, height, 1, bgfx::TextureFormat::D24,   samplerFlags);
				gbuffer = bgfx::createFrameBuffer(BX_COUNTOF(gbufferTex), gbufferTex, true);

				if (bgfx::isValid(lightBuffer) )
				{
					bgfx::destroyFrameBuffer(lightBuffer);
				}

				lightBuffer = bgfx::createFrameBuffer(width, height, bgfx::TextureFormat::BGRA8, samplerFlags);
			}

			imguiBeginFrame(mouseState.m_mx
				, mouseState.m_my
				, (mouseState.m_buttons[entry::MouseButton::Left  ] ? IMGUI_MBUT_LEFT  : 0)
				| (mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
				, 0
				, width
				, height
				);

			imguiBeginScrollArea("Settings", width - width / 5 - 10, 10, width / 5, height / 3, &scrollArea);
			imguiSeparatorLine();

			imguiSlider("Num lights", numLights, 1, 2048);

			if (imguiCheck("Show G-Buffer.", showGBuffer) )
			{
				showGBuffer = !showGBuffer;
			}

			if (imguiCheck("Show light scissor.", showScissorRects) )
			{
				showScissorRects = !showScissorRects;
			}

			if (imguiCheck("Animate mesh.", animateMesh) )
			{
				animateMesh = !animateMesh;
			}

			imguiSlider("Lights animation speed", lightAnimationSpeed, 0.0f, 0.4f, 0.01f);

			imguiEndScrollArea();
			imguiEndFrame();

			// Update camera.
			cameraUpdate(deltaTime, mouseState);
			cameraGetViewMtx(view);

			// Setup views
			float vp[16];
			float invMvp[16];
			{
				bgfx::setViewRect(RENDER_PASS_GEOMETRY_ID,      0, 0, width, height);
				bgfx::setViewRect(RENDER_PASS_LIGHT_ID,         0, 0, width, height);
				bgfx::setViewRect(RENDER_PASS_COMBINE_ID,       0, 0, width, height);
				bgfx::setViewRect(RENDER_PASS_DEBUG_LIGHTS_ID,  0, 0, width, height);
				bgfx::setViewRect(RENDER_PASS_DEBUG_GBUFFER_ID, 0, 0, width, height);

				bgfx::setViewFrameBuffer(RENDER_PASS_LIGHT_ID, lightBuffer);

				float proj[16];
				mtxProj(proj, 60.0f, float(width)/float(height), 0.1f, 100.0f);

				bgfx::setViewFrameBuffer(RENDER_PASS_GEOMETRY_ID, gbuffer);
				bgfx::setViewTransform(RENDER_PASS_GEOMETRY_ID, view, proj);

				bx::mtxMul(vp, view, proj);
				bx::mtxInverse(invMvp, vp);

				bx::mtxOrtho(proj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f);
				bgfx::setViewTransform(RENDER_PASS_LIGHT_ID,   NULL, proj);
				bgfx::setViewTransform(RENDER_PASS_COMBINE_ID, NULL, proj);

				const float aspectRatio = float(height)/float(width);
				const float size = 10.0f;
				bx::mtxOrtho(proj, -size, size, size*aspectRatio, -size*aspectRatio, 0.0f, 1000.0f);
				bgfx::setViewTransform(RENDER_PASS_DEBUG_GBUFFER_ID, NULL, proj);

				bx::mtxOrtho(proj, 0.0f, (float)width, 0.0f, (float)height, 0.0f, 1000.0f);
				bgfx::setViewTransform(RENDER_PASS_DEBUG_LIGHTS_ID, NULL, proj);
			}

			const uint32_t dim = 11;
			const float offset = (float(dim-1) * 3.0f) * 0.5f;

			// Draw into geometry pass.
			for (uint32_t yy = 0; yy < dim; ++yy)
			{
				for (uint32_t xx = 0; xx < dim; ++xx)
				{
					float mtx[16];
					if (animateMesh)
					{
						bx::mtxRotateXY(mtx, time*1.023f + xx*0.21f, time*0.03f + yy*0.37f);
					}
					else
					{
						bx::mtxIdentity(mtx);
					}
					mtx[12] = -offset + float(xx)*3.0f;
					mtx[13] = -offset + float(yy)*3.0f;
					mtx[14] = 0.0f;

					// Set transform for draw call.
					bgfx::setTransform(mtx);

					// Set vertex and fragment shaders.
					bgfx::setProgram(geomProgram);

					// Set vertex and index buffer.
					bgfx::setVertexBuffer(vbh);
					bgfx::setIndexBuffer(ibh);

					// Bind textures.
					bgfx::setTexture(0, s_texColor,  textureColor);
					bgfx::setTexture(1, s_texNormal, textureNormal);

					// Set render states.
					bgfx::setState(0
						| BGFX_STATE_RGB_WRITE
						| BGFX_STATE_ALPHA_WRITE
						| BGFX_STATE_DEPTH_WRITE
						| BGFX_STATE_DEPTH_TEST_LESS
						| BGFX_STATE_MSAA
						);

					// Submit primitive for rendering to view 0.
					bgfx::submit(RENDER_PASS_GEOMETRY_ID);
				}
			}

			// Draw lights into light buffer.
			for (int32_t light = 0; light < numLights; ++light)
			{
				Sphere lightPosRadius;

				float lightTime = time * lightAnimationSpeed * (sinf(light/float(numLights) * bx::piHalf ) * 0.5f + 0.5f);
				lightPosRadius.m_center[0] = sinf( ( (lightTime + light*0.47f) + bx::piHalf*1.37f ) )*offset;
				lightPosRadius.m_center[1] = cosf( ( (lightTime + light*0.69f) + bx::piHalf*1.49f ) )*offset;
				lightPosRadius.m_center[2] = sinf( ( (lightTime + light*0.37f) + bx::piHalf*1.57f ) )*2.0f;
				lightPosRadius.m_radius = 2.0f;

				Aabb aabb;
				sphereToAabb(aabb, lightPosRadius);

				float box[8][3] =
				{
					{ aabb.m_min[0], aabb.m_min[1], aabb.m_min[2] },
					{ aabb.m_min[0], aabb.m_min[1], aabb.m_max[2] },
					{ aabb.m_min[0], aabb.m_max[1], aabb.m_min[2] },
					{ aabb.m_min[0], aabb.m_max[1], aabb.m_max[2] },
					{ aabb.m_max[0], aabb.m_min[1], aabb.m_min[2] },
					{ aabb.m_max[0], aabb.m_min[1], aabb.m_max[2] },
					{ aabb.m_max[0], aabb.m_max[1], aabb.m_min[2] },
					{ aabb.m_max[0], aabb.m_max[1], aabb.m_max[2] },
				};

				float xyz[3];
				bx::vec3MulMtxH(xyz, box[0], vp);
				float minx = xyz[0];
				float miny = xyz[1];
				float maxx = xyz[0];
				float maxy = xyz[1];
				float maxz = xyz[2];

				for (uint32_t ii = 1; ii < 8; ++ii)
				{
					bx::vec3MulMtxH(xyz, box[ii], vp);
					minx = bx::fmin(minx, xyz[0]);
					miny = bx::fmin(miny, xyz[1]);
					maxx = bx::fmax(maxx, xyz[0]);
					maxy = bx::fmax(maxy, xyz[1]);
					maxz = bx::fmax(maxz, xyz[2]);
				}

				// Cull light if it's fully behind camera.
				if (maxz >= 0.0f)
				{
					float x0 = bx::fclamp( (minx * 0.5f + 0.5f) * width,  0.0f, (float)width);
					float y0 = bx::fclamp( (miny * 0.5f + 0.5f) * height, 0.0f, (float)height);
					float x1 = bx::fclamp( (maxx * 0.5f + 0.5f) * width,  0.0f, (float)width);
					float y1 = bx::fclamp( (maxy * 0.5f + 0.5f) * height, 0.0f, (float)height);

					if (showScissorRects)
					{
						bgfx::TransientVertexBuffer tvb;
						bgfx::TransientIndexBuffer tib;
						if (bgfx::allocTransientBuffers(&tvb, DebugVertex::ms_decl, 4, &tib, 8) )
						{
							uint32_t abgr = 0x8000ff00;

							DebugVertex* vertex = (DebugVertex*)tvb.data;
							vertex->m_x = x0;
							vertex->m_y = y0;
							vertex->m_z = 0.0f;
							vertex->m_abgr = abgr;
							++vertex;

							vertex->m_x = x1;
							vertex->m_y = y0;
							vertex->m_z = 0.0f;
							vertex->m_abgr = abgr;
							++vertex;

							vertex->m_x = x1;
							vertex->m_y = y1;
							vertex->m_z = 0.0f;
							vertex->m_abgr = abgr;
							++vertex;

							vertex->m_x = x0;
							vertex->m_y = y1;
							vertex->m_z = 0.0f;
							vertex->m_abgr = abgr;

							uint16_t* indices = (uint16_t*)tib.data;
							*indices++ = 0;
							*indices++ = 1;
							*indices++ = 1;
							*indices++ = 2;
							*indices++ = 2;
							*indices++ = 3;
							*indices++ = 3;
							*indices++ = 0;

							bgfx::setProgram(lineProgram);
							bgfx::setVertexBuffer(&tvb);
							bgfx::setIndexBuffer(&tib);
							bgfx::setState(0
								| BGFX_STATE_RGB_WRITE
								| BGFX_STATE_PT_LINES
								| BGFX_STATE_BLEND_ALPHA
								);
							bgfx::submit(RENDER_PASS_DEBUG_LIGHTS_ID);
						}
					}

					uint8_t val = light&7;
					float lightRgbInnerR[4] =
					{
						val & 0x1 ? 1.0f : 0.25f,
						val & 0x2 ? 1.0f : 0.25f,
						val & 0x4 ? 1.0f : 0.25f,
						0.8f,
					};

					// Draw light.
					bgfx::setUniform(u_lightPosRadius, &lightPosRadius);
					bgfx::setUniform(u_lightRgbInnerR, lightRgbInnerR);
					bgfx::setUniform(u_mtx, invMvp);
					const uint16_t scissorHeight = uint16_t(y1-y0);
					bgfx::setScissor(uint16_t(x0), height-scissorHeight-uint16_t(y0), uint16_t(x1-x0), scissorHeight);
					bgfx::setTexture(0, s_normal, gbuffer, 1);
					bgfx::setTexture(1, s_depth,  gbuffer, 2);
					bgfx::setProgram(lightProgram);
					bgfx::setState(0
						| BGFX_STATE_RGB_WRITE
						| BGFX_STATE_ALPHA_WRITE
						| BGFX_STATE_BLEND_ADD
						);
					screenSpaceQuad( (float)width, (float)height, texelHalf, s_originBottomLeft);
					bgfx::submit(RENDER_PASS_LIGHT_ID);
				}
			}

			// Combine color and light buffers.
			bgfx::setTexture(0, s_albedo, gbuffer,     0);
			bgfx::setTexture(1, s_light,  lightBuffer, 0);
			bgfx::setProgram(combineProgram);
			bgfx::setState(0
				| BGFX_STATE_RGB_WRITE
				| BGFX_STATE_ALPHA_WRITE
				);
			screenSpaceQuad( (float)width, (float)height, texelHalf, s_originBottomLeft);
			bgfx::submit(RENDER_PASS_COMBINE_ID);

			if (showGBuffer)
			{
				const float aspectRatio = float(width)/float(height);

				// Draw debug GBuffer.
				for (uint32_t ii = 0; ii < BX_COUNTOF(gbufferTex); ++ii)
				{
					float mtx[16];
					bx::mtxSRT(mtx
						, aspectRatio, 1.0f, 1.0f
						, 0.0f, 0.0f, 0.0f
						, -7.9f - BX_COUNTOF(gbufferTex)*0.1f*0.5f + ii*2.1f*aspectRatio, 4.0f, 0.0f
						);

					bgfx::setTransform(mtx);
					bgfx::setProgram(debugProgram);
					bgfx::setVertexBuffer(vbh);
					bgfx::setIndexBuffer(ibh, 0, 6);
					bgfx::setTexture(0, s_texColor, gbufferTex[ii]);
					bgfx::setState(BGFX_STATE_RGB_WRITE);
					bgfx::submit(RENDER_PASS_DEBUG_GBUFFER_ID);
				}
			}
		}

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

	// Cleanup.
	cameraDestroy();
	imguiDestroy();

	if (bgfx::isValid(gbuffer) )
	{
		bgfx::destroyFrameBuffer(gbuffer);
		bgfx::destroyFrameBuffer(lightBuffer);
	}

	bgfx::destroyIndexBuffer(ibh);
	bgfx::destroyVertexBuffer(vbh);

	bgfx::destroyProgram(geomProgram);
	bgfx::destroyProgram(lightProgram);
	bgfx::destroyProgram(combineProgram);
	bgfx::destroyProgram(debugProgram);
	bgfx::destroyProgram(lineProgram);

	bgfx::destroyTexture(textureColor);
	bgfx::destroyTexture(textureNormal);
	bgfx::destroyUniform(s_texColor);
	bgfx::destroyUniform(s_texNormal);

	bgfx::destroyUniform(s_albedo);
	bgfx::destroyUniform(s_normal);
	bgfx::destroyUniform(s_depth);
	bgfx::destroyUniform(s_light);

	bgfx::destroyUniform(u_lightPosRadius);
	bgfx::destroyUniform(u_lightRgbInnerR);
	bgfx::destroyUniform(u_mtx);

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

	return 0;
}
Example #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);

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

	// Get renderer capabilities info.
	const bgfx::Caps* caps = bgfx::getCaps();
	bool instancingSupported = 0 != (caps->supported & BGFX_CAPS_INSTANCING);

	// Create vertex stream declaration.
	PosNormalTangentTexcoordVertex::init();

	calcTangents(s_cubeVertices
		, BX_COUNTOF(s_cubeVertices)
		, PosNormalTangentTexcoordVertex::ms_decl
		, s_cubeIndices
		, BX_COUNTOF(s_cubeIndices)
		);

	// Create static vertex buffer.
	bgfx::VertexBufferHandle vbh = bgfx::createVertexBuffer(
		  bgfx::makeRef(s_cubeVertices, sizeof(s_cubeVertices) )
		, PosNormalTangentTexcoordVertex::ms_decl
		);

	// Create static index buffer.
	bgfx::IndexBufferHandle ibh = bgfx::createIndexBuffer(bgfx::makeRef(s_cubeIndices, sizeof(s_cubeIndices) ) );

	// Create texture sampler uniforms.
	bgfx::UniformHandle s_texColor  = bgfx::createUniform("s_texColor",  bgfx::UniformType::Int1);
	bgfx::UniformHandle s_texNormal = bgfx::createUniform("s_texNormal", bgfx::UniformType::Int1);

	uint16_t numLights = 4;
	bgfx::UniformHandle u_lightPosRadius = bgfx::createUniform("u_lightPosRadius", bgfx::UniformType::Vec4, numLights);
	bgfx::UniformHandle u_lightRgbInnerR = bgfx::createUniform("u_lightRgbInnerR", bgfx::UniformType::Vec4, numLights);

	// Create program from shaders.
	bgfx::ProgramHandle program = loadProgram(instancingSupported ? "vs_bump_instanced" : "vs_bump", "fs_bump");

	// Load diffuse texture.
	bgfx::TextureHandle textureColor = loadTexture("fieldstone-rgba.dds");

	// Load normal texture.
	bgfx::TextureHandle textureNormal = loadTexture("fieldstone-n.dds");

	int64_t timeOffset = bx::getHPCounter();

	while (!entry::processEvents(width, height, debug, reset) )
	{
		// Set view 0 default viewport.
		bgfx::setViewRect(0, 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 view 0.
		bgfx::touch(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;

		float time = (float)( (now-timeOffset)/freq);

		// Use debug font to print information about this example.
		bgfx::dbgTextClear();
		bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/06-bump");
		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Loading textures.");
		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, -7.0f };

		// Set view and projection matrix for view 0.
		const bgfx::HMD* hmd = bgfx::getHMD();
		if (NULL != hmd && 0 != (hmd->flags & BGFX_HMD_RENDERING) )
		{
			float view[16];
			bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);

			float proj[16];
			bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);

			bgfx::setViewTransform(0, view, proj);

			// Set view 0 default viewport.
			//
			// Use HMD's width/height since HMD's internal frame buffer size
			// might be much larger than window size.
			bgfx::setViewRect(0, 0, 0, hmd->width, hmd->height);
		}
		else
		{
			float view[16];
			bx::mtxLookAt(view, eye, at);

			float proj[16];
			bx::mtxProj(proj, 60.0f, float(width)/float(height), 0.1f, 100.0f);
			bgfx::setViewTransform(0, view, proj);

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

		float lightPosRadius[4][4];
		for (uint32_t ii = 0; ii < numLights; ++ii)
		{
			lightPosRadius[ii][0] = sinf( (time*(0.1f + ii*0.17f) + ii*bx::piHalf*1.37f ) )*3.0f;
			lightPosRadius[ii][1] = cosf( (time*(0.2f + ii*0.29f) + ii*bx::piHalf*1.49f ) )*3.0f;
			lightPosRadius[ii][2] = -2.5f;
			lightPosRadius[ii][3] = 3.0f;
		}

		bgfx::setUniform(u_lightPosRadius, lightPosRadius, numLights);

		float lightRgbInnerR[4][4] =
		{
			{ 1.0f, 0.7f, 0.2f, 0.8f },
			{ 0.7f, 0.2f, 1.0f, 0.8f },
			{ 0.2f, 1.0f, 0.7f, 0.8f },
			{ 1.0f, 0.4f, 0.2f, 0.8f },
		};

		bgfx::setUniform(u_lightRgbInnerR, lightRgbInnerR, numLights);

		const uint16_t instanceStride = 64;
		const uint16_t numInstances = 3;

		if (instancingSupported)
		{
			// Write instance data for 3x3 cubes.
			for (uint32_t yy = 0; yy < 3; ++yy)
			{
				const bgfx::InstanceDataBuffer* idb = bgfx::allocInstanceDataBuffer(numInstances, instanceStride);
				if (NULL != idb)
				{
					uint8_t* data = idb->data;

					for (uint32_t xx = 0; xx < 3; ++xx)
					{
						float* mtx = (float*)data;
						bx::mtxRotateXY(mtx, time*0.023f + xx*0.21f, time*0.03f + yy*0.37f);
						mtx[12] = -3.0f + float(xx)*3.0f;
						mtx[13] = -3.0f + float(yy)*3.0f;
						mtx[14] = 0.0f;

						data += instanceStride;
					}

					// Set instance data buffer.
					bgfx::setInstanceDataBuffer(idb, numInstances);

					// Set vertex and index buffer.
					bgfx::setVertexBuffer(vbh);
					bgfx::setIndexBuffer(ibh);

					// Bind textures.
					bgfx::setTexture(0, s_texColor, textureColor);
					bgfx::setTexture(1, s_texNormal, textureNormal);

					// Set render states.
					bgfx::setState(0
						| BGFX_STATE_RGB_WRITE
						| BGFX_STATE_ALPHA_WRITE
						| BGFX_STATE_DEPTH_WRITE
						| BGFX_STATE_DEPTH_TEST_LESS
						| BGFX_STATE_MSAA
						);

					// Submit primitive for rendering to view 0.
					bgfx::submit(0, program);
				}
			}
		}
		else
		{
			for (uint32_t yy = 0; yy < 3; ++yy)
			{
				for (uint32_t xx = 0; xx < 3; ++xx)
				{
					float mtx[16];
					bx::mtxRotateXY(mtx, time*0.023f + xx*0.21f, time*0.03f + yy*0.37f);
					mtx[12] = -3.0f + float(xx)*3.0f;
					mtx[13] = -3.0f + float(yy)*3.0f;
					mtx[14] = 0.0f;

					// Set transform for draw call.
					bgfx::setTransform(mtx);

					// Set vertex and index buffer.
					bgfx::setVertexBuffer(vbh);
					bgfx::setIndexBuffer(ibh);

					// Bind textures.
					bgfx::setTexture(0, s_texColor, textureColor);
					bgfx::setTexture(1, s_texNormal, textureNormal);

					// Set render states.
					bgfx::setState(0
						| BGFX_STATE_RGB_WRITE
						| BGFX_STATE_ALPHA_WRITE
						| BGFX_STATE_DEPTH_WRITE
						| BGFX_STATE_DEPTH_TEST_LESS
						| BGFX_STATE_MSAA
						);

					// Submit primitive for rendering to view 0.
					bgfx::submit(0, program);
				}
			}
		}

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

	// Cleanup.
	bgfx::destroyIndexBuffer(ibh);
	bgfx::destroyVertexBuffer(vbh);
	bgfx::destroyProgram(program);
	bgfx::destroyTexture(textureColor);
	bgfx::destroyTexture(textureNormal);
	bgfx::destroyUniform(s_texColor);
	bgfx::destroyUniform(s_texNormal);
	bgfx::destroyUniform(u_lightPosRadius);
	bgfx::destroyUniform(u_lightRgbInnerR);

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

	return 0;
}
Example #9
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/";
		break;

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

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

	const bgfx::Memory* mem;

	calcTangents(s_cubeVertices, countof(s_cubeVertices), s_PosNormalTangentTexcoordDecl, s_cubeIndices, countof(s_cubeIndices) );

	// Create static vertex buffer.
	mem = bgfx::makeRef(s_cubeVertices, sizeof(s_cubeVertices) );
	bgfx::VertexBufferHandle vbh = bgfx::createVertexBuffer(mem, s_PosNormalTangentTexcoordDecl);

	// Create static index buffer.
	mem = bgfx::makeRef(s_cubeIndices, sizeof(s_cubeIndices) );
	bgfx::IndexBufferHandle ibh = bgfx::createIndexBuffer(mem);

	// Create texture sampler uniforms.
	bgfx::UniformHandle u_texColor = bgfx::createUniform("u_texColor", bgfx::UniformType::Uniform1iv);
	bgfx::UniformHandle u_texNormal = bgfx::createUniform("u_texNormal", bgfx::UniformType::Uniform1iv);

	uint16_t numLights = 4;
	bgfx::UniformHandle u_lightPosRadius = bgfx::createUniform("u_lightPosRadius", bgfx::UniformType::Uniform4fv, numLights);
	bgfx::UniformHandle u_lightRgbInnerR = bgfx::createUniform("u_lightRgbInnerR", bgfx::UniformType::Uniform4fv, numLights);

	// Load vertex shader.
	mem = loadShader("vs_bump");
	bgfx::VertexShaderHandle vsh = bgfx::createVertexShader(mem);

	// Load fragment shader.
	mem = loadShader("fs_bump");
	bgfx::FragmentShaderHandle fsh = bgfx::createFragmentShader(mem);

	// Create program from shaders.
	bgfx::ProgramHandle program = bgfx::createProgram(vsh, fsh);

	// We can destroy vertex and fragment shader here since
	// their reference is kept inside bgfx after calling createProgram.
	// Vertex and fragment shader will be destroyed once program is^
	// destroyed.
	bgfx::destroyVertexShader(vsh);
	bgfx::destroyFragmentShader(fsh);

	// Load diffuse texture.
	mem = loadTexture("fieldstone-rgba.dds");
	bgfx::TextureHandle textureColor = bgfx::createTexture(mem);

	// Load normal texture.
	mem = loadTexture("fieldstone-n.dds");
	bgfx::TextureHandle textureNormal = bgfx::createTexture(mem);

	while (!processEvents(width, height, debug, reset) )
	{
		// Set view 0 default viewport.
		bgfx::setViewRect(0, 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 view 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;

		float time = (float)(now/freq);

		// Use debug font to print information about this example.
		bgfx::dbgTextClear();
		bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/06-bump");
		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Loading textures.");
		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, -7.0f };
		
		float view[16];
		float proj[16];
		mtxLookAt(view, eye, at);
		mtxProj(proj, 60.0f, 16.0f/9.0f, 0.1f, 100.0f);

		float lightPosRadius[4][4];
		for (uint32_t ii = 0; ii < numLights; ++ii)
		{
			lightPosRadius[ii][0] = sin( (time*(0.1f + ii*0.17f) + float(ii*M_PI_2)*1.37f ) )*3.0f;
			lightPosRadius[ii][1] = cos( (time*(0.2f + ii*0.29f) + float(ii*M_PI_2)*1.49f ) )*3.0f;
			lightPosRadius[ii][2] = -2.5f;
			lightPosRadius[ii][3] = 3.0f;
		}

		bgfx::setUniform(u_lightPosRadius, lightPosRadius, numLights);

		float lightRgbInnerR[4][4] =
		{
			{ 1.0f, 0.7f, 0.2f, 0.8f },
			{ 0.7f, 0.2f, 1.0f, 0.8f },
			{ 0.2f, 1.0f, 0.7f, 0.8f },
			{ 1.0f, 0.4f, 0.2f, 0.8f },
		};

		bgfx::setUniform(u_lightRgbInnerR, lightRgbInnerR, numLights);

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

		const uint16_t instanceStride = 64;
		const bgfx::InstanceDataBuffer* idb = bgfx::allocInstanceDataBuffer(9, instanceStride);
		if (NULL != idb)
		{
			uint8_t* data = idb->data;

			// Write instance data for 3x3 cubes.
			for (uint32_t yy = 0; yy < 3; ++yy)
			{
				for (uint32_t xx = 0; xx < 3; ++xx)
				{
					float* mtx = (float*)data;
					mtxRotateXY(mtx, time*0.023f + xx*0.21f, time*0.03f + yy*0.37f);
					mtx[12] = -3.0f + float(xx)*3.0f;
					mtx[13] = -3.0f + float(yy)*3.0f;
					mtx[14] = 0.0f;

					float* color = (float*)&data[64];
					color[0] = sin(time+float(xx)/11.0f)*0.5f+0.5f;
					color[1] = cos(time+float(yy)/11.0f)*0.5f+0.5f;
					color[2] = sin(time*3.0f)*0.5f+0.5f;
					color[3] = 1.0f;

					data += instanceStride;
				}
			}

			uint16_t numInstances = (uint16_t)( (data - idb->data)/instanceStride);

			// Set vertex and fragment shaders.
			bgfx::setProgram(program);

			// Set vertex and index buffer.
			bgfx::setVertexBuffer(vbh);
			bgfx::setIndexBuffer(ibh);

			// Set instance data buffer.
			bgfx::setInstanceDataBuffer(idb, numInstances);

			// Bind textures.
			bgfx::setTexture(0, u_texColor, textureColor);
			bgfx::setTexture(1, u_texNormal, textureNormal);

			// Set render states.
			bgfx::setState(0
				|BGFX_STATE_RGB_WRITE
				|BGFX_STATE_ALPHA_WRITE
				|BGFX_STATE_DEPTH_WRITE
				|BGFX_STATE_DEPTH_TEST_LESS
				|BGFX_STATE_MSAA
				);

			// Submit primitive for rendering to view 0.
			bgfx::submit(0);
		}

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

	// Cleanup.
	bgfx::destroyIndexBuffer(ibh);
	bgfx::destroyVertexBuffer(vbh);
	bgfx::destroyProgram(program);
	bgfx::destroyTexture(textureColor);
	bgfx::destroyTexture(textureNormal);
	bgfx::destroyUniform(u_texColor);
	bgfx::destroyUniform(u_texNormal);
	bgfx::destroyUniform(u_lightPosRadius);
	bgfx::destroyUniform(u_lightRgbInnerR);

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

	return 0;
}
Example #10
0
 void fCurve::getActualTangents(sInt i, sF32& dd0, sF32& ds1) const
 {
   calcTangents(m_keys.begin() + i, dd0, ds1);
 }
Example #11
0
int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	const char* filePath = cmdLine.findOption('f');
	if (NULL == filePath)
	{
		help("Input file name must be specified.");
		return EXIT_FAILURE;
	}

	const char* outFilePath = cmdLine.findOption('o');
	if (NULL == outFilePath)
	{
		help("Output file name must be specified.");
		return EXIT_FAILURE;
	}

	float scale = 1.0f;
	const char* scaleArg = cmdLine.findOption('s', "scale");
	if (NULL != scaleArg)
	{
		scale = (float)atof(scaleArg);
	}

	cmdLine.hasArg(s_obbSteps, '\0', "obb");
	s_obbSteps = bx::uint32_min(bx::uint32_max(s_obbSteps, 1), 90);

	uint32_t packNormal = 0;
	cmdLine.hasArg(packNormal, '\0', "packnormal");

	uint32_t packUv = 0;
	cmdLine.hasArg(packUv, '\0', "packuv");
	
	bool ccw = cmdLine.hasArg("ccw");
	bool flipV = cmdLine.hasArg("flipv");
	bool hasTangent = cmdLine.hasArg("tangent");

	FILE* file = fopen(filePath, "r");
	if (NULL == file)
	{
		printf("Unable to open input file '%s'.", filePath);
		exit(EXIT_FAILURE);
	}

	int64_t parseElapsed = -bx::getHPCounter();
	int64_t triReorderElapsed = 0;

	uint32_t size = (uint32_t)fsize(file);
	char* data = new char[size+1];
	size = (uint32_t)fread(data, 1, size, file);
	data[size] = '\0';
	fclose(file);

	// https://en.wikipedia.org/wiki/Wavefront_.obj_file

	Vector3Array positions;
	Vector3Array normals;
	Vector3Array texcoords;
	Index3Map indexMap;
	TriangleArray triangles;
	GroupArray groups;

	uint32_t num = 0;

	Group group;
	group.m_startTriangle = 0;
	group.m_numTriangles = 0;

	char commandLine[2048];
	uint32_t len = sizeof(commandLine);
	int argc;
	char* argv[64];
	const char* next = data;
	do
	{
		next = tokenizeCommandLine(next, commandLine, len, argc, argv, countof(argv), '\n');
		if (0 < argc)
		{
			if (0 == strcmp(argv[0], "#") )
			{
				if (2 < argc
				&&  0 == strcmp(argv[2], "polygons") )
				{
				}
			}
			else if (0 == strcmp(argv[0], "f") )
			{
				Triangle triangle;

				for (uint32_t edge = 0, numEdges = argc-1; edge < numEdges; ++edge)
				{
					Index3 index;
					index.m_texcoord = -1;
					index.m_normal = -1;
					index.m_vertexIndex = -1;

					char* vertex = argv[edge+1];
					char* texcoord = strchr(vertex, '/');
					if (NULL != texcoord)
					{
						*texcoord++ = '\0';

						char* normal = strchr(texcoord, '/');
						if (NULL != normal)
						{
							*normal++ = '\0';
							index.m_normal = atoi(normal)-1;
						}

						index.m_texcoord = atoi(texcoord)-1;
					}

					index.m_position = atoi(vertex)-1;

					uint64_t hash0 = index.m_position;
					uint64_t hash1 = uint64_t(index.m_texcoord)<<20;
					uint64_t hash2 = uint64_t(index.m_normal)<<40;
					uint64_t hash = hash0^hash1^hash2;

					std::pair<Index3Map::iterator, bool> result = indexMap.insert(std::make_pair(hash, index) );
					if (!result.second)
					{
						Index3& oldIndex = result.first->second;
						BX_UNUSED(oldIndex);
						BX_CHECK(oldIndex.m_position == index.m_position
							&& oldIndex.m_texcoord == index.m_texcoord
							&& oldIndex.m_normal == index.m_normal
							, "Hash collision!"
							);
					}

					switch (edge)
					{
					case 0:
					case 1:
					case 2:
						triangle.m_index[edge] = hash;
						if (2 == edge)
						{
							if (ccw)
							{
								std::swap(triangle.m_index[1], triangle.m_index[2]);
							}
							triangles.push_back(triangle);
						}
						break;

					default:
						if (ccw)
						{
							triangle.m_index[2] = triangle.m_index[1];
							triangle.m_index[1] = hash;
						}
						else
						{
							triangle.m_index[1] = triangle.m_index[2];
							triangle.m_index[2] = hash;
						}
						triangles.push_back(triangle);
						break;
					}
				}
			}
			else if (0 == strcmp(argv[0], "g") )
			{
				EXPECT(1 < argc);
				group.m_name = argv[1];
			}
			else if (*argv[0] == 'v')
			{
				group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
				if (0 < group.m_numTriangles)
				{
					groups.push_back(group);
					group.m_startTriangle = (uint32_t)(triangles.size() );
					group.m_numTriangles = 0;
				}

				if (0 == strcmp(argv[0], "vn") )
				{
					Vector3 normal;
					normal.x = (float)atof(argv[1]);
					normal.y = (float)atof(argv[2]);
					normal.z = (float)atof(argv[3]);

					normals.push_back(normal);
				}
				else if (0 == strcmp(argv[0], "vp") )
				{
					static bool once = true;
					if (once)
					{
						once = false;
						printf("warning: 'parameter space vertices' are unsupported.\n");
					}
				}
				else if (0 == strcmp(argv[0], "vt") )
				{
					Vector3 texcoord;
					texcoord.x = (float)atof(argv[1]);
					texcoord.y = 0.0f;
					texcoord.z = 0.0f;
					switch (argc)
					{
					case 4:
						texcoord.z = (float)atof(argv[3]);
						// fallthrough
					case 3:
						texcoord.y = (float)atof(argv[2]);
						break;

					default:
						break;
					}

					texcoords.push_back(texcoord);
				}
				else
				{
					float px = (float)atof(argv[1]);
					float py = (float)atof(argv[2]);
					float pz = (float)atof(argv[3]);
					float pw = 1.0f;
					if (argc > 4)
					{
						pw = (float)atof(argv[4]);
					}

					float invW = scale/pw;
					px *= invW;
					py *= invW;
					pz *= invW;

					Vector3 pos;
					pos.x = px;
					pos.y = py;
					pos.z = pz;

					positions.push_back(pos);
				}
			}
			else if (0 == strcmp(argv[0], "usemtl") )
			{
				std::string material(argv[1]);

				if (material != group.m_material)
				{
					group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
					if (0 < group.m_numTriangles)
					{
						groups.push_back(group);
						group.m_startTriangle = (uint32_t)(triangles.size() );
						group.m_numTriangles = 0;
					}
				}

				group.m_material = material;
			}
// unsupported tags
// 				else if (0 == strcmp(argv[0], "mtllib") )
// 				{
// 				}
// 				else if (0 == strcmp(argv[0], "o") )
// 				{
// 				}
// 				else if (0 == strcmp(argv[0], "s") )
// 				{
// 				}
		}

		++num;
	}
	while ('\0' != *next);

	group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
	if (0 < group.m_numTriangles)
	{
		groups.push_back(group);
		group.m_startTriangle = (uint32_t)(triangles.size() );
		group.m_numTriangles = 0;
	}

	delete [] data;

	int64_t now = bx::getHPCounter();
	parseElapsed += now;
	int64_t convertElapsed = -now;

	struct GroupSortByMaterial
	{
		bool operator()(const Group& _lhs, const Group& _rhs)
		{
			return _lhs.m_material < _rhs.m_material;
		}
	};

	std::sort(groups.begin(), groups.end(), GroupSortByMaterial() );

	bool hasColor = false;
	bool hasNormal;
	bool hasTexcoord;
	{
		Index3Map::const_iterator it = indexMap.begin();
		hasNormal = -1 != it->second.m_normal;
		hasTexcoord = -1 != it->second.m_texcoord;

		if (!hasTexcoord
		&&  texcoords.size() == positions.size() )
		{
			hasTexcoord = true;

			for (Index3Map::iterator it = indexMap.begin(), itEnd = indexMap.end(); it != itEnd; ++it)
			{
				it->second.m_texcoord = it->second.m_position;
			}
		}

		if (!hasNormal
		&&  normals.size() == positions.size() )
		{
			hasNormal = true;

			for (Index3Map::iterator it = indexMap.begin(), itEnd = indexMap.end(); it != itEnd; ++it)
			{
				it->second.m_normal = it->second.m_position;
			}
		}
	}

	bgfx::VertexDecl decl;
	decl.begin();
	decl.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);

	if (hasColor)
	{
		decl.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
	}

	if (hasTexcoord)
	{
		switch (packUv)
		{
		default:
		case 0:
			decl.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
			break;

		case 1:
			decl.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Half);
			break;
		}
	}

	if (hasNormal)
	{
		hasTangent &= hasTexcoord;

		switch (packNormal)
		{
		default:
		case 0:
			decl.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float);
			if (hasTangent)
			{
				decl.add(bgfx::Attrib::Tangent, 4, bgfx::AttribType::Float);
			}
			break;

		case 1:
			decl.add(bgfx::Attrib::Normal, 4, bgfx::AttribType::Uint8, true, true);
			if (hasTangent)
			{
				decl.add(bgfx::Attrib::Tangent, 4, bgfx::AttribType::Uint8, true, true);
			}
			break;
		}
	}
	decl.end();

	uint32_t stride = decl.getStride();
	uint8_t* vertexData = new uint8_t[triangles.size() * 3 * stride];
	uint16_t* indexData = new uint16_t[triangles.size() * 3];
	int32_t numVertices = 0;
	int32_t numIndices = 0;
	int32_t numPrimitives = 0;

	uint8_t* vertices = vertexData;
	uint16_t* indices = indexData;

	std::string material = groups.begin()->m_material;

	PrimitiveArray primitives;

	bx::CrtFileWriter writer;
	if (0 != writer.open(outFilePath) )
	{
		printf("Unable to open output file '%s'.", outFilePath);
		exit(EXIT_FAILURE);
	}

	Primitive prim;
	prim.m_startVertex = 0;
	prim.m_startIndex = 0;

	uint32_t positionOffset = decl.getOffset(bgfx::Attrib::Position);
	uint32_t color0Offset = decl.getOffset(bgfx::Attrib::Color0);

	uint32_t ii = 0;
	for (GroupArray::const_iterator groupIt = groups.begin(); groupIt != groups.end(); ++groupIt, ++ii)
	{
		for (uint32_t tri = groupIt->m_startTriangle, end = tri + groupIt->m_numTriangles; tri < end; ++tri)
		{
			if (material != groupIt->m_material
			||  65533 < numVertices)
			{
				prim.m_numVertices = numVertices - prim.m_startVertex;
				prim.m_numIndices = numIndices - prim.m_startIndex;
				if (0 < prim.m_numVertices)
				{
					primitives.push_back(prim);
				}

				triReorderElapsed -= bx::getHPCounter();
				for (PrimitiveArray::const_iterator primIt = primitives.begin(); primIt != primitives.end(); ++primIt)
				{
					const Primitive& prim = *primIt;
					triangleReorder(indexData + prim.m_startIndex, prim.m_numIndices, numVertices, 32);
				}
				triReorderElapsed += bx::getHPCounter();

				if (hasTangent)
				{
					calcTangents(vertexData, numVertices, decl, indexData, numIndices);
				}

				write(&writer, vertexData, numVertices, decl, indexData, numIndices, material, primitives);
				primitives.clear();

				for (Index3Map::iterator indexIt = indexMap.begin(); indexIt != indexMap.end(); ++indexIt)
				{
					indexIt->second.m_vertexIndex = -1;
				}

				vertices = vertexData;
				indices = indexData;
				numVertices = 0;
				numIndices = 0;
				prim.m_startVertex = 0;
				prim.m_startIndex = 0;
				++numPrimitives;

				material = groupIt->m_material;
			}

			Triangle& triangle = triangles[tri];
			for (uint32_t edge = 0; edge < 3; ++edge)
			{
				uint64_t hash = triangle.m_index[edge];
				Index3& index = indexMap[hash];
				if (index.m_vertexIndex == -1)
				{
		 			index.m_vertexIndex = numVertices++;

					float* position = (float*)(vertices + positionOffset);
					memcpy(position, &positions[index.m_position], 3*sizeof(float) );

					if (hasColor)
					{
						uint32_t* color0 = (uint32_t*)(vertices + color0Offset);
						*color0 = rgbaToAbgr(numVertices%255, numIndices%255, 0, 0xff);
					}

					if (hasTexcoord)
					{
						float uv[2];
						memcpy(uv, &texcoords[index.m_texcoord], 2*sizeof(float) );

						if (flipV)
						{
							uv[1] = -uv[1];
						}

						bgfx::vertexPack(uv, true, bgfx::Attrib::TexCoord0, decl, vertices);
					}

					if (hasNormal)
					{
						float normal[4];
						vec3Norm(normal, (float*)&normals[index.m_normal]);
						bgfx::vertexPack(normal, true, bgfx::Attrib::Normal, decl, vertices);
					}

					vertices += stride;
				}

				*indices++ = (uint16_t)index.m_vertexIndex;
				++numIndices;
			}
		}

		if (0 < numVertices)
		{
			prim.m_numVertices = numVertices - prim.m_startVertex;
			prim.m_numIndices = numIndices - prim.m_startIndex;
			prim.m_name = groupIt->m_name;
			primitives.push_back(prim);
			prim.m_startVertex = numVertices;
			prim.m_startIndex = numIndices;
		}

		BX_TRACE("%3d: s %5d, n %5d, %s\n"
			, ii
			, groupIt->m_startTriangle
			, groupIt->m_numTriangles
			, groupIt->m_material.c_str()
			);
	}

	if (0 < primitives.size() )
	{
		triReorderElapsed -= bx::getHPCounter();
		for (PrimitiveArray::const_iterator primIt = primitives.begin(); primIt != primitives.end(); ++primIt)
		{
			const Primitive& prim = *primIt;
			triangleReorder(indexData + prim.m_startIndex, prim.m_numIndices, numVertices, 32);
		}
		triReorderElapsed += bx::getHPCounter();

		if (hasTangent)
		{
			calcTangents(vertexData, numVertices, decl, indexData, numIndices);
		}

		write(&writer, vertexData, numVertices, decl, indexData, numIndices, material, primitives);
	}

	printf("size: %d\n", uint32_t(writer.seek() ) );
	writer.close();

	delete [] indexData;
	delete [] vertexData;

	now = bx::getHPCounter();
	convertElapsed += now;

	printf("parse %f [s]\ntri reorder %f [s]\nconvert %f [s]\n# %d, g %d, p %d, v %d, i %d\n"
		, double(parseElapsed)/bx::getHPFrequency()
		, double(triReorderElapsed)/bx::getHPFrequency()
		, double(convertElapsed)/bx::getHPFrequency()
		, num
		, uint32_t(groups.size() )
		, numPrimitives
		, numVertices
		, numIndices
		);

	return EXIT_SUCCESS;
}