Skip to content

Commit

Permalink
BUGFIX: Revert capture inverted render visual correction, Apply
Browse files Browse the repository at this point in the history
- Revert much of 3932b26 as there was a test oversight resulting in a bug
  - To be re-applied later
- Fixes capture bug that prevents toolkit from adding replacements
  - Extra hierarchy under instance prims was added, and has now been removed
- Fixes long-standing bug where capturer incorrectly sets doublesidedness
- REMIX-2493
  • Loading branch information
nv-nfreybler committed Dec 14, 2023
2 parents e701287 + 22f6af8 commit f84d99e
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 177 deletions.
4 changes: 2 additions & 2 deletions src/dxvk/rtx_render/rtx_game_capturer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ namespace dxvk {
m_pCap->camera.farPlane = kMaxFarPlane;
}
// If the app is being rendered upside-down, we need to plan accordingly
m_pCap->camera.bFlipMeshes = (projMat[0][0] * projMat[1][1] < 0.0f);
m_pCap->camera.bFlipVertAperture = (projMat[0][0] * projMat[1][1] < 0.0f);
m_pCap->camera.firstTime = m_pCap->currentFrameNum;
}
assert(!isnan(m_pCap->camera.fov));
Expand Down Expand Up @@ -489,7 +489,7 @@ namespace dxvk {
const size_t numVertices = geomData.vertexCount;
assert(numVertices > 0);
const size_t numIndices = geomData.indexCount;
const bool isDoubleSided = geomData.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
const bool isDoubleSided = geomData.cullMode == VK_CULL_MODE_NONE;
if (bIsNewMesh) {
assert(pMesh->lssData.buffers.positionBufs.size() == 0);
assert(pMesh->lssData.buffers.normalBufs.size() == 0);
Expand Down
224 changes: 61 additions & 163 deletions src/lssusd/game_exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ pxr::UsdStageRefPtr GameExporter::createInstanceStage(const Export& exportData)
assert(rootMaterialsPrim);
const auto rootInstancesPrim = pxr::UsdGeomXform::Define(instanceStage,gRootInstancesPath);
assert(rootInstancesPrim);
const auto rootCameraPrim = pxr::UsdGeomXform::Define(instanceStage, gRootLightCamera);
assert(rootCameraPrim);
dxvk::Logger::debug("[GameExporter][" + exportData.debugId + "] Creating instance stage");

// capture meta data
Expand Down Expand Up @@ -569,8 +567,27 @@ void GameExporter::exportMeshes(const Export& exportData, ExportContext& ctx) {
}
meshStage->GetRootLayer()->SetCustomLayerData(customLayerData);

// Some meshes require visual correction to make DCC tool QoL easier.
pxr::SdfPath meshXformSdfPath;
if(exportData.meta.bCorrectBakedTransforms) {
// First: calculate xform required to correct this mesh
pxr::GfMatrix4d correctionXform{1.0};
// Visually undo world transform if it's baked into mesh vertices
correctionXform.SetTranslateOnly(pxr::GfVec3d(mesh.origin));
correctionXform = correctionXform.GetInverse();

// Second: apply xform
const auto correctionXformSdfPath = gStageRootPath.AppendElementString("visual_correction");
auto correctionXformSchema = pxr::UsdGeomXform::Define(meshStage, correctionXformSdfPath);
auto meshXformOp = correctionXformSchema.AddTransformOp();
assert(meshXformOp);
meshXformOp.Set(correctionXform);
meshXformSdfPath = correctionXformSdfPath.AppendElementString(meshName);
} else {
meshXformSdfPath = gStageRootPath.AppendElementString(meshName);
}

// Build mesh xform prim on mesh stage, make it visible
const auto meshXformSdfPath = gStageRootPath.AppendElementString(meshName);
pxr::UsdGeomXformable meshXformSchema;
if (isSkeleton) {
meshXformSchema = pxr::UsdSkelRoot::Define(meshStage, meshXformSdfPath);
Expand All @@ -583,29 +600,6 @@ void GameExporter::exportMeshes(const Export& exportData, ExportContext& ctx) {
assert(meshXformVisibilityAttr);
meshXformVisibilityAttr.Set(gVisibilityInherited);

// Visually undo world transform if it's baked into mesh vertices
if(exportData.meta.bCorrectBakedTransforms) {
auto meshXformOp = meshXformSchema.AddTransformOp();
assert(meshXformOp);
pxr::GfMatrix4d xform{1.0};
// Note: Don't call flipXForm for flipping here, because we need a post-flipping but SetScale function will clean-up the translation
if (!exportData.camera.bFlipMeshes) {
xform.SetTranslateOnly(pxr::GfVec3d(mesh.origin));
xform = xform.GetInverse();
} else {
if (!exportData.meta.isZUp) {
xform.SetTranslateOnly(pxr::GfVec3d(mesh.origin[0], -mesh.origin[1], mesh.origin[2]));
xform = xform.GetInverse();
xform[1][1] *= -1.0;
} else {
xform.SetTranslateOnly(pxr::GfVec3d(mesh.origin[0], mesh.origin[1], -mesh.origin[2]));
xform = xform.GetInverse();
xform[2][2] *= -1.0;
}
}
meshXformOp.Set(xform);
}

// Build mesh geometry prim under above xform
const auto meshSchemaSdfPath = meshXformSdfPath.AppendChild(gTokMesh);
pxr::UsdGeomMesh meshSchema = pxr::UsdGeomMesh::Define(meshStage, meshSchemaSdfPath);
Expand Down Expand Up @@ -745,7 +739,7 @@ void GameExporter::exportMeshes(const Export& exportData, ExportContext& ctx) {

const std::string relMeshStagePath = relMeshDirPath + meshName + ctx.extension;
auto meshInstanceUsdReferences = meshInstanceXformSchema.GetPrim().GetReferences();
meshInstanceUsdReferences.AddReference(relMeshStagePath);
meshInstanceUsdReferences.AddReference(relMeshStagePath,meshXformSdfPath);

auto meshInstanceXformVisibilityAttr = meshInstanceXformSchema.CreateVisibilityAttr();
assert(meshInstanceXformVisibilityAttr);
Expand Down Expand Up @@ -860,34 +854,44 @@ void GameExporter::exportColorOpacityBufferSet(const BufSet<Color>& bufSet, pxr:
void GameExporter::exportInstances(const Export& exportData, ExportContext& ctx) {
assert(exportData.bExportInstanceStage);
dxvk::Logger::debug("[GameExporter][" + exportData.debugId + "][exportInstances] Begin");

// First apply any visual corrections globally
if(exportData.meta.bCorrectBakedTransforms) {
auto rootInstancesXformSchema = pxr::UsdGeomXform::Get(ctx.instanceStage,gRootInstancesPath);
assert(rootInstancesXformSchema);
setStageOffsetXform(rootInstancesXformSchema, exportData.stageOrigin, exportData.meta.isLHS);

pxr::GfMatrix4d xform{1.0};
if (exportData.meta.bCorrectBakedTransforms) {
xform.SetTranslateOnly(-exportData.stageOrigin);
}
xform = exportData.meta.isLHS ? ToRHS(xform) : xform;

auto transformOp = rootInstancesXformSchema.AddTransformOp();
assert(transformOp);
transformOp.Set(xform);
}

// Now process each individual instance
for(const auto& [instId,instanceData] : exportData.instances) {
// Build base Xform prim for instance to reside in
auto instanceName = (instanceData.isSky ? "sky_" : "inst_") + std::string(instanceData.instanceName);
pxr::SdfPath instancePath = gRootInstancesPath.AppendElementString(instanceName);
auto instanceXformSchema = pxr::UsdGeomXform::Define(ctx.instanceStage, instancePath);

pxr::SdfPath meshRefPath = instancePath.AppendElementString("ref");
pxr::UsdGeomXformable meshRefXformSchema;
pxr::UsdGeomXformable instanceXformSchema;
const bool isSkeleton = !instanceData.boneXForms.empty();
if (isSkeleton) {
meshRefXformSchema = pxr::UsdSkelRoot::Define(ctx.instanceStage, meshRefPath);
instanceXformSchema = pxr::UsdSkelRoot::Define(ctx.instanceStage, instancePath);
} else {
meshRefXformSchema = pxr::UsdGeomXform::Define(ctx.instanceStage, meshRefPath);
instanceXformSchema = pxr::UsdGeomXform::Define(ctx.instanceStage, instancePath);
}
assert(meshRefXformSchema);
assert(instanceXformSchema);

// Attach reference to mesh in question
const Reference& meshLssReference = ctx.meshReferences[instanceData.meshId];
auto instanceUsdReferences = meshRefXformSchema.GetPrim().GetReferences();
auto instanceUsdReferences = instanceXformSchema.GetPrim().GetReferences();
instanceUsdReferences.AddInternalReference(meshLssReference.instanceSdfPath);

// Set instanced mesh to now be visible
auto visibilityAttr = meshRefXformSchema.CreateVisibilityAttr();
auto visibilityAttr = instanceXformSchema.CreateVisibilityAttr();
assert(visibilityAttr);
visibilityAttr.Set(gVisibilityInherited);

Expand All @@ -902,13 +906,13 @@ void GameExporter::exportInstances(const Export& exportData, ExportContext& ctx)
const Reference& matLssReference = ctx.matReferences[instanceData.matId];
const auto shaderMatSchema = pxr::UsdShadeMaterial::Get(ctx.instanceStage, matLssReference.instanceSdfPath);
assert(shaderMatSchema);
pxr::UsdShadeMaterialBindingAPI(meshRefXformSchema.GetPrim()).Bind(shaderMatSchema);
pxr::UsdShadeMaterialBindingAPI(instanceXformSchema.GetPrim()).Bind(shaderMatSchema);
}

if (isSkeleton) {
// Set instance skeleton pose / animation
const auto skelPoseSdfPath = meshRefPath.AppendChild(gTokPose);
const auto skelSkelSdfPath = meshRefPath.AppendChild(gTokSkel);
const auto skelPoseSdfPath = instancePath.AppendChild(gTokPose);
const auto skelSkelSdfPath = instancePath.AppendChild(gTokSkel);
auto skelAnimationSchema = pxr::UsdSkelAnimation::Define(ctx.instanceStage, skelPoseSdfPath);
assert(skelAnimationSchema);
const lss::Skeleton& skel = ctx.skeletons[instanceData.meshId];
Expand All @@ -927,7 +931,7 @@ void GameExporter::exportInstances(const Export& exportData, ExportContext& ctx)
auto animationSource = skelBindingSchema.CreateAnimationSourceRel();
animationSource.SetTargets({skelPoseSdfPath});
} else {
const auto meshSchemaSdfPath = meshRefPath.AppendChild(gTokMesh);
const auto meshSchemaSdfPath = instancePath.AppendChild(gTokMesh);
pxr::UsdGeomMesh meshSchema = pxr::UsdGeomMesh::Define(ctx.instanceStage, meshSchemaSdfPath);
pxr::UsdGeomPrimvarsAPI primvarsAPI(meshSchema.GetPrim());

Expand All @@ -950,53 +954,20 @@ void GameExporter::exportInstances(const Export& exportData, ExportContext& ctx)
#undef _SetDrawMetadata
}

const auto& mesh = exportData.meshes.at(instanceData.meshId);
// Move instance back to its original positions by undoing the visual correction.
// a.k.a. Invert the invert done in exportMeshes
pxr::GfMatrix4d commonXform{1.0};
if (exportData.meta.bCorrectBakedTransforms) {
if (!exportData.camera.bFlipMeshes) {
commonXform.SetTranslateOnly(pxr::GfVec3d(mesh.origin));
} else {
// This translation actually combines 2 steps of translation corrections for bake-corrected + flipped meshes:
// 1. Move instance back to it's original position inside root by undoing the flipped visual correction (-mesh.origin[1] or -mesh.origin[2])
// 2. Cancel out the root translation on the flipped dimension (Y or Z), then move exactly the same distance to the other side of flip axis:
// P y/z P'
// |--------->|--------->|
// stageOrigin[1/2] * 2
if (!exportData.meta.isZUp) {
commonXform.SetTranslateOnly(pxr::GfVec3d(mesh.origin[0], -mesh.origin[1] + exportData.stageOrigin[1] * 2, mesh.origin[2]));
} else {
commonXform.SetTranslateOnly(pxr::GfVec3d(mesh.origin[0], mesh.origin[1], -mesh.origin[2] + exportData.stageOrigin[2] * 2));
}
}
} else {
flipXForm(exportData, commonXform);
}

setTimeSampledXforms<true>(ctx.instanceStage, instancePath,
instanceData.firstTime, instanceData.finalTime, instanceData.xforms,
exportData.meta, commonXform);
exportData.meta);
setVisibilityTimeSpan(ctx.instanceStage, instancePath, instanceData.firstTime, instanceData.finalTime, exportData.meta.numFramesCaptured);
}
dxvk::Logger::debug("[GameExporter][" + exportData.debugId + "][exportInstances] End");
}

void GameExporter::exportCamera(const Export& exportData, ExportContext& ctx) {
dxvk::Logger::debug("[GameExporter][" + exportData.debugId + "][exportCamera] Begin");

auto gRootCamerasPath = gRootNodePath.AppendChild(kTokCameras);

static const pxr::TfToken kTokCamera("Camera");
const pxr::SdfPath cameraSdfPath = gRootCamerasPath.AppendChild(kTokCamera);
const pxr::SdfPath cameraSdfPath = gRootNodePath.AppendChild(kTokCamera);
auto geomCamera = pxr::UsdGeomCamera::Define(ctx.instanceStage, cameraSdfPath);

if (exportData.camera.bFlipMeshes) {
auto rootCamerasXformSchema = pxr::UsdGeomXform::Get(ctx.instanceStage, gRootCamerasPath);
assert(rootCamerasXformSchema);
setStageOffsetXform(rootCamerasXformSchema, pxr::GfVec3f { 0.f,0.f,0.f }, exportData.meta.isLHS);
}

// Create Gf Camera which will convert FOV + Aspect Ratio -> Usd Camera Attributes
pxr::GfCamera simpleCam;
simpleCam.SetPerspectiveFromAspectRatioAndFieldOfView(
Expand All @@ -1010,6 +981,14 @@ void GameExporter::exportCamera(const Export& exportData, ExportContext& ctx) {
auto horizontalAperture = geomCamera.CreateHorizontalApertureAttr();
horizontalAperture.Set(simpleCam.GetHorizontalAperture());

// Set Vertical aperture
auto verticalAperture = geomCamera.CreateVerticalApertureAttr();
float verticalApertureVal = simpleCam.GetVerticalAperture();
if(exportData.camera.bFlipVertAperture) {
verticalApertureVal *= (-1.f);
}
verticalAperture.Set(verticalApertureVal);

// Set focal length
auto focalLength = geomCamera.CreateFocalLengthAttr();
focalLength.Set(simpleCam.GetFocalLength());
Expand All @@ -1021,17 +1000,12 @@ void GameExporter::exportCamera(const Export& exportData, ExportContext& ctx) {
// Camera position needs to be adjusted if we're visually correcting baked transforms
pxr::GfMatrix4d commonXform{1.0};
if(exportData.meta.bCorrectBakedTransforms) {
pxr::GfVec3f stageOrigin = exportData.stageOrigin;
flipXForm(exportData, commonXform);
commonXform.SetTranslateOnly(pxr::GfVec3d(stageOrigin));
commonXform.SetTranslateOnly(pxr::GfVec3d(exportData.stageOrigin));
commonXform = commonXform.GetInverse();
} else {
flipXForm(exportData, commonXform);
}

setCameraTimeSampledXforms(ctx.instanceStage, cameraSdfPath,
exportData.camera.firstTime, exportData.camera.finalTime, exportData.camera.xforms[0],
exportData.meta, commonXform);
setTimeSampledXforms<false>(ctx.instanceStage, cameraSdfPath,
exportData.camera.firstTime, exportData.camera.finalTime, exportData.camera.xforms,
exportData.meta, commonXform);

// Must modify here, since there may be existing data set earlier
pxr::VtDictionary customLayerData = ctx.instanceStage->GetRootLayer()->GetCustomLayerData();
Expand Down Expand Up @@ -1095,13 +1069,9 @@ void GameExporter::exportSphereLights(const Export& exportData, ExportContext& c
shaping.Apply(sphereLight.GetPrim());
}

// Sphere light position needs to be adjusted if we're visually flipping back upside down issue
pxr::GfMatrix4d commonXform { 1.0 };
flipXForm(exportData, commonXform);

setTimeSampledXforms<false>(lightStage, lightAssetSdfPath,
sphereLightData.firstTime, sphereLightData.finalTime, sphereLightData.xforms,
exportData.meta, commonXform);
exportData.meta);

pxr::UsdLuxLightAPI lightAPI(sphereLight.GetPrim());
setLightIntensityOnTimeSpan(lightAPI, sphereLightData.intensity, sphereLightData.firstTime, sphereLightData.finalTime, exportData.meta.numFramesCaptured);
Expand Down Expand Up @@ -1148,15 +1118,7 @@ void GameExporter::exportDistantLights(const Export& exportData, ExportContext&
angleAttr.Set(distantLightData.angle);

static const pxr::GfVec3d distantLightDefault(0.0,0.0,-1.0);
pxr::GfVec3f distantLightDirection = distantLightData.direction;
if (exportData.camera.bFlipMeshes) {
if (!exportData.meta.isZUp) {
distantLightDirection[1] *= -1.0f;
} else {
distantLightDirection[2] *= -1.0f;
}
}
const auto directionQuatF = pxr::GfQuatf{pxr::GfRotation(distantLightDefault, distantLightDirection).GetQuat()};
const auto directionQuatF = pxr::GfQuatf{pxr::GfRotation(distantLightDefault, distantLightData.direction).GetQuat()};
auto orientAttr = distantLightSchema.AddOrientOp();
assert(orientAttr);
orientAttr.Set(directionQuatF);
Expand Down Expand Up @@ -1410,59 +1372,6 @@ void GameExporter::setLightIntensityOnTimeSpan(const pxr::UsdLuxLightAPI& luxLig
}
}

void GameExporter::setCameraTimeSampledXforms(const pxr::UsdStageRefPtr stage,
const pxr::SdfPath sdfPath,
const float firstTime,
const float finalTime,
const SampledXform& cameraXform,
const Export::Meta& meta,
const pxr::GfMatrix4d& commonXform) {
assert(stage);
assert(sdfPath != pxr::SdfPath());

const bool isSingleFrame = meta.numFramesCaptured == 1;
const pxr::UsdTimeCode timeCode = isSingleFrame ? pxr::UsdTimeCode::Default() : pxr::UsdTimeCode(cameraXform.time);

auto xform = cameraXform.xform * commonXform;
xform = meta.isLHS ? ToRHS(xform) : xform;
const pxr::GfVec3d translation = xform.ExtractTranslation();
pxr::GfVec3f scale(xform.GetRow3(0).GetLength(), xform.GetRow3(1).GetLength(), xform.GetRow3(2).GetLength());

pxr::GfVec3f rotation;
const auto r = xform.GetOrthonormalized().ExtractRotationMatrix();

if (r.GetHandedness() > 0) {
// Proper pure rotation - easy case.
rotation = ToEuler<pxr::GfVec3f>(r);
} else {
// Doing Improper Rotation for flipped matrix
ExtractEulerImproper<true>(rotation, r, pxr::GfVec3d(-1.0, -1.0, -1.0));
// Rotate camera around camera Up axis with 180 degrees
if (!meta.isZUp) {
rotation[0] -= 180.0f;
rotation[1] = -rotation[1];
rotation[2] = 180.0f - rotation[2];
} else {
rotation[0] -= 180.0f;
rotation[1] = -rotation[1];
rotation[2] = -rotation[2] - 180.0f;
}
}

// Apply transformation OPs
auto geomXformable = pxr::UsdGeomXformable::Get(stage, sdfPath);
auto translateOp = geomXformable.AddTranslateOp();
assert(translateOp);
auto rotateOp = geomXformable.AddRotateZYXOp();
assert(rotateOp);
auto scaleOp = geomXformable.AddScaleOp();
assert(scaleOp);

translateOp.Set(translation, timeCode);
rotateOp.Set(rotation, timeCode);
scaleOp.Set(scale, timeCode);
}

pxr::UsdStageRefPtr GameExporter::findOpenOrCreateStage(const std::string path, const bool bClearIfExists) {
const bool bLayerAlreadyExists = pxr::TfIsFile(path);
pxr::SdfLayerRefPtr alreadyExistentLayer;
Expand All @@ -1478,15 +1387,4 @@ pxr::UsdStageRefPtr GameExporter::findOpenOrCreateStage(const std::string path,
return stage;
}

void GameExporter::flipXForm(const Export& exportData,
pxr::GfMatrix4d& commonXform) {
if (exportData.camera.bFlipMeshes) {
if (!exportData.meta.isZUp) {
commonXform.SetScale(pxr::GfVec3d(1.0, -1.0, 1.0));
} else {
commonXform.SetScale(pxr::GfVec3d(1.0, 1.0, -1.0));
}
}
}

}
Loading

0 comments on commit f84d99e

Please sign in to comment.