public void Clear() { placeholder = null; chunk = null; voxelIndex = -1; collider = null; }
/// <summary> /// Removes all existing voxels in this chunk. /// </summary> public void ClearVoxels(byte light) { if (lightSources != null) { int lightSourcesCount = lightSources.Count; for (int k = 0; k < lightSourcesCount; k++) { if (lightSources [k].gameObject != null) { DestroyImmediate(lightSources [k].gameObject); } } lightSources.Clear(); } if (placeholders != null) { int phCount = placeholders.Count; for (int k = 0; k < phCount; k++) { VoxelPlaceholder ph = placeholders [k]; if (ph != null) { DestroyImmediate(ph.gameObject); } } placeholders.Clear(); } Voxel.Clear(voxels, light); }
bool RayCastFast(Vector3 origin, Vector3 direction, out VoxelHitInfo hitInfo, float maxDistance = 0, bool createChunksIfNeeded = false, byte minOpaque = 0, ColliderTypes colliderTypes = ColliderTypes.AnyCollider) { bool voxelHit = RayCastFastVoxel(origin, direction, out hitInfo, maxDistance, createChunksIfNeeded, minOpaque); if ((colliderTypes & ColliderTypes.OnlyVoxels) != 0) { return(voxelHit); } if (voxelHit) { maxDistance = hitInfo.distance - 0.1f; } // Cast a normal raycast to detect normal gameobjects within ray RaycastHit hit; if (Physics.Raycast(origin + 0.3f * direction, direction, out hit, maxDistance - 0.3f)) { hitInfo.distance = hit.distance; hitInfo.point = hit.point; hitInfo.normal = hit.normal; hitInfo.collider = hit.collider; // Check if gameobject is a dynamic voxel if (hit.collider != null) { if ((colliderTypes & ColliderTypes.IgnorePlayer) != 0 && characterController != null && characterController.name.Equals(hit.collider.name)) { return(false); } hitInfo.voxelIndex = -1; VoxelPlaceholder placeholder = hit.collider.GetComponentInParent <VoxelPlaceholder> (); if (placeholder != null) { hitInfo.chunk = placeholder.chunk; hitInfo.voxelIndex = placeholder.voxelIndex; hitInfo.voxel = placeholder.chunk.voxels [placeholder.voxelIndex]; hitInfo.voxelCenter = placeholder.transform.position; hitInfo.placeholder = placeholder; } } return(true); } else { return(voxelHit); } }
/// <summary> /// Adds a torch. /// </summary> GameObject TorchAttachInt(VoxelHitInfo hitInfo, ItemDefinition torchDefinition = null, bool refreshChunks = true) { // Make sure the voxel exists (has not been removed just before this call) and is solid if (hitInfo.chunk.voxels [hitInfo.voxelIndex].hasContent != 1 || hitInfo.chunk.voxels [hitInfo.voxelIndex].opaque < FULL_OPAQUE) { return(null); } // Placeholder for attaching the torch VoxelPlaceholder placeHolder = GetVoxelPlaceholder(hitInfo.chunk, hitInfo.voxelIndex, true); if (placeHolder == null) { return(null); } // Position of the voxel containing the "light" of the torch Vector3 voxelLightPosition = hitInfo.voxelCenter + hitInfo.normal; VoxelChunk chunk; int voxelIndex; if (!GetVoxelIndex(voxelLightPosition, out chunk, out voxelIndex)) { return(null); } // Make sure it's empty where the light gameobject will be placed if (chunk.voxels[voxelIndex].opaque >= 2) { return(null); } // Updates current chunk if (chunk.lightSources != null) { foreach (var x in chunk.lightSources) { if (x.voxelIndex == voxelIndex) { // Restriction 2: no second torch on the same voxel index return(null); } } } if (torchDefinition == null) { // Get an inventory item with name Torch int itemCount = allItems.Count; for (int k = 0; k < itemCount; k++) { if (allItems [k].item.category == ItemCategory.Torch) { torchDefinition = allItems [k].item; break; } } } if (torchDefinition == null) { return(null); } // Instantiate torch prefab GameObject torch = Instantiate <GameObject> (torchDefinition.prefab); torch.name = TORCH_NAME; // Parent the torch gameobject to the voxel placeholder torch.transform.SetParent(placeHolder.transform, false); // Position torch on the wall torch.transform.position = hitInfo.chunk.transform.position + GetVoxelChunkPosition(hitInfo.voxelIndex) + hitInfo.normal * 0.5f; // Rotate torch according to wall normal if (hitInfo.normal.y == -1) // downwards { torch.transform.Rotate(180f, 0, 0); } else if (hitInfo.normal.y == 0) // side wall { torch.transform.Rotate(hitInfo.normal.z * 40f, 0, hitInfo.normal.x * -40f); } Item itemInfo = torch.AddComponent <Item> (); itemInfo.itemDefinition = torchDefinition; itemInfo.canPickOnApproach = false; itemInfo.canBeDestroyed = true; itemInfo.itemChunk = hitInfo.chunk; itemInfo.itemVoxelIndex = hitInfo.voxelIndex; // Add light source to chunk LightSource lightSource = new LightSource(); lightSource.gameObject = torch; lightSource.voxelIndex = voxelIndex; lightSource.itemDefinition = torchDefinition; lightSource.hitInfo = hitInfo; lightSource.lightIntensity = torchDefinition.lightIntensity; chunk.AddLightSource(lightSource); chunk.modified = true; // Add script to remove light source from chunk when torch or voxel is destroyed LightSourceRemoval sr = torch.AddComponent <LightSourceRemoval> (); sr.env = this; sr.chunk = chunk; Light pointLight = torch.GetComponentInChildren <Light> (); if (pointLight != null) { pointLight.enabled = true; } // Make torch collider ignore player's collider to avoid collisions if (characterControllerCollider != null) { Physics.IgnoreCollision(torch.GetComponent <Collider> (), characterControllerCollider); } // Recompute lightmap if (refreshChunks) { UpdateChunkRR(chunk); } // Trigger torch event if (!isLoadingGame && OnTorchAttached != null) { OnTorchAttached(chunk, lightSource); } return(torch); }
void WriteChunkData_10(BinaryWriter bw, VoxelChunk chunk) { // Chunk position EncodeVector3Binary(bw, chunk.position); bw.Write(chunk.isAboveSurface ? (byte)1 : (byte)0); int voxelDefinitionIndex = 0; VoxelDefinition prevVD = null; // Count voxels words int k = 0; int numWords = 0; while (k < chunk.voxels.Length) { if (chunk.voxels [k].hasContent == 1) { VoxelDefinition voxelDefinition = chunk.voxels [k].type; if (voxelDefinition.isDynamic) { k++; continue; } if (voxelDefinition != prevVD) { if (!saveVoxelDefinitionsDict.TryGetValue(voxelDefinition, out voxelDefinitionIndex)) { k++; continue; } prevVD = voxelDefinition; } Color32 tintColor = chunk.voxels [k].color; int flags = 0; if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation()) { flags = chunk.voxels [k].GetFlags(); } k++; while (k < chunk.voxels.Length && chunk.voxels [k].type == voxelDefinition && chunk.voxels [k].color.r == tintColor.r && chunk.voxels [k].color.g == tintColor.g && chunk.voxels [k].color.b == tintColor.b && voxelDefinition.renderType != RenderType.Custom && chunk.voxels [k].GetFlags() == flags) { k++; } numWords++; } else { k++; } } bw.Write((Int16)numWords); // Write voxels k = 0; while (k < chunk.voxels.Length) { if (chunk.voxels [k].hasContent == 1) { int voxelIndex = k; VoxelDefinition voxelDefinition = chunk.voxels [k].type; if (voxelDefinition.isDynamic) { k++; continue; } if (voxelDefinition != prevVD) { if (!saveVoxelDefinitionsDict.TryGetValue(voxelDefinition, out voxelDefinitionIndex)) { k++; continue; } prevVD = voxelDefinition; } Color32 tintColor = chunk.voxels [k].color; byte flags = 0; if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation()) { flags = chunk.voxels [k].GetFlags(); } int repetitions = 1; k++; while (k < chunk.voxels.Length && chunk.voxels [k].type == voxelDefinition && chunk.voxels [k].color.r == tintColor.r && chunk.voxels [k].color.g == tintColor.g && chunk.voxels [k].color.b == tintColor.b && voxelDefinition.renderType != RenderType.Custom && chunk.voxels [k].GetFlags() == flags) { repetitions++; k++; } bw.Write((Int16)voxelDefinitionIndex); bw.Write(tintColor.r); bw.Write(tintColor.g); bw.Write(tintColor.b); bw.Write((Int16)voxelIndex); bw.Write((Int16)repetitions); bw.Write(flags); if (voxelDefinition.renderType == RenderType.Custom) { // Check rotation VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, voxelIndex, false); if (placeholder != null) { Vector3 angles = placeholder.transform.eulerAngles; if (angles.x != 0 || angles.y != 0 || angles.z != 0) { bw.Write((byte)1); // has rotation EncodeVector3Binary(bw, angles); } else { bw.Write((byte)0); } } else { bw.Write((byte)0); } } else { bw.Write((byte)0); } } else { k++; } } // Write number of light sources int lightCount = chunk.lightSources != null ? chunk.lightSources.Count : 0; bw.Write((Int16)lightCount); if (lightCount > 0) { for (k = 0; k < lightCount; k++) { LightSource lightSource = chunk.lightSources [k]; int voxelIndex = lightSource.hitInfo.voxelIndex; Vector3 normal = lightSource.hitInfo.normal; int itemIndex = 0; ItemDefinition id = lightSource.itemDefinition; if (id != null) { saveItemDefinitionsDict.TryGetValue(id, out itemIndex); } bw.Write((Int16)voxelIndex); EncodeVector3Binary(bw, normal); bw.Write((Int16)itemIndex); } } // Write number of items int itemCount = chunk.items != null ? chunk.items.count : 0; bw.Write((Int16)itemCount); if (itemCount > 0) { for (k = 0; k < itemCount; k++) { Int16 itemIndex = 0; Int16 itemQuantity = 0; Vector3 itemPosition = Misc.vector3zero; Item item = chunk.items.values [k]; if (item != null && item.itemDefinition != null) { ItemDefinition id = item.itemDefinition; int idIndex; if (saveItemDefinitionsDict.TryGetValue(id, out idIndex)) { itemIndex = ((Int16)idIndex); itemPosition = item.transform.position; bw.Write((Int16)item.quantity); } } bw.Write(itemIndex); EncodeVector3Binary(bw, itemPosition); bw.Write(itemQuantity); } } }
/// <summary> /// Clears chunk state before returning it to the pool. This method is called when this chunk is reused. /// </summary> public void PrepareForReuse(byte light) { isAboveSurface = false; needsMeshRebuild = false; isPopulated = false; inqueue = false; modified = false; renderState = ChunkRenderState.Pending; allowTrees = true; lightmapSignature = -1; frustumCheckIteration = 0; navMesh = null; navMeshSourceIndex = -1; inconclusiveNeighbours = 0; renderingFrame = -1; if (lightSources != null) { lightSources.Clear(); } if (placeholders != null) { int count = placeholders.Count; for (int k = 0; k < count; k++) { VoxelPlaceholder placeholder = placeholders [k]; if (placeholder != null && placeholder.gameObject != null) { DestroyImmediate(placeholder.gameObject); } } placeholders.Clear(); } if (items != null) { for (int k = 0; k < items.count; k++) { Item item = items.values [k]; if (item != null && item.gameObject != null) { DestroyImmediate(item.gameObject); } } items.Clear(); } if (_left != null) { _left.right = null; _left = null; } if (_right != null) { _right.left = null; _right = null; } if (_forward != null) { _forward.back = null; _forward = null; } if (_back != null) { _back.forward = null; _back = null; } if (_top != null) { _top.bottom = null; _top = null; } if (_bottom != null) { _bottom.top = null; _bottom = null; } ClearVoxels(light); mr.enabled = false; }
/// <summary> /// Performs the voxel damage. /// </summary> /// <returns>The actual damage taken by the voxe.</returns> /// <param name="hitInfo">Hit info.</param> /// <param name="damage">Damage.</param> /// <param name="addParticles">If set to <c>true</c> add particles.</param> int DamageVoxelFast(ref VoxelHitInfo hitInfo, int damage, bool addParticles, bool playSound, bool allowDamageEvent = true) { VoxelChunk chunk = hitInfo.chunk; if (hitInfo.voxel.typeIndex == 0) { return(0); } VoxelDefinition voxelType = hitInfo.voxel.type; byte voxelTypeResistancePoints = voxelType.resistancePoints; if (buildMode) { if (damage > 0) { damage = 255; } } else { if (voxelTypeResistancePoints == (byte)0) { damage = 0; } else if (voxelTypeResistancePoints == (byte)255) { if (playSound) { PlayImpactSound(hitInfo.voxel.type.impactSound, hitInfo.voxelCenter); } damage = 0; } } if (allowDamageEvent && OnVoxelDamaged != null) { OnVoxelDamaged(chunk, hitInfo.voxelIndex, ref damage); } if (damage == 0) { return(0); } // Gets ambient light near surface float voxelLight = GetVoxelLight(hitInfo.point + hitInfo.normal * 0.5f); // Get voxel damage indicator GO's name bool destroyed = voxelType.renderType == RenderType.CutoutCross; int resistancePointsLeft = 0; VoxelPlaceholder placeholder = null; if (!destroyed) { placeholder = GetVoxelPlaceholder(chunk, hitInfo.voxelIndex, true); resistancePointsLeft = placeholder.resistancePointsLeft - damage; if (resistancePointsLeft < 0) { resistancePointsLeft = 0; destroyed = true; } placeholder.resistancePointsLeft = resistancePointsLeft; } if (voxelType.renderType == RenderType.Empty) { addParticles = false; } int particlesAmount; if (destroyed) { // Add recoverable voxel on the scene (not for vegetation) if (voxelType.renderType != RenderType.Empty && voxelType.renderType != RenderType.CutoutCross && voxelType.canBeCollected && !buildMode) { bool create = true; if (OnVoxelBeforeDropItem != null) { OnVoxelBeforeDropItem(chunk, hitInfo, out create); } if (create) { CreateRecoverableVoxel(hitInfo.voxelCenter, voxelDefinitions [hitInfo.voxel.typeIndex], hitInfo.voxel.color); } } // Destroy the voxel VoxelDestroyFast(chunk, hitInfo.voxelIndex); // Check if grass is on top and remove it as well VoxelChunk topChunk; int topIndex; if (GetVoxelIndex(hitInfo.voxelCenter + Misc.vector3up, out topChunk, out topIndex, false)) { if (topChunk.voxels [topIndex].typeIndex != 0 && voxelDefinitions [topChunk.voxels [topIndex].typeIndex].renderType == RenderType.CutoutCross) { byte light = topChunk.voxels [topIndex].lightMesh; topChunk.voxels [topIndex].Clear(light); topChunk.modified = true; } } // Max particles particlesAmount = 20; if (playSound) { PlayDestructionSound(voxelDefinitions [hitInfo.voxel.typeIndex].destructionSound, hitInfo.voxelCenter); } } else { // Add damage indicator if (placeholder == null) { placeholder = GetVoxelPlaceholder(chunk, hitInfo.voxelIndex, true); } if (placeholder.damageIndicator == null) { if (damagedVoxelPrefab == null) { damagedVoxelPrefab = Resources.Load <GameObject> ("VoxelPlay/Prefabs/DamagedVoxel"); } GameObject g = Instantiate <GameObject> (damagedVoxelPrefab); g.name = DAMAGE_INDICATOR; Transform tDamageIndicator = g.transform; placeholder.damageIndicator = tDamageIndicator.GetComponent <Renderer> (); tDamageIndicator.SetParent(placeholder.transform, false); tDamageIndicator.localPosition = placeholder.bounds.center; tDamageIndicator.localScale = placeholder.bounds.size * 1.001f; } int textureIndex = FastMath.FloorToInt((5f * resistancePointsLeft) / voxelTypeResistancePoints); if (world.voxelDamageTextures.Length > 0) { if (textureIndex >= world.voxelDamageTextures.Length) { textureIndex = world.voxelDamageTextures.Length - 1; } Material mi = placeholder.damageIndicatorMaterial; // gets a copy of material the first time it's used mi.mainTexture = world.voxelDamageTextures [textureIndex]; mi.SetFloat("_VoxelLight", voxelLight); placeholder.damageIndicator.enabled = true; } // Particle amount depending of damage particlesAmount = (6 - textureIndex) + 3; // Sets health recovery for the voxel placeholder.StartHealthRecovery(chunk, hitInfo.voxelIndex, world.damageDuration); if (playSound) { PlayImpactSound(voxelDefinitions [hitInfo.voxel.typeIndex].impactSound, hitInfo.voxelCenter); } } // Add random particles if (!addParticles) { particlesAmount = 0; } for (int k = 0; k < particlesAmount; k++) { int ppeIndex = GetParticleFromPool(); if (ppeIndex < 0) { continue; } // Scale of particle Renderer particleRenderer = particlePool [ppeIndex].renderer; if (destroyed) { if (voxelType.renderType == RenderType.CutoutCross) // smaller particles for vegetation { particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.03f, 0.04f); } else { particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.04f, 0.1f); } } else { particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.03f, 0.06f); } // Set particle texture Material instanceMat = particleRenderer.sharedMaterial; SetParticleMaterialTextures(instanceMat, voxelDefinitions [hitInfo.voxel.typeIndex], hitInfo.voxel.color); instanceMat.mainTextureOffset = new Vector2(Random.value, Random.value); instanceMat.mainTextureScale = Misc.vector2one * 0.05f; instanceMat.SetFloat("_VoxelLight", voxelLight); instanceMat.SetFloat("_FlashDelay", 0); // Set position Rigidbody rb = particlePool [ppeIndex].rigidBody; if (destroyed) { Vector3 expelDir = Random.insideUnitSphere; Vector3 pos = hitInfo.voxelCenter + expelDir; particleRenderer.transform.position = pos; rb.AddForce(expelDir * (Random.value * 125f)); } else { Vector3 pos = hitInfo.point; Vector3 v1 = new Vector3(-hitInfo.normal.y, hitInfo.normal.z, hitInfo.normal.x); Vector3 v2 = new Vector3(-hitInfo.normal.z, hitInfo.normal.x, hitInfo.normal.y); Vector3 dx = (Random.value - 0.5f) * 0.7f * v1; Vector3 dy = (Random.value - 0.5f) * 0.7f * v2; particleRenderer.transform.position = pos + hitInfo.normal * 0.001f + dx + dy; rb.AddForce(cameraMain.transform.forward * (Random.value * -125f)); } rb.AddForce(Misc.vector3up * 25f); rb.AddTorque(Random.onUnitSphere * 100f); rb.useGravity = true; // Self-destruct particlePool [ppeIndex].destructionTime = Time.time + 2.5f + Random.value; } return(damage); }
/// <summary> /// Converts a voxel into dynamic type /// </summary> /// <param name="chunk">Chunk.</param> /// <param name="voxelIndex">Voxel index.</param> GameObject VoxelSetDynamic(VoxelChunk chunk, int voxelIndex, bool addRigidbody, float duration) { if (chunk == null || chunk.voxels [voxelIndex].hasContent == 0) { return(null); } VoxelDefinition vd = voxelDefinitions [chunk.voxels [voxelIndex].typeIndex]; if (!vd.renderType.supportsDynamic()) { return(null); } VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, voxelIndex, true); if (placeholder == null) { return(null); } // Add rigid body if (addRigidbody) { Rigidbody rb = placeholder.GetComponent <Rigidbody> (); if (rb == null) { placeholder.rb = placeholder.gameObject.AddComponent <Rigidbody> (); } } // If it's a custom model ignore it as it's already a gameobject if (placeholder.modelMeshFilter != null) { return(placeholder.gameObject); } VoxelDefinition vdDyn = vd.dynamicDefinition; if (vdDyn == null) { // Setup and save voxel definition vd.dynamicDefinition = vdDyn = ScriptableObject.CreateInstance <VoxelDefinition> (); vdDyn.isDynamic = true; vdDyn.doNotSave = true; vdDyn.staticDefinition = vd; vdDyn.renderType = RenderType.Custom; vdDyn.textureIndexBottom = vd.textureIndexBottom; vdDyn.textureIndexSide = vd.textureIndexSide; vdDyn.textureIndexTop = vd.textureIndexTop; vdDyn.textureThumbnailTop = vd.textureThumbnailTop; vdDyn.textureThumbnailSide = vd.textureThumbnailSide; vdDyn.textureThumbnailBottom = vd.textureThumbnailBottom; vdDyn.scale = vd.scale; vdDyn.offset = vd.offset; vdDyn.offsetRandomRange = vd.offsetRandomRange; vdDyn.rotation = vd.rotation; vdDyn.rotationRandomY = vd.rotationRandomY; vdDyn.sampleColor = vd.sampleColor; vdDyn.promotesTo = vd.promotesTo; vdDyn.playerDamageDelay = vd.playerDamageDelay; vdDyn.playerDamage = vd.playerDamage; vdDyn.pickupSound = vd.pickupSound; vdDyn.landingSound = vd.landingSound; vdDyn.jumpSound = vd.jumpSound; vdDyn.impactSound = vd.impactSound; vdDyn.footfalls = vd.footfalls; vdDyn.destructionSound = vd.destructionSound; vdDyn.canBeCollected = vd.canBeCollected; vdDyn.dropItem = GetItemDefinition(ItemCategory.Voxel, vd); vdDyn.buildSound = vd.buildSound; vdDyn.navigatable = true; vdDyn.windAnimation = false; vdDyn.model = MakeDynamicCubeFromVoxel(chunk, voxelIndex); vdDyn.name = vd.name + " (Dynamic)"; AddVoxelTextures(vdDyn); } // Clear any vegetation on top if voxel can be moved (has a rigidbody) to avoid floating grass block if (placeholder.rb != null) { VoxelChunk topChunk; int topIndex; if (GetVoxelIndex(chunk, voxelIndex, 0, 1, 0, out topChunk, out topIndex)) { if (topChunk.voxels [topIndex].hasContent == 1 && voxelDefinitions [topChunk.voxels [topIndex].typeIndex].renderType == RenderType.CutoutCross) { VoxelDestroyFast(topChunk, topIndex); } } } Color32 color = chunk.voxels [voxelIndex].color; chunk.voxels [voxelIndex].Set(vdDyn, color); if (duration > 0) { placeholder.SetCancelDynamic(duration); } // Refresh neighbours RebuildNeighbours(chunk, voxelIndex); return(placeholder.gameObject); }
void RenderModelsInVoxels(VoxelChunk chunk, FastList <ModelInVoxel> mivs) { instancingManager.ClearChunk(chunk); Quaternion rotation = Misc.quaternionZero; Vector3 position; for (int k = 0; k < mivs.count; k++) { ModelInVoxel miv = mivs.values [k]; VoxelDefinition voxelDefinition = miv.vd; bool createGO = voxelDefinition.createGameObject || !voxelDefinition.gpuInstancing; if (createGO) { VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, miv.voxelIndex, true); bool createModel = true; if (placeholder.modelInstance != null) { if (placeholder.modelTemplate != voxelDefinition.model) { DestroyImmediate(placeholder.modelInstance); } else { createModel = false; } } MeshFilter mf; Mesh mesh = null; if (createModel || placeholder.modelMeshFilter == null || placeholder.modelMeshFilter.sharedMesh == null || placeholder.modelMeshRenderer == null) { if (voxelDefinition.model == null) { continue; } placeholder.modelTemplate = voxelDefinition.model; placeholder.modelInstance = Instantiate(voxelDefinition.model); placeholder.modelInstance.name = "DynamicVoxelInstance"; // Note: placeHolder.modelInstance layer must be different from layerVoxels to allow dynamic voxels collide with terrain. So don't set its layer to layer voxels placeholder.modelMeshRenderer = placeholder.modelInstance.GetComponent <MeshRenderer> (); if (voxelDefinition.gpuInstancing) { if (placeholder.modelMeshRenderer != null) { placeholder.modelMeshRenderer.enabled = false; } } else { mf = placeholder.modelMeshFilter = placeholder.modelInstance.GetComponent <MeshFilter> (); if (mf != null) { mesh = mf.sharedMesh = Instantiate <Mesh> (mf.sharedMesh); mesh.hideFlags = HideFlags.DontSave; } } } else { mf = placeholder.modelMeshFilter; if (mf != null) { mesh = mf.sharedMesh; } } // Parent model to the placeholder Transform tModel = placeholder.modelInstance.transform; tModel.SetParent(placeholder.transform, false); tModel.transform.localPosition = Misc.vector3zero; tModel.transform.localScale = voxelDefinition.scale; if (voxelDefinition.gpuInstancing) { rotation = placeholder.transform.localRotation; } else { // Adjust lighting if (effectiveGlobalIllumination || chunk.voxels [miv.voxelIndex].isColored) { // Update mesh colors float voxelLight = chunk.voxels [miv.voxelIndex].lightMesh / 15f; Color32 color = chunk.voxels [miv.voxelIndex].color; color.r = (byte)(color.r * voxelLight); color.g = (byte)(color.g * voxelLight); color.b = (byte)(color.b * voxelLight); modelMeshColors.Clear(); for (int c = 0; c < mesh.vertexCount; c++) { modelMeshColors.Add(color); } mesh.SetColors(modelMeshColors); mesh.UploadMeshData(false); } } if (!tModel.gameObject.activeSelf) { tModel.gameObject.SetActive(true); } position = placeholder.transform.position; } else { // pure gpu instancing, no gameobject position = GetVoxelPosition(chunk, miv.voxelIndex); rotation = voxelDefinition.GetRotation(position); // deterministic rotation // User rotation float rot = chunk.voxels [miv.voxelIndex].GetTextureRotationDegrees(); if (rot != 0) { rotation *= Quaternion.Euler(0, rot, 0); } // Custom position position = position + rotation * voxelDefinition.GetOffset(position); } if (voxelDefinition.gpuInstancing) { instancingManager.AddVoxel(chunk, miv.voxelIndex, position, rotation, voxelDefinition.scale); } } }
void RenderModelsInVoxels(VoxelChunk chunk, FastList <ModelInVoxel> mivs) { instancedRenderer.ClearChunk(chunk); // deactivate all models in this chunk // we need to iterate the placeholders list entirely to address the case when the voxel is not using GPU instancing. In this case the gameobject renderer needs to be disabled // and we need to do this way because mivs won't contain the custom voxel since it may be termporarily converted to a transparent voxels due to see-through effect if (chunk.placeholders != null) { int count = chunk.placeholders.Count; for (int k = 0; k < count; k++) { if (chunk.placeholders.entries [k].key >= 0) { VoxelPlaceholder placeHolder = chunk.placeholders.entries [k].value; if (placeHolder != null) { placeHolder.ToggleRenderers(false); } } } } Quaternion rotation = Misc.quaternionZero; Vector3 position; for (int k = 0; k < mivs.count; k++) { ModelInVoxel miv = mivs.values [k]; VoxelDefinition voxelDefinition = miv.vd; bool createGO = voxelDefinition.createGameObject || !voxelDefinition.gpuInstancing; if (VoxelIsHidden(chunk, miv.voxelIndex)) { continue; } if (createGO) { VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, miv.voxelIndex, true); bool createModel = true; if (placeholder.modelInstance != null) { if (placeholder.modelTemplate != voxelDefinition.prefab) { DestroyImmediate(placeholder.modelInstance); placeholder.originalMeshColors32 = null; placeholder.lastMivTintColor = Misc.color32White; } else { createModel = false; } } if (createModel || placeholder.modelInstance == null) { if (voxelDefinition.prefab == null) { continue; } placeholder.modelTemplate = voxelDefinition.prefab; placeholder.modelInstance = Instantiate(voxelDefinition.prefab); placeholder.modelInstance.name = "DynamicVoxelInstance"; // Note: placeHolder.modelInstance layer must be different from layerVoxels to allow dynamic voxels collide with terrain. So don't set its layer to layer voxels placeholder.modelMeshRenderers = placeholder.modelInstance.GetComponentsInChildren <MeshRenderer> (); if (voxelDefinition.gpuInstancing) { placeholder.ToggleRenderers(false); } else { placeholder.modelMeshFilter = placeholder.modelInstance.GetComponentInChildren <MeshFilter> (true); } // Parent model to the placeholder Transform tModel = placeholder.modelInstance.transform; tModel.SetParent(placeholder.transform, false); tModel.transform.localPosition = Misc.vector3zero; tModel.transform.localScale = voxelDefinition.scale; } else { placeholder.ToggleRenderers(true); } if (voxelDefinition.gpuInstancing) { rotation = placeholder.transform.localRotation; } else { // Adjust lighting if (effectiveGlobalIllumination || chunk.voxels [miv.voxelIndex].isColored) { // Update mesh colors MeshFilter mf = placeholder.modelMeshFilter; if (mf != null) { Mesh mesh = mf.sharedMesh; if (mesh != null) { Color32 tintColor = chunk.voxels [miv.voxelIndex].color; tintColor.a = (byte)(chunk.voxels[miv.voxelIndex].lightMesh + (chunk.voxels[miv.voxelIndex].torchLight << 4)); if (placeholder.lastMivTintColor.a != tintColor.a || placeholder.lastMivTintColor.r != tintColor.r || placeholder.lastMivTintColor.g != tintColor.g || placeholder.lastMivTintColor.b != tintColor.b) { mf.sharedMesh = BakeMeshLighting(mf, tintColor); placeholder.lastMivTintColor = tintColor; } } } } } if (!placeholder.modelInstance.gameObject.activeSelf) { placeholder.modelInstance.gameObject.SetActive(true); } position = placeholder.transform.position; } else { // pure gpu instancing, no gameobject position = GetVoxelPosition(chunk, miv.voxelIndex); rotation = voxelDefinition.GetRotation(position); // deterministic rotation // User rotation float rot = chunk.voxels [miv.voxelIndex].GetTextureRotationDegrees(); if (rot != 0) { rotation *= Quaternion.Euler(0, rot, 0); } // Custom position position += rotation * voxelDefinition.GetOffset(position); } if (voxelDefinition.gpuInstancing) { instancedRenderer.AddVoxel(chunk, miv.voxelIndex, position, rotation, voxelDefinition.scale); } } }