#ifdef Q_OS_WIN // // Created by Bradley Austin Davis on 2015/05/29 // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OglplusHelpers.h" using namespace oglplus; using namespace oglplus::shapes; static const char * SIMPLE_TEXTURED_VS = R"VS(#version 410 core #pragma line __LINE__ uniform mat4 Projection = mat4(1); uniform mat4 ModelView = mat4(1); layout(location = 0) in vec3 Position; layout(location = 1) in vec2 TexCoord; out vec2 vTexCoord; void main() { gl_Position = Projection * ModelView * vec4(Position, 1); vTexCoord = TexCoord; } )VS"; static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core #pragma line __LINE__ uniform sampler2D sampler; uniform float Alpha = 1.0; in vec2 vTexCoord; out vec4 vFragColor; void main() { vec4 c = texture(sampler, vTexCoord); c.a = min(Alpha, c.a); vFragColor = c; } )FS"; ProgramPtr loadDefaultShader() { ProgramPtr result; compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS); return result; } void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) { using namespace oglplus; try { result = ProgramPtr(new Program()); // attach the shaders to the program result->AttachShader( VertexShader() .Source(GLSLSource(vs)) .Compile() ); result->AttachShader( FragmentShader() .Source(GLSLSource(fs)) .Compile() ); result->Link(); } catch (ProgramBuildError & err) { Q_UNUSED(err); Q_ASSERT_X(false, "compileProgram", "Failed to build shader program"); qFatal((const char*)err.Message); result.reset(); } } ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) { using namespace oglplus; Vec3f a(1, 0, 0); Vec3f b(0, 1, 0); if (aspect > 1) { b[1] /= aspect; } else { a[0] *= aspect; } return ShapeWrapperPtr( new shapes::ShapeWrapper({ "Position", "TexCoord" }, shapes::Plane(a, b), *program) ); } // Return a point's cartesian coordinates on a sphere from pitch and yaw static glm::vec3 getPoint(float yaw, float pitch) { return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), glm::sin(-pitch), glm::cos(-pitch) * (-glm::cos(yaw))); } class SphereSection : public DrawingInstructionWriter, public DrawMode { public: using IndexArray = std::vector; using PosArray = std::vector; using TexArray = std::vector; /// The type of the index container returned by Indices() // vertex positions PosArray _pos_data; // vertex tex coords TexArray _tex_data; IndexArray _idx_data; unsigned int _prim_count{ 0 }; public: SphereSection( const float fov, const float aspectRatio, const int slices_, const int stacks_) { //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } int gridSize = std::max(slices_, stacks_); int gridSizeLog2 = 1; while (1 << gridSizeLog2 < gridSize) { ++gridSizeLog2; } gridSize = (1 << gridSizeLog2) + 1; // Compute number of vertices needed int vertices = gridSize * gridSize; _pos_data.resize(vertices * 3); _tex_data.resize(vertices * 2); // Compute vertices positions and texture UV coordinate for (int y = 0; y <= gridSize; ++y) { for (int x = 0; x <= gridSize; ++x) { } } for (int i = 0; i < gridSize; i++) { float stacksRatio = (float)i / (float)(gridSize - 1); // First stack is 0.0f, last stack is 1.0f // abs(theta) <= fov / 2.0f float pitch = -fov * (stacksRatio - 0.5f); for (int j = 0; j < gridSize; j++) { float slicesRatio = (float)j / (float)(gridSize - 1); // First slice is 0.0f, last slice is 1.0f // abs(phi) <= fov * aspectRatio / 2.0f float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); int vertex = i * gridSize + j; int posOffset = vertex * 3; int texOffset = vertex * 2; vec3 pos = getPoint(yaw, pitch); _pos_data[posOffset] = pos.x; _pos_data[posOffset + 1] = pos.y; _pos_data[posOffset + 2] = pos.z; _tex_data[texOffset] = slicesRatio; _tex_data[texOffset + 1] = stacksRatio; } } // done with vertices int rowLen = gridSize; // gridsize now refers to the triangles, not the vertices, so reduce by one // or die by fencepost error http://en.wikipedia.org/wiki/Off-by-one_error --gridSize; int quads = gridSize * gridSize; for (int t = 0; t < quads; ++t) { int x = ((t & 0x0001) >> 0) | ((t & 0x0004) >> 1) | ((t & 0x0010) >> 2) | ((t & 0x0040) >> 3) | ((t & 0x0100) >> 4) | ((t & 0x0400) >> 5) | ((t & 0x1000) >> 6) | ((t & 0x4000) >> 7); int y = ((t & 0x0002) >> 1) | ((t & 0x0008) >> 2) | ((t & 0x0020) >> 3) | ((t & 0x0080) >> 4) | ((t & 0x0200) >> 5) | ((t & 0x0800) >> 6) | ((t & 0x2000) >> 7) | ((t & 0x8000) >> 8); int i = x * (rowLen) + y; _idx_data.push_back(i); _idx_data.push_back(i + 1); _idx_data.push_back(i + rowLen + 1); _idx_data.push_back(i + rowLen + 1); _idx_data.push_back(i + rowLen); _idx_data.push_back(i); } _prim_count = quads * 2; } /// Returns the winding direction of faces FaceOrientation FaceWinding(void) const { return FaceOrientation::CCW; } typedef GLuint(SphereSection::*VertexAttribFunc)(std::vector&) const; /// Makes the vertex positions and returns the number of values per vertex template GLuint Positions(std::vector& dest) const { dest.clear(); dest.insert(dest.begin(), _pos_data.begin(), _pos_data.end()); return 3; } /// Makes the vertex normals and returns the number of values per vertex template GLuint Normals(std::vector& dest) const { dest.clear(); return 3; } /// Makes the vertex tangents and returns the number of values per vertex template GLuint Tangents(std::vector& dest) const { dest.clear(); return 3; } /// Makes the vertex bi-tangents and returns the number of values per vertex template GLuint Bitangents(std::vector& dest) const { dest.clear(); return 3; } /// Makes the texture coordinates returns the number of values per vertex template GLuint TexCoordinates(std::vector& dest) const { dest.clear(); dest.insert(dest.begin(), _tex_data.begin(), _tex_data.end()); return 2; } typedef VertexAttribsInfo< SphereSection, std::tuple< VertexPositionsTag, VertexNormalsTag, VertexTangentsTag, VertexBitangentsTag, VertexTexCoordinatesTag > > VertexAttribs; Spheref MakeBoundingSphere(void) const { GLfloat min_x = _pos_data[3], max_x = _pos_data[3]; GLfloat min_y = _pos_data[4], max_y = _pos_data[4]; GLfloat min_z = _pos_data[5], max_z = _pos_data[5]; for (std::size_t v = 0, vn = _pos_data.size() / 3; v != vn; ++v) { GLfloat x = _pos_data[v * 3 + 0]; GLfloat y = _pos_data[v * 3 + 1]; GLfloat z = _pos_data[v * 3 + 2]; if (min_x > x) min_x = x; if (min_y > y) min_y = y; if (min_z > z) min_z = z; if (max_x < x) max_x = x; if (max_y < y) max_y = y; if (max_z < z) max_z = z; } Vec3f c( (min_x + max_x) * 0.5f, (min_y + max_y) * 0.5f, (min_z + max_z) * 0.5f ); return Spheref( c.x(), c.y(), c.z(), Distance(c, Vec3f(min_x, min_y, min_z)) ); } /// Queries the bounding sphere coordinates and dimensions template void BoundingSphere(oglplus::Sphere& bounding_sphere) const { bounding_sphere = oglplus::Sphere(MakeBoundingSphere()); } /// Returns element indices that are used with the drawing instructions const IndexArray & Indices(Default = Default()) const { return _idx_data; } /// Returns the instructions for rendering of faces DrawingInstructions Instructions(PrimitiveType primitive) const { DrawingInstructions instr = this->MakeInstructions(); DrawOperation operation; operation.method = DrawOperation::Method::DrawElements; operation.mode = primitive; operation.first = 0; operation.count = _prim_count * 3; operation.restart_index = DrawOperation::NoRestartIndex(); operation.phase = 0; this->AddInstruction(instr, operation); return std::move(instr); } /// Returns the instructions for rendering of faces DrawingInstructions Instructions(Default = Default()) const { return Instructions(PrimitiveType::Triangles); } }; ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov, float aspect, int slices, int stacks) { using namespace oglplus; return ShapeWrapperPtr( new shapes::ShapeWrapper({ "Position", "TexCoord" }, SphereSection(fov, aspect, slices, stacks), *program) ); } #endif