Hi merkvilson, thanks for reaching out us.
With regard to your question, there's unfortunately no means to detect single click in the Object Manager via current API.
Hi Dan, thanks for following up.
With regard to the code posted I think there are two things to check:
the expression used to compute the linear interpolation is incorrect: it should rather be
spot1.x + (spot2.x - spot1.x) / pointcount*x as well as for
Hi Tamura-san, thanks for reaching out us.
I apologize for coming a little bit late than usual, but although the question's resolution was straightforward at the begin finding a way to retrieve and set NormalTag values was less straightforward than expected.
Going back to your question, transforming a vector stored in a normal tag undergoes the same rules of transforming any other vector: left multiply the vector by a transformation matrix is the way to go.
An exception to this rules is that in the case of directions (as normals are) the transformation matrix should have the translation vector set to 0,0,0 otherwise the results might be unpredictable.
Being said that, the less-straightforward part: although I would have imagined that getting/setting values in a NormalTag should have followed the same rules for any other variable tag, I experienced some troubles on the course of "properly" reaching the values.
To recap here a list of old threads in the forum talking about NormalTag:
In Modifiying the normal-tag it's described how to set the values by accessing in write-mode the low-level data, but I'd say that it could be discouraging at a first sight especially for newbies.
The way to go is instead to use GetAllHighlevelData / SetAllHighlevelData which are indeed more friendly.
By using these two methods, you're expected to receive (in the getter function) or to pass (in the setter function) a list of values ranging from
65535. Given a mesh with 2 polys the normal list returned by - or passed to - will look like:
normalList = [ x1a1 x1a2 x1a3 x1b1 x1b2 x1b3 x1c1 x1c2 x1c3 x1d1 x1d2 x1d3 x2a1 x2a2 x2a3 x2b1 x2b2 x2b3 x2c1 x2c2 x2c3 x2d1 x2d2 x2d3] containing 24 elements (2 polys x 4 normals per poly x 3 component per normal)
In addition the values stored in such a list should be converted from float [-1, 1] to a int representation [0, 65535] given the following mapping schema:
[ 0.0, 1.0] --> [0, 32000] [ -0.0, -1.0] --> [65536, 33536] // note that 0 == 65536 in UInt16
Given all this information, there's one last point to note down (which looks like a bug in the current
GetAllHighlevelData implementation). Assuming we still have a 2 polys mesh and we set the values of a NormalTag to a list of values composed by
[3200, 6400, 9600, 12800, 16000, 19200, 22400, 25600, 28800, 62335, 59135, 55935, 3200, 6400, 9600, 12800, 16000, 19200, 22400, 25600, 28800, 62335, 59135, 55935]
[3200, 6400, 9600, 12800, 16000, 19200, 22400, 25600, 28800, 62335, 59135, 55935, 6400, 9600, 12800, 16000, 19200, 22400, 25600, 28800, 62335, 59135, 55935, 3200]
shifting for the second poly the returned stored values by one unit (note that the last value is
3200 whilst it should be
Then a realignment operation is required for every polygon expect for the first.
Finally the code looks like
import c4d, math # convert from [-1,1] to [0,65535] def ConvertFromReal(value): res = 0 if value < 0: res = int(value * 32000 + 65536) else: res = int(value * 32000) return res # convert [-1,1]-represented vector to [0,65535]-represented vector def ConvertFromRealVector(vec): res = c4d.Vector() for i in xrange(3): res[i] = ConvertFromReal(vec[i]) return res # helper function to set the values in a NormalTag def SetAllHighLevelNormals(tag, normalList): dataList =  for normal in normalList: temp = ConvertFromRealVector(normal) dataList.append(int(temp)) dataList.append(int(temp)) dataList.append(int(temp)) tag.SetAllHighlevelData(dataList) # convert [0,65535] to [-1,1] def ConvertToReal(value): res = 0 if value > 33535: # should be converted in a negative value res = float((value - 65536) / 32000) else: res = float(value / 32000) return res # convert [0,65535]-represented vector to [-1,1]-represented vector def ConvertToRealVector(vec): res = c4d.Vector() for i in xrange(3): res[i] = ConvertToReal(vec[i]) return res # helper function to get the values in a NormalTag def GetAllHighLevelNormals(tag): # access the high-level data of the NormalTag data = tag.GetAllHighlevelData() # NOTE: returned data is currently shifted polygonCnt = len(data) / 12 normalList = ; for i in xrange(polygonCnt): # realign the normal data subData = data[i*12:i*12 + 12] shiftedSubData = subData[-i:] + subData[:-i] for j in xrange(4): normal = c4d.Vector(shiftedSubData[ j * 3 + 0], shiftedSubData[ j * 3 + 1], shiftedSubData[ j * 3 + 2]) normal = ConvertToRealVector(normal) normalList.append(normal) return normalList # Create a normal Tag def CreateNormalTag(op): # create a NormalTag polyCnt = op.GetPolygonCount() nrmTag = c4d.NormalTag(polyCnt) if nrmTag is None: return # insert the tag op.InsertTag(nrmTag) c4d.EventAdd() # Main function def main(): print op if op is None: return if not op.GetType() == c4d.Opolygon: return nrmTag = op.GetTag(c4d.Tnormal) if nrmTag is None: CreateNormalTag(op) nrmTag = op.GetTag(c4d.Tnormal) # let's assume that all the normals stored in NormalTag should point up-ward a = c4d.Vector(0.0, 1.0, 0.0) b = c4d.Vector(0.0, 1.0, 0.0) c = c4d.Vector(0.0, 1.0, 0.0) d = c4d.Vector(0.0, 1.0, 0.0) polyCnt = op.GetPolygonCount() normalList = [a,b,c,d] * polyCnt # set the normal values SetAllHighLevelNormals(nrmTag, normalList) # create a transformation matrix and its inverse rotAxis = c4d.Vector(0,0,1) rotAngle = math.radians(45) trf = c4d.Matrix() trf = c4d.utils.RotAxisToMatrix(rotAxis, rotAngle) trf.off = c4d.Vector(0,100,0) itrf = ~trf # get all points and transform them accordingly to the matrix points = op.GetAllPoints() for i in xrange(len(points)): points[i] = itrf.Mul(points[i]) op.SetAllPoints(points) # get all the values stored in the normal tag and transform them accordingly to the matrix normalList = GetAllHighLevelNormals(nrmTag) for i in xrange(len(normalList)): normalList[i] = itrf.MulV(normalList[i]) normalList[i].Normalize() SetAllHighLevelNormals(nrmTag, normalList) op.Message(c4d.MSG_UPDATE) c4d.EventAdd() # Execute main() if __name__=='__main__': main()
I'm sorry for the length discussion and if there are still doubts around the setting / getting values for a NormalTag just drop a note.
Hi Nikolay, thanks for following up and for further detailing you request.
With regard to the
FieldObject class you've pin-pointed, it has, unfortunately, nothing to do with vector fields but rather with MoGraph Fields as you've already spotted. Just for the sake of completeness this manual describes it.
Going back to the root, I'm sorry to confirm that there's no public API fitting with the precise scope you mentioned above to retrieve for a given point in space the force applied by a particle operator in that specific position.
Hi Dan, thanks for following up here.
With regard to your comment, the texture tag's matrix takes place when passed as 4th argument of the GenerateUVW(). This matrix is actually responsible for positioning the "virtual mapping gizmo" in space with respect to the object itself
To be more clear image you need you've a plane placed on the origin and laying on XZ. You need to create a flat mapping on it rotated by 45 degrees on
w, stretched by 0.5 on
u and 2.0 on
v and shifted from the origin of a certain amount.
Assuming you also have a material existing in the Material Manager running the script below will execute the task.
# Main function def main(): # check that an object is selected if op is None: return # get the active material activeMat = doc.GetActiveMaterial() if activeMat is None: return # instantiate a TextureTag texTag = c4d.TextureTag() if texTag is None: return # set the mapping type texTag[c4d.TEXTURETAG_PROJECTION] = c4d.TEXTURETAG_PROJECTION_FLAT # turn off tiling texTag[c4d.TEXTURETAG_TILE] = False # link to the active material texTag[c4d.TEXTURETAG_MATERIAL] = activeMat # apply some modification to the spatial position of the texturetag angleDeg = -90 # orient the virtual flat gizmo from xy-plane to xz-plane texTagMatrix = c4d.utils.RotAxisToMatrix(c4d.Vector(1,0,0), math.radians(angleDeg)) # rotate around the local Z-axis by 30degs angleDeg = 45 texTagMatrix = texTagMatrix * c4d.utils.RotAxisToMatrix(c4d.Vector(0,0,1), math.radians(angleDeg)) # stretch the texture by 0.5 on X-axis and 2 on Z-axis texTagMatrix.Scale(c4d.Vector(0.5,1,2)) # apply some offset to the texture texTagMatrix.off = c4d.Vector(50,0,-100) # generate the corresponding UVWTag using the mapping settings specific in the TextureTag uvwTag = c4d.utils.GenerateUVW(op, op.GetMg(), texTag, texTagMatrix) texTag[c4d.TEXTURETAG_PROJECTION] = 6 if op.GetTag(c4d.Tuvw) is None: op.InsertTag(uvwTag) if op.GetTag(c4d.Ttexture) is None: op.InsertTag(texTag) c4d.EventAdd() # Execute main() if __name__=='__main__': main()
In this context the TextureTag's matrix actually plays the role that can be seen by manipulating the mapping when an object is in Texture Mode
Hi Nikolay, thanks for reaching out us.
With regard to your question, if your scope is to retrieve the values of the parameters bound to such particle "operators" BaseContainers and Parameters are the relevant words.
With regard to parameters, being these entities derived from the C4DAtom class I warmly recommend to look at the Parameter and Parameter Properties section in the C4D Atom Manual where accessing the value belonging to a parameter is presented.
Finally, but consider it more like a rough thought, you might think about having a twin "representation" of your particles so that a ghost "C4D particle" representation can interact with the Cinema 4D particle operators and give you the chance to derive the behavior that your particles will have to undergo when interacting with such operators.
Hi @rownn, thanks for having provided further details.
Assuming that you're aware of the limitation in terms of appearance that you might face by using the functionalities delivered by GeClipMap, the only way to go to access a bitmap which is supposed to contain a rendering of the scene in Python is to execute the rendering invoking the BaseDocument::RenderDocument() and pass the
BaseBitmap you intend to manipulate via the
Hi Boony2000, thanks for reaching out us.
With regard to your question, it's currently not possible to specify on which monitor a GeDialog should open by default.
It's worthy also noting out that the way the windowing system in Cinema 4D has been designed, beside giving the user the freedom to properly place a GeDialog in any screen, guarantees that, once the dialog has been positioned and layout saved, it will re-appear on the proper screen as soon that layout will be loaded again.
Hi Ogers, thanks for reaching us.
With regard to your question, I need some further explanation since I actually don't get what you mean by:
to get these parameters from c4d lights
Which parameters are you referring to?
What is the final intent of the Light Translator plugin?
Hi Roger thanks for reaching us and sorry for coming late back.
It took a bit of research to find the "way" things are supposed to work and this slowed the whole discussion a bit.
With regard to your request, I need to spend a few word on clarifying how
GeDatais created on the fly and both the original pointer and size of the memory chunk are passed to instantiate the
GeData. As this temporary
GeDatais copied in the
GeDatagoes out of scope and gets destroyed with the side effect that also source memory chunk values are lost.
ByteArrayis created on the fly and the original pointer and size of the memory chunk are passed to instantiate the
ByteArray. As this temporary
ByteArrayis used to fill the
ByteArraygoes out of scope and gets destroyed with the side effect that also source memory chunk values are lost.
Considering this scenario, what's written in the documentation is, to a certain extent, misleading, because it's not actually taking the ownership, but rather it's copying the data of the memory chunk in the
GeData) and destroying the memory chunk used as source.
If this is not intended to happen, you can eventually consider, after using the
SetMemory in the
GeData or in the
BaseContainer to use the corresponding
GetMemory() (BaseContainer / GeData) and/or
GetMemoryAndRelease()(BaseContainer / GeData) to access the new pointer being stored and use it for any further reason.
Last but not least, when using the
GetMemoryAndRelease() beware of properly freeing the memory being referenced by the pointer returned otherwise you'll end up in memory leak issues.