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); } }
protected virtual void DoHit(int damage) { if (crosshairHitInfo.voxelIndex < 0) { return; } lastHitButtonPressed = Time.time; seekTarget = crosshairHitInfo; // make character approach target if needed seeking = !TargetIsReachable(); if (seeking) { seekAction = SeekAction.Hit; } else { StartCoroutine(CompleteHit(damage)); } }
/// <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 LoadSaveBinaryFileFormat_4(BinaryReader br, bool preservePlayerPosition = false) { // Character controller transform position Vector3 v = DecodeVector3Binary(br); if (!preservePlayerPosition) { characterController.transform.position = v; } // Character controller transform rotation Vector3 angles = DecodeVector3Binary(br); if (!preservePlayerPosition) { characterController.transform.rotation = Quaternion.Euler(angles); } // Character controller's camera local rotation angles = DecodeVector3Binary(br); if (!preservePlayerPosition) { cameraMain.transform.localRotation = Quaternion.Euler(angles); // Pass initial rotation to mouseLook script characterController.GetComponent <VoxelPlayFirstPersonController> ().mouseLook.Init(characterController.transform, cameraMain.transform, null); } // Read voxel definition table InitSaveGameStructs(); int vdCount = br.ReadInt16(); for (int k = 0; k < vdCount; k++) { saveVoxelDefinitionsList.Add(br.ReadString()); } int numChunks = br.ReadInt32(); VoxelDefinition voxelDefinition = defaultVoxel; int prevVdIndex = -1; Color32 voxelColor = Misc.color32White; for (int c = 0; c < numChunks; c++) { // Read chunks // Get chunk position Vector3 chunkPosition = DecodeVector3Binary(br); VoxelChunk chunk = GetChunkUnpopulated(chunkPosition); byte isAboveSurface = br.ReadByte(); chunk.isAboveSurface = isAboveSurface == 1; chunk.back = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null; chunk.allowTrees = false; chunk.modified = true; chunk.isPopulated = true; chunk.voxelSignature = chunk.lightmapSignature = -1; chunk.renderState = ChunkRenderState.Pending; SetChunkOctreeIsDirty(chunkPosition, false); ChunkClearFast(chunk); // Read voxels int numWords = br.ReadInt16(); for (int k = 0; k < numWords; k++) { // Voxel definition int vdIndex = br.ReadInt16(); if (prevVdIndex != vdIndex) { if (vdIndex >= 0 && vdIndex < vdCount) { voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]); prevVdIndex = vdIndex; } } // RGB voxelColor.r = br.ReadByte(); voxelColor.g = br.ReadByte(); voxelColor.b = br.ReadByte(); // Voxel index int voxelIndex = br.ReadInt16(); // Repetitions int repetitions = br.ReadInt16(); if (voxelDefinition == null) { continue; } // Water level (only for transparent) byte waterLevel = 15; if (voxelDefinition.renderType == RenderType.Water) { waterLevel = br.ReadByte(); } if (voxelDefinition.renderType == RenderType.Custom) { byte hasCustomRotation = br.ReadByte(); if (hasCustomRotation == 1) { Vector3 voxelAngles = DecodeVector3Binary(br); saveVoxelCustomRotations.Add(GetVoxelPosition(chunkPosition, voxelIndex), voxelAngles); } } for (int i = 0; i < repetitions; i++) { chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor); if (voxelDefinition.renderType == RenderType.Water) { chunk.voxels [voxelIndex + i].SetWaterLevel(waterLevel); } } } // Read light sources int lightCount = br.ReadInt16(); VoxelHitInfo hitInfo = new VoxelHitInfo(); for (int k = 0; k < lightCount; k++) { // Voxel index hitInfo.voxelIndex = br.ReadInt16(); // Voxel center hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex); // Normal hitInfo.normal = DecodeVector3Binary(br); hitInfo.chunk = chunk; TorchAttach(hitInfo); } } }
void LoadGameBinaryFileFormat_10(BinaryReader br, bool preservePlayerPosition = false) { // Character controller transform position & rotation Vector3 pos = DecodeVector3Binary(br); Vector3 characterRotationAngles = DecodeVector3Binary(br); Vector3 cameraLocalRotationAngles = DecodeVector3Binary(br); if (!preservePlayerPosition) { if (characterController != null) { characterController.transform.position = pos; characterController.transform.rotation = Quaternion.Euler(characterRotationAngles); cameraMain.transform.localRotation = Quaternion.Euler(cameraLocalRotationAngles); characterController.UpdateLook(); } } InitSaveGameStructs(); // Read voxel definition table int vdCount = br.ReadInt16(); for (int k = 0; k < vdCount; k++) { saveVoxelDefinitionsList.Add(br.ReadString()); } // Read item definition table int idCount = br.ReadInt16(); for (int k = 0; k < idCount; k++) { saveItemDefinitionsList.Add(br.ReadString()); } int numChunks = br.ReadInt32(); VoxelDefinition voxelDefinition = defaultVoxel; int prevVdIndex = -1; Color32 voxelColor = Misc.color32White; for (int c = 0; c < numChunks; c++) { // Read chunks // Get chunk position Vector3 chunkPosition = DecodeVector3Binary(br); VoxelChunk chunk = GetChunkUnpopulated(chunkPosition); byte isAboveSurface = br.ReadByte(); chunk.isAboveSurface = isAboveSurface == 1; chunk.back = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null; chunk.allowTrees = false; chunk.modified = true; chunk.isPopulated = true; chunk.voxelSignature = chunk.lightmapSignature = -1; chunk.renderState = ChunkRenderState.Pending; SetChunkOctreeIsDirty(chunkPosition, false); ChunkClearFast(chunk); // Read voxels int numWords = br.ReadInt16(); for (int k = 0; k < numWords; k++) { // Voxel definition int vdIndex = br.ReadInt16(); if (prevVdIndex != vdIndex) { if (vdIndex >= 0 && vdIndex < vdCount) { voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]); prevVdIndex = vdIndex; } } // RGB voxelColor.r = br.ReadByte(); voxelColor.g = br.ReadByte(); voxelColor.b = br.ReadByte(); // Voxel index int voxelIndex = br.ReadInt16(); // Repetitions int repetitions = br.ReadInt16(); byte flags = br.ReadByte(); byte hasCustomRotation = br.ReadByte(); if (voxelDefinition == null) { continue; } // Custom voxel flags if (voxelDefinition.renderType == RenderType.Custom) { flags &= 0xF; // custom voxels do not store texture rotation; their transform has the final rotation if (hasCustomRotation == 1) { Vector3 voxelAngles = DecodeVector3Binary(br); delayedVoxelCustomRotations.Add(GetVoxelPosition(chunkPosition, voxelIndex), voxelAngles); } } for (int i = 0; i < repetitions; i++) { chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor); if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation()) { chunk.voxels [voxelIndex + i].SetFlags(flags); } if (voxelDefinition.lightIntensity > 0) { chunk.AddLightSource(voxelIndex + i, voxelDefinition.lightIntensity); } } } // Read light sources int lightCount = br.ReadInt16(); VoxelHitInfo hitInfo = new VoxelHitInfo(); for (int k = 0; k < lightCount; k++) { // Voxel index hitInfo.voxelIndex = br.ReadInt16(); // Voxel center hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex); // Normal hitInfo.normal = DecodeVector3Binary(br); hitInfo.chunk = chunk; // Item definition int itemIndex = br.ReadInt16(); if (itemIndex < 0 || itemIndex >= idCount) { continue; } string itemDefinitionName = saveItemDefinitionsList [itemIndex]; ItemDefinition itemDefinition = GetItemDefinition(itemDefinitionName); TorchAttach(hitInfo, itemDefinition); } // Read items int itemCount = br.ReadInt16(); for (int k = 0; k < itemCount; k++) { // Voxel index int itemIndex = br.ReadInt16(); if (itemIndex < 0 || itemIndex >= idCount) { continue; } string itemDefinitionName = saveItemDefinitionsList [itemIndex]; Vector3 itemPosition = DecodeVector3Binary(br); int quantity = br.ReadInt16(); ItemSpawn(itemDefinitionName, itemPosition, quantity); } } // Destroy any object with VoxelPlaySaveThis component to avoid repetitions VoxelPlaySaveThis [] gos = FindObjectsOfType <VoxelPlaySaveThis> (); for (int k = 0; k < gos.Length; k++) { DestroyImmediate(gos [k].gameObject); } // Load gameobjects int goCount = br.ReadInt16(); Dictionary <string, string> data = new Dictionary <string, string> (); for (int k = 0; k < goCount; k++) { string prefabPath = br.ReadString(); GameObject o = Resources.Load <GameObject> (prefabPath); if (o != null) { o = Instantiate <GameObject> (o) as GameObject; o.name = br.ReadString(); VoxelPlaySaveThis go = o.GetComponent <VoxelPlaySaveThis> (); if (go == null) { DestroyImmediate(o); continue; } o.transform.position = DecodeVector3Binary(br); o.transform.eulerAngles = DecodeVector3Binary(br); o.transform.localScale = DecodeVector3Binary(br); data.Clear(); Int16 dataCount = br.ReadInt16(); for (int j = 0; j < dataCount; j++) { string key = br.ReadString(); string value = br.ReadString(); data [key] = value; } go.SendMessage("OnLoadGame", data); } } }
void WriteChunkData_7(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++) { VoxelHitInfo hi = chunk.lightSources [k].hitInfo; bw.Write((Int16)hi.voxelIndex); EncodeVector3Binary(bw, hi.normal); } } // 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++) { Item item = chunk.items.values [k]; if (item == null || item.itemDefinition == null) { continue; } ItemDefinition id = item.itemDefinition; int idIndex; if (saveItemDefinitionsDict.TryGetValue(id, out idIndex)) { bw.Write((Int16)idIndex); EncodeVector3Binary(bw, item.transform.position); bw.Write((Int16)item.quantity); } } } }
void LoadGameBinaryFileFormat_7(BinaryReader br, bool preservePlayerPosition = false) { // Character controller transform position & rotation Vector3 pos = DecodeVector3Binary(br); Vector3 characterRotationAngles = DecodeVector3Binary(br); Vector3 cameraLocalRotationAngles = DecodeVector3Binary(br); if (!preservePlayerPosition) { if (characterController != null) { characterController.transform.position = pos; characterController.transform.rotation = Quaternion.Euler(characterRotationAngles); cameraMain.transform.localRotation = Quaternion.Euler(cameraLocalRotationAngles); characterController.UpdateLook(); } } InitSaveGameStructs(); // Read voxel definition table int vdCount = br.ReadInt16(); for (int k = 0; k < vdCount; k++) { saveVoxelDefinitionsList.Add(br.ReadString()); } // Read item definition table int idCount = br.ReadInt16(); for (int k = 0; k < idCount; k++) { saveItemDefinitionsList.Add(br.ReadString()); } int numChunks = br.ReadInt32(); VoxelDefinition voxelDefinition = defaultVoxel; int prevVdIndex = -1; Color32 voxelColor = Misc.color32White; for (int c = 0; c < numChunks; c++) { // Read chunks // Get chunk position Vector3 chunkPosition = DecodeVector3Binary(br); VoxelChunk chunk = GetChunkUnpopulated(chunkPosition); byte isAboveSurface = br.ReadByte(); chunk.isAboveSurface = isAboveSurface == 1; chunk.back = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null; chunk.allowTrees = false; chunk.modified = true; chunk.isPopulated = true; chunk.voxelSignature = chunk.lightmapSignature = -1; chunk.renderState = ChunkRenderState.Pending; SetChunkOctreeIsDirty(chunkPosition, false); ChunkClearFast(chunk); // Read voxels int numWords = br.ReadInt16(); for (int k = 0; k < numWords; k++) { // Voxel definition int vdIndex = br.ReadInt16(); if (prevVdIndex != vdIndex) { if (vdIndex >= 0 && vdIndex < vdCount) { voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]); prevVdIndex = vdIndex; } } // RGB voxelColor.r = br.ReadByte(); voxelColor.g = br.ReadByte(); voxelColor.b = br.ReadByte(); // Voxel index int voxelIndex = br.ReadInt16(); // Repetitions int repetitions = br.ReadInt16(); byte flags = br.ReadByte(); byte hasCustomRotation = br.ReadByte(); if (voxelDefinition == null) { continue; } // Custom voxel flags if (voxelDefinition.renderType == RenderType.Custom) { if (hasCustomRotation == 1) { Vector3 voxelAngles = DecodeVector3Binary(br); delayedVoxelCustomRotations.Add(GetVoxelPosition(chunkPosition, voxelIndex), voxelAngles); } } for (int i = 0; i < repetitions; i++) { chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor); if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation()) { chunk.voxels [voxelIndex + i].SetFlags(flags); } } } // Read light sources int lightCount = br.ReadInt16(); VoxelHitInfo hitInfo = new VoxelHitInfo(); for (int k = 0; k < lightCount; k++) { // Voxel index hitInfo.voxelIndex = br.ReadInt16(); // Voxel center hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex); // Normal hitInfo.normal = DecodeVector3Binary(br); hitInfo.chunk = chunk; TorchAttach(hitInfo); } // Read items int itemCount = br.ReadInt16(); for (int k = 0; k < itemCount; k++) { // Voxel index int itemIndex = br.ReadInt16(); if (itemIndex < 0 || itemIndex >= idCount) { continue; } string itemDefinitionName = saveItemDefinitionsList [itemIndex]; Vector3 itemPosition = DecodeVector3Binary(br); int quantity = br.ReadInt16(); ItemSpawn(itemDefinitionName, itemPosition, quantity); } } }
/// <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); }
bool HitVoxelFast(Vector3 origin, Vector3 direction, int damage, out VoxelHitInfo hitInfo, float maxDistance = 0, int damageRadius = 1, bool addParticles = true, bool playSound = true, bool allowDamageEvent = true) { RayCastFast(origin, direction, out hitInfo, maxDistance, false, 0, ColliderTypes.IgnorePlayer); VoxelChunk chunk = hitInfo.chunk; if (chunk == null || hitInfo.voxelIndex < 0) { lastHitInfo.chunk = null; lastHitInfo.voxelIndex = -1; return(false); } lastHitInfo = hitInfo; DamageVoxelFast(ref hitInfo, damage, addParticles, playSound, allowDamageEvent); bool button1Pressed = input.GetButton(InputButtonNames.Button1); bool button2Pressed = input.GetButton(InputButtonNames.Button2); if ((button1Pressed || button2Pressed) && OnVoxelClick != null) { OnVoxelClick(chunk, hitInfo.voxelIndex, button1Pressed ? 0 : 1, hitInfo); } if (damageRadius > 1) { Vector3 otherPos; Vector3 explosionPosition = hitInfo.voxelCenter + hitInfo.normal * damageRadius; damageRadius--; for (int y = -damageRadius; y <= damageRadius; y++) { otherPos.y = lastHitInfo.voxelCenter.y + y; for (int z = -damageRadius; z <= damageRadius; z++) { otherPos.z = lastHitInfo.voxelCenter.z + z; for (int x = -damageRadius; x <= damageRadius; x++) { if (x == 0 && z == 0 && y == 0) { continue; } VoxelChunk otherChunk; int otherIndex; otherPos.x = lastHitInfo.voxelCenter.x + x; if (GetVoxelIndex(otherPos, out otherChunk, out otherIndex, false)) { if (GetVoxelVisibility(otherChunk, otherIndex)) { FastVector.NormalizedDirection(ref explosionPosition, ref otherPos, ref direction); if (RayCast(explosionPosition, direction, out hitInfo)) { DamageVoxelFast(ref hitInfo, damage, addParticles, playSound, allowDamageEvent); } } } } } } } return(true); }
VoxelChunk RayCastFastVoxel(Vector3 origin, Vector3 direction, out VoxelHitInfo hitInfo, float maxDistance = 0, bool createChunksIfNeeded = false, byte minOpaque = 0) { #if DEBUG_RAYCAST GameObject o; #endif float maxDistanceSqr = maxDistance == 0 ? 1000 * 1000 : maxDistance * maxDistance; // Ray march throuch chunks until hit one loaded chunk Vector3 position = origin; VoxelChunk chunk = null; hitInfo = new VoxelHitInfo(); hitInfo.voxelIndex = -1; Vector3 viewDirSign = new Vector3(Mathf.Sign(direction.x), Mathf.Sign(direction.y), Mathf.Sign(direction.z)); Vector3 viewSign = (viewDirSign + Misc.vector3one) * 0.5f; // 0 = left, 1 = right float vxz, vzy, vxy; if (direction.y != 0) { float a = direction.x / direction.y; float b = direction.z / direction.y; vxz = Mathf.Sqrt(1f + a * a + b * b); } else { vxz = 1000000f; } if (direction.x != 0) { float a = direction.z / direction.x; float b = direction.y / direction.x; vzy = Mathf.Sqrt(1f + a * a + b * b); } else { vzy = 1000000f; } if (direction.z != 0) { float a = direction.x / direction.z; float b = direction.y / direction.z; vxy = Mathf.Sqrt(1f + a * a + b * b); } else { vxy = 1000000f; } Vector3 v3 = new Vector3(vzy, vxz, vxy); Vector3 viewSign16 = viewSign * 16f; Vector3 viewDirSignOffset = viewDirSign * 0.002f; int chunkX, chunkY, chunkZ; int chunkCount = 0; float t; Vector3 normal = Misc.vector3zero, db; // bool notFirstVoxel = false; while (chunkCount++ < 500) // safety counter to avoid any potential infinite loop // Check max distance { float distSqr = (position.x - origin.x) * (position.x - origin.x) + (position.y - origin.y) * (position.y - origin.y) + (position.z - origin.z) * (position.z - origin.z); if (distSqr > maxDistanceSqr) { return(null); } #if DEBUG_RAYCAST o = GameObject.CreatePrimitive(PrimitiveType.Cube); o.transform.localScale = Misc.Vector3one * 0.15f; o.transform.position = position; DestroyImmediate(o.GetComponent <Collider>()); o.GetComponent <Renderer>().material.color = Color.blue; #endif chunkX = FastMath.FloorToInt(position.x / 16f); chunkY = FastMath.FloorToInt(position.y / 16f); chunkZ = FastMath.FloorToInt(position.z / 16f); chunk = null; if (createChunksIfNeeded) { chunk = GetChunkOrCreate(chunkX, chunkY, chunkZ); } else { int x00 = WORLD_SIZE_DEPTH * WORLD_SIZE_HEIGHT * (chunkX + WORLD_SIZE_WIDTH); int y00 = WORLD_SIZE_DEPTH * (chunkY + WORLD_SIZE_HEIGHT); int hash = x00 + y00 + chunkZ; chunk = GetChunkIfExists(hash); } chunkX *= 16; chunkY *= 16; chunkZ *= 16; if (chunk) { // Ray-march through chunk Voxel[] voxels = chunk.voxels; Vector3 inPosition = position; for (int k = 0; k < 64; k++) { #if DEBUG_RAYCAST o = GameObject.CreatePrimitive(PrimitiveType.Sphere); o.transform.localScale = Misc.Vector3one * 0.1f; o.transform.position = inPosition; DestroyImmediate(o.GetComponent <Collider>()); o.GetComponent <Renderer>().material.color = Color.yellow; #endif // Check voxel content int fy = FastMath.FloorToInt(inPosition.y); int py = fy - chunkY; int fz = FastMath.FloorToInt(inPosition.z); int pz = fz - chunkZ; int fx = FastMath.FloorToInt(inPosition.x); int px = fx - chunkX; if (px < 0 || px > 15 || py < 0 || py > 15 || pz < 0 || pz > 15) { break; } int voxelIndex = py * ONE_Y_ROW + pz * ONE_Z_ROW + px; if (voxels [voxelIndex].hasContent == 1 && !voxels [voxelIndex].type.ignoresRayCast && (minOpaque == 255 || voxels [voxelIndex].opaque >= minOpaque)) { VoxelDefinition vd = voxelDefinitions [voxels [voxelIndex].typeIndex]; if (vd.renderType != RenderType.Custom || !vd.modelUsesCollider) { // Check max distance distSqr = (inPosition.x - origin.x) * (inPosition.x - origin.x) + (inPosition.y - origin.y) * (inPosition.y - origin.y) + (inPosition.z - origin.z) * (inPosition.z - origin.z); if (distSqr > maxDistanceSqr) { return(null); } // Check water level or grass height float voxelHeight = 0; if (vd.renderType == RenderType.Water) { voxelHeight = voxels [voxelIndex].GetWaterLevel() / 15f; } else if (vd.renderType == RenderType.CutoutCross) { voxelHeight = vd.scale.y; } bool hit = true; Vector3 voxelCenter = new Vector3(chunkX + px + 0.5f, chunkY + py + 0.5f, chunkZ + pz + 0.5f); Vector3 localHitPos = inPosition - voxelCenter; if (voxelHeight > 0 && voxelHeight < 1f && direction.y != 0) { t = localHitPos.y + 0.5f - voxelHeight; if (t > 0) { t = t * Mathf.Sqrt(1 + (direction.x * direction.x + direction.z * direction.z) / (direction.y * direction.y)); localHitPos += t * direction; hit = localHitPos.x >= -0.5f && localHitPos.x <= 0.5f && localHitPos.z >= -0.5f && localHitPos.z <= 0.5f; } } if (hit) { hitInfo = new VoxelHitInfo(); hitInfo.chunk = chunk; hitInfo.voxel = voxels [voxelIndex]; hitInfo.point = inPosition - normal; hitInfo.distance = Mathf.Sqrt(distSqr); hitInfo.voxelIndex = voxelIndex; hitInfo.voxelCenter = voxelCenter; if (localHitPos.y >= 0.495) { hitInfo.normal = Misc.vector3up; } else if (localHitPos.y <= -0.495) { hitInfo.normal = Misc.vector3down; } else if (localHitPos.x < -0.495) { hitInfo.normal = Misc.vector3left; } else if (localHitPos.x > 0.495) { hitInfo.normal = Misc.vector3right; } else if (localHitPos.z < -0.495) { hitInfo.normal = Misc.vector3back; } else if (localHitPos.z > 0.495) { hitInfo.normal = Misc.vector3forward; } #if DEBUG_RAYCAST o = GameObject.CreatePrimitive(PrimitiveType.Sphere); o.transform.localScale = Misc.Vector3one * 0.15f; o.transform.position = inPosition; DestroyImmediate(o.GetComponent <Collider>()); o.GetComponent <Renderer>().material.color = Color.red; #endif return(chunk); } } } db.x = (fx + viewSign.x - inPosition.x) * v3.x; db.y = (fy + viewSign.y - inPosition.y) * v3.y; db.z = (fz + viewSign.z - inPosition.z) * v3.z; db.x = db.x < 0 ? -db.x : db.x; db.y = db.y < 0 ? -db.y : db.y; db.z = db.z < 0 ? -db.z : db.z; t = db.x; normal.x = viewDirSignOffset.x; normal.y = 0; normal.z = 0; if (db.y < t) { t = db.y; normal.x = 0; normal.y = viewDirSignOffset.y; } if (db.z < t) { t = db.z; normal.x = 0; normal.y = 0; normal.z = viewDirSignOffset.z; } inPosition.x += direction.x * t + normal.x; inPosition.y += direction.y * t + normal.y; inPosition.z += direction.z * t + normal.z; // notFirstVoxel = true; } } db.x = (chunkX + viewSign16.x - position.x) * v3.x; db.y = (chunkY + viewSign16.y - position.y) * v3.y; db.z = (chunkZ + viewSign16.z - position.z) * v3.z; db.x = db.x < 0 ? -db.x : db.x; db.y = db.y < 0 ? -db.y : db.y; db.z = db.z < 0 ? -db.z : db.z; t = db.x; normal.x = viewDirSignOffset.x; normal.y = 0; normal.z = 0; if (db.y < t) { t = db.y; normal.x = 0; normal.y = viewDirSignOffset.y; } if (db.z < t) { t = db.z; normal.x = 0; normal.y = 0; normal.z = viewDirSignOffset.z; } position.x += direction.x * t + normal.x; position.y += direction.y * t + normal.y; position.z += direction.z * t + normal.z; // notFirstVoxel = true; } return(null); }
void CheckCommonKeys() { bool leftAltPressed = input.GetButton(InputButtonNames.LeftAlt); bool leftShiftPressed = input.GetButton(InputButtonNames.LeftShift); bool leftControlPressed = input.GetButton(InputButtonNames.LeftControl); bool fire1Clicked = false; bool fire2Clicked = false; bool overUI = EventSystem.current.IsPointerOverGameObject(); if (!overUI) { fire1Clicked = input.GetButtonDown(InputButtonNames.Button1); fire2Clicked = input.GetButtonClick(InputButtonNames.Button2); } if (!leftShiftPressed && !leftAltPressed && !leftControlPressed) { if (crosshairOnBlock && input.GetButtonClick(InputButtonNames.Button1)) { env.TriggerVoxelClickEvent(crosshairHitInfo.chunk, crosshairHitInfo.voxelIndex, 0); } if (fire1Clicked) { firePressed = true; if (ModelPreviewCancel()) { firePressed = false; lastHitButtonPressed = Time.time + 0.5f; } if (firePressed && Time.time - lastHitButtonPressed > player.GetHitDelay()) { timeSeeking = Time.time; lastReachDistance = float.MaxValue; if (crosshairHitInfo.item != null) { crosshairHitInfo.item.PickItem(); crosshairOnBlock = false; firePressed = false; } else { DoHit(player.GetHitDamage()); } } } if (fire2Clicked) { timeSeeking = Time.time; lastReachDistance = float.MaxValue; seekTarget = crosshairHitInfo; // make character approach target if needed seeking = !TargetIsReachable(true); if (seeking) { seekAction = SeekAction.Move; } else { DoBuild(curPos, transform.forward, crosshairHitInfo.voxelCenter); } } } if (input.GetButtonDown(InputButtonNames.Build)) { env.SetBuildMode(!env.buildMode); if (env.buildMode) { env.ShowMessage("<color=green>Entered <color=yellow>Build Mode</color>. Press <color=white>B</color> to cancel.</color>"); } else { env.ShowMessage("<color=green>Back to <color=yellow>Normal Mode</color>.</color>"); } } else if (input.GetButtonDown(InputButtonNames.SeeThroughUp)) { env.seeThroughHeightOffset++; } else if (input.GetButtonDown(InputButtonNames.SeeThroughDown)) { env.seeThroughHeightOffset--; } }
void LoadSaveFileFormat_1_0(StringReader sr, bool preservePlayerPosition = false) { // Character controller transform position Vector3 v = DecodeVector3(sr.ReadLine()); characterController.transform.position = v; // Character controller transform rotation Vector3 angles = DecodeVector3(sr.ReadLine()); if (!preservePlayerPosition) { characterController.transform.rotation = Quaternion.Euler(angles); } // Character controller's camera local rotation angles = DecodeVector3(sr.ReadLine()); if (!preservePlayerPosition) { cameraMain.transform.localRotation = Quaternion.Euler(angles); // Pass initial rotation to mouseLook script characterController.GetComponent <VoxelPlayFirstPersonController>().mouseLook.Init(characterController.transform, cameraMain.transform, null); } // Read voxel definition table InitSaveGameStructs(); int vdCount = int.Parse(sr.ReadLine()); for (int k = 0; k < vdCount; k++) { saveVoxelDefinitionsList.Add(sr.ReadLine()); } // Read chunks int chunkX, chunkY, chunkZ; while (true) { string line = sr.ReadLine(); if (line == null) { return; } // Get chunk position Vector3 chunkPosition = DecodeVector3(line); GetChunkCoordinates(chunkPosition, out chunkX, out chunkY, out chunkZ); VoxelChunk chunk = GetChunkOrCreate(chunkX, chunkY, chunkZ); chunk.modified = true; ChunkClearFast(chunk); line = sr.ReadLine(); if (line == null) { return; } // Read voxels int numWords = int.Parse(line, CultureInfo.InvariantCulture); for (int k = 0; k < numWords; k++) { line = sr.ReadLine(); if (line == null) { return; } string[] wordData = line.Split(LOAD_DATA_SEPARATOR); if (wordData.Length != 3) { continue; } // Voxel definition int vdIndex = int.Parse(wordData[0]); VoxelDefinition voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList[vdIndex]); if (voxelDefinition == null) { voxelDefinition = defaultVoxel; // should not happen } // Voxel index int voxelIndex = int.Parse(wordData[1]); // Repetitions int repetitions = int.Parse(wordData[2]); for (int i = 0; i < repetitions; i++) { chunk.voxels[voxelIndex + i].Set(voxelDefinition, Misc.color32White); } ChunkRequestRefresh(chunk, true, true); } // Read light sources line = sr.ReadLine(); if (line == null) { return; } int lightCount = int.Parse(line); VoxelHitInfo hitInfo = new VoxelHitInfo(); for (int k = 0; k < lightCount; k++) { // Voxel index line = sr.ReadLine(); if (line == null) { return; } hitInfo.voxelIndex = int.Parse(line); // Voxel center line = sr.ReadLine(); if (line == null) { return; } hitInfo.voxelCenter = DecodeVector3(line); // Normal line = sr.ReadLine(); if (line == null) { return; } hitInfo.normal = DecodeVector3(line); hitInfo.chunk = chunk; TorchAttach(hitInfo); } } }
void WriteChunkData1_2(TextWriter sw, VoxelChunk chunk) { // Chunk position sw.WriteLine(chunk.position.x + "," + chunk.position.y + "," + chunk.position.z); if (sb == null) { sb = new StringBuilder(); } else { sb.Length = 0; } int k = 0; int numWords = 0; // Compute voxels while (k < chunk.voxels.Length) { if (chunk.voxels[k].hasContent == 1) { int voxelIndex = k; VoxelDefinition voxelDefinition = chunk.voxels[k].type; int voxelDefinitionIndex; if (!saveVoxelDefinitionsDict.TryGetValue(voxelDefinition, out voxelDefinitionIndex)) { continue; } Color32 tintColor = chunk.voxels[k].color; int waterLevel = 15; if (voxelDefinition.renderType == RenderType.Water) { waterLevel = chunk.voxels [k].GetWaterLevel(); } 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 && chunk.voxels[k].GetWaterLevel() == waterLevel) { repetitions++; k++; } if (numWords > 0) { sb.AppendLine(); } sb.Append(voxelDefinitionIndex); sb.Append(","); sb.Append(tintColor.r); sb.Append(","); sb.Append(tintColor.g); sb.Append(","); sb.Append(tintColor.b); sb.Append(","); sb.Append(voxelIndex); sb.Append(","); sb.Append(repetitions); if (voxelDefinition.renderType == RenderType.Water) { sb.Append(","); sb.Append(waterLevel); } numWords++; } else { k++; } } // Write number of voxels sw.WriteLine(numWords); if (numWords > 0) { // Write voxels data sw.WriteLine(sb.ToString()); } // Write number of light sources int lightCount = chunk.lightSources != null ? chunk.lightSources.Count : 0; sb.Length = 0; sb.Append(lightCount.ToString()); if (lightCount > 0) { for (k = 0; k < lightCount; k++) { VoxelHitInfo hi = chunk.lightSources[k].hitInfo; sb.AppendLine(); sb.AppendLine(hi.voxelIndex.ToString()); sb.Append(hi.voxelCenter.x); sb.Append(","); sb.Append(hi.voxelCenter.y); sb.Append(","); sb.AppendLine(hi.voxelCenter.z.ToString()); sb.Append(hi.normal.x); sb.Append(","); sb.Append(hi.normal.y); sb.Append(","); sb.Append(hi.normal.z); } } sw.WriteLine(sb.ToString()); }
void ModelPlaceTorches(Vector3 position, ModelDefinition model, int rotationDegrees = 0) { if (model == null || model.torches == null) { return; } FastVector.Floor(ref position); Vector3 pos; int modelOneYRow = model.sizeZ * model.sizeX; int modelOneZRow = model.sizeX; int halfSizeX = model.sizeX / 2; int halfSizeZ = model.sizeZ / 2; if (rotationDegrees == 360) { switch (UnityEngine.Random.Range(0, 4)) { case 0: rotationDegrees = 90; break; case 1: rotationDegrees = 180; break; case 2: rotationDegrees = 270; break; } } // ensure all voxel definitions are present int tmp; for (int b = 0; b < model.torches.Length; b++) { int bitIndex = model.torches [b].voxelIndex; int py = bitIndex / modelOneYRow; int remy = bitIndex - py * modelOneYRow; int pz = remy / modelOneZRow; int px = remy - pz * modelOneZRow; Vector3 normal = model.torches [b].normal; switch (rotationDegrees) { case 90: tmp = px; px = halfSizeZ - pz; pz = halfSizeX - tmp; tmp = (int)normal.x; normal.x = normal.z; normal.z = -tmp; break; case 180: px = halfSizeX - px; pz = halfSizeZ - pz; normal.x = -normal.x; normal.z = -normal.z; break; case 270: tmp = px; px = pz - halfSizeZ; pz = tmp - halfSizeX; tmp = (int)normal.x; normal.x = normal.z; normal.z = tmp; break; default: px -= halfSizeX; pz -= halfSizeZ; break; } pos.x = position.x + model.offsetX + px; pos.y = position.y + model.offsetY + py; pos.z = position.z + model.offsetZ + pz; pos -= normal; VoxelChunk chunk; int voxelIndex; VoxelHitInfo hitInfo = new VoxelHitInfo(); if (GetVoxelIndex(pos, out chunk, out voxelIndex)) { hitInfo.chunk = chunk; hitInfo.voxelIndex = voxelIndex; hitInfo.normal = normal; hitInfo.voxelCenter = pos + Misc.vector3half; TorchAttach(hitInfo, model.torches [b].itemDefinition, false); } } }