lodspheremesh.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:26k
- // lodspheremesh.cpp
- //
- // Copyright (C) 2000-2007, Chris Laurel <claurel@shatters.net>
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <cmath>
- #include <cassert>
- #include <iostream>
- #include <algorithm>
- #include <celmath/mathlib.h>
- #include <celmath/vecmath.h>
- #include "gl.h"
- #include "glext.h"
- #include "vecgl.h"
- #include "lodspheremesh.h"
- using namespace std;
- //#define SHOW_PATCH_VISIBILITY
- //#define SHOW_FRUSTUM
- //#define VERTEX_BUFFER_OBJECTS_ENABLED
- static bool trigArraysInitialized = false;
- static int maxDivisions = 16384;
- static int thetaDivisions = maxDivisions;
- static int phiDivisions = maxDivisions / 2;
- static int minStep = 128;
- static float* sinPhi = NULL;
- static float* cosPhi = NULL;
- static float* sinTheta = NULL;
- static float* cosTheta = NULL;
- // largest vertex:
- // position - 3 floats,
- // normal - 3 floats,
- // tangent - 3 floats,
- // tex coords - 2 floats * MAX_SPHERE_MESH_TEXTURES
- static int MaxVertexSize = 3 + 3 + 3 + MAX_SPHERE_MESH_TEXTURES * 2;
- #ifdef SHOW_PATCH_VISIBILITY
- static const int MaxPatchesShown = 4096;
- static int visiblePatches[MaxPatchesShown];
- #endif
- static void InitTrigArrays()
- {
- sinTheta = new float[thetaDivisions + 1];
- cosTheta = new float[thetaDivisions + 1];
- sinPhi = new float[phiDivisions + 1];
- cosPhi = new float[phiDivisions + 1];
- int i;
- for (i = 0; i <= thetaDivisions; i++)
- {
- double theta = (double) i / (double) thetaDivisions * 2.0 * PI;
- sinTheta[i] = (float) sin(theta);
- cosTheta[i] = (float) cos(theta);
- }
- for (i = 0; i <= phiDivisions; i++)
- {
- double phi = ((double) i / (double) phiDivisions - 0.5) * PI;
- sinPhi[i] = (float) sin(phi);
- cosPhi[i] = (float) cos(phi);
- }
- trigArraysInitialized = true;
- }
- static float getSphereLOD(float discSizeInPixels)
- {
- if (discSizeInPixels < 10)
- return -3.0f;
- else if (discSizeInPixels < 20)
- return -2.0f;
- else if (discSizeInPixels < 50)
- return -1.0f;
- else if (discSizeInPixels < 200)
- return 0.0f;
- else if (discSizeInPixels < 1200)
- return 1.0f;
- else if (discSizeInPixels < 7200)
- return 2.0f;
- else if (discSizeInPixels < 53200)
- return 3.0f;
- else
- return 4.0f;
- }
- LODSphereMesh::LODSphereMesh() :
- vertices(NULL),
- vertexBuffersInitialized(false),
- useVertexBuffers(false)
- {
- if (!trigArraysInitialized)
- InitTrigArrays();
- int maxThetaSteps = thetaDivisions / minStep;
- int maxPhiSteps = phiDivisions / minStep;
- maxVertices = (maxPhiSteps + 1) * (maxThetaSteps + 1);
- vertices = new float[MaxVertexSize * maxVertices];
- nIndices = maxPhiSteps * 2 * (maxThetaSteps + 1);
- indices = new unsigned short[nIndices];
- }
- LODSphereMesh::~LODSphereMesh()
- {
- if (vertices != NULL)
- delete[] vertices;
- }
- static Point3f spherePoint(int theta, int phi)
- {
- return Point3f(cosPhi[phi] * cosTheta[theta],
- sinPhi[phi],
- cosPhi[phi] * sinTheta[theta]);
- }
- void LODSphereMesh::render(const GLContext& context,
- const Frustum& frustum,
- float pixWidth,
- Texture** tex,
- int nTextures)
- {
- render(context,
- Normals | TexCoords0, frustum, pixWidth, tex,
- nTextures);
- }
- void LODSphereMesh::render(const GLContext& context,
- unsigned int attributes,
- const Frustum& frustum,
- float pixWidth,
- Texture* tex0,
- Texture* tex1,
- Texture* tex2,
- Texture* tex3)
- {
- Texture* textures[MAX_SPHERE_MESH_TEXTURES];
- int nTextures = 0;
- if (tex0 != NULL)
- textures[nTextures++] = tex0;
- if (tex1 != NULL)
- textures[nTextures++] = tex1;
- if (tex2 != NULL)
- textures[nTextures++] = tex2;
- if (tex3 != NULL)
- textures[nTextures++] = tex3;
- render(context, attributes, frustum, pixWidth, textures, nTextures);
- }
- void LODSphereMesh::render(const GLContext& context,
- unsigned int attributes,
- const Frustum& frustum,
- float pixWidth,
- Texture** tex,
- int nTextures)
- {
- int lod = 64;
- float lodBias = getSphereLOD(pixWidth);
- if (lodBias < 0.0f)
- {
- if (lodBias < -30)
- lodBias = -30;
- lod = lod / (1 << (int) (-lodBias));
- if (lod < 2)
- lod = 2;
- }
- else if (lodBias > 0.0f)
- {
- if (lodBias > 30)
- lodBias = 30;
- lod = lod * (1 << (int) lodBias);
- if (lod > maxDivisions)
- lod = maxDivisions;
- }
- int step = maxDivisions / lod;
- int thetaExtent = maxDivisions;
- int phiExtent = thetaExtent / 2;
- int split = 1;
- if (step < minStep)
- {
- split = minStep / step;
- thetaExtent /= split;
- phiExtent /= split;
- }
- if (tex == NULL)
- nTextures = 0;
- // Need to have vertex programs enabled in order to make
- // use of surface tangents.
- if (!context.getVertexProcessor())
- attributes &= ~Tangents;
- RenderInfo ri(step, attributes, frustum, context);
- // If one of the textures is split into subtextures, we may have to
- // use extra patches, since there can be at most one subtexture per patch.
- int i;
- int minSplit = 1;
- for (i = 0; i < nTextures; i++)
- {
- float pixelsPerTexel = pixWidth * 2.0f /
- ((float) tex[i]->getWidth() / 2.0f);
- double l = log(pixelsPerTexel) / log(2.0);
- ri.texLOD[i] = max(min(tex[i]->getLODCount() - 1, (int) l), 0);
- if (tex[i]->getUTileCount(ri.texLOD[i]) > minSplit)
- minSplit = tex[i]->getUTileCount(ri.texLOD[i]);
- if (tex[i]->getVTileCount(ri.texLOD[i]) > minSplit)
- minSplit = tex[i]->getVTileCount(ri.texLOD[i]);
- }
- if (split < minSplit)
- {
- thetaExtent /= (minSplit / split);
- phiExtent /= (minSplit / split);
- split = minSplit;
- if (phiExtent <= ri.step)
- ri.step /= ri.step / phiExtent;
- }
- // Set the current textures
- nTexturesUsed = nTextures;
- for (i = 0; i < nTextures; i++)
- {
- tex[i]->beginUsage();
- textures[i] = tex[i];
- subtextures[i] = 0;
- if (nTextures > 1)
- glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
- glEnable(GL_TEXTURE_2D);
- }
- #ifdef VERTEX_BUFFER_OBJECTS_ENABLED
- if (!vertexBuffersInitialized)
- {
- // TODO: assumes that the same context is used every time we
- // render. Valid now, but not necessarily in the future. Still,
- // would only cause problems if we rendered in two different contexts
- // and only one had vertex buffer objects.
- vertexBuffersInitialized = true;
- if (context.extensionSupported("GL_ARB_vertex_buffer_object"))
- {
- for (int i = 0; i < NUM_SPHERE_VERTEX_BUFFERS; i++)
- {
- GLuint vbname = 0;
- glx::glGenBuffersARB(1, &vbname);
- vertexBuffers[i] = (unsigned int) vbname;
- glx::glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffers[i]);
- glx::glBufferDataARB(GL_ARRAY_BUFFER_ARB,
- maxVertices * MaxVertexSize * sizeof(float),
- NULL,
- GL_STREAM_DRAW_ARB);
- }
- glx::glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
- glx::glGenBuffersARB(1, &indexBuffer);
- useVertexBuffers = true;
- // HACK: delete the user arrays--we shouldn't need to allocate
- // these at all if we're using vertex buffer objects.
- delete[] vertices;
- }
- }
- #endif
- if (useVertexBuffers)
- {
- currentVB = 0;
- glx::glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffers[currentVB]);
- }
- // Set up the mesh vertices
- int nRings = phiExtent / ri.step;
- int nSlices = thetaExtent / ri.step;
- int n2 = 0;
- for (i = 0; i < nRings; i++)
- {
- for (int j = 0; j <= nSlices; j++)
- {
- indices[n2 + 0] = i * (nSlices + 1) + j;
- indices[n2 + 1] = (i + 1) * (nSlices + 1) + j;
- n2 += 2;
- }
- }
- if (useVertexBuffers)
- {
- glx::glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBuffer);
- glx::glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
- nIndices * sizeof(indices[0]),
- indices,
- GL_DYNAMIC_DRAW_ARB);
- }
- // Compute the size of a vertex
- vertexSize = 3;
- if ((attributes & Tangents) != 0)
- vertexSize += 3;
- for (i = 0; i < nTextures; i++)
- vertexSize += 2;
- glEnableClientState(GL_VERTEX_ARRAY);
- if ((attributes & Normals) != 0)
- glEnableClientState(GL_NORMAL_ARRAY);
- for (i = 0; i < nTextures; i++)
- {
- if (nTextures > 1)
- glx::glClientActiveTextureARB(GL_TEXTURE0_ARB + i);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- }
- glDisableClientState(GL_COLOR_ARRAY);
- if ((attributes & Tangents) != 0)
- {
- VertexProcessor* vproc = context.getVertexProcessor();
- vproc->enableAttribArray(6);
- }
- if (split == 1)
- {
- renderSection(0, 0, thetaExtent, ri);
- }
- else
- {
- // Render the sphere section by section.
- /*int reject = 0; Unused*/
- // Compute the vertices of the view frustum. These will be used for
- // culling patches.
- ri.fp[0] = Planef::intersection(frustum.getPlane(Frustum::Near),
- frustum.getPlane(Frustum::Top),
- frustum.getPlane(Frustum::Left));
- ri.fp[1] = Planef::intersection(frustum.getPlane(Frustum::Near),
- frustum.getPlane(Frustum::Top),
- frustum.getPlane(Frustum::Right));
- ri.fp[2] = Planef::intersection(frustum.getPlane(Frustum::Near),
- frustum.getPlane(Frustum::Bottom),
- frustum.getPlane(Frustum::Left));
- ri.fp[3] = Planef::intersection(frustum.getPlane(Frustum::Near),
- frustum.getPlane(Frustum::Bottom),
- frustum.getPlane(Frustum::Right));
- ri.fp[4] = Planef::intersection(frustum.getPlane(Frustum::Far),
- frustum.getPlane(Frustum::Top),
- frustum.getPlane(Frustum::Left));
- ri.fp[5] = Planef::intersection(frustum.getPlane(Frustum::Far),
- frustum.getPlane(Frustum::Top),
- frustum.getPlane(Frustum::Right));
- ri.fp[6] = Planef::intersection(frustum.getPlane(Frustum::Far),
- frustum.getPlane(Frustum::Bottom),
- frustum.getPlane(Frustum::Left));
- ri.fp[7] = Planef::intersection(frustum.getPlane(Frustum::Far),
- frustum.getPlane(Frustum::Bottom),
- frustum.getPlane(Frustum::Right));
- #ifdef SHOW_PATCH_VISIBILITY
- {
- for (int i = 0; i < MaxPatchesShown; i++)
- visiblePatches[i] = 0;
- }
- #endif // SHOW_PATCH_VISIBILITY
- int nPatches = 0;
- {
- int extent = maxDivisions / 2;
- for (int i = 0; i < 2; i++)
- {
- for (int j = 0; j < 2; j++)
- {
- nPatches += renderPatches(i * extent / 2, j * extent,
- extent, split / 2, ri);
- }
- }
- }
- // cout << "Rendered " << nPatches << " of " << square(split) << " patchesn";
- }
- glDisableClientState(GL_VERTEX_ARRAY);
- if ((attributes & Normals) != 0)
- glDisableClientState(GL_NORMAL_ARRAY);
- if ((attributes & Tangents) != 0)
- {
- VertexProcessor* vproc = context.getVertexProcessor();
- vproc->disableAttribArray(6);
- }
- for (i = 0; i < nTextures; i++)
- {
- tex[i]->endUsage();
- if (nTextures > 1)
- {
- glx::glClientActiveTextureARB(GL_TEXTURE0_ARB + i);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
- }
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- if (i > 0)
- glDisable(GL_TEXTURE_2D);
- }
- if (nTextures > 1)
- {
- glx::glClientActiveTextureARB(GL_TEXTURE0_ARB);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- }
- if (useVertexBuffers)
- {
- glx::glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
- glx::glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
- vertices = NULL;
- }
- #ifdef SHOW_FRUSTUM
- // Debugging code for visualizing the frustum.
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluPerspective(45.0, 1.3333f, 1.0f, 100.0f);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_LIGHTING);
- glColor4f(1, 0, 0, 1);
- glTranslatef(0, 0, -20);
- glBegin(GL_LINES);
- glVertex(ri.fp[0]); glVertex(ri.fp[1]);
- glVertex(ri.fp[0]); glVertex(ri.fp[2]);
- glVertex(ri.fp[3]); glVertex(ri.fp[1]);
- glVertex(ri.fp[3]); glVertex(ri.fp[2]);
- glVertex(ri.fp[4]); glVertex(ri.fp[5]);
- glVertex(ri.fp[4]); glVertex(ri.fp[6]);
- glVertex(ri.fp[7]); glVertex(ri.fp[5]);
- glVertex(ri.fp[7]); glVertex(ri.fp[6]);
- glVertex(ri.fp[0]); glVertex(ri.fp[4]);
- glVertex(ri.fp[1]); glVertex(ri.fp[5]);
- glVertex(ri.fp[2]); glVertex(ri.fp[6]);
- glVertex(ri.fp[3]); glVertex(ri.fp[7]);
- glEnd();
- // Render axes representing the unit sphere.
- glColor4f(0, 1, 0, 1);
- glBegin(GL_LINES);
- glVertex3f(-1, 0, 0); glVertex3f(1, 0, 0);
- glVertex3f(0, -1, 0); glVertex3f(0, 1, 0);
- glVertex3f(0, 0, -1); glVertex3f(1, 0, 1);
- glEnd();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- #endif
- #ifdef SHOW_PATCH_VISIBILITY
- // Debugging code for visualizing the frustum.
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_LIGHTING);
- glColor4f(1, 0, 1, 1);
- {
- int width = split;
- int height = width / 2;
- float patchWidth = 1.0f / (float) width;
- float patchHeight = 1.0f / (float) height;
- if (width * height <= MaxPatchesShown)
- {
- for (int i = 0; i < height; i++)
- {
- for (int j = 0; j < width; j++)
- {
- glPushMatrix();
- glTranslatef(-0.5f + j * patchWidth,
- 1.0f - i * patchHeight,
- 0.0f);
- if (visiblePatches[i * width + j])
- glBegin(GL_QUADS);
- else
- glBegin(GL_LINE_LOOP);
- glVertex3f(0.0f, 0.0f, 0.0f);
- glVertex3f(0.0f, -patchHeight, 0.0f);
- glVertex3f(patchWidth, -patchHeight, 0.0f);
- glVertex3f(patchWidth, 0.0f, 0.0f);
- glEnd();
- glPopMatrix();
- }
- }
- }
- }
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- #endif // SHOW_PATCH_VISIBILITY
- }
- int LODSphereMesh::renderPatches(int phi0, int theta0,
- int extent,
- int level,
- const RenderInfo& ri)
- {
- int thetaExtent = extent;
- int phiExtent = extent / 2;
- // Compute the plane separating this section of the sphere from
- // the rest of the sphere. If the view frustum lies entirely
- // on the side of the plane that does not contain the sphere
- // patch, we cull the patch.
- Point3f p0 = spherePoint(theta0, phi0);
- Point3f p1 = spherePoint(theta0 + thetaExtent, phi0);
- Point3f p2 = spherePoint(theta0 + thetaExtent,
- phi0 + phiExtent);
- Point3f p3 = spherePoint(theta0, phi0 + phiExtent);
- Vec3f v0 = p1 - p0;
- Vec3f v2 = p3 - p2;
- Vec3f normal;
- if (v0.lengthSquared() > v2.lengthSquared())
- normal = (p0 - p3) ^ v0;
- else
- normal = (p2 - p1) ^ v2;
- // If the normal is near zero length, something's going wrong
- assert(normal.length() != 0.0f);
- normal.normalize();
- Planef separatingPlane(normal, p0);
- bool outside = true;
- #if 1
- for (int k = 0; k < 8; k++)
- {
- if (separatingPlane.distanceTo(ri.fp[k]) > 0.0f)
- {
- outside = false;
- break;
- }
- }
- // If this patch is outside the view frustum, so are all of its subpatches
- if (outside)
- return 0;
- #else
- outside = false;
- #endif
- // Second cull test uses the bounding sphere of the patch
- #if 0
- // Is this a better choice for the patch center?
- Point3f patchCenter = spherePoint(theta0 + thetaExtent / 2,
- phi0 + phiExtent / 2);
- #else
- // . . . or is the average of the points better?
- Point3f patchCenter = Point3f(p0.x + p1.x + p2.x + p3.x,
- p0.y + p1.y + p2.y + p3.y,
- p0.z + p1.z + p2.z + p3.z) * 0.25f;
- #endif
- float boundingRadius = 0.0f;
- boundingRadius = max(boundingRadius, patchCenter.distanceTo(p0));
- boundingRadius = max(boundingRadius, patchCenter.distanceTo(p1));
- boundingRadius = max(boundingRadius, patchCenter.distanceTo(p2));
- boundingRadius = max(boundingRadius, patchCenter.distanceTo(p3));
- if (ri.frustum.testSphere(patchCenter, boundingRadius) == Frustum::Outside)
- outside = true;
- if (outside)
- {
- return 0;
- }
- else if (level == 1)
- {
- renderSection(phi0, theta0, thetaExtent, ri);
- return 1;
- }
- else
- {
- int nRendered = 0;
- for (int i = 0; i < 2; i++)
- {
- for (int j = 0; j < 2; j++)
- {
- nRendered += renderPatches(phi0 + phiExtent / 2 * i,
- theta0 + thetaExtent / 2 * j,
- extent / 2,
- level / 2,
- ri);
- }
- }
- return nRendered;
- }
- }
- void LODSphereMesh::renderSection(int phi0, int theta0, int extent,
- const RenderInfo& ri)
- {
- #ifdef SHOW_PATCH_VISIBILITY
- {
- int width = thetaDivisions / extent;
- int height = phiDivisions / extent;
- int x = theta0 / extent;
- int y = phi0 / extent;
- if (width * height <= MaxPatchesShown)
- visiblePatches[y * width + x] = 1;
- }
- #endif // SHOW_PATCH_VISIBILITY
- GLsizei stride = (GLsizei) (vertexSize * sizeof(float));
- int tangentOffset = 3;
- int texCoordOffset = ((ri.attributes & Tangents) != 0) ? 6 : 3;
- float* vertexBase = useVertexBuffers ? (float*) NULL : vertices;
- glVertexPointer(3, GL_FLOAT, stride, vertexBase + 0);
- if ((ri.attributes & Normals) != 0)
- glNormalPointer(GL_FLOAT, stride, vertexBase);
- for (int tc = 0; tc < nTexturesUsed; tc++)
- {
- if (nTexturesUsed > 1)
- glx::glClientActiveTextureARB(GL_TEXTURE0_ARB + tc);
- glTexCoordPointer(2, GL_FLOAT, stride, vertexBase + (tc * 2) + texCoordOffset);
- }
- if ((ri.attributes & Tangents) != 0)
- {
- VertexProcessor* vproc = ri.context.getVertexProcessor();
- vproc->attribArray(6, 3, GL_FLOAT, stride, vertexBase + tangentOffset);
- }
- // assert(ri.step >= minStep);
- // assert(phi0 + extent <= maxDivisions);
- // assert(theta0 + extent / 2 < maxDivisions);
- // assert(isPow2(extent));
- int thetaExtent = extent;
- int phiExtent = extent / 2;
- int theta1 = theta0 + thetaExtent;
- int phi1 = phi0 + phiExtent;
- /*int n3 = 0; Unused*/
- /*int n2 = 0; Unused*/
- float du[MAX_SPHERE_MESH_TEXTURES];
- float dv[MAX_SPHERE_MESH_TEXTURES];
- float u0[MAX_SPHERE_MESH_TEXTURES];
- float v0[MAX_SPHERE_MESH_TEXTURES];
- if (useVertexBuffers)
- {
- // Calling glBufferDataARB() with NULL before mapping the buffer
- // is a hint to OpenGL that previous contents of vertex buffer will
- // be discarded and overwritten. It enables renaming in the driver,
- // hopefully resulting in performance gains.
- glx::glBufferDataARB(GL_ARRAY_BUFFER_ARB,
- maxVertices * vertexSize * sizeof(float),
- NULL,
- GL_STREAM_DRAW_ARB);
- vertices = reinterpret_cast<float*>(glx::glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB));
- if (vertices == NULL)
- return;
- }
- // Set the current texture. This is necessary because the texture
- // may be split into subtextures.
- for (int tex = 0; tex < nTexturesUsed; tex++)
- {
- du[tex] = (float) 1.0f / thetaDivisions;;
- dv[tex] = (float) 1.0f / phiDivisions;;
- u0[tex] = 1.0f;
- v0[tex] = 1.0f;
- if (textures[tex] != NULL)
- {
- int uTexSplit = textures[tex]->getUTileCount(ri.texLOD[tex]);
- int vTexSplit = textures[tex]->getVTileCount(ri.texLOD[tex]);
- int patchSplit = maxDivisions / extent;
- assert(patchSplit >= uTexSplit && patchSplit >= vTexSplit);
- int u = theta0 / thetaExtent;
- int v = phi0 / phiExtent;
- int patchesPerUSubtex = patchSplit / uTexSplit;
- int patchesPerVSubtex = patchSplit / vTexSplit;
- du[tex] *= uTexSplit;
- dv[tex] *= vTexSplit;
- u0[tex] = 1.0f - ((float) (u % patchesPerUSubtex) /
- (float) patchesPerUSubtex);
- v0[tex] = 1.0f - ((float) (v % patchesPerVSubtex) /
- (float) patchesPerVSubtex);
- u0[tex] += theta0 * du[tex];
- v0[tex] += phi0 * dv[tex];
- u /= patchesPerUSubtex;
- v /= patchesPerVSubtex;
- if (nTexturesUsed > 1)
- glx::glActiveTextureARB(GL_TEXTURE0_ARB + tex);
- TextureTile tile = textures[tex]->getTile(ri.texLOD[tex],
- uTexSplit - u - 1,
- vTexSplit - v - 1);
- du[tex] *= tile.du;
- dv[tex] *= tile.dv;
- u0[tex] = u0[tex] * tile.du + tile.u;
- v0[tex] = v0[tex] * tile.dv + tile.v;
- // We track the current texture to avoid unnecessary and costly
- // texture state changes.
- if (tile.texID != subtextures[tex])
- {
- glBindTexture(GL_TEXTURE_2D, tile.texID);
- subtextures[tex] = tile.texID;
- }
- }
- }
- int vindex = 0;
- for (int phi = phi0; phi <= phi1; phi += ri.step)
- {
- float cphi = cosPhi[phi];
- float sphi = sinPhi[phi];
- if ((ri.attributes & Tangents) != 0)
- {
- for (int theta = theta0; theta <= theta1; theta += ri.step)
- {
- float ctheta = cosTheta[theta];
- float stheta = sinTheta[theta];
- vertices[vindex] = cphi * ctheta;
- vertices[vindex + 1] = sphi;
- vertices[vindex + 2] = cphi * stheta;
- // Compute the tangent--required for bump mapping
- vertices[vindex + 3] = stheta;
- vertices[vindex + 4] = 0.0f;
- vertices[vindex + 5] = -ctheta;
- vindex += 6;
- for (int tex = 0; tex < nTexturesUsed; tex++)
- {
- vertices[vindex] = u0[tex] - theta * du[tex];
- vertices[vindex + 1] = v0[tex] - phi * dv[tex];
- vindex += 2;
- }
- }
- }
- else
- {
- for (int theta = theta0; theta <= theta1; theta += ri.step)
- {
- float ctheta = cosTheta[theta];
- float stheta = sinTheta[theta];
- vertices[vindex] = cphi * ctheta;
- vertices[vindex + 1] = sphi;
- vertices[vindex + 2] = cphi * stheta;
- vindex += 3;
- for (int tex = 0; tex < nTexturesUsed; tex++)
- {
- vertices[vindex] = u0[tex] - theta * du[tex];
- vertices[vindex + 1] = v0[tex] - phi * dv[tex];
- vindex += 2;
- }
- }
- }
- }
- if (useVertexBuffers)
- {
- vertices = NULL;
- if (!glx::glUnmapBufferARB(GL_ARRAY_BUFFER_ARB))
- return;
- }
- // TODO: Fix this--number of rings can reach zero and cause dropout
- // int nRings = max(phiExtent / ri.step, 1); // buggy
- int nRings = phiExtent / ri.step;
- int nSlices = thetaExtent / ri.step;
- unsigned short* indexBase = useVertexBuffers ? (unsigned short*) NULL : indices;
- for (int i = 0; i < nRings; i++)
- {
- glDrawElements(GL_QUAD_STRIP,
- (nSlices + 1) * 2,
- GL_UNSIGNED_SHORT,
- indexBase + (nSlices + 1) * 2 * i);
- }
- // Cycle through the vertex buffers
- if (useVertexBuffers)
- {
- currentVB++;
- if (currentVB == NUM_SPHERE_VERTEX_BUFFERS)
- currentVB = 0;
- glx::glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffers[currentVB]);
- }
- }