CHotSpot _oHotSpot; // The hotspot object that will permit user to left/right click on us in the scene to move/rotate/scale us and invoke our context-sensitive menu. #endregion Fields #region Methods public override void FinishIntialization() { base.FinishIntialization(); //=== Retreive the rim skinned mesh so we can manually set the softbody rim verts to the position & normals for seamless connection to main skinned body === _oMeshRim = (CBSkinBaked)CBMesh.Create(null, _oBodyBase, _sBlenderInstancePath_CSoftBody + ".oMeshSoftBodyRim", typeof(CBSkinBaked)); // Retrieve the skinned softbody rim mesh Blender just created so we can pin softbody at runtime _oMeshRim.transform.SetParent(transform); _aMapRimVerts = CByteArray.GetArray_USHORT("'CBody'", _sBlenderInstancePath_CSoftBody_FullyQualfied + ".aMapRimVerts.Unity_GetBytes()"); // Read the rim traversal map from our CSoftBodyBase instance //=== Backup the position of the particles at startup time (so we can keep softbody from extreme deformation during rapid body movements like pose teleport) === _aFlexParticlesAtStart = new Vector3[_oFlexParticles.m_particlesCount]; for (int nParticle = 0; nParticle < _oFlexParticles.m_particlesCount; nParticle++) _aFlexParticlesAtStart[nParticle] = _oBoneAnchor.worldToLocalMatrix.MultiplyPoint(_oFlexParticles.m_particles[nParticle].pos); //###LEARN: How to properly convert from world to local (taking into account the full path of the transform we're converting about) //=== Backup each shape's rest position so we can expand / contract soft body volume without loss of information === _aShapeRestPosOrig = new Vector3[_oFlexShapeMatching.m_shapeIndicesCount]; Array.Copy(_oFlexShapeMatching.m_shapeRestPositions, _aShapeRestPosOrig, _oFlexShapeMatching.m_shapeIndicesCount); //=== Instantiate the FlexProcessor component so we get hooks to update ourselves during game frames === uFlex.FlexProcessor oFlexProc = CUtility.FindOrCreateComponent(gameObject, typeof(uFlex.FlexProcessor)) as uFlex.FlexProcessor; oFlexProc._oFlexProcessor = this; //=== Instantiate the debug visualizer for internal Softbody structure analysis === CVisualizeSoftBody oVisSB = CUtility.FindOrCreateComponent(gameObject, typeof(CVisualizeSoftBody)) as CVisualizeSoftBody; oVisSB.enabled = false; }
uFlex.FlexSprings _oFlexSprings; // Reference to spring component we're modifying to make pinning possible #endregion Fields #region Methods public void Initialize(ref List<ushort> aMapPinnedParticles, CBSkinBaked oMeshSoftBodyPinnedParticles) { //=== Obtain reference to the objects we'll need at every game frame === _aMapPinnedParticles = aMapPinnedParticles; _nNumMappingsSkinToSim = _aMapPinnedParticles.Count / 2; // Each mapping takes two slots in _aMapPinnedParticles _oMeshSoftBodyPinnedParticles = oMeshSoftBodyPinnedParticles; _oFlexParticles = CUtility.FindOrCreateComponent(gameObject, typeof(uFlex.FlexParticles)) as uFlex.FlexParticles; // Find or create these necessary compoenent from our parent _oFlexSprings = CUtility.FindOrCreateComponent(gameObject, typeof(uFlex.FlexSprings)) as uFlex.FlexSprings; //=== Create extra particles and springs for our parent's Flex objects === ushort nStartOfExtraParticles = (ushort)_oFlexParticles.m_particlesCount; // Remeber the split point between our parent's Flex particles/springs and the new ones we're adding ushort nStartOfExtraSprings = (ushort)_oFlexSprings.m_springsCount; _oFlexParticles.m_particlesCount += _nNumMappingsSkinToSim; // Add just the number of particles and springs we need (have a 1:1 relationship) _oFlexSprings. m_springsCount += _nNumMappingsSkinToSim; Array.Resize<uFlex.Particle> (ref _oFlexParticles.m_particles, _oFlexParticles.m_particlesCount); Array.Resize<bool> (ref _oFlexParticles.m_particlesActivity, _oFlexParticles.m_particlesCount); Array.Resize<Vector3> (ref _oFlexParticles.m_velocities, _oFlexParticles.m_particlesCount); Array.Resize<float> (ref _oFlexParticles.m_densities, _oFlexParticles.m_particlesCount); Array.Resize<Color> (ref _oFlexParticles.m_colours, _oFlexParticles.m_particlesCount); Array.Resize<int> (ref _oFlexSprings.m_springIndices, _oFlexSprings.m_springsCount * 2); Array.Resize<float> (ref _oFlexSprings.m_springRestLengths, _oFlexSprings.m_springsCount); Array.Resize<float> (ref _oFlexSprings.m_springCoefficients, _oFlexSprings.m_springsCount); //=== Define the extra particles and springs between the skinned portion and the simulated portion === ushort nNextPinnedParticle = nStartOfExtraParticles; ushort nNextSpring = nStartOfExtraSprings; for (ushort nMapping = 0; nMapping < _nNumMappingsSkinToSim; nMapping++) { //###F ###BUG??? Assumes all skinned particles are used? ###CHECK! ushort nMappingX2 = (ushort)(nMapping * 2); //ushort nParticleSkinned = _aMapPinnedParticles[nMappingX2 + 0]; // The index of the skinned particle in this mapping (sparsely populated) ushort nParticleSimulatedMoving = _aMapPinnedParticles[nMappingX2 + 1]; // The index of the simulated particle in this mapping (moves as usual but has a spring to nParticleSimulatedPinned pinned particle) ushort nParticleSimulatedPinned = nNextPinnedParticle++; // The ordinal of the (new) pinned Flex-simulated particle is the next ordinal. ushort nSpring = nNextSpring++; _aMapPinnedParticles[nMappingX2 + 1] = nParticleSimulatedPinned; // We never need to address the moving particle after this loop... so re-use the map to point to the pinned particle we'll need to move everyframe instead. _oFlexParticles.m_particles[nParticleSimulatedPinned].invMass = 0; // The extra particle is always the skinned one which we drive so it gets infinite mass to not be simulated _oFlexParticles.m_particles[nParticleSimulatedPinned].pos = _oFlexParticles.m_particles[nParticleSimulatedMoving].pos; _oFlexParticles.m_colours[nParticleSimulatedPinned] = Color.gray; //###TODO: Standardize Flex colors! _oFlexParticles.m_particlesActivity[nParticleSimulatedPinned] = true; _oFlexSprings.m_springRestLengths [nSpring] = 0.0f; // Springs between (master) skinned particle and (slave) simulated one is always zero... (we want simulated particle to stick as close to where it should be!) _oFlexSprings.m_springCoefficients[nSpring] = 1.0f; //###TUNE: Make as stiff as possible for a given Flex iteration count. ###IMPROVE: Enable game-time setting of this! ###F _oFlexSprings.m_springIndices[nSpring * 2 + 0] = nParticleSimulatedPinned; // Create the link between the two simulated particles (pinned and moving) _oFlexSprings.m_springIndices[nSpring * 2 + 1] = nParticleSimulatedMoving; } }
public override void OnDeserializeFromBlender() { base.OnDeserializeFromBlender(); _sBlenderInstancePath_CCloth = CBCloth.s_sNameClothSrc_HACK; //=== Create the skinned-portion of the cloth. It will be responsible for driving Flex particles that heavily influence their corresponding particles in fully-simulated cloth mesh === _oBSkinBaked_SkinnedPortion = (CBSkinBaked)CBSkinBaked.Create(null, _oBodyBase, "." + _sBlenderInstancePath_CCloth + ".oMeshClothSkinned", typeof(CBSkinBaked)); //###WEAK#13!!! F*****g dot!! _oBSkinBaked_SkinnedPortion.transform.SetParent(transform); _oBSkinBaked_SkinnedPortion._oSkinMeshRendNow.enabled = false; // Skinned portion invisible to the user. Only used to guide simulated portion //=== Receive the aMapPinnedParticles array Blender created to map the skinned verts to their pertinent simulated ones === List<ushort> aMapPinnedParticles = CByteArray.GetArray_USHORT("'CBody'", _oBodyBase._sBlenderInstancePath_CBodyBase + "." + _sBlenderInstancePath_CCloth + ".aMapPinnedParticles.Unity_GetBytes()"); //=== Create the simulated part of the cloth === MeshFilter oMeshFilter = GetComponent<MeshFilter>(); MeshRenderer oMeshRend = GetComponent<MeshRenderer>(); //oMeshRend.sharedMaterial = Resources.Load("Materials/BasicColors/TransWhite25") as Material; //####SOON? Get mats! ###F oMeshRend.sharedMaterial = Resources.Load("Materials/Test-2Sided") as Material; //####SOON? Get mats! ###F _oBSkinBaked_SkinnedPortion._oSkinMeshRendNow.sharedMaterial = oMeshRend.sharedMaterial; // Skinned part has same material _oMeshNow = oMeshFilter.sharedMesh; _oMeshNow.MarkDynamic(); // Docs say "Call this before assigning vertices to get better performance when continually updating mesh" //=== Create the 'cloth at startup' mesh. It won't get simulated and is used to reset simulated cloth to its startup position === _oBMeshClothAtStartup = CBMesh.Create(null, _oBodyBase, "." + _sBlenderInstancePath_CCloth + ".oMeshClothSimulated", typeof(CBMesh)); _oBMeshClothAtStartup.transform.SetParent(_oBodyBase.FindBone("chestUpper")); // Reparent this 'backup' mesh to the chest bone so it rotates and moves with the body _oBMeshClothAtStartup.GetComponent<MeshRenderer>().enabled = false; //_oBMeshClothAtStartup.gameObject.SetActive(false); // De activate it so it takes no cycle. It merely exists for backup purposes //=== Create the Flex object for our simulated part === CFlex.CreateFlexObject(gameObject, _oMeshNow, _oMeshNow, uFlex.FlexBodyType.Cloth, uFlex.FlexInteractionType.None, CGame.INSTANCE.nMassCloth, Color.yellow); uFlex.FlexProcessor oFlexProc = CUtility.FindOrCreateComponent(gameObject, typeof(uFlex.FlexProcessor)) as uFlex.FlexProcessor; oFlexProc._oFlexProcessor = this; _oFlexParticles = GetComponent<uFlex.FlexParticles>(); _oFlexSprings = GetComponent<uFlex.FlexSprings>(); //=== Create the managing object and related hotspot === _oObj = new CObject(this, 0, typeof(EFlexCloth), "Cloth " + gameObject.name); //###IMPROVE: Name of soft body to GUI _oObj.PropGroupBegin("", "", true); _oObj.PropAdd(EFlexCloth.Tightness, "Tightness", 1.0f, 0.01f, 2.5f, ""); _oObj.PropAdd(EFlexCloth.Length, "Length", 1.0f, 0.50f, 1.10f, ""); _oObj.PropAdd(EFlexCloth.ClothMass, "Mass", 1.0f, 0.0001f, 1000.0f, ""); _oObj.FinishInitialization(); _oWatchBone = _oBodyBase.FindBone("chestUpper"); //####HACK ####DESIGN: Assumes this cloth is a top! _oHotSpot = CHotSpot.CreateHotspot(this, _oWatchBone, "Clothing", false, new Vector3(0, 0.22f, 0.04f)); //###IMPROVE!!! Position offset that makes sense for that piece of clothing (from center of its verts?) //=== Backup the startup cloth arrays so we can adjust in a non-destructive way === _aSpringRestLengthsBAK = new float[_oFlexSprings.m_springsCount]; System.Array.Copy(_oFlexSprings.m_springRestLengths, _aSpringRestLengthsBAK, _oFlexSprings.m_springsCount); //=== Create the Flex-to-skinned-mesh component responsible to guide selected Flex particles to skinned-mesh positions === _oPinnedParticles = CUtility.FindOrCreateComponent(gameObject, typeof(CPinnedParticles)) as CPinnedParticles; _oPinnedParticles.Initialize(ref aMapPinnedParticles, _oBSkinBaked_SkinnedPortion); }