/************************************************************************************ Filename : Render_Device.cpp Content : Platform renderer for simple scene graph - implementation Created : September 6, 2012 Authors : Andrew Reisse Copyright : Copyright 2012 Oculus VR, LLC All Rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ************************************************************************************/ #include "../Render/Render_Device.h" #include "../Render/Render_Font.h" #include "Kernel/OVR_Log.h" #include "Util/Util_Render_Stereo.h" using namespace OVR::Util::Render; namespace OVR { namespace Render { void Model::Render(const Matrix4f& ltw, RenderDevice* ren) { if(Visible) { AutoGpuProf prof(ren, "Model_Render"); Matrix4f m = ltw * GetMatrix(); ren->Render(m, this); } } void Container::Render(const Matrix4f& ltw, RenderDevice* ren) { Matrix4f m = ltw * GetMatrix(); for(unsigned i = 0; i < Nodes.GetSize(); i++) { Nodes[i]->Render(m, ren); } } Matrix4f SceneView::GetViewMatrix() const { Matrix4f view = Matrix4f(GetOrientation().Conj()) * Matrix4f::Translation(GetPosition()); return view; } void LightingParams::Update(const Matrix4f& view, const Vector3f* SceneLightPos) { Version++; for (int i = 0; i < LightCount; i++) { LightPos[i] = view.Transform(SceneLightPos[i]); } } void Scene::Render(RenderDevice* ren, const Matrix4f& view) { AutoGpuProf prof(ren, "Scene_Render"); Lighting.Update(view, LightPos); ren->SetLighting(&Lighting); World.Render(view, ren); } uint16_t CubeIndices[] = { 0, 1, 3, 3, 1, 2, 5, 4, 6, 6, 4, 7, 8, 9, 11, 11, 9, 10, 13, 12, 14, 14, 12, 15, 16, 17, 19, 19, 17, 18, 21, 20, 22, 22, 20, 23 }; // Colors are specified for planes perpendicular to the axis // For example, "xColor" is the color of the y-z plane Model* Model::CreateAxisFaceColorBox(float x1, float x2, Color xcolor, float y1, float y2, Color ycolor, float z1, float z2, Color zcolor) { float t; if(x1 > x2) { t = x1; x1 = x2; x2 = t; } if(y1 > y2) { t = y1; y1 = y2; y2 = t; } if(z1 > z2) { t = z1; z1 = z2; z2 = t; } Model* box = new Model(); uint16_t startIndex = 0; // Cube startIndex = box->AddVertex(Vector3f(x1, y2, z1), ycolor); box->AddVertex(Vector3f(x2, y2, z1), ycolor); box->AddVertex(Vector3f(x2, y2, z2), ycolor); box->AddVertex(Vector3f(x1, y2, z2), ycolor); box->AddVertex(Vector3f(x1, y1, z1), ycolor); box->AddVertex(Vector3f(x2, y1, z1), ycolor); box->AddVertex(Vector3f(x2, y1, z2), ycolor); box->AddVertex(Vector3f(x1, y1, z2), ycolor); box->AddVertex(Vector3f(x1, y1, z2), xcolor); box->AddVertex(Vector3f(x1, y1, z1), xcolor); box->AddVertex(Vector3f(x1, y2, z1), xcolor); box->AddVertex(Vector3f(x1, y2, z2), xcolor); box->AddVertex(Vector3f(x2, y1, z2), xcolor); box->AddVertex(Vector3f(x2, y1, z1), xcolor); box->AddVertex(Vector3f(x2, y2, z1), xcolor); box->AddVertex(Vector3f(x2, y2, z2), xcolor); box->AddVertex(Vector3f(x1, y1, z1), zcolor); box->AddVertex(Vector3f(x2, y1, z1), zcolor); box->AddVertex(Vector3f(x2, y2, z1), zcolor); box->AddVertex(Vector3f(x1, y2, z1), zcolor); box->AddVertex(Vector3f(x1, y1, z2), zcolor); box->AddVertex(Vector3f(x2, y1, z2), zcolor); box->AddVertex(Vector3f(x2, y2, z2), zcolor); box->AddVertex(Vector3f(x1, y2, z2), zcolor); enum { // CubeVertexCount = sizeof(CubeVertices)/sizeof(CubeVertices[0]), CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0]) }; // Renumber indices for(int i = 0; i < CubeIndexCount / 3; i++) { box->AddTriangle(CubeIndices[i * 3] + startIndex, CubeIndices[i * 3 + 1] + startIndex, CubeIndices[i * 3 + 2] + startIndex); } return box; } void Model::AddSolidColorBox(float x1, float y1, float z1, float x2, float y2, float z2, Color c) { float t; if(x1 > x2) { t = x1; x1 = x2; x2 = t; } if(y1 > y2) { t = y1; y1 = y2; y2 = t; } if(z1 > z2) { t = z1; z1 = z2; z2 = t; } // Cube vertices and their normals. Vector3f CubeVertices[][3] = { { Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f) }, { Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f) }, { Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f) }, { Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f) }, { Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f) }, { Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f) }, { Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f) }, { Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f) }, { Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f) }, { Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f) }, { Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f) }, { Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f) }, { Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f) }, { Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f) }, { Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f) }, { Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f) }, { Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f) }, { Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f) }, { Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f) }, { Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f) }, { Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f) }, { Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f) }, { Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f) }, { Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f) } }; uint16_t startIndex = GetNextVertexIndex(); enum { CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]), CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0]) }; for(int v = 0; v < CubeVertexCount; v++) { AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2])); } // Renumber indices for(int i = 0; i < CubeIndexCount / 3; i++) { AddTriangle(CubeIndices[i * 3] + startIndex, CubeIndices[i * 3 + 1] + startIndex, CubeIndices[i * 3 + 2] + startIndex); } } // Adds box at specified location to current vertices. void Model::AddBox(Color c, Vector3f origin, Vector3f size) { Vector3f s = size * 0.5f; Vector3f o = origin; uint16_t i = GetNextVertexIndex(); AddVertex(-s.x + o.x, s.y + o.y, -s.z + o.z, c, 0, 1, 0, 0, -1); AddVertex(s.x + o.x, s.y + o.y, -s.z + o.z, c, 1, 1, 0, 0, -1); AddVertex(s.x + o.x, -s.y + o.y, -s.z + o.z, c, 1, 0, 0, 0, -1); AddVertex(-s.x + o.x, -s.y + o.y, -s.z + o.z, c, 0, 0, 0, 0, -1); AddTriangle(2 + i, 1 + i, 0 + i); AddTriangle(0 + i, 3 + i, 2 + i); AddVertex(s.x + o.x, s.y + o.y, s.z + o.z, c, 1, 1, 0, 0, 1); AddVertex(-s.x+ o.x, s.y + o.y, s.z + o.z, c, 0, 1, 0, 0, 1); AddVertex(-s.x+ o.x, -s.y + o.y, s.z + o.z, c, 0, 0, 0, 0, 1); AddVertex(s.x + o.x, -s.y + o.y, s.z + o.z, c, 1, 0, 0, 0, 1); AddTriangle(6 + i, 5 + i, 4 + i); AddTriangle(4 + i, 7 + i, 6 + i); AddVertex(-s.x + o.x, s.y + o.y, -s.z + o.z, c, 1, 0, -1, 0, 0); AddVertex(-s.x + o.x, s.y + o.y, s.z + o.z, c, 1, 1, -1, 0, 0); AddVertex(-s.x + o.x, -s.y + o.y, s.z + o.z, c, 0, 1, -1, 0, 0); AddVertex(-s.x + o.x, -s.y + o.y, -s.z + o.z, c, 0, 0, -1, 0, 0); AddTriangle(10 + i, 11 + i, 8 + i); AddTriangle(8 + i, 9 + i, 10 + i); AddVertex(s.x + o.x, s.y + o.y, -s.z + o.z, c, 1, 0, 1, 0, 0); AddVertex(s.x + o.x, -s.y + o.y, -s.z + o.z, c, 0, 0, 1, 0, 0); AddVertex(s.x + o.x, -s.y + o.y, s.z + o.z, c, 0, 1, 1, 0, 0); AddVertex(s.x + o.x, s.y + o.y, s.z + o.z, c, 1, 1, 1, 0, 0); AddTriangle(14 + i, 15 + i, 12 + i); AddTriangle(12 + i, 13 + i, 14 + i); AddVertex(-s.x+ o.x, -s.y + o.y, s.z + o.z, c, 0, 1, 0, -1, 0); AddVertex(s.x + o.x, -s.y + o.y, s.z + o.z, c, 1, 1, 0, -1, 0); AddVertex(s.x + o.x, -s.y + o.y, -s.z + o.z, c, 1, 0, 0, -1, 0); AddVertex(-s.x+ o.x, -s.y + o.y, -s.z + o.z, c, 0, 0, 0, -1, 0); AddTriangle(18 + i, 19 + i, 16 + i); AddTriangle(16 + i, 17 + i, 18 + i); AddVertex(-s.x + o.x, s.y + o.y, -s.z + o.z, c, 0, 0, 0, 1, 0); AddVertex(s.x + o.x, s.y + o.y, -s.z + o.z, c, 1, 0, 0, 1, 0); AddVertex(s.x + o.x, s.y + o.y, s.z + o.z, c, 1, 1, 0, 1, 0); AddVertex(-s.x + o.x, s.y + o.y, s.z + o.z, c, 0, 1, 0, 1, 0); AddTriangle(20 + i, 21 + i, 22 + i); AddTriangle(22 + i, 23 + i, 20 + i); } Model* Model::CreateBox(Color c, Vector3f origin, Vector3f size) { Model *box = new Model(); box->AddBox(c, Vector3f(0), size); box->SetPosition(origin); return box; } // Triangulation of a cylinder centered at the origin Model* Model::CreateCylinder(Color color, Vector3f origin, float height, float radius, int sides) { Model *cyl = new Model(); float halfht = height * 0.5f; for(uint16_t i = 0; i < sides; i++) { float x = cosf(MATH_FLOAT_TWOPI * i / float(sides)); float y = sinf(MATH_FLOAT_TWOPI * i / float(sides)); cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, 0, 0, 1); cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, 0, 0, -1); uint16_t j = 0; if(i < sides - 1) { j = i + 1; cyl->AddTriangle(0, i * 4 + 4, i * 4); cyl->AddTriangle(1, i * 4 + 1, i * 4 + 5); } float nx = cosf(MATH_FLOAT_PI * (0.5f + 2.0f * i / float(sides))); float ny = sinf(MATH_FLOAT_PI * (0.5f + 2.0f * i / float(sides))); cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, nx, ny, 0); cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, nx, ny, 0); cyl->AddTriangle(i * 4 + 2, j * 4 + 2, i * 4 + 3); cyl->AddTriangle(i * 4 + 3, j * 4 + 2, j * 4 + 3); } cyl->SetPosition(origin); return cyl; }; //Triangulation of a cone centered at the origin Model* Model::CreateCone(Color color, Vector3f origin, float height, float radius, int sides) { Model *cone = new Model(); float halfht = height * 0.5f; cone->AddVertex(0.0f, 0.0f, -1.0f*halfht, color, 0, 0, 0, 0, -1); for(uint16_t i = 0; i < sides; i++) { float x = cosf(MATH_FLOAT_TWOPI * i / float(sides)); float y = sinf(MATH_FLOAT_TWOPI * i / float(sides)); cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, 0, 0, -1); uint16_t j = 1; if(i < sides - 1) { j = i + 1; } float next_x = cosf(MATH_FLOAT_TWOPI * j / float(sides)); float next_y = sinf(MATH_FLOAT_TWOPI * j / float(sides)); Vector3f normal = Vector3f(x, y, -halfht).Cross(Vector3f(next_x, next_y, -halfht)); cone->AddVertex(0.0f, 0.0f, halfht, color, 1, 0, normal.x, normal.y, normal.z); cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, normal.x, normal.y, normal.z); cone->AddTriangle(0, 3*i + 1, 3*j + 1); cone->AddTriangle(3*i + 2, 3*j + 3, 3*i + 3); } cone->SetPosition(origin); return cone; }; //Triangulation of a sphere centered at the origin Model* Model::CreateSphere(Color color, Vector3f origin, float radius, int sides) { Model *sphere = new Model(); uint16_t usides = (uint16_t) sides; uint16_t halfsides = usides/2; for(uint16_t k = 0; k < halfsides; k++) { float z = cosf(MATH_FLOAT_PI * k / float(halfsides)); float z_r = sinf(MATH_FLOAT_PI * k / float(halfsides)); // the radius of the cross circle with coordinate z if (k == 0) { // add north and south poles sphere->AddVertex(0.0f, 0.0f, radius, color, 0, 0, 0, 0, 1); sphere->AddVertex(0.0f, 0.0f, -radius, color, 1, 1, 0, 0, -1); } else { for(uint16_t i = 0; i < sides; i++) { float x = cosf(MATH_FLOAT_TWOPI * i / float(sides)) * z_r; float y = sinf(MATH_FLOAT_TWOPI * i / float(sides)) * z_r; uint16_t j = 0; if(i < sides - 1) { j = i + 1; } sphere->AddVertex(radius * x, radius * y, radius * z, color, 0, 1, x, y, z); uint16_t indi = 2 + (k -1)*usides + i; uint16_t indj = 2 + (k -1)*usides + j; if (k == 1) // NorthPole sphere->AddTriangle(0, j + 2, i + 2); else if (k == halfsides - 1) //SouthPole { sphere->AddTriangle(1, indi, indj); sphere->AddTriangle(indi, indi - usides, indj); sphere->AddTriangle(indi - usides, indj - usides, indj); } else { sphere->AddTriangle(indi, indi - usides, indj); sphere->AddTriangle(indi - usides, indj - usides, indj); } } } // end else } sphere->SetPosition(origin); return sphere; }; Model* Model::CreateGrid(Vector3f origin, Vector3f stepx, Vector3f stepy, int halfx, int halfy, int nmajor, Color minor, Color major) { Model* grid = new Model(Prim_Lines); float halfxf = (float)halfx; float halfyf = (float)halfy; for(int jn = 0; jn <= halfy; jn++) { float j = (float)jn; grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 0, 0.5f), grid->AddVertex((stepx * halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 1, 0.5f)); if(j) grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 0, 0.5f), grid->AddVertex((stepx * halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 1, 0.5f)); } for(int in = 0; in <= halfx; in++) { float i = (float)in; grid->AddLine(grid->AddVertex((stepx * i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f), grid->AddVertex((stepx * i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f)); if(i) grid->AddLine(grid->AddVertex((stepx * -i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f), grid->AddVertex((stepx * -i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f)); } grid->SetPosition(origin); return grid; } //------------------------------------------------------------------------------------- void ShaderFill::Set(PrimitiveType prim) const { Shaders->Set(prim); for(int i = 0; i < 8 && VtxTextures[i] != NULL; ++i) VtxTextures[i]->Set(i, Shader_Vertex); for (int i = 0; i < 8 && Textures[i] != NULL; ++i) Textures[i]->Set(i); } //------------------------------------------------------------------------------------- // ***** Rendering RenderDevice::RenderDevice() : DistortionClearColor(0, 0, 0), TotalTextureMemoryUsage(0), FadeOutBorderFraction(0) { // Ensure these are different, so that the first time it's run, things actually get initialized. PostProcessShaderActive = PostProcessShader_Count; PostProcessShaderRequested = PostProcessShader_DistortionAndChromAb; } void RenderDevice::Shutdown() { // This runs before the subclass's Shutdown(), where the context, etc, may be deleted. pTextVertexBuffer.Clear(); pPostProcessShader.Clear(); pFullScreenVertexBuffer.Clear(); pDistortionMeshVertexBuffer[0].Clear(); pDistortionMeshVertexBuffer[1].Clear(); pDistortionMeshIndexBuffer[0].Clear(); pDistortionMeshIndexBuffer[1].Clear(); LightingBuffer.Clear(); } Fill* RenderDevice::CreateTextureFill(Render::Texture* t, bool useAlpha) { ShaderSet* shaders = CreateShaderSet(); shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP)); shaders->SetShader(LoadBuiltinShader(Shader_Fragment, useAlpha ? FShader_AlphaTexture : FShader_Texture)); Fill* f = new ShaderFill(*shaders); f->SetTexture(0, t); return f; } void LightingParams::Set(ShaderSet* s) const { s->SetUniform4fvArray("Ambient", 1, &Ambient); s->SetUniform1f("LightCount", LightCount); s->SetUniform4fvArray("LightPos", (int)LightCount, LightPos); s->SetUniform4fvArray("LightColor", (int)LightCount, LightColor); } void RenderDevice::SetLighting(const LightingParams* lt) { if (!LightingBuffer) LightingBuffer = *CreateBuffer(); LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams)); SetCommonUniformBuffer(1, LightingBuffer); } float RenderDevice::MeasureText(const Font* font, const char* str, float size, float strsize[2], const size_t charRange[2], Vector2f charRangeRect[2]) { size_t length = strlen(str); float w = 0; float xp = 0; float yp = 0; for (size_t i = 0; i < length; i++) { if (str[i] == '\n') { yp += font->lineheight; if(xp > w) { w = xp; } xp = 0; continue; } // Record top-left charRange rectangle coordinate. if (charRange && charRangeRect && (i == charRange[0])) charRangeRect[0] = Vector2f(xp, yp); // Tab followed by a numbers sets position to specified offset. if (str[i] == '\t') { char *p = 0; float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10); i += p - (str + i + 1); xp = tabPixels; } else { const Font::Char* ch = &font->chars[str[i]]; xp += ch->advance; } // End of character range. // Store 'xp' after advance, yp will advance later. if (charRange && charRangeRect && (i == charRange[1])) charRangeRect[1] = Vector2f(xp, yp); } if (xp > w) { w = xp; } float scale = (size / font->lineheight); if (strsize) { strsize[0] = scale * w; strsize[1] = scale * (yp + font->lineheight); } if (charRange && charRangeRect) { // Selection rectangle ends in teh bottom. charRangeRect[1].y += font->lineheight; charRangeRect[0] *= scale; charRangeRect[1] *= scale; } return (size / font->lineheight) * w; } void RenderDevice::RenderText(const Font* font, const char* str, float x, float y, float size, Color c, const Matrix4f* view) { size_t length = strlen(str); // Do not attempt to render if we have an empty string. if (length == 0) { return; } if(!pTextVertexBuffer) { pTextVertexBuffer = *CreateBuffer(); if(!pTextVertexBuffer) { return; } } if(!font->fill) { font->fill = CreateTextureFill(Ptr( *CreateTexture(Texture_R, font->twidth, font->theight, font->tex)), true); } pTextVertexBuffer->Data(Buffer_Vertex, NULL, length * 6 * sizeof(Vertex)); Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, length * 6 * sizeof(Vertex), Map_Discard); if(!vertices) { return; } Matrix4f m = Matrix4f(size / font->lineheight, 0, 0, 0, 0, size / font->lineheight, 0, 0, 0, 0, 0, 0, x, y, 0, 1).Transposed(); if (view) m = (*view) * m; float xp = 0, yp = (float)font->ascent; int ivertex = 0; for (size_t i = 0; i < length; i++) { if(str[i] == '\n') { yp += font->lineheight; xp = 0; continue; } // Tab followed by a numbers sets position to specified offset. if(str[i] == '\t') { char *p = 0; float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10); i += p - (str + i + 1); xp = tabPixels; continue; } const Font::Char* ch = &font->chars[str[i]]; Vertex* chv = &vertices[ivertex]; for(int j = 0; j < 6; j++) { chv[j].C = c; } float x = xp + ch->x; float y = yp - ch->y; float cx = font->twidth * (ch->u2 - ch->u1); float cy = font->theight * (ch->v2 - ch->v1); chv[0] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1); chv[1] = Vertex(Vector3f(x + cx, y, 0), c, ch->u2, ch->v1); chv[2] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2); chv[3] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1); chv[4] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2); chv[5] = Vertex(Vector3f(x, y + cy, 0), c, ch->u1, ch->v2); ivertex += 6; xp += ch->advance; } pTextVertexBuffer->Unmap(vertices); Render(font->fill, pTextVertexBuffer, NULL, m, 0, ivertex, Prim_Triangles); } void RenderDevice::FillRect(float left, float top, float right, float bottom, Color c, const Matrix4f* matrix) { if(!pTextVertexBuffer) { pTextVertexBuffer = *CreateBuffer(); if(!pTextVertexBuffer) { return; } } // Get!! Fill* fill = CreateSimpleFill(); pTextVertexBuffer->Data(Buffer_Vertex, NULL, 6 * sizeof(Vertex)); Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, 6 * sizeof(Vertex), Map_Discard); if(!vertices) { return; } vertices[0] = Vertex(Vector3f(left, top, 0.0f), c); vertices[1] = Vertex(Vector3f(right, top, 0.0f), c); vertices[2] = Vertex(Vector3f(left, bottom, 0.0f), c); vertices[3] = Vertex(Vector3f(left, bottom, 0.0f), c); vertices[4] = Vertex(Vector3f(right, top, 0.0f), c); vertices[5] = Vertex(Vector3f(right, bottom, 0.0f), c); pTextVertexBuffer->Unmap(vertices); if (matrix == NULL) Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, 6, Prim_Triangles); else Render(fill, pTextVertexBuffer, NULL, *matrix, 0, 6, Prim_Triangles); } void RenderDevice::FillGradientRect(float left, float top, float right, float bottom, Color col_top, Color col_btm, const Matrix4f* matrix) { if(!pTextVertexBuffer) { pTextVertexBuffer = *CreateBuffer(); if(!pTextVertexBuffer) { return; } } // Get!! Fill* fill = CreateSimpleFill(); pTextVertexBuffer->Data(Buffer_Vertex, NULL, 6 * sizeof(Vertex)); Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, 6 * sizeof(Vertex), Map_Discard); if(!vertices) { return; } vertices[0] = Vertex(Vector3f(left, top, 0.0f), col_top); vertices[1] = Vertex(Vector3f(right, top, 0.0f), col_top); vertices[2] = Vertex(Vector3f(left, bottom, 0.0f), col_btm); vertices[3] = Vertex(Vector3f(left, bottom, 0.0f), col_btm); vertices[4] = Vertex(Vector3f(right, top, 0.0f), col_top); vertices[5] = Vertex(Vector3f(right, bottom, 0.0f), col_btm); pTextVertexBuffer->Unmap(vertices); if (matrix) Render(fill, pTextVertexBuffer, NULL, *matrix, 0, 6, Prim_Triangles); else Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, 6, Prim_Triangles); } void RenderDevice::FillTexturedRect(float left, float top, float right, float bottom, float ul, float vt, float ur, float vb, Color c, Ptr tex) { if(!pTextVertexBuffer) { pTextVertexBuffer = *CreateBuffer(); if(!pTextVertexBuffer) { return; } } static Fill *fill = NULL; if ( fill == NULL ) { fill = CreateTextureFill(tex, false); } fill->SetTexture ( 0, tex ); pTextVertexBuffer->Data(Buffer_Vertex, NULL, 6 * sizeof(Vertex)); Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, 6 * sizeof(Vertex), Map_Discard); if(!vertices) { return; } vertices[0] = Vertex(Vector3f(left, top, 0.0f), c, ul, vt); vertices[1] = Vertex(Vector3f(right, top, 0.0f), c, ur, vt); vertices[2] = Vertex(Vector3f(left, bottom, 0.0f), c, ul, vb); vertices[3] = Vertex(Vector3f(left, bottom, 0.0f), c, ul, vb); vertices[4] = Vertex(Vector3f(right, top, 0.0f), c, ur, vt); vertices[5] = Vertex(Vector3f(right, bottom, 0.0f), c, ur, vb); pTextVertexBuffer->Unmap(vertices); Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, 6, Prim_Triangles); } void RenderDevice::RenderLines ( int NumLines, Color c, float *x, float *y, float *z /*= NULL*/ ) { OVR_ASSERT ( x != NULL ); OVR_ASSERT ( y != NULL ); // z can be NULL for 2D stuff. if(!pTextVertexBuffer) { pTextVertexBuffer = *CreateBuffer(); if(!pTextVertexBuffer) { return; } } // Get!! Fill* fill = CreateSimpleFill(); int NumVerts = NumLines * 2; pTextVertexBuffer->Data(Buffer_Vertex, NULL, NumVerts * sizeof(Vertex)); Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, NumVerts * sizeof(Vertex), Map_Discard); if(!vertices) { return; } if ( z != NULL ) { for ( int VertNum = 0; VertNum < NumVerts; VertNum++ ) { vertices[VertNum] = Vertex(Vector3f(x[VertNum], y[VertNum], z[VertNum]), c); } } else { for ( int VertNum = 0; VertNum < NumVerts; VertNum++ ) { vertices[VertNum] = Vertex(Vector3f(x[VertNum], y[VertNum], 1.0f), c); } } pTextVertexBuffer->Unmap(vertices); Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, NumVerts, Prim_Lines); } void RenderDevice::RenderImage(float left, float top, float right, float bottom, ShaderFill* image, unsigned char alpha, const Matrix4f* view) { Color c = Color(255, 255, 255, alpha); Ptr m = *new Model(Prim_Triangles); m->AddVertex(left, bottom, 0.0f, c, 0.0f, 0.0f); m->AddVertex(right, bottom, 0.0f, c, 1.0f, 0.0f); m->AddVertex(right, top, 0.0f, c, 1.0f, 1.0f); m->AddVertex(left, top, 0.0f, c, 0.0f, 1.0f); m->AddTriangle(2,1,0); m->AddTriangle(0,3,2); m->Fill = image; if (view) Render(*view, m); else Render(Matrix4f(), m); } bool RenderDevice::initPostProcessSupport(PostProcessType pptype) { if(pptype == PostProcess_None) { return true; } if (PostProcessShaderRequested != PostProcessShaderActive) { pPostProcessShader.Clear(); PostProcessShaderActive = PostProcessShaderRequested; } if (!pPostProcessShader) { Shader *vs = NULL; Shader *ppfs = NULL; if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb) { ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb); vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess); } else if (PostProcessShaderActive == PostProcessShader_MeshDistortionAndChromAb || PostProcessShaderActive == PostProcessShader_MeshDistortionAndChromAbHeightmapTimewarp) { ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessMeshWithChromAb); vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcessMesh); } else if (PostProcessShaderActive == PostProcessShader_MeshDistortionAndChromAbTimewarp) { ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessMeshWithChromAbTimewarp); vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcessMeshTimewarp); } else if (PostProcessShaderActive == PostProcessShader_MeshDistortionAndChromAbPositionalTimewarp) { ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessMeshWithChromAbPositionalTimewarp); vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcessMeshPositionalTimewarp); } else { OVR_ASSERT(false); } OVR_ASSERT(ppfs); // Means the shader failed to compile - look in the debug spew. OVR_ASSERT(vs); pPostProcessShader = *CreateShaderSet(); pPostProcessShader->SetShader(vs); pPostProcessShader->SetShader(ppfs); } // Heightmap method does the timewarp on the first pass if (!pPostProcessHeightmapShader && PostProcessShaderActive == PostProcessShader_MeshDistortionAndChromAbHeightmapTimewarp) { Shader *vs = NULL; Shader *ppfs = NULL; ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessHeightmapTimewarp); vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcessHeightmapTimewarp); OVR_ASSERT(ppfs); // Means the shader failed to compile - look in the debug spew. OVR_ASSERT(vs); pPostProcessHeightmapShader = *CreateShaderSet(); pPostProcessHeightmapShader->SetShader(vs); pPostProcessHeightmapShader->SetShader(ppfs); } if(!pFullScreenVertexBuffer) { pFullScreenVertexBuffer = *CreateBuffer(); const Render::Vertex QuadVertices[] = { Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0), Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0), Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1), Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1) }; pFullScreenVertexBuffer->Data(Buffer_Vertex | Buffer_ReadOnly, QuadVertices, sizeof(QuadVertices)); } return true; } void RenderDevice::SetProjection(const Matrix4f& proj) { Proj = proj; SetWorldUniforms(proj); } void RenderDevice::BeginScene(PostProcessType pptype) { BeginRendering(); initPostProcessSupport(pptype); SetWorldUniforms(Proj); SetExtraShaders(NULL); } void RenderDevice::FinishScene() { SetExtraShaders(0); //SetDefaultRenderTarget(); } void RenderDevice::PrecalculatePostProcess(PostProcessType pptype, const StereoEyeParams &stereoParamsLeft, const StereoEyeParams &stereoParamsRight, const HmdRenderInfo &hmdRenderInfo ) { PostProcessingType = pptype; if (( pptype == PostProcess_MeshDistortion ) || ( pptype == PostProcess_MeshDistortionTimewarp ) || ( pptype == PostProcess_MeshDistortionPositionalTimewarp ) || ( pptype == PostProcess_MeshDistortionHeightmapTimewarp)) { for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; // Get the mesh data. int numVerts = 0; int numTris = 0; DistortionMeshVertexData *pRawVerts = NULL; uint16_t *pIndices = NULL; DistortionMeshCreate ( &pRawVerts, &pIndices, &numVerts, &numTris, stereoParams, hmdRenderInfo ); int numIndices = numTris * 3; // Convert to final vertex data. DistortionVertex *pVerts = (DistortionVertex*)OVR_ALLOC ( sizeof(DistortionVertex) * numVerts ); DistortionVertex *pCurVert = pVerts; DistortionMeshVertexData *pCurRawVert = pRawVerts; for ( int vertNum = 0; vertNum < numVerts; vertNum++ ) { pCurVert->Pos.x = pCurRawVert->ScreenPosNDC.x; pCurVert->Pos.y = pCurRawVert->ScreenPosNDC.y; pCurVert->TexR = pCurRawVert->TanEyeAnglesR; pCurVert->TexG = pCurRawVert->TanEyeAnglesG; pCurVert->TexB = pCurRawVert->TanEyeAnglesB; // Convert [0.0f,1.0f] to [0,255] pCurVert->Col.R = (uint8_t)( floorf ( Alg::Max ( pCurRawVert->Shade, 0.0f ) * 255.999f ) ); pCurVert->Col.G = pCurVert->Col.R; pCurVert->Col.B = pCurVert->Col.R; pCurVert->Col.A = (uint8_t)( floorf ( pCurRawVert->TimewarpLerp * 255.999f ) ); pCurRawVert++; pCurVert++; } DistortionMeshNumTris[eyeNum] = numTris; pDistortionMeshVertexBuffer[eyeNum] = *CreateBuffer(); pDistortionMeshVertexBuffer[eyeNum]->Data ( Buffer_Vertex | Buffer_ReadOnly, pVerts, sizeof(DistortionVertex) * numVerts ); pDistortionMeshIndexBuffer[eyeNum] = *CreateBuffer(); pDistortionMeshIndexBuffer[eyeNum]->Data ( Buffer_Index | Buffer_ReadOnly, pIndices, ( sizeof(uint16_t) * numIndices ) ); DistortionMeshDestroy ( pRawVerts, pIndices ); OVR_FREE ( pVerts ); } if(pptype == PostProcess_MeshDistortionHeightmapTimewarp) { // Create the positional timewarp rectangular heightmap mesh for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; // Get the mesh data. int numVerts = 0; int numTris = 0; HeightmapMeshVertexData *pRawVerts = NULL; uint16_t *pIndices = NULL; HeightmapMeshCreate ( &pRawVerts, &pIndices, &numVerts, &numTris, stereoParams, hmdRenderInfo ); int numIndices = numTris * 3; // Convert to final vertex data. HeightmapVertex *pVerts = (HeightmapVertex*)OVR_ALLOC ( sizeof(HeightmapVertex) * numVerts ); HeightmapVertex *pCurVert = pVerts; HeightmapMeshVertexData *pCurRawVert = pRawVerts; for ( int vertNum = 0; vertNum < numVerts; vertNum++ ) { pCurVert->Pos.x = pCurRawVert->ScreenPosNDC.x; pCurVert->Pos.y = pCurRawVert->ScreenPosNDC.y; Vector2f texCoord = pCurRawVert->TanEyeAngles; pCurVert->Tex.x = texCoord.x; pCurVert->Tex.y = texCoord.y; pCurVert->Tex.z = (uint8_t)( floorf ( pCurRawVert->TimewarpLerp * 255.999f ) ); pCurRawVert++; pCurVert++; } HeightmapMeshNumTris[eyeNum] = numTris; pHeightmapMeshVertexBuffer[eyeNum] = *CreateBuffer(); pHeightmapMeshVertexBuffer[eyeNum]->Data ( Buffer_Vertex, pVerts, sizeof(HeightmapVertex) * numVerts ); pHeightmapMeshIndexBuffer[eyeNum] = *CreateBuffer(); pHeightmapMeshIndexBuffer[eyeNum]->Data ( Buffer_Index, pIndices, ( sizeof(uint16_t) * numIndices ) ); HeightmapMeshDestroy ( pRawVerts, pIndices ); OVR_FREE ( pVerts ); } } } else { // ...no setup needed for other distortion types. } } void RenderDevice::ApplyPostProcess(Matrix4f const &matNowFromWorldStart, Matrix4f const &matNowFromWorldEnd, Matrix4f const &matRenderFromWorldLeft, Matrix4f const &matRenderFromWorldRight, StereoEyeParams const &stereoParamsLeft, StereoEyeParams const &stereoParamsRight, RenderTarget* pHmdSpaceLayerRenderTargetLeftOrBothEyes, RenderTarget* pHmdSpaceLayerRenderTargetRight, RenderTarget* pOverlayLayerRenderTargetLeftOrBothEyes, RenderTarget* pOverlayLayerRenderTargetRight, RenderTarget* pOutputTarget) { if(pOutputTarget != NULL) { SetRenderTarget(pOutputTarget->pColorTex, pOutputTarget->pDepthTex); } else { SetDefaultRenderTarget(); } SetExtraShaders(0); bool usingOverlay = pOverlayLayerRenderTargetLeftOrBothEyes != NULL; switch( PostProcessingType ) { case PostProcess_MeshDistortion: { Recti vp ( 0, 0, WindowWidth, WindowHeight ); SetViewport(vp); float r, g, b, a; DistortionClearColor.GetRGBA(&r, &g, &b, &a); Clear(r, g, b, a); Matrix4f dummy; ShaderFill fill(pPostProcessShader); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetLeftOrBothEyes->pColorTex : NULL)); pPostProcessShader->SetUniform1f("UseOverlay", usingOverlay ? 1.0f : 0.0f); pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParamsLeft.EyeToSourceUV.Scale.x, stereoParamsLeft.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParamsLeft.EyeToSourceUV.Offset.x, stereoParamsLeft.EyeToSourceUV.Offset.y ); Render(&fill, pDistortionMeshVertexBuffer[0], pDistortionMeshIndexBuffer[0], dummy, 0, DistortionMeshNumTris[0] * 3, Prim_Triangles, Mesh_Distortion); if ( pHmdSpaceLayerRenderTargetRight != NULL ) { fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetRight->pColorTex : NULL)); } pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParamsRight.EyeToSourceUV.Scale.x, stereoParamsRight.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParamsRight.EyeToSourceUV.Offset.x, stereoParamsRight.EyeToSourceUV.Offset.y ); Render(&fill, pDistortionMeshVertexBuffer[1], pDistortionMeshIndexBuffer[1], dummy, 0, DistortionMeshNumTris[1] * 3, Prim_Triangles, Mesh_Distortion); } break; case PostProcess_MeshDistortionTimewarp: { Recti vp ( 0, 0, WindowWidth, WindowHeight ); SetViewport(vp); float r, g, b, a; DistortionClearColor.GetRGBA(&r, &g, &b, &a); Clear(r, g, b, a); ShaderFill fill(pPostProcessShader); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetLeftOrBothEyes->pColorTex : NULL)); pPostProcessShader->SetUniform1f("UseOverlay", usingOverlay ? 1.0f : 0.0f); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { Matrix4f const &matRenderFromWorld = ( eyeNum == 0 ) ? matRenderFromWorldLeft : matRenderFromWorldRight; const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; Matrix4f matRenderFromNowStart = TimewarpComputePoseDelta ( matRenderFromWorld, matNowFromWorldStart, stereoParams.HmdToEyeViewOffset ); Matrix4f matRenderFromNowEnd = TimewarpComputePoseDelta ( matRenderFromWorld, matNowFromWorldEnd, stereoParams.HmdToEyeViewOffset ); pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParams.EyeToSourceUV.Scale.x, stereoParams.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParams.EyeToSourceUV.Offset.x, stereoParams.EyeToSourceUV.Offset.y ); pPostProcessShader->SetUniform3x3f("EyeRotationStart", matRenderFromNowStart); pPostProcessShader->SetUniform3x3f("EyeRotationEnd", matRenderFromNowEnd); Matrix4f dummy; if ( ( pHmdSpaceLayerRenderTargetRight != NULL ) && ( eyeNum == 1 ) ) { fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetRight->pColorTex : NULL)); } Render(&fill, pDistortionMeshVertexBuffer[eyeNum], pDistortionMeshIndexBuffer[eyeNum], dummy, 0, DistortionMeshNumTris[eyeNum] * 3, Prim_Triangles, Mesh_Distortion); } } break; case PostProcess_MeshDistortionPositionalTimewarp: { Recti vp( 0, 0, WindowWidth, WindowHeight ); SetViewport(vp); float r, g, b, a; DistortionClearColor.GetRGBA(&r, &g, &b, &a); Clear(r, g, b, a); ShaderFill fill(pPostProcessShader); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex ); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pDepthTex, Shader_Vertex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetLeftOrBothEyes->pColorTex : NULL)); pPostProcessShader->SetUniform1f("UseOverlay", usingOverlay ? 1.0f : 0.0f); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { Matrix4f const &matRenderFromWorld = ( eyeNum == 0 ) ? matRenderFromWorldLeft : matRenderFromWorldRight; const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; Matrix4f matRenderFromNowStart = TimewarpComputePoseDelta ( matRenderFromWorld, matNowFromWorldStart, stereoParams.HmdToEyeViewOffset ); Matrix4f matRenderFromNowEnd = TimewarpComputePoseDelta ( matRenderFromWorld, matNowFromWorldEnd, stereoParams.HmdToEyeViewOffset ); pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParams.EyeToSourceUV.Scale.x, stereoParams.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParams.EyeToSourceUV.Offset.x, stereoParams.EyeToSourceUV.Offset.y ); // DepthProjector values can also be calculated as: // float DepthProjectorX = FarClip / (FarClip - NearClip); // float DepthProjectorY = (-FarClip * NearClip) / (FarClip - NearClip); pPostProcessShader->SetUniform2f("DepthProjector", -stereoParams.RenderedProjection.M[2][2], stereoParams.RenderedProjection.M[2][3]); pPostProcessShader->SetUniform2f("DepthDimSize", (float)pHmdSpaceLayerRenderTargetLeftOrBothEyes->Size.w, (float)pHmdSpaceLayerRenderTargetLeftOrBothEyes->Size.h); pPostProcessShader->SetUniform4x4f("EyeRotationStart", matRenderFromNowStart); pPostProcessShader->SetUniform4x4f("EyeRotationEnd", matRenderFromNowEnd); Matrix4f dummy; if ( ( pHmdSpaceLayerRenderTargetRight != NULL ) && ( eyeNum == 1 ) ) { OVR_ASSERT(pHmdSpaceLayerRenderTargetRight->pDepthTex != NULL); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pColorTex ); fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pDepthTex, Shader_Vertex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetRight->pColorTex : NULL)); } Render(&fill, pDistortionMeshVertexBuffer[eyeNum], pDistortionMeshIndexBuffer[eyeNum], dummy, 0, DistortionMeshNumTris[eyeNum] * 3, Prim_Triangles, Mesh_Distortion); } } break; case PostProcess_MeshDistortionHeightmapTimewarp: { // Create pass1 textures if not already done for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { Texture* templateTexture = NULL; switch(eyeNum) { case 0: templateTexture = pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex; break; case 1: templateTexture = pHmdSpaceLayerRenderTargetRight->pColorTex; break; default: OVR_ASSERT(false); } if( templateTexture == NULL) { if(HeightmapTimewarpRTs[eyeNum].pColorTex != NULL) { HeightmapTimewarpRTs[eyeNum].pColorTex = NULL; HeightmapTimewarpRTs[eyeNum].pDepthTex = NULL; } } else if( HeightmapTimewarpRTs[eyeNum].pColorTex == NULL || HeightmapTimewarpRTs[eyeNum].Size.w != templateTexture->GetWidth() || HeightmapTimewarpRTs[eyeNum].Size.h != templateTexture->GetHeight()) { HeightmapTimewarpRTs[eyeNum].Size.w = templateTexture->GetWidth(); HeightmapTimewarpRTs[eyeNum].Size.h = templateTexture->GetHeight(); HeightmapTimewarpRTs[eyeNum].pColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | templateTexture->GetSamples(), HeightmapTimewarpRTs[eyeNum].Size.w, HeightmapTimewarpRTs[eyeNum].Size.h, NULL); HeightmapTimewarpRTs[eyeNum].pColorTex->SetSampleMode ( Sample_ClampBorder | Sample_Linear); HeightmapTimewarpRTs[eyeNum].pDepthTex = *CreateTexture(Texture_Depth | Texture_RenderTarget | Texture_SampleDepth | templateTexture->GetSamples(), HeightmapTimewarpRTs[eyeNum].Size.w, HeightmapTimewarpRTs[eyeNum].Size.h, NULL); } } Matrix4f identity; // Pass 1 - do heightmap-based positional time warp { SetDepthMode(true, true); ShaderFill heightmapFill(pPostProcessHeightmapShader); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; switch(eyeNum) { case 0: { heightmapFill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex ); heightmapFill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pDepthTex, Shader_Vertex ); SetRenderTarget(HeightmapTimewarpRTs[eyeNum]); // output to temp buffers } break; case 1: if ( ( pHmdSpaceLayerRenderTargetRight != NULL ) ) { heightmapFill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pColorTex ); heightmapFill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pDepthTex, Shader_Vertex ); SetRenderTarget(HeightmapTimewarpRTs[eyeNum]); // output to temp buffers } break; default: OVR_ASSERT(false); } SetViewport(stereoParams.RenderedViewport); Clear(); Matrix4f const &matRenderFromWorld = ( eyeNum == 0 ) ? matRenderFromWorldLeft : matRenderFromWorldRight; Matrix4f matRenderFromNowStart = TimewarpComputePoseDeltaPosition ( matRenderFromWorld, matNowFromWorldStart, stereoParams.HmdToEyeViewOffset ); Matrix4f matRenderFromNowEnd = TimewarpComputePoseDeltaPosition ( matRenderFromWorld, matNowFromWorldEnd, stereoParams.HmdToEyeViewOffset ); pPostProcessHeightmapShader->SetUniform2f("EyeToSourceUVScale", stereoParams.EyeToSourceUV.Scale.x, stereoParams.EyeToSourceUV.Scale.y ); pPostProcessHeightmapShader->SetUniform2f("EyeToSourceUVOffset", stereoParams.EyeToSourceUV.Offset.x, stereoParams.EyeToSourceUV.Offset.y ); pPostProcessHeightmapShader->SetUniform2f("DepthDimSize", (float)pHmdSpaceLayerRenderTargetLeftOrBothEyes->Size.w, (float)pHmdSpaceLayerRenderTargetLeftOrBothEyes->Size.h); // TODO: Combining "proj * xform * invProj" leads to artifacts due to precision loss with the inversion pPostProcessHeightmapShader->SetUniform4x4f("EyeXformStart", stereoParams.RenderedProjection * matRenderFromNowStart); pPostProcessHeightmapShader->SetUniform4x4f("EyeXformEnd", stereoParams.RenderedProjection * matRenderFromNowEnd); //pPostProcessHeightmapShader->SetUniform4x4f("EyeXformStart", stereoParams.RenderedProjection * matRenderFromNowStart * stereoParams.RenderedProjection.Inverted()); //pPostProcessHeightmapShader->SetUniform4x4f("EyeXformEnd", stereoParams.RenderedProjection * matRenderFromNowEnd * stereoParams.RenderedProjection.Inverted()); //pPostProcessHeightmapShader->SetUniform4x4f("Projection", stereoParams.RenderedProjection); pPostProcessHeightmapShader->SetUniform4x4f("InvProjection", stereoParams.RenderedProjection.Inverted()); Render(&heightmapFill, pHeightmapMeshVertexBuffer[eyeNum], pHeightmapMeshIndexBuffer[eyeNum], identity, 0, HeightmapMeshNumTris[eyeNum] * 3, Prim_Triangles, Mesh_Heightmap); } } // Pass 2 - do distortion { if(pOutputTarget != NULL) { SetRenderTarget(pOutputTarget->pColorTex, pOutputTarget->pDepthTex); } else { SetDefaultRenderTarget(); } SetDepthMode(false, false); Recti vp( 0, 0, WindowWidth, WindowHeight ); SetViewport(vp); float r, g, b, a; DistortionClearColor.GetRGBA(&r, &g, &b, &a); Clear(r, g, b, a); ShaderFill fill(pPostProcessShader); fill.SetTexture ( 0, HeightmapTimewarpRTs[0].pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetLeftOrBothEyes->pColorTex : NULL)); pPostProcessShader->SetUniform1f("UseOverlay", usingOverlay ? 1.0f : 0.0f); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; // TODO: Could avoid the need for these vars since the mesh doesn't actually to time warping pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParams.EyeToSourceUV.Scale.x, stereoParams.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParams.EyeToSourceUV.Offset.x, stereoParams.EyeToSourceUV.Offset.y ); if ( ( HeightmapTimewarpRTs[1].pColorTex != NULL ) && ( eyeNum == 1 ) ) { fill.SetTexture ( 0, HeightmapTimewarpRTs[1].pColorTex ); fill.SetTexture ( 1, (usingOverlay ? pOverlayLayerRenderTargetRight->pColorTex : NULL)); } Render(&fill, pDistortionMeshVertexBuffer[eyeNum], pDistortionMeshIndexBuffer[eyeNum], identity, 0, DistortionMeshNumTris[eyeNum] * 3, Prim_Triangles, Mesh_Distortion); } } } break; case PostProcess_PixelDistortion: { float r, g, b, a; DistortionClearColor.GetRGBA(&r, &g, &b, &a); Clear(r, g, b, a); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; // Always use the half-FB viewport, not the "current scene" VP which may be totally different. SetViewport(stereoParams.DistortionViewport); // TODO: precalc a bunch of things to make the shader faster. // Note that the shader currently doesn't use Distortion.K[0], it hardwires it to 1.0. pPostProcessShader->SetUniform4f("HmdWarpParam", 1.0f, stereoParams.Distortion.Lens.K[1], stereoParams.Distortion.Lens.K[2], stereoParams.Distortion.Lens.K[3]); // We now only support one distortion type for shaders. OVR_ASSERT ( stereoParams.Distortion.Lens.Eqn == Distortion_RecipPoly4 ); pPostProcessShader->SetUniform3f("DistortionClearColor", DistortionClearColor.R, DistortionClearColor.G, DistortionClearColor.B ); // These are stored as deltas off the "main" distortion coefficients, but // in the shader we use them as absolute values. pPostProcessShader->SetUniform4f("ChromAbParam", stereoParams.Distortion.Lens.ChromaticAberration[0] + 1.0f, stereoParams.Distortion.Lens.ChromaticAberration[1], stereoParams.Distortion.Lens.ChromaticAberration[2] + 1.0f, stereoParams.Distortion.Lens.ChromaticAberration[3]); // From [-1,1] space (for a single eye), offset by LensCenter, scaled to TanEyeAngle space. // Done this way so that the shader can do a multiply-add, rather than a subtract-then-multiply. Vector2f localTanEyeAngleOffset = -stereoParams.Distortion.LensCenter.EntrywiseMultiply(stereoParams.Distortion.TanEyeAngleScale); pPostProcessShader->SetUniform2f("TanEyeAngleScale", stereoParams.Distortion.TanEyeAngleScale.x, stereoParams.Distortion.TanEyeAngleScale.y); pPostProcessShader->SetUniform2f("TanEyeAngleOffset", localTanEyeAngleOffset.x, localTanEyeAngleOffset.y); // From TanEyeAngle space to the source the app rendered the standard projective scene to. pPostProcessShader->SetUniform2f("EyeToSourceUVScale", stereoParams.EyeToSourceUV.Scale.x, stereoParams.EyeToSourceUV.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceUVOffset", stereoParams.EyeToSourceUV.Offset.x, stereoParams.EyeToSourceUV.Offset.y ); pPostProcessShader->SetUniform2f("EyeToSourceNDCScale", stereoParams.EyeToSourceNDC.Scale.x, stereoParams.EyeToSourceNDC.Scale.y ); pPostProcessShader->SetUniform2f("EyeToSourceNDCOffset", stereoParams.EyeToSourceNDC.Offset.x, stereoParams.EyeToSourceNDC.Offset.y ); // Fade out towards the last bit of the edge, rather than having a sharp edge that attracts the eye. pPostProcessShader->SetUniform1f("EdgeFadeScale", 1.0f / FadeOutBorderFraction ); // Vertex texture coordinates are (0,0) to (1,1). This moves them to (-1,-1),(1,1) Matrix4f texm(2, 0, 0, -1, 0, 2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1); pPostProcessShader->SetUniform4x4f("Texm", texm); // Input vertices are (0,0) to (1,1). This moves them to (-1,-1),(1,1), i.e. filling the viewport (which will be set to the correct half of the screen). Matrix4f view(2, 0, 0, -1, 0, 2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1); ShaderFill fill(pPostProcessShader); if ( ( pHmdSpaceLayerRenderTargetRight != NULL ) && ( eyeNum == 1 ) ) { fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetRight->pColorTex ); } else { fill.SetTexture ( 0, pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex ); } Render(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip); } } break; case PostProcess_NoDistortion: { // Just splat the thing on the framebuffer with no distortion. Clear ( 0.0f, 0.4f, 0.0f, 1.0f, 1.0f ); // 1:1 mapping to screen pixels. Matrix4f ortho; ortho.SetIdentity(); ortho.M[0][0] = 1.0f; ortho.M[0][3] = 0.0f; ortho.M[1][1] = -1.0f; ortho.M[1][3] = 0.0f; ortho.M[2][2] = 0; SetProjection(ortho); int rtWidth = pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex->GetWidth(); int rtHeight = pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex->GetHeight(); for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { const StereoEyeParams &stereoParams = ( eyeNum == 0 ) ? stereoParamsLeft : stereoParamsRight; SetViewport ( stereoParams.DistortionViewport ); Ptr pTex; if ( ( pHmdSpaceLayerRenderTargetRight != NULL ) && ( eyeNum == 1 ) ) { pTex = pHmdSpaceLayerRenderTargetRight->pColorTex; } else { pTex = pHmdSpaceLayerRenderTargetLeftOrBothEyes->pColorTex; } float ul = (float)( stereoParams.RenderedViewport.x ) / (float)rtWidth; float ur = (float)( stereoParams.RenderedViewport.x + stereoParams.RenderedViewport.w ) / (float)rtWidth; float vt = (float)( stereoParams.RenderedViewport.y ) / (float)rtHeight; float vb = (float)( stereoParams.RenderedViewport.y + stereoParams.RenderedViewport.h ) / (float)rtHeight; FillTexturedRect ( -1.0f, -1.0f, 1.0f, 1.0f, ul, vt, ur, vb, Color(255,255,255,255), pTex ); } } break; default: OVR_ASSERT ( !"Unknown distortion type" ); } } bool CollisionModel::TestPoint(const Vector3f& p) const { for(unsigned i = 0; i < Planes.GetSize(); i++) if(Planes[i].TestSide(p) > 0) { return 0; } return 1; } bool CollisionModel::TestRay(const Vector3f& origin, const Vector3f& norm, float& len, Planef* ph) const { if(TestPoint(origin)) { len = 0; *ph = Planes[0]; return true; } Vector3f fullMove = origin + norm * len; int crossing = -1; float cdot1 = 0, cdot2 = 0; for(unsigned i = 0; i < Planes.GetSize(); ++i) { float dot2 = Planes[i].TestSide(fullMove); if(dot2 > 0) { return false; } float dot1 = Planes[i].TestSide(origin); if(dot1 > 0) { if(dot2 <= 0) { //OVR_ASSERT(crossing==-1); if(crossing == -1) { crossing = i; cdot2 = dot2; cdot1 = dot1; } else { if(dot2 > cdot2) { crossing = i; cdot2 = dot2; cdot1 = dot1; } } } } } if(crossing < 0) { return false; } OVR_ASSERT(TestPoint(origin + norm * len)); len = len * cdot1 / (cdot1 - cdot2) - 0.05f; if(len < 0) { len = 0; } float tp = Planes[crossing].TestSide(origin + norm * len); OVR_ASSERT(fabsf(tp) < 0.05f + MATH_FLOAT_TOLERANCE); OVR_UNUSED(tp); if(ph) { *ph = Planes[crossing]; } return true; } int GetNumMipLevels(int w, int h) { int n = 1; while(w > 1 || h > 1) { w >>= 1; h >>= 1; n++; } return n; } void FilterRgba2x2(const uint8_t* src, int w, int h, uint8_t* dest) { for(int j = 0; j < (h & ~1); j += 2) { const uint8_t* psrc = src + (w * j * 4); uint8_t* pdest = dest + ((w >> 1) * (j >> 1) * 4); for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4) { pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2; pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2; pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2; pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2; } } } int GetTextureSize(int format, int w, int h) { switch (format & Texture_TypeMask) { case Texture_R: return w*h; case Texture_RGBA: return w*h*4; case Texture_DXT1: { int bw = (w+3)/4, bh = (h+3)/4; return bw * bh * 8; } case Texture_DXT3: case Texture_DXT5: { int bw = (w+3)/4, bh = (h+3)/4; return bw * bh * 16; } default: OVR_ASSERT(0); } return 0; } }}