aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp')
-rw-r--r--PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp2107
1 files changed, 2107 insertions, 0 deletions
diff --git a/PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp b/PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp
new file mode 100644
index 00000000..3c59cfef
--- /dev/null
+++ b/PhysX_3.4/Samples/SampleFramework/renderer/src/Renderer.cpp
@@ -0,0 +1,2107 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+#include "RendererFoundation.h"
+#include <Renderer.h>
+#include <RendererDesc.h>
+#include "ogl/OGLRenderer.h"
+#include "gles2/GLES2Renderer.h"
+#include "d3d9/D3D9Renderer.h"
+#if defined(RENDERER_ENABLE_LIBGNM)
+#include "ps4/GnmRenderer.h"
+#endif
+#include "d3d11/D3D11Renderer.h"
+#include "null/NULLRenderer.h"
+#include <RendererMesh.h>
+#include <RendererMeshContext.h>
+
+#include <RendererMaterial.h>
+#include <RendererMaterialDesc.h>
+#include <RendererMaterialInstance.h>
+#include <RendererProjection.h>
+
+#include <RendererTarget.h>
+#include <RendererTexture2D.h>
+#include <RendererTexture2DDesc.h>
+
+#include <RendererVertexBuffer.h>
+#include <RendererVertexBufferDesc.h>
+#include <RendererMesh.h>
+#include <RendererMeshDesc.h>
+
+#include <RendererLight.h>
+#include "PsUtilities.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "PsUtilities.h"
+#include "extensions/PxDefaultStreams.h"
+
+#if defined(RENDERER_ANDROID)
+#include <android/AndroidSampleUserInputIds.h>
+#endif
+
+// for PsString.h
+#include <PsString.h>
+#include <PxTkFile.h>
+
+namespace Ps = physx::shdfnd;
+
+using namespace SampleRenderer;
+
+
+/* Screen pads for tablets.
+ Initialize two controls - second one is the vertically mirrored version of the first one with respect to the screen center
+ All values are in relative units, given that bottom-left point is (-1.0f, -1.0f), center is (0.0f, 0.0f) and top-right point is (1.0f, 1.0f) */
+#if defined(RENDERER_TABLET)
+const PxReal gControlSizeRelative = 0.16f;
+const PxReal gControlMarginRelative = 0.08f;
+const PxU8 CONTROL_COUNT = 2;
+
+const PxU8 TEXT_HEIGHT = 16;
+const PxU8 TEXT_VERTICAL_SPACING = TEXT_HEIGHT / 4;
+#endif
+
+Renderer *Renderer::createRenderer(const RendererDesc &desc, const char* assetDir, bool enableMaterialCaching)
+{
+ Renderer *renderer = 0;
+ const bool valid = desc.isValid();
+ if(valid)
+ {
+ switch(desc.driver)
+ {
+ case DRIVER_GLES2:
+#if defined(RENDERER_ENABLE_GLES2)
+ renderer = new GLES2Renderer(desc, assetDir);
+ // disable material caching, not implemented!
+ enableMaterialCaching = false;
+#endif
+ break;
+
+ case DRIVER_OPENGL:
+#if defined(RENDERER_ENABLE_OPENGL)
+ renderer = new OGLRenderer(desc, assetDir);
+#endif
+ break;
+
+ case DRIVER_DIRECT3D9:
+#if defined(RENDERER_ENABLE_DIRECT3D9)
+ renderer = new D3D9Renderer(desc, assetDir);
+#endif
+ break;
+
+ case DRIVER_DIRECT3D11:
+#if defined(RENDERER_ENABLE_DIRECT3D11)
+ renderer = new D3D11Renderer(desc, assetDir);
+#endif
+ break;
+
+ case DRIVER_LIBGCM:
+#if defined(RENDERER_ENABLE_LIBGCM)
+ // not implemented, PS3 should use OpenGL
+#endif
+ break;
+
+ case DRIVER_LIBGNM:
+#if defined(RENDERER_ENABLE_LIBGNM)
+ renderer = new GnmRenderer(desc, assetDir);
+ // disable material caching, not implemented!
+ enableMaterialCaching = false;
+#endif
+ break;
+
+ case DRIVER_NULL:
+ renderer = new NullRenderer(desc,assetDir);
+ break;
+ }
+
+ if(renderer)
+ {
+ renderer->setEnableMaterialCaching(enableMaterialCaching);
+ }
+ }
+ if(renderer && !renderer->isOk())
+ {
+ renderer->release();
+ renderer = 0;
+ }
+ RENDERER_ASSERT(renderer, "Failed to create renderer!");
+ return renderer;
+}
+
+const char *Renderer::getDriverTypeName(DriverType type)
+{
+ const char *name = 0;
+ switch(type)
+ {
+ case DRIVER_OPENGL: name = "OpenGL"; break;
+ case DRIVER_GLES2: name = "OpenGL ES 2.0"; break;
+ case DRIVER_DIRECT3D9: name = "Direct3D9"; break;
+ case DRIVER_DIRECT3D11: name = "Direct3D11"; break;
+ case DRIVER_LIBGCM: name = "LibGCM"; break;
+ case DRIVER_LIBGNM: name = "LibGNM"; break;
+ case DRIVER_NULL: name = "NullRenderer"; break;
+ }
+ RENDERER_ASSERT(name, "Unable to find Name String for Renderer Driver Type.");
+ return name;
+}
+
+
+Renderer::Renderer(DriverType driver, PxErrorCallback* errorCallback, const char* assetDir) :
+ m_driver (driver),
+ m_errorCallback (errorCallback),
+ m_textMaterial (NULL),
+ m_textMaterialInstance (NULL),
+ m_screenquadOpaqueMaterial (NULL),
+ m_screenquadOpaqueMaterialInstance (NULL),
+ m_screenquadAlphaMaterial (NULL),
+ m_screenquadAlphaMaterialInstance (NULL),
+ m_useShadersForTextRendering (true),
+ m_assetDir (assetDir),
+ m_cacheDir (NULL),
+ m_enableTessellation (false),
+ m_enableWireframe (false),
+ m_enableBlendingOverride (false),
+ m_enableBlendingCull (false),
+ mEnableMaterialCaching (true)
+{
+ m_pixelCenterOffset = 0;
+ setAmbientColor(RendererColor(64,64,64, 255));
+ setFog(RendererColor(0,0,10,255), 20000.0f);
+ setClearColor(RendererColor(133,153,181,255));
+ Ps::strlcpy(m_deviceName, sizeof(m_deviceName), "UNKNOWN");
+#ifdef RENDERER_TABLET
+ m_buttons.reserve(16);
+#endif
+}
+
+Renderer::~Renderer(void)
+{
+ PX_ASSERT(!m_screenquadOpaqueMaterial);
+ PX_ASSERT(!m_screenquadOpaqueMaterialInstance);
+ PX_ASSERT(!m_screenquadAlphaMaterial);
+ PX_ASSERT(!m_screenquadAlphaMaterialInstance);
+
+ PX_ASSERT(!m_textMaterial);
+ PX_ASSERT(!m_textMaterialInstance);
+
+ for (tMaterialCache::iterator it = m_materialCache.begin(); it != m_materialCache.end(); ++it)
+ {
+ ::free((void*)it->first.vertexShaderPath);
+ ::free((void*)it->first.fragmentShaderPath);
+ ::free((void*)it->first.geometryShaderPath);
+ ::free((void*)it->first.domainShaderPath);
+ ::free((void*)it->first.hullShaderPath);
+
+ PX_ASSERT(it->second == NULL);
+ }
+}
+
+
+void Renderer::release(void)
+{
+ closeScreenquad();
+ delete this;
+}
+
+// Create a 2D or 3D texture, depending on texture depth
+RendererTexture* SampleRenderer::Renderer::createTexture(const RendererTextureDesc &desc)
+{
+ return desc.depth > 1 ? createTexture3D(desc) : createTexture2D(desc);
+}
+
+// get the driver type for this renderer.
+Renderer::DriverType Renderer::getDriverType(void) const
+{
+ return m_driver;
+}
+
+// get the offset to the center of a pixel relative to the size of a pixel (so either 0 or 0.5).
+PxF32 Renderer::getPixelCenterOffset(void) const
+{
+ return m_pixelCenterOffset;
+}
+
+// get the name of the hardware device.
+const char *Renderer::getDeviceName(void) const
+{
+ return m_deviceName;
+}
+
+const char *Renderer::getAssetDir()
+{
+ return m_assetDir.c_str();
+}
+
+void Renderer::setAssetDir(const char * assetDir)
+{
+ m_assetDir = assetDir;
+}
+
+// adds a mesh to the render queue.
+void Renderer::queueMeshForRender(RendererMeshContext &mesh)
+{
+ RENDERER_ASSERT( mesh.isValid(), "Mesh Context is invalid.");
+ if(mesh.isValid())
+ {
+ if (mesh.screenSpace)
+ {
+ m_screenSpaceMeshes.push_back(mesh);
+ }
+ else
+ {
+ switch (mesh.material->getType())
+ {
+ case RendererMaterial::TYPE_LIT:
+ if (mesh.material->getBlending())
+ m_visibleLitTransparentMeshes.push_back(mesh);
+ else
+ m_visibleLitMeshes.push_back(mesh);
+ break;
+ default: //case RendererMaterial::TYPE_UNLIT:
+ m_visibleUnlitMeshes.push_back(mesh);
+ // break;
+ }
+ }
+ }
+}
+
+struct RenderMeshContextHasMesh
+{
+ RenderMeshContextHasMesh(const RendererMesh* mesh) : mMesh(mesh) { }
+
+ bool operator()(const RendererMeshContext& meshContext)
+ {
+ return meshContext.mesh == mMesh;
+ }
+
+ const RendererMesh* mMesh;
+};
+
+void SampleRenderer::Renderer::removeMeshFromRenderQueue(RendererMesh& mesh)
+{
+ MeshVector* meshContextQueues[] = { &m_visibleLitMeshes,
+ &m_visibleUnlitMeshes,
+ &m_screenSpaceMeshes,
+ &m_visibleLitTransparentMeshes };
+
+ // All queues must be searched, as a single mesh can be used in multiple contexts
+ for (PxU32 i = 0; i < PX_ARRAY_SIZE(meshContextQueues); ++i)
+ {
+ MeshVector& meshContextQueue = *meshContextQueues[i];
+ meshContextQueue.erase(std::remove_if(meshContextQueue.begin(),
+ meshContextQueue.end(),
+ RenderMeshContextHasMesh(&mesh)),
+ meshContextQueue.end());
+ }
+}
+
+// adds a light to the render queue.
+void Renderer::queueLightForRender(RendererLight &light)
+{
+ RENDERER_ASSERT(!light.isLocked(), "Light is already locked to a Renderer.");
+ if(!light.isLocked())
+ {
+ light.m_renderer = this;
+ m_visibleLights.push_back(&light);
+ }
+}
+
+void SampleRenderer::Renderer::removeLightFromRenderQueue(RendererLight &light)
+{
+ m_visibleLights.erase(std::remove(m_visibleLights.begin(), m_visibleLights.end(), &light));
+}
+
+// renders the current scene to the offscreen buffers. empties the render queue when done.
+void Renderer::render(const physx::PxMat44 &eye, const RendererProjection &proj, RendererTarget *target, bool depthOnly)
+{
+ PX_PROFILE_ZONE("Renderer_render",0);
+ const PxU32 numLights = (PxU32)m_visibleLights.size();
+ if(target)
+ {
+ target->bind();
+ }
+ // TODO: Sort meshes by material..
+ ScopedRender renderSection(*this);
+ if(renderSection)
+ {
+ if(!depthOnly)
+ {
+ // YOU CAN PASS THE PROJECTION MATIX RIGHT INTO THIS FUNCTION!
+ // TODO: Get rid of this.
+ if (m_screenSpaceMeshes.size())
+ {
+ physx::PxMat44 id = physx::PxMat44(PxIdentity);
+ bindViewProj(id, RendererProjection(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f)); //TODO: pass screen space matrices
+ renderMeshes(m_screenSpaceMeshes, RendererMaterial::PASS_UNLIT); //render screen space stuff first so stuff we occlude doesn't waste time on shading.
+ }
+ }
+
+ sortMeshes(eye);
+
+ if(depthOnly)
+ {
+ PX_PROFILE_ZONE("Renderer_render_depthOnly",0);
+ bindAmbientState(RendererColor(0,0,0,255));
+ bindViewProj(eye, proj);
+ renderMeshes(m_visibleLitMeshes, RendererMaterial::PASS_DEPTH);
+ renderMeshes(m_visibleUnlitMeshes, RendererMaterial::PASS_DEPTH);
+ }
+ else if(numLights > RENDERER_DEFERRED_THRESHOLD)
+ {
+ PX_PROFILE_ZONE("Renderer_render_deferred",0);
+ bindDeferredState();
+ bindViewProj(eye, proj);
+ renderMeshes(m_visibleLitMeshes, RendererMaterial::PASS_UNLIT);
+ renderMeshes(m_visibleUnlitMeshes, RendererMaterial::PASS_UNLIT);
+ renderMeshes(m_visibleLitTransparentMeshes, RendererMaterial::PASS_UNLIT);
+ renderDeferredLights();
+ }
+ else if(numLights > 0)
+ {
+ PX_PROFILE_ZONE("Renderer_render_lit",0);
+ bindAmbientState(m_ambientColor);
+ bindFogState(m_fogColor, m_fogDistance);
+ bindViewProj(eye, proj);
+
+#if RENDERER_ENABLE_SINGLE_PASS_LIGHTING
+ for(PxU32 i=0; i<numLights; i++)
+ {
+ m_visibleLights[i]->bind(i);
+ }
+ renderMeshes(m_visibleLitMeshes, m_visibleLights[0]->getPass());
+ for(PxU32 i=0; i<numLights; i++)
+ {
+ m_visibleLights[i]->m_renderer = NULL;
+ }
+#else
+ RendererLight &light0 = *m_visibleLights[0];
+ light0.bind();
+ renderMeshes(m_visibleLitMeshes, light0.getPass());
+
+ bindAmbientState(RendererColor(0,0,0,255));
+ beginMultiPass();
+ for(PxU32 i=1; i<numLights; i++)
+ {
+ RendererLight &light = *m_visibleLights[i];
+ light.bind();
+ renderMeshes(m_visibleLitMeshes, light.getPass());
+ }
+ endMultiPass();
+#endif
+ renderMeshes(m_visibleUnlitMeshes, RendererMaterial::PASS_UNLIT);
+
+ ///////////////////////////////////////////////////////////////////////////
+ if (m_visibleLitTransparentMeshes.size() > 0)
+ {
+ light0.bind();
+ renderMeshes(m_visibleLitTransparentMeshes, light0.getPass());
+
+ bindAmbientState(RendererColor(0,0,0,255));
+ beginTransparentMultiPass();
+ for(PxU32 i=1; i<numLights; i++)
+ {
+ RendererLight &light = *m_visibleLights[i];
+ light.bind();
+ renderMeshes(m_visibleLitTransparentMeshes, light.getPass());
+ }
+ endTransparentMultiPass();
+ }
+ ///////////////////////////////////////////////////////////////////////////
+
+ for (physx::PxU32 i = 0; i < numLights; ++i)
+ m_visibleLights[i]->m_renderer = 0;
+ }
+ else
+ {
+ PX_PROFILE_ZONE("Renderer_render_unlit",0);
+ bindAmbientState(RendererColor(0,0,0,255));
+ bindViewProj(eye, proj);
+ renderMeshes(m_visibleLitMeshes, RendererMaterial::PASS_UNLIT);
+ renderMeshes(m_visibleUnlitMeshes, RendererMaterial::PASS_UNLIT);
+ renderMeshes(m_visibleLitTransparentMeshes, RendererMaterial::PASS_UNLIT);
+ }
+ }
+ {
+ PX_PROFILE_ZONE("Cleanup", 0);
+ if (target) target->unbind();
+ m_visibleLitMeshes.clear();
+ m_visibleUnlitMeshes.clear();
+ m_visibleLitTransparentMeshes.clear();
+ m_screenSpaceMeshes.clear();
+ m_visibleLights.clear();
+ }
+}
+
+// sets the ambient lighting color.
+void Renderer::setAmbientColor(const RendererColor &ambientColor)
+{
+ m_ambientColor = ambientColor;
+ m_ambientColor.a = 255;
+}
+
+void Renderer::setFog(const RendererColor &fogColor, float fogDistance)
+{
+ m_fogColor = fogColor;
+ m_fogDistance = fogDistance;
+}
+
+void Renderer::setClearColor(const RendererColor &clearColor)
+{
+ m_clearColor = clearColor;
+ m_clearColor.a = 255;
+}
+
+Renderer::TessellationParams::TessellationParams()
+{
+ setDefault();
+}
+
+void Renderer::TessellationParams::setDefault()
+{
+ tessFactor = PxVec4(6.f, 6.f, 3.f, 100.f);
+ tessMinMaxDistance[0] = 5.0f;
+ tessMinMaxDistance[1] = 50;
+ tessHeightScaleAndBias[0] = 1.0f;
+ tessHeightScaleAndBias[1] = 0.5f;
+ tessUVScale[0] = 1.0f;
+ tessUVScale[1] = 1.0f;
+}
+
+std::string SampleRenderer::Renderer::TessellationParams::toString()
+{
+ std::stringstream ss;
+ ss << "TessParams = " << std::endl;
+ ss << "\tTessFactor: " << tessFactor.x << " " << tessFactor.y << " " << tessFactor.z << " " << tessFactor.w << std::endl;
+ ss << "\tTessMinMax: " << tessMinMaxDistance[0] << " " << tessMinMaxDistance[1] << std::endl;
+ ss << "\tTessHeightScale: " << tessHeightScaleAndBias[0] << " " << tessHeightScaleAndBias[1] << std::endl;
+ ss << "\tTessUVScale: " << tessUVScale[0] << " " << tessUVScale[1] << std::endl;
+ return ss.str();
+}
+
+void Renderer::setTessellationParams(const TessellationParams& params)
+{
+ m_tessellationParams = params;
+}
+
+static PX_INLINE PxU32 createPixel(PxU8 r, PxU8 g, PxU8 b)
+{
+ // G = bits 0-7
+ // B = bits 8-15
+ // R = bits 16-23
+ return (r << 16) | (g << 8) | b;
+}
+
+void Renderer::formatScreenshot(PxU32 width, PxU32 height, PxU32 sizeInBytes, int rPosition, int gPosition, int bPosition, bool bFlipY, void* screenshotData)
+{
+ if (width <= 0 || height <= 0 || sizeInBytes <= 0)
+ return;
+
+ PxU32 srcByteStride = sizeInBytes / (width * height);
+ RENDERER_ASSERT((srcByteStride > 0) && (srcByteStride <= sizeof(PxU32)), "Invalid image format.");
+ if (srcByteStride <= 0 || srcByteStride > sizeof(PxU32))
+ return;
+
+ PxU32* pSrc = static_cast<PxU32*>(screenshotData);
+ PxU8* pSrcByte = static_cast<PxU8*>(screenshotData);
+ if (bFlipY)
+ {
+ for (PxU32 i = 0; i < height/2; ++i)
+ {
+ PxU32* pSrc2 = static_cast<PxU32*>(screenshotData) + width * (height - i - 1);
+ PxU8* pSrc2Byte = static_cast<PxU8*>(screenshotData) + srcByteStride * width * (height - i - 1);
+ for (PxU32 j = 0; j < width; ++j, pSrcByte+=srcByteStride, pSrc2Byte+=srcByteStride, ++pSrc, ++pSrc2)
+ {
+ PxU32 temp = createPixel(pSrcByte[rPosition], pSrcByte[gPosition], pSrcByte[bPosition]);
+ *pSrc = createPixel(pSrc2Byte[rPosition], pSrc2Byte[gPosition], pSrc2Byte[bPosition]);
+ *pSrc2 = temp;
+ }
+ }
+ // Format the middle scanline
+ if (height % 2 == 1)
+ {
+ for (PxU32 j = 0; j < width; ++j, pSrcByte+=srcByteStride,++pSrc)
+ {
+ *pSrc = createPixel(pSrcByte[rPosition], pSrcByte[gPosition], pSrcByte[bPosition]);
+ }
+ }
+ }
+ else
+ {
+ for (PxU32 i = 0; i < height; ++i)
+ {
+ for (PxU32 j = 0; j < width; ++j, pSrcByte+=srcByteStride,++pSrc)
+ {
+ *pSrc = createPixel(pSrcByte[rPosition], pSrcByte[gPosition], pSrcByte[bPosition]);
+ }
+ }
+ }
+}
+
+void Renderer::renderMeshes(std::vector<RendererMeshContext>& meshes, RendererMaterial::Pass pass)
+{
+ PX_PROFILE_ZONE("Renderer_renderMeshes",0);
+
+ RendererMaterial *lastMaterial = 0;
+ RendererMaterialInstance *lastMaterialInstance = 0;
+ const RendererMesh *lastMesh = 0;
+
+ const PxU32 numMeshes = (PxU32)meshes.size();
+ for(PxU32 i=0; i<numMeshes; i++)
+ {
+ RendererMeshContext& context = meshes[i];
+
+ if(!context.mesh->willRender())
+ {
+ continue;
+ }
+
+ // the mesh context should be bound before the material, this is
+ // necessary because the materials read state from the graphics device
+ // for instance to determine if two-sided lighting should be enabled
+ // for gles 2 this doesn't work yet due to internal hacks
+#if !defined(RENDERER_ENABLE_GLES2)
+ bindMeshContext(context);
+#endif
+
+ bool instanced = context.mesh->getInstanceBuffer()?true:false;
+
+ if(context.materialInstance && context.materialInstance != lastMaterialInstance)
+ {
+ if(lastMaterial) lastMaterial->unbind();
+ lastMaterialInstance = context.materialInstance;
+ lastMaterial = &context.materialInstance->getMaterial();
+ lastMaterial->bind(pass, lastMaterialInstance, instanced);
+ }
+ else if(context.material != lastMaterial)
+ {
+ if(lastMaterial) lastMaterial->unbind();
+ lastMaterialInstance = 0;
+ lastMaterial = context.material;
+ lastMaterial->bind(pass, lastMaterialInstance, instanced);
+ }
+
+#if defined(RENDERER_ENABLE_GLES2)
+ bindMeshContext(context);
+#endif
+
+ if(lastMaterial) lastMaterial->bindMeshState(instanced);
+ if(context.mesh != lastMesh)
+ {
+ if(lastMesh) lastMesh->unbind();
+ lastMesh = context.mesh;
+ if(lastMesh) lastMesh->bind();
+ }
+ if(lastMesh) context.mesh->render(context.material);
+ }
+ if(lastMesh) lastMesh->unbind();
+ if(lastMaterial) lastMaterial->unbind();
+}
+
+struct CompareMeshCameraDist
+{
+ CompareMeshCameraDist(const physx::PxVec3& t_) : t(t_) { }
+ bool operator()(const RendererMeshContext& c1, const RendererMeshContext& c2) const
+ {
+ // Equivalent, return false
+ if (NULL == c1.transform && NULL == c2.transform)
+ return false;
+ // NULL transforms will be rendered first
+ if (NULL == c1.transform)
+ return true;
+ if (NULL == c2.transform)
+ return false;
+ // A larger magnitude means further away, which should be ordered first
+ // TODO: Use bones
+ return (c1.transform->getPosition() - t).magnitudeSquared() > (c2.transform->getPosition() - t).magnitudeSquared();
+ }
+ physx::PxVec3 t;
+};
+
+void Renderer::sortMeshes(const physx::PxMat44& eye)
+{
+ CompareMeshCameraDist meshComp(eye.getPosition());
+ std::sort(m_visibleLitTransparentMeshes.begin(), m_visibleLitTransparentMeshes.end(), meshComp);
+}
+
+void Renderer::renderDeferredLights(void)
+{
+ PX_PROFILE_ZONE("Renderer_renderDeferredLights",0);
+ const PxU32 numLights = (PxU32)m_visibleLights.size();
+ for(PxU32 i=0; i<numLights; i++)
+ {
+ renderDeferredLight(*m_visibleLights[i]);
+ }
+}
+
+bool Renderer::CompareRenderMaterialDesc::operator()(const RendererMaterialDesc& desc1, const RendererMaterialDesc& desc2) const
+{
+ if (desc1.type != desc2.type)
+ return desc1.type < desc2.type;
+
+ if (desc1.alphaTestFunc != desc2.alphaTestFunc)
+ return desc1.alphaTestFunc < desc2.alphaTestFunc;
+
+ if (desc1.alphaTestRef != desc2.alphaTestRef)
+ return desc1.alphaTestRef < desc2.alphaTestRef;
+
+ if (desc1.blending != desc2.blending)
+ return desc1.blending < desc2.blending;
+
+ if (desc1.srcBlendFunc != desc2.srcBlendFunc)
+ return desc1.srcBlendFunc < desc2.srcBlendFunc;
+
+ if (desc1.dstBlendFunc != desc2.dstBlendFunc)
+ return desc1.dstBlendFunc < desc2.dstBlendFunc;
+
+ int result = Ps::stricmp(desc1.vertexShaderPath, desc2.vertexShaderPath);
+ if (result != 0)
+ return result < 0;
+
+ result = Ps::stricmp(desc1.fragmentShaderPath, desc2.fragmentShaderPath);
+ if (result != 0)
+ return result < 0;
+
+ // Geometry, hull and domain shaders are optional, so only check when present
+ // * stricmp only works properly for well defined pointers, so only test when both present
+ // * if one material has a particular shader when the other material does not, the one WITH should be first
+ if (desc1.geometryShaderPath || desc2.geometryShaderPath)
+ {
+ if (desc1.geometryShaderPath && desc2.geometryShaderPath)
+ result = Ps::stricmp(desc1.geometryShaderPath, desc2.geometryShaderPath);
+ else
+ result = desc1.geometryShaderPath ? -1 : 1;
+ }
+
+ if (0 == result && (desc1.hullShaderPath || desc2.hullShaderPath))
+ {
+ if (desc1.hullShaderPath && desc2.hullShaderPath)
+ result = Ps::stricmp(desc1.hullShaderPath, desc2.hullShaderPath);
+ else
+ result = desc1.hullShaderPath ? -1 : 1;
+ }
+
+ if (0 == result && (desc1.domainShaderPath || desc2.domainShaderPath))
+ {
+ if (desc1.domainShaderPath && desc2.domainShaderPath)
+ result = Ps::stricmp(desc1.domainShaderPath, desc2.domainShaderPath);
+ else
+ result = desc1.domainShaderPath ? -1 : 1;
+ }
+
+ return result < 0;
+}
+
+RendererMaterial* Renderer::hasMaterialAlready(const RendererMaterialDesc& desc)
+{
+ if (mEnableMaterialCaching)
+ {
+ tMaterialCache::iterator it = m_materialCache.find(desc);
+
+ if (it != m_materialCache.end())
+ {
+ PX_ASSERT(it->second != NULL);
+ it->second->incRefCount();
+ return it->second;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+static const char* local_strdup(const char* input)
+{
+ if (input == NULL)
+ {
+ return NULL;
+ }
+
+ unsigned int strLen = (unsigned int)strlen(input) + 1;
+ char* retStr = (char*)::malloc(strLen);
+
+ PX_ASSERT(retStr);
+
+ if (NULL != retStr)
+ {
+#if PX_WINDOWS
+ strcpy_s(retStr, strLen, input);
+#else
+ strncpy(retStr, input, strLen);
+#endif
+ }
+ return retStr;
+}
+
+
+
+void Renderer::registerMaterial(const RendererMaterialDesc& desc, RendererMaterial* mat)
+{
+ if (mEnableMaterialCaching)
+ {
+ tMaterialCache::iterator it = m_materialCache.find(desc);
+
+ if (it == m_materialCache.end())
+ {
+ RendererMaterialDesc descCopy = desc;
+ descCopy.vertexShaderPath = local_strdup(desc.vertexShaderPath);
+ descCopy.fragmentShaderPath = local_strdup(desc.fragmentShaderPath);
+ descCopy.geometryShaderPath = local_strdup(desc.geometryShaderPath);
+ descCopy.domainShaderPath = local_strdup(desc.domainShaderPath);
+ descCopy.hullShaderPath = local_strdup(desc.hullShaderPath);
+
+ PX_ASSERT(mat->m_refCount == 1);
+ m_materialCache[descCopy] = mat;
+ }
+ else
+ {
+ PX_ASSERT(it->second == NULL);
+ it->second = mat;
+ }
+ }
+}
+
+
+
+void Renderer::releaseAllMaterials()
+{
+ for (tMaterialCache::iterator it = m_materialCache.begin(); it != m_materialCache.end(); ++it)
+ {
+ PX_ASSERT(it->second != NULL);
+ delete it->second;
+
+ it->second = NULL;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include "RendererMemoryMacros.h"
+#include "RendererMaterialDesc.h"
+#include "RendererTexture2DDesc.h"
+
+// PT: text stuff from ICE. Adapted quickly, should be cleaned up when we have some time.
+
+struct FntInfo
+{
+ PxReal u0;
+ PxReal v0;
+ PxReal u1;
+ PxReal v1;
+ PxU32 dx;
+ PxU32 dy;
+};
+
+class FntData
+{
+public:
+ FntData();
+ ~FntData();
+
+ void Reset();
+ PxU32 ComputeSize(const char* text, PxReal& width, PxReal& height, PxReal scale, bool forceFixWidthNumbers) const;
+
+ bool Load(Renderer& renderer, const char* filename);
+
+ PX_FORCE_INLINE PxU32 GetNbFnts() const { return mNbFnts; }
+ PX_FORCE_INLINE const FntInfo* GetFnts() const { return mFnts; }
+ PX_FORCE_INLINE PxU32 GetMaxDx() const { return mMaxDx; }
+ PX_FORCE_INLINE PxU32 GetMaxDy() const { return mMaxDy; }
+ PX_FORCE_INLINE const PxU8* GetXRef() const { return mXRef; }
+ PX_FORCE_INLINE PxU32 GetMaxDxNumbers() const { return mMaxDxNumbers; }
+ PX_FORCE_INLINE bool IsFixWidthCharacter(unsigned char c) const { return mFixWidthCharacters[c]; }
+
+private:
+ PxU32 mNbFnts;
+ FntInfo* mFnts;
+ PxU32 mMaxDx, mMaxDy;
+ PxU8 mXRef[256];
+ bool mFixWidthCharacters[256];
+ PxU32 mMaxDxNumbers;
+public:
+ RendererTexture2D* mTexture;
+};
+
+FntData::FntData()
+{
+ mNbFnts = 0;
+ mFnts = NULL;
+ mMaxDx = 0;
+ mMaxDy = 0;
+ memset(mXRef, 0, 256*sizeof(PxU8));
+ memset(mFixWidthCharacters, 0, 256 * sizeof(bool));
+ for (int i = '0'; i <= '9'; i++)
+ {
+ mFixWidthCharacters[i] = true;
+ }
+ //mFixWidthCharacters[size_t(' ')] = true;
+ mFixWidthCharacters[size_t('.')] = true;
+ mFixWidthCharacters[size_t('+')] = true;
+ mFixWidthCharacters[size_t('-')] = true;
+ mFixWidthCharacters[size_t('*')] = true;
+ mFixWidthCharacters[size_t('/')] = true;
+ mMaxDxNumbers = 0;
+ mTexture = NULL;
+}
+
+FntData::~FntData()
+{
+ Reset();
+}
+
+void FntData::Reset()
+{
+ SAFE_RELEASE(mTexture);
+ DELETEARRAY(mFnts);
+ mNbFnts = 0;
+ mMaxDx = 0;
+ mMaxDy = 0;
+ memset(mXRef, 0, 256*sizeof(PxU8));
+ memset(mFixWidthCharacters, 0, 256 * sizeof(bool));
+ mMaxDxNumbers = 0;
+}
+
+// Compute size of a given text
+PxU32 FntData::ComputeSize(const char* text, PxReal& width, PxReal& height, PxReal scale, bool forceFixWidthNumbers) const
+{
+ // Get and check length
+ if(!text) return 0;
+ PxU32 Nb = (PxU32)strlen((const char*)text);
+ if(!Nb) return 0;
+
+ PxReal x = 0.0f;
+ PxReal y = 0.0f;
+
+ width = -1.0f;
+ height = -1.0f;
+
+ // Loop through characters
+ for(PxU32 j=0;j<Nb;j++)
+ {
+ if(text[j]!='\n')
+ {
+ // Catch current character index
+ const PxU32 i = mXRef[(unsigned char)(text[j])];
+
+ // Catch size of current character
+ const PxReal sx = PxReal((forceFixWidthNumbers && mFixWidthCharacters[(size_t)text[j]]) ? mMaxDxNumbers : mFnts[i].dx) * scale;
+ const PxReal sy = PxReal(mFnts[i].dy) * scale;
+
+ // Keep track of greatest dimensions
+ if((x+sx)>width) width = x+sx;
+ if((y+sy)>height) height = y+sy;
+
+ // Adjust x for next character
+ x += sx + 1.0f;
+ }
+ else
+ {
+ // Jump to next line
+ x = 0.0f;
+ y += PxReal(mMaxDy) * scale;
+ }
+ }
+ return Nb;
+}
+
+#include "PxTkFile.h"
+
+#if PX_INTEL_FAMILY
+static const bool gFlip = false;
+#elif PX_PPC
+static const bool gFlip = true;
+#elif PX_ARM_FAMILY
+static const bool gFlip = false;
+#else
+#error Unknown platform!
+#endif
+
+PX_INLINE void Flip(PxU32& v)
+{
+ PxU8* b = (PxU8*)&v;
+
+ PxU8 temp = b[0];
+ b[0] = b[3];
+ b[3] = temp;
+ temp = b[1];
+ b[1] = b[2];
+ b[2] = temp;
+}
+
+PX_INLINE void Flip(PxI32& v)
+{
+ Flip((PxU32&)v);
+}
+
+PX_INLINE void Flip(PxF32& v)
+{
+ Flip((PxU32&)v);
+}
+
+static PxU32 read32(File* fp)
+{
+ PxU32 data;
+ size_t numRead = fread(&data, 1, 4, fp);
+ if(numRead != 4) { fclose(fp); return 0; }
+ if(gFlip)
+ Flip(data);
+ return data;
+}
+
+bool FntData::Load(Renderer& renderer, const char* filename)
+{
+ File* fp = NULL;
+ PxToolkit::fopen_s(&fp, filename, "rb");
+ if(!fp)
+ return false;
+
+ // Init texture
+ {
+ const PxU32 width = read32(fp);
+ const PxU32 height = read32(fp);
+ PxU8* data = new PxU8[width*height*4];
+ const size_t size = width*height*4;
+ size_t numRead = fread(data, 1, size, fp);
+ if(numRead != size) { fclose(fp); return false; }
+
+ /* if(gFlip)
+ {
+ PxU32* data32 = (PxU32*)data;
+ for(PxU32 i=0;i<width*height;i++)
+ {
+ Flip(data32[i]);
+ }
+ }*/
+
+ RendererTexture2DDesc tdesc;
+ tdesc.format = RendererTexture2D::FORMAT_B8G8R8A8;
+ tdesc.width = width;
+ tdesc.height = height;
+ tdesc.filter = RendererTexture2D::FILTER_ANISOTROPIC;
+ tdesc.numLevels = 1;
+ /*
+ tdesc.filter;
+ tdesc.addressingU;
+ tdesc.addressingV;
+ tdesc.renderTarget;
+ */
+ PX_ASSERT(tdesc.isValid());
+ mTexture = renderer.createTexture2D(tdesc);
+ PX_ASSERT(mTexture);
+ if(!mTexture)
+ {
+ DELETEARRAY(data);
+ fclose(fp);
+ return false;
+ }
+
+ const PxU32 componentCount = 4;
+
+ if(mTexture)
+ {
+ PxU32 pitch = 0;
+ void* buffer = mTexture->lockLevel(0, pitch);
+ PX_ASSERT(buffer);
+ if(buffer)
+ {
+ PxU8* levelDst = (PxU8*)buffer;
+ const PxU8* levelSrc = (PxU8*)data;
+ const PxU32 levelWidth = mTexture->getWidthInBlocks();
+ const PxU32 levelHeight = mTexture->getHeightInBlocks();
+ PX_ASSERT(levelWidth * mTexture->getBlockSize() <= pitch); // the pitch can't be less than the source row size.
+ for(PxU32 row=0; row<levelHeight; row++)
+ {
+ // copy per pixel to handle RBG case, based on component count
+ for(PxU32 col=0; col<levelWidth; col++)
+ {
+ *levelDst++ = levelSrc[0];
+ *levelDst++ = levelSrc[1];
+ *levelDst++ = levelSrc[2];
+ *levelDst++ = levelSrc[3];
+ levelSrc += componentCount;
+ }
+ }
+ }
+ mTexture->unlockLevel(0);
+ }
+ DELETEARRAY(data);
+ }
+
+ mNbFnts = read32(fp);
+
+ mFnts = new FntInfo[mNbFnts];
+ const size_t size = mNbFnts*sizeof(FntInfo);
+ size_t numRead = fread(mFnts, 1, size, fp);
+ if(numRead != size) { fclose(fp); return false; }
+ if(gFlip)
+ {
+ for(PxU32 i=0;i<mNbFnts;i++)
+ {
+ Flip(mFnts[i].u0);
+ Flip(mFnts[i].v0);
+ Flip(mFnts[i].u1);
+ Flip(mFnts[i].v1);
+ Flip(mFnts[i].dx);
+ Flip(mFnts[i].dy);
+ }
+ }
+
+ mMaxDx = read32(fp);
+ mMaxDy = read32(fp);
+
+ const size_t xRefSize = 256*sizeof(PxU8);
+ numRead = fread(mXRef, 1, xRefSize, fp);
+ if(numRead != xRefSize) { fclose(fp); return false; }
+
+ for (unsigned int c = 0; c < 256; c++)
+ {
+ if (mFixWidthCharacters[c])
+ {
+ mMaxDxNumbers = PxMax(mFnts[mXRef[c]].dx, mMaxDxNumbers);
+ }
+ }
+
+ fclose(fp);
+ return true;
+}
+
+
+struct ClipBox
+{
+ PxReal mXMin;
+ PxReal mYMin;
+ PxReal mXMax;
+ PxReal mYMax;
+};
+
+static bool ClipQuad(Renderer::TextVertex* /*quad*/, const ClipBox& /*clip_box*/)
+{
+ return true;
+}
+
+static bool GenerateTextQuads( const char* text, PxU32 nb_characters,
+ Renderer::TextVertex* fnt_verts, PxU16* fnt_indices, const ClipBox& clip_box, const FntData* fnt_data, PxReal& x, PxReal& y, PxReal scale_x, PxReal scale_y, PxU32 color,
+ PxReal* x_min, PxReal* y_min, PxReal* x_max, PxReal* y_max, PxU32* nb_lines, PxU32* nb_active_characters, bool forceFixWidthNumbers)
+{
+ // Checkings
+ if(!text || !fnt_verts || !fnt_indices || !fnt_data) return false;
+
+ PxReal mX = x;
+
+ //////////
+
+ Renderer::TextVertex* V = fnt_verts;
+ PxU16* I = fnt_indices;
+ PxU16 Offset = 0;
+ PxU32 NbActiveCharacters = 0; // Number of non-NULL characters (the ones to render)
+
+ PxReal XMin = 100000.0f, XMax = -100000.0f;
+ PxReal YMin = 100000.0f, YMax = -100000.0f;
+
+ PxU32 NbLines = 1; // Number of lines
+
+ // Loop through characters
+ for(PxU32 j=0;j<nb_characters;j++)
+ {
+ if(text[j]!='\n')
+ {
+ PxU32 i = fnt_data->GetXRef()[(size_t)text[j]];
+ const FntInfo character = fnt_data->GetFnts()[i];
+
+ // Character size
+ const PxReal sx = PxReal(character.dx) * scale_x;
+ const PxReal sy = PxReal(character.dy) * scale_y;
+
+ if (forceFixWidthNumbers && fnt_data->IsFixWidthCharacter(text[j]))
+ {
+ // move forward half the distance
+ x += (fnt_data->GetMaxDxNumbers() - character.dx) * scale_x * 0.5f;
+ }
+
+ if(text[j]!=' ')
+ {
+ const PxReal rhw = 1.0f;
+
+
+ // Initialize the vertices
+ V[0].p.x = x; V[0].p.y = y+sy; V[0].u = character.u0; V[0].v = character.v1;
+ V[1].p.x = x; V[1].p.y = y; V[1].u = character.u0; V[1].v = character.v0;
+ V[2].p.x = x+sx; V[2].p.y = y+sy; V[2].u = character.u1; V[2].v = character.v1;
+ V[3].p.x = x+sx; V[3].p.y = y; V[3].u = character.u1; V[3].v = character.v0;
+ V[0].rhw = V[1].rhw = V[2].rhw = V[3].rhw = rhw;
+ V[0].p.z = V[1].p.z = V[2].p.z = V[3].p.z = 0.0f;
+ V[0].color = V[1].color = V[2].color = V[3].color = color;
+
+ if(ClipQuad(V, clip_box))
+ {
+ V+=4;
+
+ // Initialize the indices
+ *I++ = Offset+0;
+ *I++ = Offset+1;
+ *I++ = Offset+2;
+ *I++ = Offset+2;
+ *I++ = Offset+1;
+ *I++ = Offset+3;
+ Offset+=4;
+
+ NbActiveCharacters++;
+ }
+ }
+
+ if (forceFixWidthNumbers && fnt_data->IsFixWidthCharacter(text[j]))
+ {
+ // move forward theo ther half of the distance
+ x += (fnt_data->GetMaxDxNumbers() - character.dx) * scale_x * 0.5f;
+ }
+
+ //
+ if((x+sx)>XMax) XMax = x+sx; if(x<XMin) XMin = x;
+ if((y+sy)>YMax) YMax = y+sy; if(y<YMin) YMin = y;
+
+ x += sx + 1.0f;
+ }
+ else
+ {
+ // Jump to next line
+ x = mX;
+ y += PxReal(fnt_data->GetMaxDy()) * scale_y;
+ NbLines++;
+ }
+ }
+
+ if(x_min) *x_min = XMin;
+ if(y_min) *y_min = YMin;
+ if(x_max) *x_max = XMax;
+ if(y_max) *y_max = YMax;
+ if(nb_lines) *nb_lines = NbLines;
+ if(nb_active_characters) *nb_active_characters = NbActiveCharacters;
+
+ return true;
+}
+
+enum RenderTextQuadFlag_
+{
+ RTQF_ALIGN_LEFT = 0,
+ RTQF_ALIGN_CENTER = (1<<0),
+ RTQF_ALIGN_RIGHT = (1<<1),
+};
+
+struct RenderTextData
+{
+ RenderTextData(Renderer::TextVertex* pFntVerts, PxU16* pFntIndices)
+ : mpFntVerts(pFntVerts), mpFntIndices(pFntIndices) { }
+
+ ~RenderTextData()
+ {
+ DELETEARRAY(mpFntVerts);
+ DELETEARRAY(mpFntIndices);
+ }
+
+ Renderer::TextVertex* mpFntVerts;
+ PxU16* mpFntIndices;
+
+private:
+ // Disable default, copy and assign
+ RenderTextData();
+ RenderTextData(const RenderTextData&);
+ void operator=(const RenderTextData&);
+};
+
+static void RenderTextQuads(Renderer* textRender,
+ const FntData* fnts,
+ const char* text, PxReal x, PxReal y, PxU32 text_color, PxU32 shadow_color,
+ PxReal scale_x, PxReal scale_y,
+ PxU32 align_flags, PxReal max_length,
+ PxReal shadow_offset,
+ PxReal* nx, PxReal* ny,
+ const ClipBox* clip_box,
+ PxReal text_y_offset,
+ bool use_max_dy,
+ bool forceFixWidthNumbers,
+ RendererMaterial* material
+ )
+{
+ // We want to render the whole text in one run...
+ const PxReal text_x = x;
+ const PxReal text_y = y;
+
+ // Compute text size
+ PxReal Width, Height;
+ const PxU32 NbCharacters = fnts->ComputeSize(text, Width, Height, 1.0f, forceFixWidthNumbers);
+
+ // Prepare clip box
+ ClipBox CB;
+ if(clip_box)
+ {
+ CB = *clip_box;
+ }
+ else
+ {
+ PxU32 width, height;
+ textRender->getWindowSize(width, height);
+
+ const PxReal Margin = 0.0f;
+ CB.mXMin = Margin;
+ CB.mYMin = Margin;
+ CB.mXMax = PxReal(width) - Margin;
+ CB.mYMax = PxReal(height) - Margin;
+ }
+
+ // Allocate space for vertices
+ RenderTextData FntData(new Renderer::TextVertex[NbCharacters*4], new PxU16[NbCharacters*6]);
+
+ // Generate quads
+ PxReal XMin, YMin, XMax, YMax;
+ PxU32 NbLines, NbActiveCharacters;
+ GenerateTextQuads(text, NbCharacters, FntData.mpFntVerts, FntData.mpFntIndices, CB, fnts, x, y, scale_x, scale_y, text_color, &XMin, &YMin, &XMax, &YMax, &NbLines, &NbActiveCharacters, forceFixWidthNumbers);
+
+ for(PxU32 i=0;i<NbActiveCharacters*4;i++)
+ FntData.mpFntVerts[i].p.y += text_y_offset;
+
+ if(use_max_dy)
+ YMax = YMin + (PxReal)fnts->GetMaxDy();
+
+ const bool centered = (align_flags & RTQF_ALIGN_CENTER)!=0;
+ const bool align_right = (align_flags & RTQF_ALIGN_RIGHT)!=0;
+
+ if(centered || align_right)
+ {
+ const PxReal L = XMax - XMin;
+ XMax = XMin + max_length;
+ const PxReal Offset = centered ? (-FntData.mpFntVerts[0].p.x + XMin + (max_length - L)*0.5f) :
+ (-FntData.mpFntVerts[0].p.x + XMax - L);
+ // (-FntVerts[0].p.x + XMin + (max_length - L));
+ for(PxU32 i=0;i<NbActiveCharacters*4;i++)
+ FntData.mpFntVerts[i].p.x += Offset;
+ }
+
+ textRender->setupTextRenderStates();
+
+ // Handle shadow
+ if(shadow_offset!=0.0f)
+ {
+ // Allocate space for vertices
+ RenderTextData SFntData(new Renderer::TextVertex[NbCharacters*4], new PxU16[NbCharacters*6]);
+
+ // Generate quads
+ PxReal SXMin, SYMin, SXMax, SYMax;
+ PxU32 SNbLines, SNbActiveCharacters;
+ PxReal ShX = text_x + shadow_offset;
+ PxReal ShY = text_y + shadow_offset;
+ GenerateTextQuads(text, NbCharacters, SFntData.mpFntVerts, SFntData.mpFntIndices, CB, fnts, ShX, ShY, scale_x, scale_y, shadow_color, &SXMin, &SYMin, &SXMax, &SYMax, &SNbLines, &SNbActiveCharacters, forceFixWidthNumbers);
+
+ for(PxU32 i=0;i<SNbActiveCharacters*4;i++)
+ SFntData.mpFntVerts[i].p.y += text_y_offset;
+
+ if(centered || align_right)
+ {
+ const PxReal L = SXMax - SXMin;
+ SXMax = SXMin + max_length;
+ const PxReal Offset = centered ? (-SFntData.mpFntVerts[0].p.x + SXMin + (max_length - L)*0.5f) :
+ (-SFntData.mpFntVerts[0].p.x + SXMax - L);
+ for(PxU32 i=0;i<SNbActiveCharacters*4;i++)
+ SFntData.mpFntVerts[i].p.x += Offset;
+ }
+
+ textRender->renderTextBuffer(SFntData.mpFntVerts, 4*SNbActiveCharacters, SFntData.mpFntIndices, 6*SNbActiveCharacters, material);
+ }
+
+ textRender->renderTextBuffer(FntData.mpFntVerts, 4*NbActiveCharacters, FntData.mpFntIndices, 6*NbActiveCharacters, material);
+
+ textRender->resetTextRenderStates();
+}
+
+
+static FntData* gFntData = NULL;
+
+
+bool Renderer::initTexter()
+{
+ if(gFntData)
+ return true;
+
+ char filename[1024];
+ Ps::strlcpy(filename, sizeof(filename), getAssetDir());
+ Ps::strlcat(filename, sizeof(filename), "fonts/arial_black.bin");
+
+ gFntData = new FntData;
+ if(!gFntData->Load(*this, filename))
+ {
+ closeTexter();
+ return false;
+ }
+
+ {
+ RendererMaterialDesc matDesc;
+ matDesc.alphaTestFunc = RendererMaterial::ALPHA_TEST_ALWAYS;
+ matDesc.alphaTestRef = 0.0f;
+ matDesc.type = RendererMaterial::TYPE_UNLIT;
+ matDesc.blending = true;
+ matDesc.srcBlendFunc = RendererMaterial::BLEND_SRC_ALPHA;
+ matDesc.dstBlendFunc = RendererMaterial::BLEND_ONE_MINUS_SRC_ALPHA;
+ matDesc.geometryShaderPath = NULL;
+ matDesc.vertexShaderPath = "vertex/text.cg";
+ matDesc.fragmentShaderPath = "fragment/text.cg";
+ PX_ASSERT(matDesc.isValid());
+
+ m_textMaterial = createMaterial(matDesc);
+ if(!m_textMaterial)
+ {
+ closeTexter();
+ return false;
+ }
+
+ m_textMaterialInstance = new RendererMaterialInstance(*m_textMaterial);
+ if(!m_textMaterialInstance)
+ {
+ closeTexter();
+ return false;
+ }
+
+ const RendererMaterial::Variable* var = m_textMaterial->findVariable("diffuseTexture", RendererMaterial::VARIABLE_SAMPLER2D);
+ if(!var)
+ {
+ closeTexter();
+ return false;
+ }
+ m_textMaterialInstance->writeData(*var, &(gFntData->mTexture));
+ }
+
+ return true;
+}
+
+void Renderer::closeTexter()
+{
+ DELETESINGLE(m_textMaterialInstance);
+ SAFE_RELEASE(m_textMaterial);
+ DELETESINGLE(gFntData);
+}
+
+// splits string by delimeter
+#if defined(RENDERER_TABLET)
+static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
+ std::stringstream ss(s);
+ std::string item;
+ while(std::getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+static std::vector<std::string> split(const std::string &s, char delim) {
+ std::vector<std::string> elems;
+ return split(s, delim, elems);
+}
+#endif
+
+void SampleRenderer::Renderer::print(PxU32* x, PxU32* y, const char** text, PxU32 textCount, PxReal scale, PxReal shadowOffset, RendererColor* textColors, bool forceFixWidthNumbers)
+{
+ // we split string containing '\n' in a vector of strings. This prevents from need to have a really big vertex/index buffer for text in GLES2 renderer
+#if defined(RENDERER_TABLET)
+ if(std::string(*text).find('\n') != std::string::npos) {
+ std::vector<std::string> strings = split(*text, '\n');
+ PxU32 nx = *x, ny = *y;
+ for(PxU32 i = 0; i < strings.size(); ++i)
+ {
+ const char* sptr = strings[i].c_str();
+ print(&nx, &ny, &sptr, 1, scale, shadowOffset, textColors, forceFixWidthNumbers);
+ ny += TEXT_HEIGHT + TEXT_VERTICAL_SPACING;
+ }
+ return;
+ }
+#endif
+
+ if(!gFntData || !gFntData->mTexture || !m_textMaterial || !m_textMaterialInstance || 0==textCount || NULL==x || NULL==y || NULL==text || 0 == *text || (*text)[0] == '\0')
+ return;
+
+ ScopedRender renderSection(*this);
+ if (renderSection)
+ {
+ m_textMaterial->bind(RendererMaterial::PASS_UNLIT, m_textMaterialInstance, false);
+
+ if(!m_useShadersForTextRendering)
+ {
+ m_textMaterial->unbind();
+ gFntData->mTexture->select(0);
+ }
+
+ const PxU32 alignFlags = 0;
+ const float maxLength = 0.0f;
+ float* nx = NULL;
+ float* ny = NULL;
+ const ClipBox* clipBox = NULL;
+ const float textYOffset = 0.0f;
+ const bool useMaxDy = false;
+ const RendererColor defaultColor(255, 255, 255, 255);
+
+#if defined(RENDERER_TABLET)
+ shadowOffset = 0.0f;
+#endif
+
+ for (PxU32 i = 0; i < textCount; ++i)
+ {
+ const PxU32 color = convertColor(textColors ? textColors[i] : defaultColor);
+ const PxU32 shadowColor = convertColor(textColors ? RendererColor(0,0,0,textColors[i].a) : RendererColor(0,0,0,defaultColor.a));
+
+ RenderTextQuads(this,
+ gFntData,
+ text[i],
+ PxReal(x[i]), PxReal(y[i]),
+ color, shadowColor,
+ scale, scale,
+ alignFlags,
+ maxLength,
+ shadowOffset * scale,
+ nx, ny,
+ clipBox,
+ textYOffset,
+ useMaxDy,
+ forceFixWidthNumbers,
+ m_textMaterial
+ );
+ }
+
+ if(m_useShadersForTextRendering)
+ m_textMaterial->unbind();
+ }
+}
+
+bool SampleRenderer::Renderer::captureScreen(const char* filename)
+{
+ if(!filename)
+ return false;
+
+ physx::PxU32 width, height, sizeInBytes;
+ const void* data = NULL;
+ if (captureScreen(width, height, sizeInBytes, data) && sizeInBytes > 0)
+ {
+ PxDefaultFileOutputStream fileBuffer(filename);
+ return fileBuffer.write(data, sizeInBytes) > 0;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void Renderer::print(PxU32 x, PxU32 y, const char* text, PxReal scale, PxReal shadowOffset, RendererColor textColor, bool forceFixWidthNumbers)
+{
+ if (NULL == text || strlen(text) <= 0)
+ return;
+ print(&x, &y, &text, 1, scale, shadowOffset, &textColor, forceFixWidthNumbers);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool Renderer::initScreenquad()
+{
+ RendererMaterialDesc matDesc;
+ matDesc.alphaTestFunc = RendererMaterial::ALPHA_TEST_ALWAYS;
+ matDesc.alphaTestRef = 0.0f;
+ matDesc.type = RendererMaterial::TYPE_UNLIT;
+ matDesc.blending = false;
+ matDesc.vertexShaderPath = "vertex/screenquad.cg";
+ matDesc.fragmentShaderPath = "fragment/screenquad.cg";
+ matDesc.geometryShaderPath = NULL;
+#if defined(RENDERER_TABLET)
+ matDesc.srcBlendFunc = RendererMaterial::BLEND_SRC_ALPHA;
+ matDesc.dstBlendFunc = RendererMaterial::BLEND_ONE_MINUS_SRC_ALPHA;
+#else
+ matDesc.srcBlendFunc = RendererMaterial::BLEND_ONE;
+ matDesc.dstBlendFunc = RendererMaterial::BLEND_ONE;
+#endif
+ PX_ASSERT(matDesc.isValid());
+
+ m_screenquadOpaqueMaterial = createMaterial(matDesc);
+ if(!m_screenquadOpaqueMaterial)
+ {
+ closeScreenquad();
+ return false;
+ }
+
+ m_screenquadOpaqueMaterialInstance = new RendererMaterialInstance(*m_screenquadOpaqueMaterial);
+ if(!m_screenquadOpaqueMaterialInstance)
+ {
+ closeScreenquad();
+ return false;
+ }
+
+ matDesc.blending = true;
+ matDesc.srcBlendFunc = RendererMaterial::BLEND_SRC_ALPHA;
+ matDesc.dstBlendFunc = RendererMaterial::BLEND_ONE_MINUS_SRC_ALPHA;
+
+ m_screenquadAlphaMaterial = createMaterial(matDesc);
+ if(!m_screenquadAlphaMaterial)
+ {
+ closeScreenquad();
+ return false;
+ }
+
+ m_screenquadAlphaMaterialInstance = new RendererMaterialInstance(*m_screenquadAlphaMaterial);
+ if(!m_screenquadAlphaMaterialInstance )
+ {
+ closeScreenquad();
+ return false;
+ }
+
+ return true;
+}
+
+#if defined(RENDERER_TABLET)
+void Renderer::setControlPosition(int ctrl_idx, const PxVec2& pos)
+{
+ if(ctrl_idx < CONTROL_COUNT)
+ {
+ m_controlPos[ctrl_idx] = pos;
+ }
+}
+
+#include "foundation/PxBounds3.h"
+
+PxBounds3 Renderer::getCenteredControlBounds(int ctrl_idx)
+{
+ /* Return PxBounds3 with positive volume */
+ return PxBounds3(
+ PxVec3(m_controlCenteredPos[ctrl_idx].x - m_controlHalfSize.x, m_controlCenteredPos[ctrl_idx].y - m_controlHalfSize.y, -1.0f),
+ PxVec3(m_controlCenteredPos[ctrl_idx].x + m_controlHalfSize.x, m_controlCenteredPos[ctrl_idx].y + m_controlHalfSize.y, 1.0f));
+}
+
+PxBounds3 Renderer::getControlBounds(int ctrl_idx)
+{
+ /* Return PxBounds3 with positive volume */
+ return PxBounds3(
+ PxVec3(m_controlPos[ctrl_idx].x - m_controlHalfSize.x, m_controlPos[ctrl_idx].y - m_controlHalfSize.y, -1.0f),
+ PxVec3(m_controlPos[ctrl_idx].x + m_controlHalfSize.x, m_controlPos[ctrl_idx].y + m_controlHalfSize.y, 1.0f));
+}
+
+PxVec2 Renderer::getControlPosition(int ctrl_idx)
+{
+ return m_controlPos[ctrl_idx];
+}
+
+PxVec2 Renderer::getCenteredControlPosition(int ctrl_idx)
+{
+ return m_controlCenteredPos[ctrl_idx];
+}
+
+bool Renderer::initControls(RendererMaterial* controlMaterial, RendererMaterialInstance* controlMaterialInstance)
+{
+ // create quad control
+ using physx::PxReal;
+ using physx::PxU32;
+ using physx::PxU8;
+
+ m_controlMaterial = controlMaterial;
+ m_controlMaterialInstance = controlMaterialInstance;
+
+ // compute control dimensions and placement according to window aspect
+ // screen coordinate range: [-1,1]
+ PxVec2 controlSize;
+ PxVec2 controlCenterOffset;
+ {
+ PxU32 width, height;
+ getWindowSize(width, height);
+
+ PX_ASSERT(width > 0 && height > 0);
+ PX_ASSERT(gControlSizeRelative > 0.0f && gControlSizeRelative < 0.5f);
+ PX_ASSERT(gControlMarginRelative > 0.0f && gControlMarginRelative < 0.5f);
+
+ //size and margin should be relative to smaller screen dimension
+ if (width > height)
+ {
+ PxReal aspect = (PxReal)height/(PxReal)width;
+ controlSize.y = 2.0f*gControlSizeRelative;
+ controlSize.x = controlSize.y * aspect;
+ controlCenterOffset.y = 1.0f - controlSize.y*0.5f - 2.0f*gControlMarginRelative;
+ controlCenterOffset.x = 1.0f - controlSize.x*0.5f - 2.0f*gControlMarginRelative*aspect;
+ }
+ else
+ {
+ PxReal aspect = (PxReal)width/(PxReal)height;
+ controlSize.x = 2.0f*gControlSizeRelative;
+ controlSize.y = controlSize.x * aspect;
+ controlCenterOffset.x = 1.0f - controlSize.x*0.5f - 2.0f*gControlMarginRelative;
+ controlCenterOffset.y = 1.0f - controlSize.y*0.5f - 2.0f*gControlMarginRelative*aspect;
+ }
+ }
+
+ m_controlHalfSize = controlSize*0.5f;
+
+ /* Initialize sticks positions - default and current */
+ m_controlCenteredPos[0].x = -controlCenterOffset.x;
+ m_controlCenteredPos[0].y = -controlCenterOffset.y;
+ m_controlCenteredPos[1].x = controlCenterOffset.x;
+ m_controlCenteredPos[1].y = -controlCenterOffset.y;
+ m_controlPos[0] = m_controlCenteredPos[0];
+ m_controlPos[1] = m_controlCenteredPos[1];
+
+ PxReal controlVertices[] = {
+ -m_controlHalfSize.x, -m_controlHalfSize.y, 0.0f,
+ -m_controlHalfSize.x, m_controlHalfSize.y, 0.0f,
+ m_controlHalfSize.x, -m_controlHalfSize.y, 0.0f,
+ m_controlHalfSize.x, m_controlHalfSize.y, 0.0f };
+
+ PxReal controlTexcoords[] = {
+ 0.0f, 0.0f, // each face
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f };
+
+ const PxU32 controlVerticesCount = sizeof(controlVertices) / (sizeof(controlVertices[0]) * 3);
+
+ m_controlMesh[0] = initControl(controlVertices, controlTexcoords, controlVerticesCount);
+
+ for(int i = 0; i < controlVerticesCount; ++i)
+ controlVertices[3 * i] = -controlVertices[3 * i];
+
+ m_controlMesh[1] = initControl(controlVertices, controlTexcoords, controlVerticesCount);
+
+ return true;
+}
+
+void Renderer::setControlDefaultPosition(int ctrl_idx)
+{
+ m_controlPos[ctrl_idx] = m_controlCenteredPos[ctrl_idx];
+}
+
+RendererMesh* Renderer::initControl(PxReal* controlVertices, PxReal* controlTexcoords, PxU32 count)
+{
+ const PxU32 controlVerticesCount = count;
+
+ RendererVertexBufferDesc vbdesc;
+ vbdesc.hint = RendererVertexBuffer::HINT_STATIC;
+ vbdesc.maxVertices = controlVerticesCount;
+ vbdesc.semanticFormats[RendererVertexBuffer::SEMANTIC_POSITION] = RendererVertexBuffer::FORMAT_FLOAT3;
+ vbdesc.semanticFormats[RendererVertexBuffer::SEMANTIC_TEXCOORD0] = RendererVertexBuffer::FORMAT_FLOAT2;
+ RendererVertexBuffer* mVertexBuffer = createVertexBuffer(vbdesc);
+ RendererMesh* controlMesh = NULL;
+ if(mVertexBuffer)
+ {
+ RendererMeshDesc meshdesc;
+ meshdesc.primitives = RendererMesh::PRIMITIVE_TRIANGLE_STRIP;
+ meshdesc.vertexBuffers = &mVertexBuffer;
+ meshdesc.numVertexBuffers = 1;
+ meshdesc.firstVertex = 0;
+ meshdesc.numVertices = mVertexBuffer->getMaxVertices();
+ meshdesc.indexBuffer = NULL;
+ meshdesc.firstIndex = 0;
+ meshdesc.numIndices = 0;
+ meshdesc.instanceBuffer = NULL;
+ meshdesc.firstInstance = 0;
+ meshdesc.numInstances = 0;
+ controlMesh = createMesh(meshdesc);
+ RENDERER_ASSERT(controlMesh, "Failed to create Mesh.");
+ }
+ PxU32 positionStride = 0, texcoordStride = 0;
+ PxU8* locked_positions = static_cast<PxU8*>(mVertexBuffer->lockSemantic(RendererVertexBuffer::SEMANTIC_POSITION, positionStride));
+ PxU8* locked_texcoords = static_cast<PxU8*>(mVertexBuffer->lockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0, texcoordStride));
+ for(PxU32 i = 0; i < controlVerticesCount; ++i,
+ locked_positions += positionStride,
+ locked_texcoords += texcoordStride)
+ {
+ memcpy(locked_positions, controlVertices + 3 * i, sizeof(PxReal) * 3);
+ memcpy(locked_texcoords, controlTexcoords + 2 * i , sizeof(PxReal) * 2);
+ }
+ mVertexBuffer->unlockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0);
+ mVertexBuffer->unlockSemantic(RendererVertexBuffer::SEMANTIC_POSITION);
+ return controlMesh;
+}
+
+Renderer::TabletButton::TabletButton() :
+ pressedCount(0),
+ emulatedKeyCode(0),
+ defaultColor(PxVec4(1.0f, 1.0f, 1.0f, 0.4f)),
+ pressedColor(PxVec4(1.0f, 0.0f, 0.0f, 0.4f)),
+ callback(NULL)
+{
+}
+
+
+void Renderer::TabletButton::setPressedCount(physx::PxU8 p)
+{
+ pressedCount = p;
+}
+
+void Renderer::TabletButton::incPressed()
+{
+ pressedCount++;
+}
+
+void Renderer::TabletButton::decPressed()
+{
+ //sschirm: this caused issues on switching samples, because the OS keeps the touch states.
+ //PX_ASSERT(pressedCount);
+ if(pressedCount > 0)
+ pressedCount--;
+}
+
+void Renderer::addButton(const PxVec2& leftBottom, const PxVec2& rightTop, void (*func_ptr)(), RendererMaterial* controlMaterial,
+ RendererMaterialInstance* controlMaterialInstance)
+{
+ /* Create button object and save into the vector */
+ TabletButton button;
+ button.leftBottom = leftBottom;
+ button.rightTop = rightTop;
+ button.emulatedKeyCode = 0;
+ button.text = "Empty";
+ button.callback = func_ptr;
+ /* Create graphic representation of the button. That is, quad. */
+ PxReal buttonVertices[] = { leftBottom.x, leftBottom.y, 0.0f,
+ leftBottom.x, rightTop.y, 0.0f,
+ rightTop.x, leftBottom.y, 0.0f,
+ rightTop.x, rightTop.y, 0.0f };
+
+ /* Use full texture */
+ PxReal buttonTexcoords[] = { 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f};
+ const PxU32 buttonVerticesCount = sizeof(buttonVertices) / (sizeof(buttonVertices[0]) * 3);
+ button.mesh = initControl(buttonVertices, buttonTexcoords, buttonVerticesCount);
+ button.material = controlMaterial;
+ button.materialInstance = controlMaterialInstance;
+ button.setPressedCount(0);
+
+ m_buttons.push_back(button);
+}
+
+void Renderer::releaseAllButtons()
+{
+ m_buttons.clear();
+}
+
+void Renderer::bindButtonToUserInput(size_t buttonIndex, physx::PxU16 userInputId, const char* buttonName)
+{
+ PX_ASSERT(m_buttons.size() > buttonIndex);
+
+ TabletButton& button = m_buttons[buttonIndex];
+ button.emulatedKeyCode = userInputId;
+ button.text = buttonName;
+}
+#endif // RENDERER_TABLET
+
+
+void Renderer::closeScreenquad()
+{
+ DELETESINGLE(m_screenquadAlphaMaterialInstance);
+ SAFE_RELEASE(m_screenquadAlphaMaterial);
+ DELETESINGLE(m_screenquadOpaqueMaterialInstance);
+ SAFE_RELEASE(m_screenquadOpaqueMaterial);
+}
+
+ScreenQuad::ScreenQuad() :
+ mLeftUpColor (0xffffffff),
+ mLeftDownColor (0xffffffff),
+ mRightUpColor (0xffffffff),
+ mRightDownColor (0xffffffff),
+ mAlpha (1.0f),
+ mX0 (0.0f),
+ mY0 (0.0f),
+ mX1 (1.0f),
+ mY1 (1.0f)
+{
+}
+
+bool Renderer::drawScreenQuad(const ScreenQuad& screenQuad)
+{
+ RendererMaterial* screenquadMaterial;
+ RendererMaterialInstance* screenquadMaterialInstance;
+ if(screenQuad.mAlpha==1.0f)
+ {
+ screenquadMaterial = m_screenquadOpaqueMaterial;
+ screenquadMaterialInstance = m_screenquadOpaqueMaterialInstance;
+ }
+ else
+ {
+ screenquadMaterial = m_screenquadAlphaMaterial;
+ screenquadMaterialInstance = m_screenquadAlphaMaterialInstance;
+ }
+
+ if(!screenquadMaterialInstance || !screenquadMaterial)
+ return false;
+
+ screenquadMaterial->bind(RendererMaterial::PASS_UNLIT, screenquadMaterialInstance, false);
+ if(!m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ ScopedRender renderSection(*this);
+ if(renderSection)
+ {
+ setupScreenquadRenderStates();
+
+ TextVertex Verts[4];
+ const PxU16 Indices[6] = { 0,1,2,2,1,3 };
+
+ PxU32 renderWidth, renderHeight;
+ getWindowSize(renderWidth, renderHeight);
+
+ // Initalize the vertices
+ const PxReal xCoeff = PxReal(renderWidth);
+ const PxReal yCoeff = PxReal(renderHeight);
+ const PxReal x0 = screenQuad.mX0 * xCoeff;
+ const PxReal y0 = screenQuad.mY0 * yCoeff;
+ const PxReal sx = screenQuad.mX1 * xCoeff;
+ const PxReal sy = screenQuad.mY1 * yCoeff;
+ const PxReal rhw = 1.0f;
+ const PxReal z = 0.0f;
+
+ RendererColor leftUpColor = screenQuad.mLeftUpColor;
+ RendererColor leftDownColor = screenQuad.mLeftDownColor;
+ RendererColor rightUpColor = screenQuad.mRightUpColor;
+ RendererColor rightDownColor = screenQuad.mRightDownColor;
+ const PxU8 alpha = PxU8(screenQuad.mAlpha*255.0f);
+ leftUpColor.a = alpha;
+ leftDownColor.a = alpha;
+ rightUpColor.a = alpha;
+ rightDownColor.a = alpha;
+
+ Verts[0].p = PxVec3(x0, sy, z); Verts[0].rhw = rhw; Verts[0].color = convertColor(leftDownColor);
+ Verts[1].p = PxVec3(x0, y0, z); Verts[1].rhw = rhw; Verts[1].color = convertColor(leftUpColor);
+ Verts[2].p = PxVec3(sx, sy, z); Verts[2].rhw = rhw; Verts[2].color = convertColor(rightDownColor);
+ Verts[3].p = PxVec3(sx, y0, z); Verts[3].rhw = rhw; Verts[3].color = convertColor(rightUpColor);
+
+ renderTextBuffer(Verts, 4, Indices, 6, screenquadMaterial);
+ resetScreenquadRenderStates();
+ }
+
+ if(m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ return true;
+}
+
+bool Renderer::drawTouchControls()
+{
+#if defined(RENDERER_TABLET)
+ renderControls();
+ renderButtons();
+#endif
+ return true;
+}
+
+#ifdef RENDERER_TABLET
+void Renderer::renderControls()
+{
+ m_controlMaterial->bind(RendererMaterial::PASS_UNLIT, m_controlMaterialInstance, false);
+ for(int i = 0; i < PX_ARRAY_SIZE(m_controlMesh); ++i)
+ {
+ RendererMeshContext ctx;
+ PxReal zero = 0;
+ physx::PxMat44 ctrl_transform(PxVec4(m_controlPos[i].x, zero, zero, zero),
+ PxVec4(m_controlPos[i].y, zero, zero, zero),
+ PxVec4(m_controlPos[i].x, zero, zero, zero),
+ PxVec4(zero, zero, zero, zero));
+ ctx.cullMode = RendererMeshContext::NONE;
+ ctx.transform = &ctrl_transform;
+ bindMeshContext(ctx);
+ m_controlMesh[i]->bind();
+ m_controlMesh[i]->render(m_controlMaterial);
+ m_controlMesh[i]->unbind();
+ }
+ m_controlMaterial->unbind();
+}
+
+std::vector<Renderer::TabletButton>& Renderer::screenButtons()
+{
+ return m_buttons;
+}
+
+void Renderer::renderButtons()
+{
+ if(m_buttons.size())
+ {
+ for(PxU32 i = 0; i < m_buttons.size(); ++i)
+ {
+ // if the button is not used do not draw it
+ if(m_buttons[i].emulatedKeyCode == 0)
+ continue;
+
+ const PxVec4& color = m_buttons[i].pressedCount ? m_buttons[i].pressedColor : m_buttons[i].defaultColor;
+ const RendererMaterial::Variable* var = m_buttons[i].materialInstance->findVariable("diffuseColor", RendererMaterial::VARIABLE_FLOAT4);
+ if(var)
+ {
+ const PxReal data[] = { color.x, color.y, color.z, color.w };
+ m_buttons[i].materialInstance->writeData(*var, data);
+ }
+
+ m_buttons[i].material->bind(RendererMaterial::PASS_UNLIT, m_buttons[i].materialInstance, false);
+ RendererMeshContext ctx;
+ physx::PxMat44 ctrl_transform(PxVec4(0.0f));
+ ctx.cullMode = RendererMeshContext::NONE;
+ ctx.transform = &ctrl_transform;
+
+ bindMeshContext(ctx);
+
+ m_buttons[i].mesh->bind();
+ m_buttons[i].mesh->render(m_buttons[i].material);
+ m_buttons[i].mesh->unbind();
+ m_buttons[i].material->unbind();
+ }
+ for(PxU32 i = 0; i < m_buttons.size(); ++i)
+ {
+ if(m_buttons[i].emulatedKeyCode == 0)
+ continue;
+
+ /* TODO: It seems that characters has variable width, so this code is not entirely valid,
+ even though it makes it look better than just printing text starting from
+ the center of the button */
+ const RendererColor textColor(255, 255, 255, 255);
+ const PxReal TEXT_CHARACTER_WIDTH = 12.0f;
+ const PxReal TEXT_CHARACTER_HEIGHT = 12.0f;
+ /* Convert relative buttons coordinates to the absolute screen coordinates */
+ PxU32 width, height;
+ getWindowSize(width, height);
+ PxVec2 absoluteLeftBottom = PxVec2((m_buttons[i].leftBottom.x + 1.0f) * ((PxReal)width / 2.0f), -(m_buttons[i].leftBottom.y - 1.0f) * ((PxReal)height / 2.0f));
+ PxVec2 absoluteRightTop = PxVec2((m_buttons[i].rightTop.x + 1.0f) * ((PxReal)width / 2.0f), -(m_buttons[i].rightTop.y - 1.0f) * ((PxReal)height / 2.0f));
+
+ PxVec2 absoluteCenter = (absoluteLeftBottom + absoluteRightTop) / 2.0f;
+ PxReal absoluteWidth = absoluteRightTop.x - absoluteLeftBottom.x;
+
+ /* Leave empty space for half character near edges of the button */
+ PxU8 characterToFit = (PxU8) (absoluteWidth / TEXT_CHARACTER_WIDTH - 1);
+
+ std::string text;
+ PxVec2 textPos;
+ /* Shrink text if not enough space and decide where to start printing */
+ if(characterToFit < m_buttons[i].text.size())
+ {
+ textPos.x = absoluteLeftBottom.x + TEXT_CHARACTER_WIDTH / 2.0f;
+ text = m_buttons[i].text.substr(0, characterToFit);
+ }
+ else
+ {
+ textPos.x = absoluteCenter.x - ((PxReal)m_buttons[i].text.size() / 2.0f) * TEXT_CHARACTER_WIDTH;
+ text = m_buttons[i].text;
+ }
+ textPos.y = absoluteCenter.y - TEXT_CHARACTER_HEIGHT / 2.0f;
+
+
+ print((PxU32) textPos.x,(PxU32) textPos.y, text.c_str(), 0.5f, 6.0f, textColor, true);
+ }
+ }
+}
+#endif
+
+bool Renderer::drawLines2D(PxU32 nbVerts, const PxReal* vertices, const RendererColor& color)
+{
+ RendererMaterial* screenquadMaterial;
+ RendererMaterialInstance* screenquadMaterialInstance;
+ screenquadMaterial = m_screenquadOpaqueMaterial;
+ screenquadMaterialInstance = m_screenquadOpaqueMaterialInstance;
+ if(!screenquadMaterialInstance || !screenquadMaterial)
+ return false;
+
+ screenquadMaterial->bind(RendererMaterial::PASS_UNLIT, screenquadMaterialInstance, false);
+ if(!m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ setupScreenquadRenderStates();
+
+ TextVertex* verts = new TextVertex[nbVerts];
+
+ PxU32 renderWidth, renderHeight;
+ getWindowSize(renderWidth, renderHeight);
+ const PxReal xCoeff = PxReal(renderWidth);
+ const PxReal yCoeff = PxReal(renderHeight);
+
+ const PxU32 convertedColor = convertColor(color);
+
+ for(PxU32 i=0;i<nbVerts;i++)
+ {
+ verts[i].p.x = vertices[i*2+0] * xCoeff;
+ verts[i].p.y = vertices[i*2+1] * yCoeff;
+ verts[i].p.z = 0.0f;
+ verts[i].rhw = 1.0f;
+ verts[i].color = convertedColor;
+ verts[i].u = 0.0f;
+ verts[i].v = 0.0f;
+ }
+
+ renderLines2D(verts, nbVerts);
+
+ DELETEARRAY(verts);
+
+ resetScreenquadRenderStates();
+ if(m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ return true;
+}
+
+bool Renderer::drawLines2D(PxU32 nbVerts, const PxReal* vertices, const RendererColor* colors)
+{
+ RendererMaterial* screenquadMaterial;
+ RendererMaterialInstance* screenquadMaterialInstance;
+ screenquadMaterial = m_screenquadOpaqueMaterial;
+ screenquadMaterialInstance = m_screenquadOpaqueMaterialInstance;
+ if(!screenquadMaterialInstance || !screenquadMaterial)
+ return false;
+
+ screenquadMaterial->bind(RendererMaterial::PASS_UNLIT, screenquadMaterialInstance, false);
+ if(!m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ setupScreenquadRenderStates();
+
+ TextVertex* verts = new TextVertex[nbVerts];
+
+ PxU32 renderWidth, renderHeight;
+ getWindowSize(renderWidth, renderHeight);
+ const PxReal xCoeff = PxReal(renderWidth);
+ const PxReal yCoeff = PxReal(renderHeight);
+
+ for(PxU32 i=0;i<nbVerts;i++)
+ {
+ verts[i].p.x = vertices[i*2+0] * xCoeff;
+ verts[i].p.y = vertices[i*2+1] * yCoeff;
+ verts[i].p.z = 0.0f;
+ verts[i].rhw = 1.0f;
+ verts[i].color = convertColor(colors[i]);
+ verts[i].u = 0.0f;
+ verts[i].v = 0.0f;
+ }
+
+ renderLines2D(verts, nbVerts);
+
+ DELETEARRAY(verts);
+
+ resetScreenquadRenderStates();
+ if(m_useShadersForTextRendering)
+ screenquadMaterial->unbind();
+
+ return true;
+}
+
+