@@ -271,6 +271,26 @@ class CFrontendIR final : public CNodePool
271271
272272 protected:
273273 friend class CFrontendIR ;
274+ // copy
275+ virtual _typed_pointer_type<IExprNode> copy (CFrontendIR* ir) const = 0;
276+ #define COPY_DEFAULT_IMPL inline _typed_pointer_type<IExprNode> copy (CFrontendIR* ir) const override final \
277+ { \
278+ auto & pool = ir->getObjectPool (); \
279+ const auto copyH = pool.emplace <std::remove_const_t <std::remove_pointer_t <decltype (this )> > >(); \
280+ if (auto * const copy = pool.deref (copyH); copyH) \
281+ *copy = *this ; \
282+ return copyH; \
283+ }
284+
285+ // child managment
286+ virtual inline _typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const {assert (false ); return {};}
287+ inline void setChild (const uint8_t ix, _typed_pointer_type<IExprNode> newChild)
288+ {
289+ assert (ix<getChildCount ());
290+ setChild_impl (ix,newChild);
291+ }
292+ virtual inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) {assert (false );}
293+
274294 // default is no special checks beyond the above
275295 struct SInvalidCheckArgs
276296 {
@@ -291,7 +311,10 @@ class CFrontendIR final : public CNodePool
291311 }
292312 return false ;
293313 }
294- virtual _typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const = 0;
314+
315+ virtual bool inline reciprocatable () const {return false ;}
316+ // unless you override it, you're not supposed to call it
317+ virtual void reciprocate (IExprNode* dst) const {assert (reciprocatable () && dst);}
295318
296319 virtual inline core::string getLabelSuffix () const {return " " ;}
297320 virtual inline std::string_view getChildName_impl (const uint8_t ix) const {return " " ;}
@@ -356,6 +379,10 @@ class CFrontendIR final : public CNodePool
356379 }
357380 std::construct_at (reinterpret_cast <SCreationParams<Count>*>(this +1 ),std::move (params));
358381 }
382+ inline CSpectralVariable (const CSpectralVariable& other)
383+ {
384+ std::uninitialized_copy_n (other.pWonky (),other.getKnotCount (),pWonky ());
385+ }
359386
360387 // encapsulation due to padding abuse
361388 inline uint8_t & uvSlot () {return pWonky ()->knots .uvSlot ();}
@@ -390,11 +417,11 @@ class CFrontendIR final : public CNodePool
390417 {
391418 return sizeof (CSpectralVariable)+sizeof (SCreationParams<Count>);
392419 }
393- /*
394- inline uint32_t getSize() const override
420+ // for copying
421+ static inline uint32_t calc_size ( const CSpectralVariable& other)
395422 {
396- return sizeof(CSpectralVariable)+sizeof(SCreationParams<1>)+(getKnotCount()-1)*sizeof(SParameter);
397- }*/
423+ return sizeof (CSpectralVariable)+sizeof (SCreationParams<1 >)+(other. getKnotCount ()-1 )*sizeof (SParameter);
424+ }
398425
399426 inline operator bool () const {return !invalid (SInvalidCheckArgs{.pool =nullptr ,.logger =nullptr });}
400427
@@ -403,8 +430,13 @@ class CFrontendIR final : public CNodePool
403430 {
404431 std::destroy_n (pWonky ()->knots .params ,getKnotCount ());
405432 }
433+ inline _typed_pointer_type<IExprNode> copy (CFrontendIR* ir) const override final
434+ {
435+ auto & pool = ir->getObjectPool ();
436+ const uint8_t count = getKnotCount ();
437+ return pool.emplace <CSpectralVariable>(*this );
438+ }
406439
407- inline _typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return {};}
408440 inline bool invalid (const SInvalidCheckArgs& args) const override
409441 {
410442 const auto knotCount = getKnotCount ();
@@ -442,13 +474,14 @@ class CFrontendIR final : public CNodePool
442474 private:
443475 SCreationParams<1 >* pWonky () {return reinterpret_cast <SCreationParams<1 >*>(this +1 );}
444476 const SCreationParams<1 >* pWonky () const {return reinterpret_cast <const SCreationParams<1 >*>(this +1 );}
445- const uint8_t * paramsBeginPadding () const {return pWonky ()->knots .params [0 ].padding ; }
477+ const uint8_t * paramsBeginPadding () const {return pWonky ()->knots .params [0 ].padding ;}
446478 };
447479 //
448480 class IUnaryOp : public obj_pool_type ::INonTrivial, public IExprNode
449481 {
450482 protected:
451483 inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override final {return child;}
484+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override final {child = newChild;}
452485
453486 public:
454487 inline uint8_t getChildCount () const override final {return 1 ;}
@@ -459,6 +492,8 @@ class CFrontendIR final : public CNodePool
459492 {
460493 protected:
461494 inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override final {return ix ? rhs:lhs;}
495+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override final {*(ix ? &rhs:&lhs) = newChild;}
496+
462497 inline std::string_view getChildName_impl (const uint8_t ix) const override final {return ix ? " rhs" :" lhs" ;}
463498
464499 public:
@@ -476,6 +511,9 @@ class CFrontendIR final : public CNodePool
476511
477512 // you can set the children later
478513 inline CMul () = default;
514+
515+ protected:
516+ COPY_DEFAULT_IMPL
479517 };
480518 class CAdd final : public IBinOp
481519 {
@@ -485,6 +523,9 @@ class CFrontendIR final : public CNodePool
485523
486524 // you can set the children later
487525 inline CAdd () = default;
526+
527+ protected:
528+ COPY_DEFAULT_IMPL
488529 };
489530 // does `1-expression`
490531 class CComplement final : public IUnaryOp
@@ -494,6 +535,9 @@ class CFrontendIR final : public CNodePool
494535
495536 // you can set the children later
496537 inline CComplement () = default;
538+
539+ protected:
540+ COPY_DEFAULT_IMPL
497541 };
498542 // Emission nodes are only allowed in BRDF expressions, not BTDF. To allow different emission on both sides, expressed unambigously.
499543 // Basic Emitter - note that it is of unit radiance so its easier to importance sample
@@ -517,8 +561,10 @@ class CFrontendIR final : public CNodePool
517561 // TODO: semantic flags/metadata (symmetries of the profile)
518562
519563 protected:
520- inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return {};}
564+ COPY_DEFAULT_IMPL
565+
521566 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
567+
522568 NBL_API2 void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
523569 };
524570 // ! Special nodes meant to be used as `CMul::rhs`, their behaviour depends on the IContributor in its MUL node relative subgraph.
@@ -547,9 +593,16 @@ class CFrontendIR final : public CNodePool
547593 // Absorption and thickness of the interface combined into a single variable, eta and `LdotX` is taken from the leaf BTDF node.
548594 // With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same
549595 typed_pointer_type<CSpectralVariable> perpTransmittance = {};
596+
550597
551598 protected:
599+ COPY_DEFAULT_IMPL
600+
552601 inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return perpTransmittance;}
602+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
603+ {
604+ perpTransmittance = block_allocator_type::_static_cast<CSpectralVariable>(newChild);
605+ }
553606
554607 inline std::string_view getChildName_impl (const uint8_t ix) const override {return " Perpendicular\\ nTransmittance" ;}
555608 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
@@ -572,8 +625,22 @@ class CFrontendIR final : public CNodePool
572625 uint8_t reciprocateEtas : 1 = false ;
573626
574627 protected:
628+ COPY_DEFAULT_IMPL
629+
575630 inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return ix ? orientedImagEta:orientedRealEta;}
631+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
632+ {
633+ *(ix ? &orientedImagEta:&orientedRealEta) = block_allocator_type::_static_cast<CSpectralVariable>(newChild);
634+ }
635+
576636 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
637+
638+ inline bool reciprocatable () const override {return true ;}
639+ inline void reciprocate (IExprNode* dst) const override
640+ {
641+ (*static_cast <CFresnel*>(dst) = *this ).reciprocateEtas = ~reciprocateEtas;
642+ }
643+
577644 inline std::string_view getChildName_impl (const uint8_t ix) const override {return ix ? " Imaginary" :" Real" ;}
578645 NBL_API2 void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
579646 };
@@ -595,7 +662,14 @@ class CFrontendIR final : public CNodePool
595662 class CThinInfiniteScatterCorrection final : public obj_pool_type::INonTrivial, public IExprNode
596663 {
597664 protected:
598- inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override final {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;}
665+ COPY_DEFAULT_IMPL
666+
667+ inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;}
668+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
669+ {
670+ *(ix ? (ix>1 ? &reflectanceBottom:&extinction):&reflectanceTop) = newChild;
671+ }
672+
599673 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
600674
601675 inline std::string_view getChildName_impl (const uint8_t ix) const override {return ix ? (ix>1 ? " reflectanceBottom" :" extinction" ):" reflectanceTop" ;}
@@ -662,9 +736,8 @@ class CFrontendIR final : public CNodePool
662736 // - Delta Reflection -> Any Cook Torrance BxDF with roughness=0 attached as BRDF
663737 // - Smooth Conductor -> above multiplied with Conductor-Fresnel
664738 // - Smooth Dielectric -> Any Cook Torrance BxDF with roughness=0 attached as BRDF on both sides of a Layer and BTDF multiplied with Dielectric-Fresnel (no imaginary component)
665- // - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel as BRDF in both sides and a Delta Transmission BTDF with `CThinInfiniteScatterCorrection` on the fresnel
739+ // - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel .... TODO description ....
666740 // - Plastic -> Similar to layering the above over Diffuse BRDF, its of uttmost importance that the BTDF is Delta Transmission.
667- // If one wants to emulate non-linear diffuse TIR color shifts, abuse `CThinInfiniteScatterCorrection`.
668741 class CDeltaTransmission final : public IBxDF
669742 {
670743 public:
@@ -673,7 +746,7 @@ class CFrontendIR final : public CNodePool
673746 inline CDeltaTransmission () = default;
674747
675748 protected:
676- inline _typed_pointer_type<IExprNode> getChildHandle_impl ( const uint8_t ix) const override { return {};}
749+ COPY_DEFAULT_IMPL
677750 };
678751 // ! Because of Schussler et. al 2017 every one of these nodes splits into 2 (if no L dependence) or 3 during canonicalization
679752 // Base diffuse node
@@ -687,8 +760,10 @@ class CFrontendIR final : public CNodePool
687760 SBasicNDFParams ndParams = {};
688761
689762 protected:
690- inline _typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return {};}
763+ COPY_DEFAULT_IMPL
764+
691765 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
766+
692767 NBL_API2 void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
693768 };
694769 // Supports anisotropy for all models
@@ -714,49 +789,114 @@ class CFrontendIR final : public CNodePool
714789 // BRDFs and BTDFs components into separate expressions, and also importance sample much better, for details see comments in CTrueIR.
715790 typed_pointer_type<CSpectralVariable> orientedRealEta = {};
716791 //
717- NDF ndf = NDF::GGX;
792+ NDF ndf : 7 = NDF::GGX;
793+ uint8_t reciprocateEta : 1 = false ;
718794
719795 protected:
796+ COPY_DEFAULT_IMPL
797+
720798 inline typed_pointer_type<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return orientedRealEta;}
799+ inline void setChild_impl (const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override {orientedRealEta = block_allocator_type::_static_cast<CSpectralVariable>(newChild);}
800+
721801 NBL_API2 bool invalid (const SInvalidCheckArgs& args) const override ;
802+
803+ inline bool reciprocatable () const override {return true ;}
804+ inline void reciprocate (IExprNode* dst) const override
805+ {
806+ (*static_cast <CCookTorrance*>(dst) = *this ).reciprocateEta = ~reciprocateEta;
807+ }
722808
723809 inline core::string getLabelSuffix () const override {return ndf!=NDF::GGX ? " \\ nNDF = Beckmann" :" \\ nNDF = GGX" ;}
724810 inline std::string_view getChildName_impl (const uint8_t ix) const override {return " Oriented η" ;}
725811 NBL_API2 void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
726812 };
813+ #undef COPY_DEFAULT_IMPL
727814#undef TYPE_NAME_STR
728815
729816
730- // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES!
731- // TODO: shall we copy and hand out a new handle?
732- inline std::span<const typed_pointer_type<const CLayer>> getMaterials () {return m_rootNodes;}
733- inline bool addMaterial (const typed_pointer_type<const CLayer> rootNode, system::logger_opt_ptr logger)
817+ // basic utilities
818+ inline typed_pointer_type<CMul> createMul (const typed_pointer_type<IExprNode> lhs, const typed_pointer_type<IExprNode> rhs)
734819 {
735- if (valid (rootNode,logger))
736- {
737- m_rootNodes.push_back (rootNode);
738- return true ;
739- }
740- return false ;
820+ if (!lhs || !rhs) // acceptable premaute optimization
821+ return {};
822+ const auto mulH = getObjectPool ().emplace <CMul>();
823+ auto * const mul = getObjectPool ().deref (mulH);
824+ mul->lhs = lhs;
825+ mul->rhs = rhs;
826+ return mulH;
827+ }
828+ inline typed_pointer_type<IExprNode> createAdd (const typed_pointer_type<IExprNode> lhs, const typed_pointer_type<IExprNode> rhs)
829+ {
830+ if (!lhs)
831+ return rhs;
832+ if (!rhs)
833+ return lhs;
834+ const auto addH = getObjectPool ().emplace <CAdd>();
835+ auto * const add = getObjectPool ().deref (addH);
836+ add->lhs = lhs;
837+ add->rhs = rhs;
838+ return addH;
839+ }
840+ inline typed_pointer_type<CComplement> createComplement (const typed_pointer_type<IExprNode> child)
841+ {
842+ if (!child)
843+ return {};
844+ const auto complH = getObjectPool ().emplace <CComplement>();
845+ getObjectPool ().deref (complH)->child = child;
846+ return complH;
741847 }
742848
743- // To quickly make a matching backface material from a frontface or vice versa
744- NBL_API2 typed_pointer_type<IExprNode> reciprocate (const typed_pointer_type<const IExprNode> other);
849+ // To quickly make a fresnel
745850 NBL_API2 typed_pointer_type<CFresnel> createNamedFresnel (const std::string_view name);
746851 inline typed_pointer_type<CFresnel> createConstantMonochromeRealFresnel (const hlsl::float32_t orientedRealEta)
747852 {
748- const auto fresnelH = getObjectPool ().emplace <CFresnel>();
853+ auto & pool = getObjectPool ();
854+ const auto fresnelH = pool.emplace <CFresnel>();
749855 CSpectralVariable::SCreationParams<1 > params = {};
750856 params.knots .params [0 ].scale = orientedRealEta;
751- if (auto * const fresnel=getObjectPool () .deref (fresnelH); fresnel)
752- fresnel->orientedRealEta = getObjectPool () .emplace <CSpectralVariable>(std::move (params));
857+ if (auto * const fresnel=pool .deref (fresnelH); fresnel)
858+ fresnel->orientedRealEta = pool .emplace <CSpectralVariable>(std::move (params));
753859 return fresnelH;
754860 }
755861
862+ // To quickly make a matching backface BxDF from a frontface or vice versa
863+ NBL_API2 typed_pointer_type<const IExprNode> reciprocate (const typed_pointer_type<const IExprNode> orig);
864+
865+ // a deep copy of the layer stack, wont copy the BxDFs
866+ NBL_API2 typed_pointer_type<CLayer> copyLayers (const typed_pointer_type<const CLayer> orig);
867+ // Reverse the linked list of layers and reciprocate their Etas
868+ NBL_API2 typed_pointer_type<CLayer> reverse (const typed_pointer_type<const CLayer> orig);
869+
870+ // first query, we check presence of btdf layers all the way through the layer stack
871+ inline bool transmissive (const typed_pointer_type<const CLayer> rootHandle) const
872+ {
873+ auto & pool = getObjectPool ();
874+ for (auto layer=pool.deref (rootHandle); layer; layer=pool.deref (layer->coated ))
875+ {
876+ // it takes only one layer without transmission to break the chain
877+ if (!layer->btdf )
878+ return false ;
879+ }
880+ return true ;
881+ }
882+
756883 // IMPORTANT: Two BxDFs are not allowed to be multiplied together.
757884 // NOTE: Right now all Spectral Variables are required to be Monochrome or 3 bucket fixed semantics, all the same wavelength.
758885 // Some things we can't check such as the compatibility of the BTDF with the BRDF (matching indices of refraction, etc.)
759- bool valid (const typed_pointer_type<const CLayer> rootHandle, system::logger_opt_ptr logger) const ;
886+ NBL_API2 bool valid (const typed_pointer_type<const CLayer> rootHandle, system::logger_opt_ptr logger) const ;
887+
888+ // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES!
889+ // TODO: shall we copy and hand out a new handle?
890+ inline std::span<const typed_pointer_type<const CLayer>> getMaterials () {return m_rootNodes;}
891+ inline bool addMaterial (const typed_pointer_type<const CLayer> rootNode, system::logger_opt_ptr logger)
892+ {
893+ if (valid (rootNode,logger))
894+ {
895+ m_rootNodes.push_back (rootNode);
896+ return true ;
897+ }
898+ return false ;
899+ }
760900
761901 // For Debug Visualization
762902 struct SDotPrinter final
0 commit comments