From b06a1949036619d126d429a958ce4b6c7eefc3ab Mon Sep 17 00:00:00 2001 From: frericp Date: Wed, 2 Feb 2022 11:48:11 +0100 Subject: [PATCH] cleanup and minor fixes --- CMakeLists.txt | 2 +- .../hdEmscripten/js/ThreeJsRenderDelegate.js | 24 +- pxr/usdImaging/hdEmscripten/testenv/test.html | 2 +- .../hdEmscripten/webRenderDelegate.cpp | 240 ++++-------------- 4 files changed, 63 insertions(+), 205 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b53b410b..e70471b9a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ set(CMAKE_CXX_FLAGS "${_PXR_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") if(PXR_ENABLE_JS_SUPPORT) # Add EMSCRIPTEN specific compiler flags (is this the right place)? - set(EMSCRIPTEN_COMPILE_FLAGS "-s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -s MODULARIZE=1 -s EXPORT_NAME='getUsdModule' -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"FS\"]' -s FORCE_FILESYSTEM=1 --pre-js ${CMAKE_SOURCE_DIR}/js/prejs.js") + set(EMSCRIPTEN_COMPILE_FLAGS "-s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s ASSERTIONS=1 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -s MODULARIZE=1 -s EXPORT_NAME='getUsdModule' -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"FS\"]' -s FORCE_FILESYSTEM=1 --pre-js ${CMAKE_SOURCE_DIR}/js/prejs.js") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EMSCRIPTEN_COMPILE_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_COMPILE_FLAGS}") diff --git a/pxr/usdImaging/hdEmscripten/js/ThreeJsRenderDelegate.js b/pxr/usdImaging/hdEmscripten/js/ThreeJsRenderDelegate.js index a573053207..7832bcebbc 100644 --- a/pxr/usdImaging/hdEmscripten/js/ThreeJsRenderDelegate.js +++ b/pxr/usdImaging/hdEmscripten/js/ThreeJsRenderDelegate.js @@ -37,7 +37,7 @@ class TextureRegistry { return; } - let blob = new Blob([loadedFile.slice(0)], {type: filetype}); + let blob = new Blob([loadedFile.slice()], {type: filetype}); let blobUrl = URL.createObjectURL(blob); // Load the texture @@ -77,14 +77,14 @@ class HydraMesh { const material = new THREE.MeshPhysicalMaterial( { side: THREE.DoubleSide, - color: new THREE.Color(0xa0a0a0) // a gray default color + color: new THREE.Color(0xa0a0a0) // a gray default color }); this._mesh = new THREE.Mesh( this._geometry, material ); this._mesh.castShadow = true; this._mesh.receiveShadow = true; - window.scene.add(this._mesh); // FIXME + window.scene.add(this._mesh); } updateOrder(attribute, attributeName, dimension = 3) { @@ -105,7 +105,6 @@ class HydraMesh { for (let i = 0; i< indices.length; i++) { this._indices.push(indices[i]); } - //this._geometry.setIndex( indicesArray ); this.updateOrder(this._points, 'position'); this.updateOrder(this._normals, 'normal'); if (this._colors) { @@ -124,7 +123,7 @@ class HydraMesh { } updateNormals(normals) { - this._normals = normals.slice(0); + this._normals = normals.slice(); this.updateOrder(this._normals, 'normal'); } @@ -151,10 +150,10 @@ class HydraMesh { // Per-vertex buffer attribute this._mesh.material.vertexColors = true; if (wasDefaultMaterial) { - // Reset the pink debugging color + // Reset the debugging color this._mesh.material.color = new THREE.Color(0xffffff); } - this._colors = data.slice(0); + this._colors = data.slice(); this.updateOrder(this._colors, 'color'); } else { console.warn(`Unsupported displayColor interpolation type '${interpolation}'.`); @@ -167,10 +166,10 @@ class HydraMesh { if (interpolation === 'facevarying') { // The UV buffer has already been prepared on the C++ side, so we just set it - this._geometry.setAttribute('uv', new THREE.Float32BufferAttribute(data, dimension)); + this._geometry.setAttribute('uv', new THREE.Float32BufferAttribute(data.slice(), dimension)); } else if (interpolation === 'vertex') { // We have per-vertex UVs, so we need to sort them accordingly - this._uvs = data.slice(0); + this._uvs = data.slice(); this.updateOrder(this._uvs, 'uv', 2); } this._geometry.attributes.uv2 = this._geometry.attributes.uv; @@ -202,7 +201,7 @@ class HydraMesh { } updatePoints(points) { - this._points = points.slice(0); + this._points = points.slice(); this.updateOrder(this._points, 'position'); } @@ -407,10 +406,7 @@ export class RenderDelegateInterface { } createBPrim(typeId, id) { - console.log('Creating BPrim: ' + typeId + ' ' + id); - /*let mesh = new HydraMesh(id, this); - this.meshes[id] = mesh; - return mesh;*/ + console.warn('BPrims are not supported!'); } createSPrim(typeId, id) { diff --git a/pxr/usdImaging/hdEmscripten/testenv/test.html b/pxr/usdImaging/hdEmscripten/testenv/test.html index 8c0277a230..e9245dbc83 100644 --- a/pxr/usdImaging/hdEmscripten/testenv/test.html +++ b/pxr/usdImaging/hdEmscripten/testenv/test.html @@ -22,7 +22,7 @@ req.responseType = "arraybuffer"; req.onload = function (oEvent) { - let arrayBuffer = req.response; // Note: not oReq.responseText + let arrayBuffer = req.response; if (arrayBuffer) { resolve(arrayBuffer); } else { diff --git a/pxr/usdImaging/hdEmscripten/webRenderDelegate.cpp b/pxr/usdImaging/hdEmscripten/webRenderDelegate.cpp index 93d5e6e664..0d045f1c18 100644 --- a/pxr/usdImaging/hdEmscripten/webRenderDelegate.cpp +++ b/pxr/usdImaging/hdEmscripten/webRenderDelegate.cpp @@ -24,10 +24,8 @@ #include "webRenderDelegate.h" #include "pxr/base/gf/vec2f.h" #include "pxr/imaging/hd/bufferArray.h" -#include "pxr/imaging/hd/coordSys.h" #include "pxr/imaging/hd/material.h" #include "pxr/imaging/hd/mesh.h" -#include "pxr/imaging/hd/basisCurves.h" #include "pxr/imaging/hd/points.h" #include "pxr/imaging/hd/bprim.h" #include "pxr/imaging/hd/tokens.h" @@ -44,9 +42,6 @@ PXR_NAMESPACE_OPEN_SCOPE using namespace emscripten; -//////////////////////////////////////////////////////////////// -// Null Prims - const std::map InterpolationStrings = { {HdInterpolationConstant, "constant"}, {HdInterpolationUniform, "uniform"}, @@ -61,8 +56,9 @@ void _runInMainThread(int funPointer) { (*function)(); } +// Only the main thread can communicate with the JS interpreter (other threads run in web workers). +// All direct invocations of JS functions need to go through the main thread. void runInMainThread(std::function fun) { - //emscripten_sync_run_in_main_thread_1((int) &_runInMainThread, &fun); em_queued_call q = {EM_FUNC_SIG_VI, (void*) &_runInMainThread}; q.args[0].vp = (void *) &fun; q.returnValue.vp = 0; @@ -78,20 +74,15 @@ class Emscripten_Rprim final : public HdMesh { , _typeId(typeId) , _renderDelegateInterface(renderDelegateInterface) , _rPrim(val::undefined()) - , _normalsValid(false) - , _adjacencyValid(false) , _meshUtil(NULL) + , _adjacencyValid(false) + , _normalsValid(false) { _rPrim = _renderDelegateInterface.call("createRPrim", std::string(typeId.GetText()), id.GetAsString()); } virtual ~Emscripten_Rprim() = default; - TfTokenVector const & GetBuiltinPrimvarNames() const override { - static const TfTokenVector primvarNames; - return primvarNames; - } - virtual void Sync(HdSceneDelegate *delegate, HdRenderParam *renderParam, HdDirtyBits *dirtyBits, @@ -100,48 +91,10 @@ class Emscripten_Rprim final : public HdMesh { // Get the id of this mesh. This is used to get various resources associated with it. SdfPath const& id = GetId(); - // TODO: Debug - //std::cout << "Syncing mesh " << id.GetAsString() << "..." << std::endl; - - // A mesh can have up to 2 representation descriptions (e.g. different draw style for front and back faces). - // We only consider the first one. This could be improved in the future. - _MeshReprConfig::DescArray descs = _GetReprDesc(reprToken); - const HdMeshReprDesc &desc = descs[0]; - - // Get all the dirty computed primvars - // TODO: Debug - // std::cout << " Evaluating dirty computed primvars..." << std::endl; - // HdExtComputationPrimvarDescriptorVector dirtyCompPrimvars; - // for (size_t i=0; i < HdInterpolationCount; ++i) { - // HdExtComputationPrimvarDescriptorVector compPrimvars; - // HdInterpolation interp = static_cast(i); - // compPrimvars = delegate->GetExtComputationPrimvarDescriptors - // (GetId(),interp); - - // for (auto const& pv: compPrimvars) { - // if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, pv.name)) { - // dirtyCompPrimvars.emplace_back(pv); - // // TODO: Debug - // std::cout << " " << pv.name.GetText() << " is dirty." << std::endl; - // } - // } - // } - - // PrimId dirty bit is internal to Hydra. - - // if (HdChangeTracker::IsExtentDirty(*dirtyBits, id)) { - // GetExtent(delegate); - // } - - // if (HdChangeTracker::IsDisplayStyleDirty(*dirtyBits, id)) { - // delegate->GetDisplayStyle(id); - // } - // Materials need to be synced before primvars, to allow the JS side to apply primvar information like // displayColor if no other material is set. if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { auto materialId = delegate->GetMaterialId(id); - //std::cout << "Setting material '" << materialId.GetAsString() << "' for mesh '" << id << "'." << std::endl; runInMainThread([&]() { _rPrim.call("setMaterial", materialId.GetAsString()); }); @@ -157,60 +110,39 @@ class Emscripten_Rprim final : public HdMesh { }); } - // The repr defines whether we should compute smooth normals for this mesh: - // per-vertex normals taken as an average of adjacent faces, and - // interpolated smoothly across faces. - /*_smoothNormals = !desc.flatShadingEnabled; - - // If the subdivision scheme is "none" or "bilinear", force us not to use - // smooth normals. - _smoothNormals = _smoothNormals && - (_topology.GetScheme() != PxOsdOpenSubdivTokens->none) && - (_topology.GetScheme() != PxOsdOpenSubdivTokens->bilinear);*/ - _smoothNormals = true; + if (HdChangeTracker::IsTopologyDirty(*dirtyBits, id)) { + // When pulling a new topology, we don't want to overwrite the + // refine level or subdiv tags, which are provided separately by the + // scene delegate, so we save and restore them. + // TODO: This was copied from the Embree mesh class. We don't actually pull subdiv and refine information, since we're not handling that kind of geometry. We always only create a triangulated mesh. + PxOsdSubdivTags subdivTags = _topology.GetSubdivTags(); + int refineLevel = _topology.GetRefineLevel(); + _topology = HdMeshTopology(delegate->GetMeshTopology(id), refineLevel); + _topology.SetSubdivTags(subdivTags); + + // Triangulate the input faces. + if (_meshUtil != NULL) delete _meshUtil; + _meshUtil = new HdMeshUtil(&_topology, GetId()); + _meshUtil->ComputeTriangleIndices(&_triangulatedIndices, &_trianglePrimitiveParams); - // If the scene delegate has provided authored normals, force us to not use - // smooth normals. - bool authoredNormals = false; - /*if (_primvarSourceMap.count(HdTokens->normals) > 0) { - authoredNormals = true; - }*/ - _smoothNormals = _smoothNormals && !authoredNormals; + runInMainThread([&]() { + _rPrim.call("updateIndices", val(typed_memory_view(3 * _triangulatedIndices.size(), reinterpret_cast(_triangulatedIndices.data())))); + }); - if (HdChangeTracker::IsTopologyDirty(*dirtyBits, id)) { - // The topology getter depends on prim type - if (_typeId == HdPrimTypeTokens->mesh) { - // When pulling a new topology, we don't want to overwrite the - // refine level or subdiv tags, which are provided separately by the - // scene delegate, so we save and restore them. - PxOsdSubdivTags subdivTags = _topology.GetSubdivTags(); - int refineLevel = _topology.GetRefineLevel(); - _topology = HdMeshTopology(delegate->GetMeshTopology(id), refineLevel); - _topology.SetSubdivTags(subdivTags); - - // Triangulate the input faces. - if (_meshUtil != NULL) delete _meshUtil; - _meshUtil = new HdMeshUtil(&_topology, GetId()); - _meshUtil->ComputeTriangleIndices(&_triangulatedIndices, &_trianglePrimitiveParams); - - runInMainThread([&]() { - _rPrim.call("updateIndices", val(typed_memory_view(3 * _triangulatedIndices.size(), reinterpret_cast(_triangulatedIndices.data())))); - }); - - _normalsValid = false; - _adjacencyValid = false; - - } else if (_typeId == HdPrimTypeTokens->basisCurves) { - delegate->GetBasisCurvesTopology(id); - } - // Other prim types don't have a topology + _normalsValid = false; + _adjacencyValid = false; } - // Points is a primvar + // Sync primvars if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, id)) { _SyncPrimvars(delegate, *dirtyBits); } + // TODO: Various sources, such as surface representation description, the topology scheme, or the availablity + // of authored normals (as a primvar) can impact whether we want to calculate smooth normals or not. We ignore + // all this and simply always generate them. + _smoothNormals = true; + // Update the smooth normals in steps: // 1. If the topology is dirty, update the adjacency table, a processed // form of the topology that helps calculate smooth normals quickly. @@ -227,25 +159,11 @@ class Emscripten_Rprim final : public HdMesh { &_adjacency, _points.size(), _points.cdata()); _normalsValid = true; - // Create a sampler for the "normals" primvar. If there are authored - // normals, the smooth normals flag has been suppressed, so it won't - // be overwritten by the primvar population below. - //_CreatePrimvarSampler(HdTokens->normals, VtValue(_computedNormals), - // HdInterpolationVertex, false); runInMainThread([&]() { _rPrim.call("updateNormals", val(typed_memory_view(3 * _computedNormals.size(), reinterpret_cast(_computedNormals.data())))); }); } - // If smooth normals are off and there are no authored normals, make sure - // there's no "normals" sampler so the renderpass can use its fallback - // behavior. - if (!_smoothNormals && !authoredNormals) { - // Force the smooth normals code to rebuild the "normals" primvar the - // next time smooth normals is enabled. - _normalsValid = false; - } - if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) { _transform = GfMatrix4f(delegate->GetTransform(id)); runInMainThread([&]() { @@ -253,35 +171,6 @@ class Emscripten_Rprim final : public HdMesh { }); } - if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) { - delegate->GetVisible(id); - } - - // Normals is a primvar - - if (HdChangeTracker::IsDoubleSidedDirty(*dirtyBits, id)) { - delegate->GetDoubleSided(id); - } - - if (HdChangeTracker::IsCullStyleDirty(*dirtyBits, id)) { - delegate->GetCullStyle(id); - } - - if (HdChangeTracker::IsInstancerDirty(*dirtyBits, id)) { - // Instancer Dirty doesn't have a corresponding scene delegate pull - } - - // InstanceIndex applies to Instancer's not Rprim - - if (HdChangeTracker::IsReprDirty(*dirtyBits, id)) { - delegate->GetReprSelector(id); - } - - // RenderTag doesn't have a change tracker test - if (*dirtyBits & HdChangeTracker::DirtyRenderTag) { - delegate->GetRenderTag(id); - } - *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits; } @@ -290,7 +179,7 @@ class Emscripten_Rprim final : public HdMesh { { // Set all bits except the varying flag return (HdChangeTracker::AllSceneDirtyBits) & - (~HdChangeTracker::Varying); + (~HdChangeTracker::Varying); } virtual HdDirtyBits _PropagateDirtyBits(HdDirtyBits bits) const override @@ -329,6 +218,7 @@ class Emscripten_Rprim final : public HdMesh { bool _normalsValid; bool _smoothNormals; + // Send primvar data to JS void _SendPrimvar(const VtValue &value, const std::string &name, const HdInterpolation &interpolation) { const std::string &ip = InterpolationStrings.at(interpolation); @@ -388,7 +278,7 @@ class Emscripten_Rprim final : public HdMesh { } case HdInterpolationConstant: case HdInterpolationVertex: { - _SendPrimvar(value, primvar.name.GetText(), ip); + _SendPrimvar(value, primvar.name.GetString(), ip); break; } default: @@ -434,27 +324,27 @@ class Emscripten_Material final : public HdMaterial { vtMat.UncheckedGet(); for (auto& [networkId, network]: hdNetworkMap.map) { - for (auto& node : network.nodes) { - val parameters = val::object(); - for (auto &[parameterName, value] : node.parameters) { - parameters.set(parameterName, value._GetJsVal()); - } + for (auto& node : network.nodes) { + val parameters = val::object(); + for (auto &[parameterName, value] : node.parameters) { + parameters.set(parameterName, value._GetJsVal()); + } - _sPrim.call("updateNode", networkId.GetString(), node.path.GetAsString(), parameters); - } + _sPrim.call("updateNode", networkId.GetString(), node.path.GetAsString(), parameters); + } - val relationships = val::array(); - int i = 0; - for (auto &relationship : network.relationships) { - val relationshipObj = val::object(); - relationshipObj.set("inputId", relationship.inputId.GetAsString()); - relationshipObj.set("inputName", relationship.inputName); - relationshipObj.set("outputId", relationship.outputId.GetAsString()); - relationshipObj.set("outputName", relationship.outputName); - relationships.set(i++, relationshipObj); - } + val relationships = val::array(); + int i = 0; + for (auto &relationship : network.relationships) { + val relationshipObj = val::object(); + relationshipObj.set("inputId", relationship.inputId.GetAsString()); + relationshipObj.set("inputName", relationship.inputName); + relationshipObj.set("outputId", relationship.outputId.GetAsString()); + relationshipObj.set("outputName", relationship.outputName); + relationships.set(i++, relationshipObj); + } - _sPrim.call("updateFinished", networkId.GetString(), relationships); + _sPrim.call("updateFinished", networkId.GetString(), relationships); } } *dirtyBits = HdMaterial::Clean; @@ -475,38 +365,14 @@ class Emscripten_Material final : public HdMaterial { Emscripten_Material &operator =(const Emscripten_Material &) = delete; }; -class Emscripten_CoordSys final : public HdCoordSys { -public: - Emscripten_CoordSys(SdfPath const& id) : HdCoordSys(id) {} - virtual ~Emscripten_CoordSys() = default; - - virtual void Sync(HdSceneDelegate *sceneDelegate, - HdRenderParam *renderParam, - HdDirtyBits *dirtyBits) override - { - *dirtyBits = HdCoordSys::Clean; - }; - - virtual HdDirtyBits GetInitialDirtyBitsMask() const override { - return HdCoordSys::AllDirty; - } - -private: - Emscripten_CoordSys() = delete; - Emscripten_CoordSys(const Emscripten_CoordSys &) = delete; - Emscripten_CoordSys &operator =(const Emscripten_CoordSys &) = delete; -}; - const TfTokenVector WebRenderDelegate::SUPPORTED_RPRIM_TYPES = { HdPrimTypeTokens->mesh, - HdPrimTypeTokens->basisCurves, HdPrimTypeTokens->points }; const TfTokenVector WebRenderDelegate::SUPPORTED_SPRIM_TYPES = { - HdPrimTypeTokens->coordSys, HdPrimTypeTokens->material }; @@ -586,8 +452,6 @@ WebRenderDelegate::CreateSprim(TfToken const& typeId, { if (typeId == HdPrimTypeTokens->material) { return new Emscripten_Material(sprimId, _renderDelegateInterface); - } else if (typeId == HdPrimTypeTokens->coordSys) { - return new Emscripten_CoordSys(sprimId); } else { TF_CODING_ERROR("Unknown Sprim Type %s", typeId.GetText()); } @@ -600,8 +464,6 @@ WebRenderDelegate::CreateFallbackSprim(TfToken const& typeId) { if (typeId == HdPrimTypeTokens->material) { return new Emscripten_Material(SdfPath::EmptyPath(), _renderDelegateInterface); - } else if (typeId == HdPrimTypeTokens->coordSys) { - return new Emscripten_CoordSys(SdfPath::EmptyPath()); } else { TF_CODING_ERROR("Unknown Sprim Type %s", typeId.GetText()); } @@ -642,7 +504,7 @@ WebRenderDelegate::DestroyBprim(HdBprim *bPrim) void WebRenderDelegate::CommitResources(HdChangeTracker *tracker) { - _renderDelegateInterface.call("CommitResources"); + _renderDelegateInterface.call("CommitResources"); } PXR_NAMESPACE_CLOSE_SCOPE