GfMatrix4d Hdx_UnitTestGLDrawing::GetViewMatrix() const { GfMatrix4d viewMatrix; viewMatrix.SetIdentity(); // rotate from z-up to y-up viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(1.0,0.0,0.0), -90.0)); viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(0, 1, 0), _rotate[1])); viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(1, 0, 0), _rotate[0])); viewMatrix *= GfMatrix4d().SetTranslate(GfVec3d(_translate[0], _translate[1], _translate[2])); return viewMatrix; }
GfMatrix4d GfFrustum::ComputeProjectionMatrix() const { // Build the projection matrix per Section 2.11 of // The OpenGL Specification: Coordinate Transforms. GfMatrix4d matrix; matrix.SetIdentity(); const double l = _window.GetMin()[0]; const double r = _window.GetMax()[0]; const double b = _window.GetMin()[1]; const double t = _window.GetMax()[1]; const double n = _nearFar.GetMin(); const double f = _nearFar.GetMax(); const double rl = r - l; const double tb = t - b; const double fn = f - n; if (_projectionType == GfFrustum::Orthographic) { matrix[0][0] = 2.0 / rl; matrix[1][1] = 2.0 / tb; matrix[2][2] = -2.0 / fn; matrix[3][0] = -(r + l) / rl; matrix[3][1] = -(t + b) / tb; matrix[3][2] = -(f + n) / fn; } else { // Perspective: // The window coordinates are specified with respect to the // reference plane (near == 1). // XXX Note: If we ever allow reference plane depth to be other // than 1.0, we'll need to revisit this. matrix[0][0] = 2.0 / rl; matrix[1][1] = 2.0 / tb; matrix[2][2] = -(f + n) / fn; matrix[2][0] = (r + l) / rl; matrix[2][1] = (t + b) / tb; matrix[3][2] = -2.0 * n * f / fn; matrix[2][3] = -1.0; matrix[3][3] = 0.0; } return matrix; }
GfFrustum & GfFrustum::Transform(const GfMatrix4d &matrix) { // We'll need the old parameters as we build up the new ones, so, work // on a newly instantiated frustum. We'll replace the contents of // this frustum with it once we are done. Note that _dirty is true // by default, so, there is no need to initialize it here. GfFrustum frustum; // Copy the projection type frustum._projectionType = _projectionType; // Transform the position of the frustum frustum._position = matrix.Transform(_position); // Transform the rotation as follows: // 1. build view and direction vectors // 2. transform them with the given matrix // 3. normalize the vectors and cross them to build an orthonormal frame // 4. construct a rotation matrix // 5. extract the new rotation from the matrix // Generate view direction and up vector GfVec3d viewDir = ComputeViewDirection(); GfVec3d upVec = ComputeUpVector(); // Transform by matrix GfVec3d viewDirPrime = matrix.TransformDir(viewDir); GfVec3d upVecPrime = matrix.TransformDir(upVec); // Normalize. Save the vec size since it will be used to scale near/far. double scale = viewDirPrime.Normalize(); upVecPrime.Normalize(); // Cross them to get the third axis. Voila. We have an orthonormal frame. GfVec3d viewRightPrime = GfCross(viewDirPrime, upVecPrime); viewRightPrime.Normalize(); // Construct a rotation matrix using the axes. // // [ right 0 ] // [ up 1 ] // [ -viewDir 0 ] // [ 0 0 0 1 ] GfMatrix4d rotMatrix; rotMatrix.SetIdentity(); // first row rotMatrix[0][0] = viewRightPrime[0]; rotMatrix[0][1] = viewRightPrime[1]; rotMatrix[0][2] = viewRightPrime[2]; // second row rotMatrix[1][0] = upVecPrime[0]; rotMatrix[1][1] = upVecPrime[1]; rotMatrix[1][2] = upVecPrime[2]; // third row rotMatrix[2][0] = -viewDirPrime[0]; rotMatrix[2][1] = -viewDirPrime[1]; rotMatrix[2][2] = -viewDirPrime[2]; // Extract rotation frustum._rotation = rotMatrix.ExtractRotation(); // Since we applied the matrix to the direction vector, we can use // its length to find out the scaling that needs to applied to the // near and far plane. frustum._nearFar = _nearFar * scale; // Use the same length to scale the view distance frustum._viewDistance = _viewDistance * scale; // Transform the reference plane as follows: // // - construct two 3D points that are on the reference plane // (left/bottom and right/top corner of the reference window) // - transform the points with the given matrix // - move the window back to one unit from the viewpoint and // extract the 2D coordinates that would form the new reference // window // // A note on how we do the last "move" of the reference window: // Using similar triangles and the fact that the reference window // is one unit away from the viewpoint, one can show that it's // sufficient to divide the x and y components of the transformed // corners by the length of the transformed direction vector. // // A 2D diagram helps: // // | // | // | | // * ------+------------+ // vp |y1 | // | // \--d1--/ |y2 // // \-------d2----------/ // // So, y1/y2 = d1/d2 ==> y1 = y2 * d1/d2 // Since d1 = 1 ==> y1 = y2 / d2 // The same argument applies to the x coordinate. // // NOTE: In an orthographic projection, the last step (division by // the length of the vector) is skipped. // // XXX NOTE2: The above derivation relies on the // fact that GetReferecePlaneDepth() is 1.0. // If we ever allow this to NOT be 1, we'll need to fix this up. const GfVec2d &min = _window.GetMin(); const GfVec2d &max = _window.GetMax(); // Construct the corner points in 3D as follows: construct a starting // point by using the x and y coordinates of the reference plane and // -1 as the z coordinate. Add the position of the frustum to generate // the actual points in world-space coordinates. GfVec3d leftBottom = _position + _rotation.TransformDir(GfVec3d(min[0], min[1], -1.0)); GfVec3d rightTop = _position + _rotation.TransformDir(GfVec3d(max[0], max[1], -1.0)); // Now, transform the corner points by the given matrix leftBottom = matrix.Transform(leftBottom); rightTop = matrix.Transform(rightTop); // Subtract the transformed frustum position from the transformed // corner points. Then, rotate the points using the rotation that would // transform the view direction vector back to (0, 0, -1). This brings // the corner points from the woorld coordinate system into the local // frustum one. leftBottom -= frustum._position; rightTop -= frustum._position; leftBottom = frustum._rotation.GetInverse().TransformDir(leftBottom); rightTop = frustum._rotation.GetInverse().TransformDir(rightTop); // Finally, use the similar triangles trick to bring the corner // points back at one unit away from the point. These scaled x and // y coordinates can be directly used to construct the new // transformed reference plane. Skip the scaling step for an // orthographic projection, though. if (_projectionType == Perspective) { leftBottom /= scale; rightTop /= scale; } frustum._window.SetMin(GfVec2d(leftBottom[0], leftBottom[1])); frustum._window.SetMax(GfVec2d(rightTop[0], rightTop[1])); // Note that negative scales in the transform have the potential // to flip the window. Fix it if necessary. GfVec2d wMin = frustum._window.GetMin(); GfVec2d wMax = frustum._window.GetMax(); // Make sure left < right if ( wMin[0] > wMax[0] ) { std::swap( wMin[0], wMax[0] ); } // Make sure bottom < top if ( wMin[1] > wMax[1] ) { std::swap( wMin[1], wMax[1] ); } frustum._window.SetMin( wMin ); frustum._window.SetMax( wMax ); *this = frustum; return *this; }
void Xform::updateSample(Time t_) { super::updateSample(t_); if (m_update_flag.bits == 0) { m_sample.flags = (m_sample.flags & ~(int)XformData::Flags::UpdatedMask); return; } if (m_update_flag.variant_set_changed) { m_summary_needs_update = true; } auto t = UsdTimeCode(t_); const auto& conf = getImportSettings(); auto& sample = m_sample; auto prev = sample; if (m_summary.type == XformSummary::Type::TRS) { auto translate = float3::zero(); auto scale = float3::one(); auto rotation = quatf::identity(); for (auto& op : m_read_ops) { switch (op.GetOpType()) { case UsdGeomXformOp::TypeTranslate: { float3 tmp; op.GetAs((GfVec3f*)&tmp, t); translate += tmp; break; } case UsdGeomXformOp::TypeScale: { float3 tmp; op.GetAs((GfVec3f*)&tmp, t); scale *= tmp; break; } case UsdGeomXformOp::TypeOrient: { quatf tmp; op.GetAs((GfQuatf*)&tmp, t); rotation *= tmp; break; } case UsdGeomXformOp::TypeRotateX: { float angle; op.GetAs(&angle, t); rotation *= rotateX(angle * Deg2Rad); break; } case UsdGeomXformOp::TypeRotateY: { float angle; op.GetAs(&angle, t); rotation *= rotateY(angle * Deg2Rad); break; } case UsdGeomXformOp::TypeRotateZ: { float angle; op.GetAs(&angle, t); rotation *= rotateZ(angle * Deg2Rad); break; } case UsdGeomXformOp::TypeRotateXYZ: // case UsdGeomXformOp::TypeRotateXZY: // case UsdGeomXformOp::TypeRotateYXZ: // case UsdGeomXformOp::TypeRotateYZX: // case UsdGeomXformOp::TypeRotateZXY: // case UsdGeomXformOp::TypeRotateZYX: // fall through { float3 euler; op.GetAs((GfVec3f*)&euler, t); rotation *= EulerToQuaternion(euler * Deg2Rad, op.GetOpType()); break; } default: break; } } if (conf.swap_handedness) { translate.x *= -1.0f; rotation = swap_handedness(sample.rotation); } sample.position = translate; sample.rotation = rotation; sample.scale = scale; } else { GfMatrix4d result; result.SetIdentity(); for (auto& op : m_read_ops) { auto m = op.GetOpTransform(t); result = m * result; } GfTransform gft; gft.SetMatrix(result); (GfMatrix4f&)sample.transform = GfMatrix4f(result); (GfVec3f&)sample.position = GfVec3f(gft.GetTranslation()); (GfQuatf&)sample.rotation = GfQuatf(gft.GetRotation().GetQuat()); (GfVec3f&)sample.scale = GfVec3f(gft.GetScale()); } int update_flags = 0; if (!near_equal(prev.position, sample.position)) { update_flags |= (int)XformData::Flags::UpdatedPosition; } if (!near_equal(prev.rotation, sample.rotation)) { update_flags |= (int)XformData::Flags::UpdatedRotation; } if (!near_equal(prev.scale, sample.scale)) { update_flags |= (int)XformData::Flags::UpdatedScale; } sample.flags = (sample.flags & ~(int)XformData::Flags::UpdatedMask) | update_flags; }
void My_TestGLDrawing::DrawTest(bool offscreen) { std::cout << "My_TestGLDrawing::DrawTest()\n"; HdPerfLog& perfLog = HdPerfLog::GetInstance(); perfLog.Enable(); // Reset all counters we care about. perfLog.ResetCache(HdTokens->extent); perfLog.ResetCache(HdTokens->points); perfLog.ResetCache(HdTokens->topology); perfLog.ResetCache(HdTokens->transform); perfLog.SetCounter(UsdImagingTokens->usdVaryingExtent, 0); perfLog.SetCounter(UsdImagingTokens->usdVaryingPrimvar, 0); perfLog.SetCounter(UsdImagingTokens->usdVaryingTopology, 0); perfLog.SetCounter(UsdImagingTokens->usdVaryingVisibility, 0); perfLog.SetCounter(UsdImagingTokens->usdVaryingXform, 0); int width = GetWidth(), height = GetHeight(); double aspectRatio = double(width)/height; GfFrustum frustum; frustum.SetPerspective(60.0, aspectRatio, 1, 100000.0); GfMatrix4d viewMatrix; viewMatrix.SetIdentity(); viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(0, 1, 0), _rotate[0])); viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(1, 0, 0), _rotate[1])); viewMatrix *= GfMatrix4d().SetTranslate(GfVec3d(_translate[0], _translate[1], _translate[2])); GfMatrix4d projMatrix = frustum.ComputeProjectionMatrix(); GfMatrix4d modelViewMatrix = viewMatrix; if (UsdGeomGetStageUpAxis(_stage) == UsdGeomTokens->z) { // rotate from z-up to y-up modelViewMatrix = GfMatrix4d().SetRotate(GfRotation(GfVec3d(1.0,0.0,0.0), -90.0)) * modelViewMatrix; } GfVec4d viewport(0, 0, width, height); _engine->SetCameraState(modelViewMatrix, projMatrix, viewport); size_t i = 0; TF_FOR_ALL(timeIt, GetTimes()) { UsdTimeCode time = *timeIt; if (*timeIt == -999) { time = UsdTimeCode::Default(); } UsdImagingGLRenderParams params; params.drawMode = GetDrawMode(); params.enableLighting = IsEnabledTestLighting(); params.enableIdRender = IsEnabledIdRender(); params.frame = time; params.complexity = _GetComplexity(); params.cullStyle = IsEnabledCullBackfaces() ? UsdImagingGLCullStyle::CULL_STYLE_BACK : UsdImagingGLCullStyle::CULL_STYLE_NOTHING; glViewport(0, 0, width, height); glEnable(GL_DEPTH_TEST); if(IsEnabledTestLighting()) { if(UsdImagingGLEngine::IsHydraEnabled()) { _engine->SetLightingState(_lightingContext); } else { _engine->SetLightingStateFromOpenGL(); } } if (!GetClipPlanes().empty()) { params.clipPlanes = GetClipPlanes(); for (size_t i=0; i<GetClipPlanes().size(); ++i) { glEnable(GL_CLIP_PLANE0 + i); } } GfVec4f const &clearColor = GetClearColor(); GLfloat clearDepth[1] = { 1.0f }; // Make sure we render to convergence. TfErrorMark mark; do { glClearBufferfv(GL_COLOR, 0, clearColor.data()); glClearBufferfv(GL_DEPTH, 0, clearDepth); _engine->Render(_stage->GetPseudoRoot(), params); } while (!_engine->IsConverged()); TF_VERIFY(mark.IsClean(), "Errors occurred while rendering!"); std::cout << "itemsDrawn " << perfLog.GetCounter(HdTokens->itemsDrawn) << std::endl; std::cout << "totalItemCount " << perfLog.GetCounter(HdTokens->totalItemCount) << std::endl; std::string imageFilePath = GetOutputFilePath(); if (!imageFilePath.empty()) { if (time != UsdTimeCode::Default()) { std::stringstream suffix; suffix << "_" << std::setw(3) << std::setfill('0') << params.frame << ".png"; imageFilePath = TfStringReplace(imageFilePath, ".png", suffix.str()); } std::cout << imageFilePath << "\n"; WriteToFile("color", imageFilePath); } i++; }