Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added PDB importer for proteins (-pdb command line argument) #109

Merged
merged 2 commits into from
Mar 21, 2025

Conversation

favreau
Copy link
Contributor

@favreau favreau commented Mar 20, 2025

No description provided.

@jeffamstutz
Copy link
Collaborator

This is awesome, thank you!

I'm going to put a few notes on how the layer tree is used, not because I'm above fixing them (they are trivial changes really), but because it's a chance to a) describe still-undocumented layer code, and b) talk through how ANARI could represent this data in a more efficient way via geometry attributes.

Copy link
Collaborator

@jeffamstutz jeffamstutz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fixes are small, but hopefully the learning experience is helpful....this is all undocumented stuff, so I'm super impressed you've done so well with it! Let me know if you have any questions

}
}

const auto pdbLocation = ctx.defaultLayer()->insert_first_child(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One recent thing I added was the ability for layer nodes to allow users to directly modify their children without needing the context or layer itself. Thus this could be instead written:

location->insert_first_child(...);

}

const auto pdbLocation = ctx.defaultLayer()->insert_first_child(
location, tsd::utility::Any(ANARI_GEOMETRY, 1));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed here you are inserting an ANARI_GEOMETRY reference into the layer -- which makes sense given an OpenUSD mental model. TSD is different in a key way -- the layer tree is only relevant to instancing objects. In the context, there are a bunch of arrays of objects by type (geometry, sampler, spatial field, surface, material, etc.)...basically everything in the world that isn't a group or instance. Thus when you say (borrowing from below)

auto spheres =
        ctx.createObject<tsd::Geometry>(tsd::tokens::geometry::sphere);

...this creates a geometry object (subtype sphere) in the geometry array in the context. Things that live in the context this way are referenced in layers via the Any container object (just an ANARIDataType and stored value). Each object in the context's object arrays are offset-stable -- meaning that their "slot" in this array is always fixed: erasure and insertion work on a stack of "open slots" that only appends if all elements are "filled". This means that when we reference an object, we reference its type and the index of the object in the context's object database. So to make a reference to a surface, you might do:

 auto sphereSurface = ctx.createSurface(spheresName.c_str(), spheres, m);
 location->insert_first_child(utility::Any(ANARI_SURFACE, sphereSurface->index()));

Each object knows its eternal index in the context, so you simply ask it for index() (i.e. no need to go find it via the context). This is how we can, when traversing the tree, get a pointer back to that object via the context with:

size_t sphereSurfaceIdx = /*index stored in a layer node*/;
auto *sphereSurface = ctx.getObject(ANARI_SURFACE, sphereSurfaceIdx);

All of this to say -- a node that merely collects child objects should be untyped, or perhaps be a transform node:

auto pdbLocation = location->insert_first_child(tsd::mat4(tsd::math::identity), "maybe filename?");

Note that basically right now you'll only put transforms, surface, volume, or light nodes in a layer. Anything else won't do anything. (which is why referencing the 1 element in the geometry list didn't contribute to what ended up in the world that gets rendered)

const std::string spheresName = basename + " (" + name + ")";

auto m = ctx.createObject<Material>(tokens::material::matte);
m->setParameter("color"_t, ANARI_FLOAT32_VEC3, &baseColor);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is per-geometry coloring, all with the same general material setup. This means I think it would be more efficient for there to be just a single material object and you assign a per-geometry uniform color. The ANARI attribute system lets you pull attribute values from geometries, even if they are uniform/constant for the whole geometry. Thus the setup would look like:

// outside the loop, make a single material
auto m = ctx.createObject<Material>(tokens::material::matte);
m->setParameter("color"_t, "color");// have this material pull from the 'color' attribute on the geometry

Then for each geometry, you would do

spheres->setParameter("color", baseColor); // ANARIDataType inferred from baseColor C++ type

...which is the same as

spheres->setParameter("color", ANARI_FLOAT32_VEC3, &baseColor); // set via data type + void* to value

Just to be complete, note that you could use any of the generic attributes too, just to learn how the system works. Which would look like:

auto m = ctx.createObject<Material>(tokens::material::matte);
m->setParameter("color"_t, "attribute0"); // have this material pull from the 'attribute0'

foreach (spheres, geometriesToProcess) {
  // ...
  spheres->setParameter("attribute0", baseColor);
  // ...
}

Note this is a direct ANARI concept that TSD is matching 1:1. Pretty much all native ANARI concepts are 1:1 except instancing (i.e. the whole layers thing).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in case it's not clear -- any object can exist in more than one parent with ANARI. So it's totally fine (and expected sometimes) that you have a single material on N surfaces, or a single sampler on N materials, etc. No need to make unique objects unless they are expected to be parameterized differently.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also (!), forgot to mention that uniform attributes (constant) values can only be set as float4, so don't drive yourself crazy if you set float3 and everything is black (default value of {0,0,0,1} when a non-float4 value is set and therefore ignored).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are totally right, I have this old habit of classifying the geometry according to the material, which does not necessarily makes sense. I have changed to the code to use only one material, together with the float4 color attribute added to each individual sphere.

@jeffamstutz
Copy link
Collaborator

Here's the cheatsheet of how I would change it based on my comments above:

--- a/tsd/src/tsd/authoring/importers/import_PDB.cpp
+++ b/tsd/src/tsd/authoring/importers/import_PDB.cpp
@@ -4,6 +4,8 @@
 #include "tsd/authoring/importers.hpp"
 #include "tsd/core/Logging.hpp"
 
+#include "detail/importer_common.hpp"
+
 #include <array>
 #include <cmath>
 #include <filesystem>
@@ -340,8 +342,11 @@ void readPDBFile(Context &ctx, const char *filename, LayerNodeRef location)
     }
   }
 
-  const auto pdbLocation = ctx.defaultLayer()->insert_first_child(
-      location, tsd::utility::Any(ANARI_GEOMETRY, 1));
+  auto pdbLocation =
+      location->insert_first_child({{}, fileOf(filename).c_str()});
+
+  auto mat = ctx.createObject<Material>(tokens::material::matte);
+  mat->setParameter("color", "color");
 
   for (const auto &[name, atoms] : atomsMap) {
     // Generate spheres for each atom
@@ -389,18 +394,16 @@ void readPDBFile(Context &ctx, const char *filename, LayerNodeRef location)
       baseColor = tsd::float3((*it).second.r / 255.0,
           (*it).second.g / 255.0,
           (*it).second.b / 255.0);
+    spheres->setParameter("color"_t, float4(baseColor, 1.f));
 
     const std::string basename =
         std::filesystem::path(filename).filename().string();
     const std::string spheresName = basename + " (" + name + ")";
 
-    auto m = ctx.createObject<Material>(tokens::material::matte);
-    m->setParameter("color"_t, ANARI_FLOAT32_VEC3, &baseColor);
-    auto sphereSurface = ctx.createSurface(spheresName.c_str(), spheres, m);
-    const auto atomsLocation = ctx.defaultLayer()->insert_first_child(
-        pdbLocation, tsd::utility::Any(ANARI_GEOMETRY, 1));

CMakeLists.txt Outdated

project(pdb_reader)

add_executable(pdb_reader main.cpp)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't catch this right away -- this looks like some stray lines left in there by accident?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea where this is coming from... Sorry... maybe some automatic auto-completion

@jeffamstutz
Copy link
Collaborator

LGTM, thanks!

@jeffamstutz jeffamstutz merged commit 5013872 into NVIDIA:next_release Mar 21, 2025
5 checks passed
favreau added a commit to favreau/VisRTX that referenced this pull request Mar 21, 2025
)

* Added PDB importer for proteins (-pdb command line argument)

* Reworked material management
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants