void MeshGeometry::render(RenderContext& rc, double /* clock */) const { if (!m_hwBuffersCurrent) { realize(); } // Track the last used material in order to avoid redundant // material bindings. unsigned int lastMaterialIndex = Submesh::DefaultMaterialIndex; rc.pushModelView(); rc.scaleModelView(m_meshScale); // Render all submeshes GLVertexBuffer* boundVertexBuffer = NULL; for (unsigned int i = 0; i < m_submeshes.size(); ++i) { const Submesh& submesh = *m_submeshes[i]; if (i < m_submeshBuffers.size() && m_submeshBuffers[i]) { boundVertexBuffer = m_submeshBuffers[i]; rc.bindVertexBuffer(submesh.vertices()->vertexSpec(), m_submeshBuffers[i], submesh.vertices()->stride()); } else { if (boundVertexBuffer) { boundVertexBuffer->unbind(); boundVertexBuffer = false; } rc.bindVertexArray(submesh.vertices()); } const vector<PrimitiveBatch*>& batches = submesh.primitiveBatches(); const vector<unsigned int>& materials = submesh.materials(); assert(batches.size() == materials.size()); // Render all batches in the submesh for (unsigned int j = 0; j < batches.size(); j++) { // If we have a new material, bind it unsigned int materialIndex = materials[j]; if (materialIndex != lastMaterialIndex) { if (materialIndex < m_materials.size()) { rc.bindMaterial(m_materials[materialIndex].ptr()); } lastMaterialIndex = materialIndex; } rc.drawPrimitives(*batches[j]); } } if (boundVertexBuffer) { boundVertexBuffer->unbind(); } rc.popModelView(); }
void SimpleTrajectoryGeometry::render(RenderContext& rc, double clock) const { double t0 = firstSampleTime(); double t1 = lastSampleTime(); double fadeRate = 0.0; double fadeStartTime = 0.0; double fadeStartValue = 1.0; // Only draw during the appropriate render pass if ((rc.pass() == RenderContext::OpaquePass && !isOpaque()) || (rc.pass() == RenderContext::TranslucentPass && isOpaque())) { return; } if (displayedPortion() == TrajectoryGeometry::WindowBeforeCurrentTime) { t0 = clock + m_windowLead - m_windowDuration; t1 = clock + m_windowLead; fadeStartTime = t0; fadeStartValue = 0.0; double fadeEndTime = fadeStartTime + m_windowDuration * m_fadeFraction; fadeRate = 1.0 / (fadeEndTime - fadeStartTime); } // Nothing to be drawn if (t1 <= t0) { return; } // Basic opacity of the plot. It may be modified based on three things: // - Approximate size in pixels of the trajectory (small trajectories will fade out) // - Distance from the camera to the 'front' (usually the current position of the oribiting body) // - 'Age' of the trajectory: typically the most recent portions are drawn more opaque than the // older parts. This is handled by setting per-vertex colors. float opacity = 0.99f * m_opacity; const float sizeFadeStart = 30.0f; const float sizeFadeEnd = 15.0f; float pixelSize = boundingSphereRadius() / (rc.modelview().translation().norm() * rc.pixelSize()); if (pixelSize < sizeFadeStart) { opacity *= std::max(0.0f, (pixelSize - sizeFadeEnd) / (sizeFadeStart - sizeFadeEnd)); } if (opacity <= 0.0f) { // Complete fade out; no need to draw anything. return; } rc.pushModelView(); if (m_frame.isValid()) { rc.rotateModelView(m_frame->orientation(clock).cast<float>()); } TrajectoryVertex vertex; vertex.color[0] = (unsigned char) (m_color.red() * 255.99f); vertex.color[1] = (unsigned char) (m_color.green() * 255.99f); vertex.color[2] = (unsigned char) (m_color.blue() * 255.99f); vertex.color[3] = 255; m_vertexData.clear(); unsigned int sampleIndex = 0; while (sampleIndex < m_samples.size() && t0 > m_samples[sampleIndex].timeTag) { ++sampleIndex; } if (sampleIndex > 0 && sampleIndex < m_samples.size()) { double dt = m_samples[sampleIndex].timeTag - m_samples[sampleIndex - 1].timeTag; double t = (t0 - m_samples[sampleIndex - 1].timeTag) / dt; Vector3d interpolated = interpolateSamples(t, dt, m_samples[sampleIndex - 1], m_samples[sampleIndex]); float alpha = std::max(0.0f, std::min(1.0f, float(fadeStartValue + (t0 - fadeStartTime) * fadeRate))); vertex.position = interpolated.cast<float>(); vertex.color[3] = (unsigned char) (alpha * 255.99f); m_vertexData.push_back(vertex); } while (sampleIndex < m_samples.size() && t1 > m_samples[sampleIndex].timeTag) { float alpha = std::max(0.0f, std::min(1.0f, float(fadeStartValue + (m_samples[sampleIndex].timeTag - fadeStartTime) * fadeRate))); vertex.position = m_samples[sampleIndex].position.cast<float>(); vertex.color[3] = (unsigned char) (alpha * 255.99f); m_vertexData.push_back(vertex); ++sampleIndex; } if (sampleIndex > 0 && sampleIndex < m_samples.size()) { double dt = m_samples[sampleIndex].timeTag - m_samples[sampleIndex - 1].timeTag; double t = (t1 - m_samples[sampleIndex - 1].timeTag) / dt; Vector3d interpolated = interpolateSamples(t, dt, m_samples[sampleIndex - 1], m_samples[sampleIndex]); float alpha = std::max(0.0f, std::min(1.0f, float(fadeStartValue + (t1 - fadeStartTime) * fadeRate))); vertex.position = interpolated.cast<float>(); vertex.color[3] = (unsigned char) (alpha * 255.99f); m_vertexData.push_back(vertex); } // Fade trajectory based on size Vector3f frontPosition = rc.modelview() * vertex.position; float frontDistance = frontPosition.norm(); // Fade trajectory based on distance to front point. This is helpful because the simple trajectory model // is not precise, and fading hides the discrepancy between the plot and the body's current position. const float fadeStart = 0.04f; const float fadeFinish = 0.01f; if (frontDistance < fadeStart * boundingSphereRadius()) { opacity *= std::max(0.0f, (frontDistance / boundingSphereRadius() - fadeFinish) / (fadeStart - fadeFinish)); } Material material; material.setDiffuse(Spectrum::White()); material.setOpacity(opacity); rc.bindMaterial(&material); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (m_vertexData.size() > 1 && opacity > 0.0f) { rc.bindVertexArray(VertexSpec::PositionColor, m_vertexData[0].position.data(), sizeof(TrajectoryVertex)); rc.drawPrimitives(PrimitiveBatch(PrimitiveBatch::LineStrip, m_vertexData.size() - 1, 0)); rc.unbindVertexArray(); } glDisable(GL_BLEND); rc.popModelView(); }
void MeshGeometry::renderShadow(RenderContext& rc, double /* clock */) const { if (!m_hwBuffersCurrent) { realize(); } // Use an extremely basic material to avoid wasting time // with pixel shader calculations when we're just interested // in depth values. Material simpleMaterial; rc.bindMaterial(&simpleMaterial); rc.pushModelView(); rc.scaleModelView(m_meshScale); // Render all submeshes GLVertexBuffer* boundVertexBuffer = NULL; for (unsigned int i = 0; i < m_submeshes.size(); ++i) { const Submesh& submesh = *m_submeshes[i]; if (i < m_submeshBuffers.size() && m_submeshBuffers[i]) { boundVertexBuffer = m_submeshBuffers[i]; rc.bindVertexBuffer(submesh.vertices()->vertexSpec(), m_submeshBuffers[i], submesh.vertices()->stride()); } else { if (boundVertexBuffer) { boundVertexBuffer->unbind(); boundVertexBuffer = false; } rc.bindVertexArray(submesh.vertices()); } const vector<PrimitiveBatch*>& batches = submesh.primitiveBatches(); const vector<unsigned int>& materials = submesh.materials(); assert(batches.size() == materials.size()); // Render all batches in the submesh for (unsigned int j = 0; j < batches.size(); j++) { // Skip mostly transparent items when drawing into the shadow // buffer. // TODO: Textures with transparent parts aren't handled here unsigned int materialIndex = materials[j]; if (materialIndex >= m_materials.size() || m_materials[materialIndex]->opacity() > 0.5f) { rc.drawPrimitives(*batches[j]); } } } if (boundVertexBuffer) { boundVertexBuffer->unbind(); } rc.popModelView(); }