• 0 Votes
    8 Posts
    2k Views
    indexofrefractionI
    I also just ran into this... < R24 you dont have BaseList2D.IsNodeBased, but as a small hack you can do: def isNodeMaterial(op): return True if op.GetType == c4d.Mmaterial and '[Node]' in op.GetBubbleHelp() else False
  • 0 Votes
    5 Posts
    1k Views
    ManuelM
    hi, from time to time i'm saying thanks to @zipit , but for sure he's contributing a lot to the community. there's not too much to say more that what have been said. Brut force is the only option without using external python librairies. You can always use an external library if you want to use a kd-tree structure to be faster. Using cpp would be a better solution if you really want to do it fast. We have in our API a kd-tree as show in this kd-stree manual. You can see on github a full example on how to use it And here are some code of me playing with different way of sampling a kd-tree using parallel method and also using voxelization to get the closest polygon. //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- /// GetClosestPolygon using voxelization //---------------------------------------------------------------------------------------- static maxon::Result<void> GETCLOSEST_POLY(BaseDocument* doc) { iferr_scope; if (doc == nullptr) return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION); // ------- Create a mesh with one poly const maxon::Int32 polyCnt{ 500 }; PolygonObject *mesh = PolygonObject::Alloc(polyCnt * 4, polyCnt ); if (mesh == nullptr) return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION); maxon::LinearCongruentialRandom<maxon::Float32> random; CPolygon *polyAdrW = mesh->GetPolygonW(); Vector* padrW = mesh->GetPointW(); const Float polySize{ 10 }; for (maxon::Int i = 0; i < polyCnt; i++) { CPolygon poly; maxon::Vector pointA(i * polySize, 0, 0); maxon::Vector pointB(i * polySize, 0, polySize); maxon::Vector pointC(i * polySize + polySize, 0, polySize); maxon::Vector pointD(i * polySize + polySize, 0, 0); const maxon::Int pointIndice = i * 4; padrW[pointIndice] = pointA; padrW[pointIndice + 1] = pointB; padrW[pointIndice + 2] = pointC; padrW[pointIndice + 3] = pointD; poly.a = pointIndice; poly.b = pointIndice + 1; poly.c = pointIndice + 2; poly.d = pointIndice + 3; polyAdrW[i] = poly; } doc->InsertObject(mesh, nullptr, nullptr); maxon::DistanceQueryRef distanceQueryRef = maxon::DistanceCalculator().Create() iferr_return; distanceQueryRef.Init(mesh, true) iferr_return; maxon::PrimitiveInformation result; const maxon::Int samplePointCnt = polyCnt; //---------------------------------------------------------------------------------------- /// sample using standard loop maxon::TimeValue start = maxon::TimeValue(maxon::TimeValue::NOW); for (maxon::Int i = 0; i < samplePointCnt; ++i) { const maxon::Vector pos{ i * polySize + polySize * 0.5, 0, 0 }; distanceQueryRef.GetClosestMeshPrimitive(pos, result); if (result.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("pos @, polyIndex @ ", pos, result.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); } ApplicationOutput("time to sample the points @ with MP", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using parallelFor start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::BaseArray< maxon::PrimitiveInformation> resultArray; resultArray.Resize(samplePointCnt) iferr_return; maxon::ParallelFor::Dynamic(0, samplePointCnt, [&resultArray, &polySize, &distanceQueryRef](maxon::Int i) { maxon::PrimitiveInformation result; const maxon::Vector pos{ i * polySize + polySize * 0.5, 0, 0 }; distanceQueryRef.GetClosestMeshPrimitive(pos, result); if (result.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("pos @, polyIndex @ ", pos, result.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); resultArray[i] = result; } ); ApplicationOutput("time to sample the points @ with MP", start.Stop()); for (auto& value : resultArray) { if (value.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("polyIndex @ ", value.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); } return maxon::OK; } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- /// Sample a KD tree with multi thread //---------------------------------------------------------------------------------------- class SampleTreeJob : public maxon::JobInterfaceTemplate<SampleTreeJob, maxon::Int> { public: SampleTreeJob() { }; MAXON_IMPLICIT SampleTreeJob(maxon::KDTree* kdtree, const maxon::Vector& samplePoint) { _kdtree = kdtree; _samplePoint = samplePoint; } // function operator with the actual workload maxon::Result<void> operator ()() { maxon::Int nearestIndex = _kdtree->FindNearest(maxon::JobRef::GetCurrentWorkerThreadIndex(), _samplePoint, nullptr); // store result return SetResult(std::move(nearestIndex)); } private: maxon::KDTree* _kdtree; maxon::Vector _samplePoint; }; static maxon::Result <maxon::BaseArray<maxon::Vector>> CreateArrayOfPoints() { iferr_scope; const maxon::Int pointCnt = 1000000; maxon::BaseArray<maxon::Vector> points; points.EnsureCapacity(pointCnt) iferr_return; maxon::LinearCongruentialRandom<maxon::Float32> random; for (maxon::Int i = 0; i < pointCnt; ++i) { maxon::Vector pos(maxon::DONT_INITIALIZE); pos.x = random.Get01() * 100000.0; pos.y = random.Get01() * 100000.0; pos.z = random.Get01() * 100000.0; points.Append(pos) iferr_return; } return points; } static maxon::Result<maxon::BaseArray<maxon::Vector>> CreateSamplePoints() { iferr_scope; // sample points const maxon::Int samplePointCnt = 10000; maxon::BaseArray<maxon::Vector> samplePoints; samplePoints.EnsureCapacity(samplePointCnt) iferr_return; for (maxon::Int i = 0; i < samplePointCnt; ++i) { const maxon::Vector pos{ i* 10.0 }; samplePoints.Append(pos) iferr_return; } return samplePoints; } static maxon::Result<void> KDTREE_MP(BaseDocument* doc) { iferr_scope; maxon::BaseArray<maxon::Vector> points = CreateArrayOfPoints() iferr_return; maxon::BaseArray<maxon::Vector> samplePoints = CreateSamplePoints() iferr_return; const maxon::Int samplePointCnt = samplePoints.GetCount(); const maxon::Int pointCnt = points.GetCount(); maxon::KDTree tree; tree.Init(maxon::JobRef::GetCurrentThreadCount()) iferr_return; // insert points for (maxon::Int i = 0; i < pointCnt; ++i) { tree.Insert(points[i], i) iferr_return; } // balance tree tree.Balance(); //---------------------------------------------------------------------------------------- /// sample using classic loop maxon::TimeValue start{ maxon::TimeValue::NOW }; for (const maxon::Vector& samplePoint : samplePoints) { // find nearest point const maxon::Int nearestIndex = tree.FindNearest(0, samplePoint, nullptr); } ApplicationOutput("time to sample the points @ ", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using parallelFor start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::ParallelFor::Dynamic(0, samplePointCnt, [&samplePoints, &tree](maxon::Int i) { const maxon::Vector samplePoint = samplePoints[i]; const maxon::Int nearestIndex = tree.FindNearest(maxon::JobRef::GetCurrentWorkerThreadIndex(), samplePoint, nullptr); } ); ApplicationOutput("time to sample the points @ with MP", start.Stop()); //---------------------------------------------------------------------------------------- /// Sample using job // create group start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::JobGroupRef group = maxon::JobGroupRef::Create() iferr_return; for (auto i = 0 ; i < samplePointCnt; i++) { // create and add job const maxon::Vector samplePoint = samplePoints[i]; auto job = SampleTreeJob::Create(&tree, samplePoint) iferr_return; group.Add(job) iferr_return; } // enqueue jobs and wait group.Enqueue(); group.Wait(); ApplicationOutput("time to sample the points @ with jobs", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using classic loop again start = maxon::TimeValue(maxon::TimeValue::NOW); for (const maxon::Vector& samplePoint : samplePoints) { // find nearest point const maxon::Int nearestIndex = tree.FindNearest(0, samplePoint, nullptr); } ApplicationOutput("time to sample the points @ second time to see if caches are envolved", start.Stop()); return maxon::OK; } Cheers, Manuel
  • Sizing Plugin Dialogs

    Cinema 4D SDK python s22 sdk
    9
    1
    1 Votes
    9 Posts
    1k Views
    ferdinandF
    Hi @blastframe, without further questions or feedback, by Monday we will consider this thread as solved and flag it accordingly. Cheers, Ferdinand
  • How to create a layer Reflection (Legacy) ?

    Cinema 4D SDK python
    2
    0 Votes
    2 Posts
    761 Views
    KantroninK
    I found the solution to my problem, on the internet, with code samples: https://www.c4dcafe.com/ipb/forums/topic/101805-materials-and-shaders-with-python/
  • 0 Votes
    6 Posts
    1k Views
    P
    Ah sorry.
  • Using Console Variable in the Script Manager?

    Cinema 4D SDK r21 python
    4
    0 Votes
    4 Posts
    425 Views
    B
    @m_magalhaes Thanks for the confirmation. Although what zipit provided also works for my use case (i.e. defining the variable from Document B but using that same variable in Document A) @zipit Thanks for the code. It works as expected
  • Copy, Paste, Flip X a Pose Morph Target?

    Cinema 4D SDK r21 python
    12
    0 Votes
    12 Posts
    3k Views
    B
    @m_magalhaes @zipit I managed to flip the mesh (not necessarily a pose morph target since I'd have to know the API). Anyhow, here is a demo of it working: https://www.dropbox.com/s/bh4p26s4m9qwljw/c4d272_flip_miror_mesh.mp4?dl=0 Here is wip script. It only works if the x-axis is dead set on 0. Also, it is slow since it has to loop within a loop. import c4d from c4d import gui # Main function def main(): neutral_geo = doc.SearchObject('neutral_geo') posed_geo = doc.SearchObject('posed_geo') neutral_world_matrix = neutral_geo.GetMg() posed_world_matrix = posed_geo.GetMg() neut_lcl_pnts = neutral_geo.GetAllPoints() neut_gbl_pnts = [point * neutral_world_matrix for point in neut_lcl_pnts] posed_lcl_pnts = posed_geo.GetAllPoints() posed_gbl_pnts = [point * posed_world_matrix for point in posed_lcl_pnts] match_pnts = [] left_pnts = [] right_pnts = [] for idx, point in enumerate(neut_gbl_pnts): if point[0] == 0.0: # ignore points at the world x axis continue if point[0] > 0.0: left_pnts.append((idx,point)) if point[0] < 0.0: right_pnts.append((idx,point)) for left in left_pnts: for right in right_pnts: if left[1][1] == right[1][1]: # check if Y pos match if left[1][2] == right[1][2]: # check if Z pos match if left[1][0] == -1 * (right[1][0]):# check if X pos are inverse match_pnts.append((left[0],right[0])) for pnt in match_pnts: reversed_left = posed_lcl_pnts[pnt[1]] reversed_left[0] = reversed_left[0] * -1 reversed_right = posed_lcl_pnts[pnt[0]] reversed_right[0] = reversed_right[0] * -1 posed_geo.SetPoint(pnt[0], reversed_left) posed_geo.SetPoint(pnt[1], reversed_right) posed_geo.Message(c4d.MSG_UPDATE) c4d.EventAdd() # Execute main() if __name__=='__main__': main()
  • Message when leaving an Edit Text

    Cinema 4D SDK python s22 sdk
    5
    0 Votes
    5 Posts
    634 Views
    ?
    @m_adam Great idea! I'll use a Timer Thanks Maxime!
  • Setting Strength Values of the Pose Morph Target?

    Cinema 4D SDK r21 python
    4
    0 Votes
    4 Posts
    923 Views
    B
    @PluginStudent @m_magalhaes Thanks for the reference link and sample code. It works as expected. Thanks also for the giving different methods in setting the strength. Provides more flexibility.
  • From Line Object to Spline

    Cinema 4D SDK python s22
    5
    0 Votes
    5 Posts
    1k Views
    ManuelM
    hi, I'll consider this thread as solved without further feedback Cheers, Manuel
  • Creating Xpresso Python Node Inputs

    Cinema 4D SDK s22 sdk python
    6
    1
    0 Votes
    6 Posts
    2k Views
    ?
    @m_adam That's a great solution, thank you!
  • 1 Votes
    3 Posts
    400 Views
    CairynC
    @m_magalhaes thanks, that'll do!
  • Parent Behavior on a "Script" Form

    Cinema 4D SDK r21 python
    8
    0 Votes
    8 Posts
    877 Views
    B
    @Cairyn Thanks for the confirmation. I'll just continue with the revised script until I find a bug (lol). RE: Ah. Don't the built-in mirror functions suffice? Not really. Correct me if I'm wrong, The built-in mirror tool 1) mirrors only the objects itself and not the mirror plane 2) when it mirrors, it creates additional copy. might be good when you are modelling and rigging, but not on animating. Again, thank you for the detailed responses. Appreciate it alot. Have a great day ahead!
  • 0 Votes
    4 Posts
    1k Views
    dskeithbuckD
    Thank you @zipit and @m_magalhaes for your replies! @zipit your response put me on the right track. The issue was that I was trying to do all of the conversions using C4D's left-handed coordinate matrices, and it was auto-fixing my "malformed" matrices. I ended up porting the Three.js Matrix4 and Euler classes from JS to python and using them in my script. So, the final code (leaving out the ported methods) looked something like: mg = self.op.GetMg() parent_mg = self.op.GetUpMg() c4d_to_three = c4d.Matrix( off=c4d.Vector(0), v1=c4d.Vector(1, 0, 0), v2=c4d.Vector(0, 1, 0), v3=c4d.Vector(0, 0, -1) ) # Convert to new coordinate space # http://www.techart3d.com/2016/02/convert-left-handed-to-right-handed-coordinates/ mg_three_coords = c4d_to_three * mg * c4d_to_three parent_mg_three_coords = c4d_to_three * parent_mg * c4d_to_three mg_mat4 = Matrix4(mg_three_coords) parent_mg_mat4 = Matrix4(parent_mg_three_coords) inv_parent_mg_mat4 = parent_mg_mat4.Clone() inv_parent_mg_mat4 = inv_parent_mg_mat4.Inverse() node_local = inv_parent_mg_mat4.Clone() node_local = node_local.MultiplyMatrices(inv_parent_mg_mat4, mg_mat4) position, scale, rotation = node_local.Decompose() if position != c4d.Vector(0): self.props["position"] = position if scale != c4d.Vector(1): self.props["scale"] = scale if rotation != c4d.Vector(0): self.props["rotation"] = rotation
  • Stuck with loop through tags

    General Talk python
    4
    0 Votes
    4 Posts
    1k Views
    ManuelM
    hi @SuperHomiak welcome to the forum. Thanks @zipit for the answer For your next threads, please help us keeping things organised and clean. I know it's not your priority but it really simplify our work here. Q&A New Functionality. How to Post Questions especially the tagging part. I've set the thread to a question and marked zipit answer as the right answer Cheers, Manuel
  • Add Custom Command in the Content Browser?

    Cinema 4D SDK r21 python
    3
    0 Votes
    3 Posts
    378 Views
    B
    @m_adam Thanks for the confirmation. Will close this for now.
  • 0 Votes
    5 Posts
    750 Views
    ?
    @m_adam Hi Maxime, I did try your example and I saw it working as a gadget but, you're right, I want to communicate between two GeDialogs. I followed your advice on using CoreMessage and found success with SpecialEventAdd. Thank you!
  • Setting the FontData for a Text Object

    Cinema 4D SDK s22 python sdk
    2
    0 Votes
    2 Posts
    551 Views
    ?
    For whatever reason, the default Text Object has no FontData in Text[c4d.PRIM_TEXT_FONT]. By creating a FontData instance, I was able to set the Font. import c4d def main(doc): textObject = c4d.BaseObject(c4d.Osplinetext) doc.InsertObject(textObject) fontData = c4d.FontData() bc = c4d.BaseContainer() bc.SetString(500, 'Arial') bc.SetString(501, '11') bc.SetInt32(502, 400) bc.SetInt32(503, 0) bc.SetString(509, 'Arial') bc.SetString(508, 'ArialMT') fontData.SetFont(bc) textObject[c4d.PRIM_TEXT_FONT] = fontData textObject[c4d.ID_BASELIST_NAME] = bc[500] textObject[c4d.PRIM_TEXT_TEXT] = bc[500] c4d.EventAdd() if __name__=='__main__': main(doc)
  • MoData.GetData()?

    Cinema 4D SDK python
    11
    0 Votes
    11 Posts
    2k Views
    K
    Oh, man... I thought I hit reply on this days ago! Sorry! So, MoData tag container didn't work, that's not where the MoData is stored. Well, there seems to be MoData there, just always empty... but I think that's because the mograph stuff uses the messaging system so heavily that just grabbing a basecontainer doesn't cut it. @zipit said in MoData.GetData()?: Hi, I was a bit overworked when I wrote this test, which is why I did made a little booboo in the code above with rather dire consequences. I have fixed this now and the timings are now correct (aside from the general diceyness of the whole test). But the general consensus is the same. BaseContainer is not slow and a good choice when we need key, value pairs. One should also keep in mind, that I did employ simplifications for list both in the insert and delete case, if we want to truly delete and insert arbitrary elements from/to a list, this type is terrible, as it has to rebuild all data each time. Cheers, zipit Thanks for revising this. Even if the basecontainer times technically look worse by comparison, in context I can still see it's no slouch! I've moved forward with this approach and so far I'm getting great performance with breaking down and stashing away the array's data into basecontainers. Still wondering if it wouldn't be better to go with a Read/Write/CopyTo implementation, but I honestly don't see a need here. It's plenty fast enough and doesn't even need to be accessed often. @m_magalhaes said in MoData.GetData()?: hi, it's a bit hard to tell as i still don't understood what you were trying to achieve. Do you want to save the mograph data and use them in c4d as a kind of library, or do you want to export them to a 3rd Party software ? Cheers, Manuel I need mograph cache tag functionality without the mograph cache tag. So I need to store MoData and be able to have it persist through file saves/loads, copying to a new document/rendering, duplication, etc. Given the potential size of the data in question (especially once you add animation into the mix) I want to make sure I'm doing this in the most efficient manner possible. Given that all the datatypes that contain the MoData aren't directly supported by the normal storage methods in c4d (basecontainers and hyperfiles), I was concerned that breaking all the data into its individual components to store it away wouldn't be efficient. I'm seeing now that I had little to be concerned about. Performance is great so far! I think I've even cleared my final hurdle, which was creating an array of MoDatas. My problem there was trying to use a BaseArray when what I needed was a PointerArray. Since PointerArray's take ownership of the pointed object, freeing the allocated MoDatas is as clean and simple as calling Reset() on the Pointer Array:) Injecting my MoData into the effector pipeline is equally simple by doing a CopyTo() or MergeData()... currently I'm doing that in ModifyPoints, but maybe I should be doing it in InitPoints? I'll figure that part out, but right now I'm just happy to have it all working! Thank you again zipit and Manuel!