Normals issue with exported mesh of mirrored linked blocks

Having an issue where the normals are getting flipped on the exported mesh when I mirror linked blocks.

Both of these stanchions are instances of the same block. Left side looks correct, where as right side is rendering inside out.

No real way for me to correct this, other than not mirror things (Which is a disaster for workload when making a symmetrical boat) or possibly having a duplicate block that starts out inside out then replace block on the other side?

As an aside this explains why my mirrored lights don’t render in Maxwell. Maxwell generally doesn’t care about normal direction… except with materials that are emitters! Feel dumb for not figuring it out sooner. I’ve been lighting both sides of boats manually for years like a chump. Hopefully this is a simple fix.

Hi Ryan - for now, see if making a mesh first, (Mesh command on the flipped block instance) checking the resulting mesh’s direction (Dir) and flipping if needed before export is any help.

-Pascal

Pascal

If I explode it the normals take the correct direction.

If I mesh before exploding, normals are incorrect and need flipping.

If I mesh after exploding, normals are correct.

but kindof not great for workflow. Defeats the purpose of the block when we decide to change the design of the stanchion or whatever.

I suppose it would be possible to write a script that cheat “mirrors” symmetrical blocks…

Like just makes a copy, rotates it 180 on Z axis, moves it to inverse of Y dimension. inverts the X axis rotation. Keep Y axis rotation same.

? Any gaps in logic here?

Might have my axes confused. if rotate around Z first then would leave X axis alone and invert Y.

EDIT Actually need to invert Z rotation? Not sure how that works. Haha pretty much everything I said is wrong.

OK I am making some progress with this script. Some gaps in my knowledge though… Namely digging out the Y dimension from the origin block so I can do math on it and plug it into the destination.

Call ToOtherSide()
Sub ToOtherSide

Dim arrObjects
arrObjects = Rhino.GetObjects("Select blocks to copy across boat", 4096, True, True)
If Not IsArray(arrObjects) Then Exit Sub

Rhino.EnableRedraw True      

' For each selected block object
Dim strObject, arrPoint, arrEnd, strCopiedBlock, arrCopiedBlockInsertPoint
For Each strObject In arrObjects
	' Get the block's insertion point
	arrPoint = Rhino.BlockInstanceInsertPoint(strObject)
	'Get the destination//////// HELP
	arrEnd = Rhino.GetPoint("to here", arrPoint)
	' Copy the object across the boat
	strCopiedBlock = Rhino.CopyObject(strObject, arrPoint, arrEnd)
	'Find copied object inert point
	arrCopiedBlockInsertPoint = Rhino.BlockInstanceInsertPoint(strCopiedBlock)
	'Rotate 180 on Z
	Rhino.RotateObject strCopiedBlock, arrCopiedBlockInsertPoint, 180
	
Next  

Rhino.EnableRedraw True      

End Sub

Got it.

Dim strObject, arrPoint, arrEnd, strCopiedBlock, arrCopiedBlockInsertPoint
For Each strObject In arrObjects
’ Get the block’s insertion point
arrPoint = Rhino.BlockInstanceInsertPoint(strObject)
‘Get the destination
arrEnd = arrPoint
arrEnd(1) = arrEnd(1) * -1
’ Copy the object across the boat
strCopiedBlock = Rhino.CopyObject(strObject, arrPoint, arrEnd)
'Find copied object inert point
arrCopiedBlockInsertPoint = Rhino.BlockInstanceInsertPoint(strCopiedBlock)
'Rotate 180 on Z
Rhino.RotateObject strCopiedBlock, arrCopiedBlockInsertPoint, 180

OK So clearly 180 degree rotation doesn’t work. I need to check block’s rotation and do the opposite on the other side? BlockInstanceXform?

Hi Ryan - you can do something like

  • Get two points from the user and with the cplane Z axis define a mirror plane
  • Find the block instance insertion point’s closest point to the plane. Make a vector from these points and use that to find the target location.

But, I’m not clear on how the rest works or in what axis the blocks are symmetrical (in themselves or just across the mirror plane?) so that you could get there by rotation…

For now, I’d Mesh and then export the flipped mesh…

-Pascal

I see, so you need the point where the axis of symmetry hits the mirror plane… and the puzzle is finding that axis, right?

-Pascal

The idea of doing it that way didn’t actually occur to me until after drawing the sketch.

I was thinking that BlockInstanceXform knows the rotation of the initial block and it would be easy to just invert a sign or something in there and apply the transform to the copied block.

I think the key might be in here

But… Starting to get over my head

Hi Ryan - if you allow user input for the axis of symmetry (Rhino.GetPoints()), this should not be too bad, work-flow wise, especially if the mirror plane can be a CPlane or World Axis. Got it handled? The target point for rotation is one of the axis points mirrored, so the rotation on the symmetry axis/mirror plane intersection can be figured out.

-Pascal

If I’m understanding what you’re saying… You are suggesting user input of the symmetry of each block to be copied across… Don’t think that will fly. There will be hundreds of light fixtures alone on this boat and like 200 lifeline stanchions alone.

Would something in here be helpful?:

Option Explicit
’Script written by Jarek Bieda

Call ResetBlockZRotation()
Sub ResetBlockZRotation()

'f will be added to the angle result value
' f=0 origin at 9 oclock,  f=-90 if origin at 12 oclock, -180 origin at 3 oclock, etc
Dim f : f = 0   
Dim d,i,r,v,block
block = Rhino.GetObject("Block Instance ?", 4096,, True)
r = DecomposeXformRotation(Rhino.BlockInstanceXform(block)) 'get rotation in radians
v = Round(r(2) * (-180 / (4 * Atn(1))), 0) + f 'conver into degrees and add 'Vray' factor
'Minus sign at 180 if clockwise 
If v < 0 Then
	v = v + 360
End If
Rhino.RotateObject block, Rhino.BlockInstanceInsertPoint(block), v, , False
Call Rhino.Print("Block rotation reset to zero")

End Sub

Function DecomposeXformRotation(arrXform)
Dim arrRotate(2)
arrRotate(0) = Rhino.ATan2(-arrXform(2, 1), arrXform(2, 2))
arrRotate(1) = Rhino.ASin(arrXform(2, 0))
arrRotate(2) = Rhino.ATan2(arrXform(1, 0), arrXform(0, 0))
DecomposeXformRotation = arrRotate
End Function

OK I’ve got this solved @Pascal. Don’t spend any more time on it.

Actually a little stumped on how I get the vector for each rotation. Is there like standard code for just using world X Y Z?

Do I copy the insert point 3x to make like xVector yVector zVector and then add 1 in the correct direction for each?

Hi Ryan - use

Rhino.WorldXYPlane()(3)

In RhinoScript a plane is just an array of a point and three vectors, the last one is Z.

-Pascal

Hrmm I’m getting into the weeds here. I think my vectors for rotation actually need to be along the block’s axes, not the world’s… Is there an easy way to do that?