public void ManageSeeThrough() { VoxelSetHidden(occludedIndices, occludedIndicesCount, false); Camera cam = currentCamera; if (cam == null) { return; } Vector3 camPos = cam.transform.position; if (seeThroughTarget == null) { if (characterController != null) { seeThroughTarget = characterController.gameObject; } if (seeThroughTarget == null) { return; } } Vector3 targetPos = seeThroughTarget.transform.position; if (occludedIndices == null || occludedIndices.Length == 0) { occludedIndices = new VoxelIndex[256]; } if (occludedChunks == null || occludedChunks.Length == 0) { occludedChunks = new VoxelChunk[20]; } int chunkCount = LineCast(targetPos, camPos, occludedChunks); // Add surrounding chunks int flag = Time.frameCount; for (int k = 0; k < chunkCount; k++) { VoxelChunk chunk = occludedChunks [k]; chunk.tempFlag = flag; } int lineChunks = chunkCount; for (int k = 0; k < lineChunks; k++) { VoxelChunk chunk = occludedChunks [k]; VoxelChunk n = chunk.top; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.bottom; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.left; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.right; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.forward; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.back; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } } float distToTarget = Vector3.Distance(targetPos, camPos); Vector3 cylinderAxis = (camPos - targetPos) / distToTarget; float radiusSqr = seeThroughRadius * seeThroughRadius; float minY = targetPos.y + seeThroughHeightOffset; occludedIndicesCount = 0; Vector3 voxelPosition = Misc.vector3zero; for (int k = 0; k < chunkCount; k++) { VoxelChunk chunk = occludedChunks [k]; Vector3 chunkPosBase = chunk.position; chunkPosBase.x = chunkPosBase.x - 8 + 0.5f; chunkPosBase.y = chunkPosBase.y - 8 + 0.5f; chunkPosBase.z = chunkPosBase.z - 8 + 0.5f; for (int voxelIndex = 0; voxelIndex < chunk.voxels.Length; voxelIndex++) { if (chunk.voxels [voxelIndex].hasContent == 1) { int py = voxelIndex >> 8; voxelPosition.y = py + chunkPosBase.y; if (voxelPosition.y < minY) { continue; } int pz = (voxelIndex & 0xF0) >> 4; int px = voxelIndex & 0xF; voxelPosition.x = px + chunkPosBase.x; voxelPosition.z = pz + chunkPosBase.z; // Check if it's inside the cylinder Vector3 v = voxelPosition; v.x -= targetPos.x; v.y -= targetPos.y; v.z -= targetPos.z; float cylinderDist = v.x * cylinderAxis.x + v.y * cylinderAxis.y + v.z * cylinderAxis.z; // Vector3.Dot (v, cylinderAxis); if (cylinderDist < 0 || cylinderDist > distToTarget) { continue; } v.x -= cylinderDist * cylinderAxis.x; v.y -= cylinderDist * cylinderAxis.y; v.z -= cylinderDist * cylinderAxis.z; float orthDistanceSqr = v.x * v.x + v.y * v.y + v.z * v.z; // (v - cylinderDist * cylinderAxis).sqrMagnitude; if (orthDistanceSqr < radiusSqr) { if (occludedIndicesCount >= occludedIndices.Length) { occludedIndices = occludedIndices.Extend(); } occludedIndices [occludedIndicesCount].chunk = chunk; occludedIndices [occludedIndicesCount].voxelIndex = voxelIndex; occludedIndicesCount++; } } } } VoxelSetHidden(occludedIndices, occludedIndicesCount, true); }
void InitClouds() { if (cloudsChunks == null) { cloudsChunks = new List <VoxelChunk> (1000); } else { cloudsChunks.Clear(); } if (!enableClouds || draftModeActive || world.cloudVoxel == null) { return; } Texture2D noise = Resources.Load <Texture2D> ("VoxelPlay/Textures/Noise"); Color32[] noises = noise.GetPixels32(); int tw = noise.width; int tz = noise.height; Vector3 pos; pos.y = CLOUDS_SPECIAL_ALTITUDE; VoxelChunk chunk, lastChunk = null; int voxelIndex; for (int z = 0; z < 1024; z++) { pos.z = z; int trow = ((z / 4 + tz / 2) % tz) * tw; int trow2 = (z / 2 % tz) * tw; for (int x = 0; x < 1024; x++) { pos.x = x; int tindex = trow + ((x / 4 + tw / 2) % tw); int tindex2 = trow2 + (x / 2 % tw); int r1 = noises [tindex].r; int r2 = noises [tindex2].r; int r = (r1 + r2) / 2; if (r < world.cloudCoverage) { Vector3 dpos = pos; dpos.x -= 512; dpos.z -= 512; VoxelSingleSet(dpos, world.cloudVoxel, out chunk, out voxelIndex, Misc.color32White); if (chunk != null && chunk != lastChunk) { chunk.cannotBeReused = true; lastChunk = chunk; if (!cloudsChunks.Contains(chunk)) { cloudsChunks.Add(chunk); } } } if (r < world.cloudCoverage * 19 / 20) { Vector3 dpos = pos; dpos.x -= 512; dpos.z -= 512; dpos.y--; VoxelSingleSet(dpos, world.cloudVoxel, out chunk, out voxelIndex, Misc.color32White); if (chunk != null && chunk != lastChunk) { chunk.cannotBeReused = true; lastChunk = chunk; if (!cloudsChunks.Contains(chunk)) { cloudsChunks.Add(chunk); } } } } } int chunksCount = cloudsChunks.Count; if (chunksCount == 0) { return; } if (cloudsRoot == null) { cloudsRoot = new GameObject(CLOUDS_ROOT_NAME); cloudsRoot.hideFlags = HideFlags.DontSave; cloudsRoot.transform.hierarchyCapacity = 1000; cloudsRoot.transform.SetParent(worldRoot, false); Vector3 initialPosition = cameraMain != null ? cameraMain.transform.position : Misc.vector3zero; cloudsRoot.transform.position = initialPosition; cloudsRoot.AddComponent <VoxelCloudsAnimator> ().cloudChunks = cloudsChunks; } for (int k = 0; k < chunksCount; k++) { if (cloudsChunks [k] == null) { continue; } ChunkRequestRefresh(cloudsChunks [k], false, true); Transform tc = cloudsChunks [k].transform; pos = cloudsChunks [k].position; pos.x += 0.5f; pos.y = world.cloudAltitude + 0.5f; pos.z += 0.5f; // avoid z-fight cloudsChunks [k].position = pos; tc.position = pos; tc.SetParent(cloudsRoot.transform, true); } }
/// <summary> /// Computes light propagation. Only Sun light. Other light sources like torches are handled in the shader itself. /// </summary>e /// <returns><c>true</c>, if lightmap was built, <c>false</c> if no changes or light detected.</returns> /// <param name="chunk">Chunk.</param> void ComputeLightmap(VoxelChunk chunk) { if (!effectiveGlobalIllumination) { return; } Voxel[] voxels = chunk.voxels; if (voxels == null) { return; } bool isAboveSurface = chunk.isAboveSurface; int lightmapSignature = 0; // used to detect lightmap changes that trigger mesh rebuild tempLightmapIndex = -1; // Get top chunk but only if it has been rendered at least once. // means that the top chunk is not available which in the case of surface will switch to the heuristic of heightmap (see else below) VoxelChunk topChunk = chunk.top; bool topChunkIsAccesible = topChunk && topChunk.isPopulated; if (topChunkIsAccesible) { for (int z = 0; z <= 15; z++) { int bottom = z * ONE_Z_ROW; int top = bottom + 15 * ONE_Y_ROW; for (int x = 0; x <= 15; x++, bottom++, top++) { byte light = topChunk.voxels [bottom].light; lightmapSignature += light; if (voxels [top].opaque < FULL_OPAQUE) { if (light > voxels [top].light) { voxels [top].light = light; tempLightmapPos [++tempLightmapIndex] = top; } } } } } else if (isAboveSurface) { for (int z = 0; z <= 15; z++) { int top = 15 * ONE_Y_ROW + z * ONE_Z_ROW; for (int x = 0; x <= 15; x++, top++) { if (voxels [top].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [top].light != light) { voxels [top].light = light; tempLightmapPos [++tempLightmapIndex] = top; } } } } } // Check bottom chunk VoxelChunk bottomChunk = chunk.bottom; bool bottomChunkIsAccesible = bottomChunk && bottomChunk.isPopulated; if (bottomChunkIsAccesible) { for (int z = 0; z <= 15; z++) { int bottom = z * ONE_Z_ROW; int top = bottom + 15 * ONE_Y_ROW; for (int x = 0; x <= 15; x++, bottom++, top++) { byte light = bottomChunk.voxels [top].light; lightmapSignature += light; if (voxels [bottom].opaque < FULL_OPAQUE) { if (light > voxels [bottom].light) { voxels [bottom].light = light; tempLightmapPos [++tempLightmapIndex] = bottom; } } } } } else if (isAboveSurface && (waterLevel == 0 || chunk.position.y - 8 > waterLevel)) { for (int z = 0; z <= 15; z++) { int bottom = z * ONE_Z_ROW; for (int x = 0; x <= 15; x++, bottom++) { if (voxels [bottom].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [bottom].light != light) { voxels [bottom].light = light; tempLightmapPos [++tempLightmapIndex] = bottom; } } } } } // Check left face VoxelChunk leftChunk = chunk.left; bool leftChunkIsAccesible = leftChunk && leftChunk.isPopulated; if (leftChunkIsAccesible) { for (int y = 15; y >= 0; y--) { int left = y * ONE_Y_ROW; int right = left + 15; for (int z = 0; z <= 15; z++, right += ONE_Z_ROW, left += ONE_Z_ROW) { byte light = leftChunk.voxels [right].light; lightmapSignature += light; if (voxels [left].opaque < FULL_OPAQUE) { if (light > voxels [left].light) { voxels [left].light = light; tempLightmapPos [++tempLightmapIndex] = left; } } } } } else if (isAboveSurface) { for (int y = 15; y >= 0; y--) { int left = y * ONE_Y_ROW; for (int z = 0; z <= 15; z++, left += ONE_Z_ROW) { if (voxels [left].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [left].light != light) { voxels [left].light = light; tempLightmapPos [++tempLightmapIndex] = left; } } } } } // Check right face VoxelChunk rightChunk = chunk.right; bool rightChunkIsAccesible = rightChunk && rightChunk.isPopulated; if (rightChunkIsAccesible) { for (int y = 15; y >= 0; y--) { int left = y * ONE_Y_ROW; int right = left + 15; for (int z = 0; z <= 15; z++, left += ONE_Z_ROW, right += ONE_Z_ROW) { byte light = rightChunk.voxels [left].light; lightmapSignature += light; if (voxels [right].opaque < FULL_OPAQUE) { if (light > voxels [right].light) { voxels [right].light = light; tempLightmapPos [++tempLightmapIndex] = right; } } } } } else if (isAboveSurface) { for (int y = 15; y >= 0; y--) { int right = y * ONE_Y_ROW + 15; for (int z = 0; z <= 15; z++, right += ONE_Z_ROW) { if (voxels [right].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [right].light != light) { voxels [right].light = light; tempLightmapPos [++tempLightmapIndex] = right; } } } } } // Check forward face VoxelChunk forwardChunk = chunk.forward; bool forwardChunkIsAccesible = forwardChunk && forwardChunk.isPopulated; if (forwardChunkIsAccesible) { for (int y = 15; y >= 0; y--) { int back = y * ONE_Y_ROW; int forward = back + 15 * ONE_Z_ROW; for (int x = 0; x <= 15; x++, back++, forward++) { byte light = forwardChunk.voxels [back].light; lightmapSignature += light; if (voxels [forward].opaque < FULL_OPAQUE) { if (light > voxels [forward].light) { voxels [forward].light = light; tempLightmapPos [++tempLightmapIndex] = forward; } } } } } else if (isAboveSurface) { for (int y = 15; y >= 0; y--) { int forward = y * ONE_Y_ROW + 15 * ONE_Z_ROW; for (int x = 0; x <= 15; x++, forward++) { if (voxels [forward].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [forward].light != light) { voxels [forward].light = light; tempLightmapPos [++tempLightmapIndex] = forward; } } } } } // Check back face VoxelChunk backChunk = chunk.back; bool backChunkIsAccesible = backChunk && backChunk.isPopulated; if (backChunkIsAccesible) { for (int y = 15; y >= 0; y--) { int back = y * ONE_Y_ROW; int forward = back + 15 * ONE_Z_ROW; for (int x = 0; x <= 15; x++, back++, forward++) { byte light = backChunk.voxels [forward].light; lightmapSignature += light; if (voxels [back].opaque < FULL_OPAQUE) { if (light > voxels [back].light) { voxels [back].light = light; tempLightmapPos [++tempLightmapIndex] = back; } } } } } else if (isAboveSurface) { for (int y = 15; y >= 0; y--) { int back = y * ONE_Y_ROW; for (int x = 0; x <= 15; x++, back++) { if (voxels [back].opaque < FULL_OPAQUE) { byte light = (byte)15; if (voxels [back].light != light) { voxels [back].light = light; tempLightmapPos [++tempLightmapIndex] = back; } } } } } int index = 0; int notIsAboveSurfaceReduction, isAboveSurfaceReduction; if (isAboveSurface) { isAboveSurfaceReduction = 1; notIsAboveSurfaceReduction = 0; } else { isAboveSurfaceReduction = 0; notIsAboveSurfaceReduction = 1; } while (index <= tempLightmapIndex) { // Pop element int voxelIndex = tempLightmapPos [index]; byte light = voxels [voxelIndex].light; index++; if (light <= voxels [voxelIndex].opaque) { continue; } int reducedLight = light - voxels [voxelIndex].opaque; // Spread light // down reducedLight -= notIsAboveSurfaceReduction; if (reducedLight == 0) { continue; } int py = voxelIndex / ONE_Y_ROW; if (py == 0) { if (bottomChunkIsAccesible) { int up = voxelIndex + 15 * ONE_Y_ROW; if (bottomChunk.voxels [up].light < reducedLight) { bottomChunkIsAccesible = false; ChunkRequestRefresh(bottomChunk, false, false); } } } else { int down = voxelIndex - ONE_Y_ROW; if (voxels [down].light < reducedLight && voxels [down].opaque < FULL_OPAQUE) { voxels [down].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [--index] = down; } } reducedLight -= isAboveSurfaceReduction; if (reducedLight == 0) { continue; } int remy = voxelIndex - py * ONE_Y_ROW; int pz = remy / ONE_Z_ROW; int px = remy - pz * ONE_Z_ROW; if (chunk.position.x == 520 && chunk.position.y == 8 && chunk.position.z == -296) { if (py == 11 && px == 11 && pz == 10) { int jj = 9; jj++; } } // backwards if (pz == 0) { if (backChunkIsAccesible) { int forward = voxelIndex + 15 * ONE_Z_ROW; if (backChunk.voxels [forward].light < reducedLight) { backChunkIsAccesible = false; ChunkRequestRefresh(backChunk, false, false); } } } else { int back = voxelIndex - ONE_Z_ROW; if (voxels [back].light < reducedLight && voxels [back].opaque < FULL_OPAQUE) { voxels [back].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [++tempLightmapIndex] = back; } } // forward if (pz == 15) { if (forwardChunkIsAccesible) { int back = voxelIndex - 15 * ONE_Z_ROW; if (forwardChunk.voxels [back].light < reducedLight) { forwardChunkIsAccesible = false; ChunkRequestRefresh(forwardChunk, false, false); } } } else { int forward = voxelIndex + ONE_Z_ROW; if (voxels [forward].light < reducedLight && voxels [forward].opaque < FULL_OPAQUE) { voxels [forward].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [++tempLightmapIndex] = forward; } } // left if (px == 0) { if (leftChunkIsAccesible) { int right = voxelIndex + 15; if (leftChunk.voxels [right].light < reducedLight) { leftChunkIsAccesible = false; ChunkRequestRefresh(leftChunk, false, false); } } } else { int left = voxelIndex - 1; if (voxels [left].light < reducedLight && voxels [left].opaque < FULL_OPAQUE) { voxels [left].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [++tempLightmapIndex] = left; } } // right if (px == 15) { if (rightChunkIsAccesible) { int left = voxelIndex - 15; if (rightChunk.voxels [left].light < reducedLight) { rightChunkIsAccesible = false; ChunkRequestRefresh(rightChunk, false, false); } } } else { int right = voxelIndex + 1; if (voxels [right].light < reducedLight && voxels [right].opaque < FULL_OPAQUE) { voxels [right].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [++tempLightmapIndex] = right; } } // up if (py == 15) { if (topChunkIsAccesible) { int down = voxelIndex - 15 * ONE_Y_ROW; if (topChunk.voxels [down].light < reducedLight) { topChunkIsAccesible = false; ChunkRequestRefresh(topChunk, false, false); } } } else { int up = voxelIndex + ONE_Y_ROW; if (voxels [up].light < reducedLight && voxels [up].opaque < FULL_OPAQUE) { voxels [up].light = (byte)reducedLight; lightmapSignature += reducedLight; tempLightmapPos [++tempLightmapIndex] = up; } } } if (lightmapSignature != chunk.lightmapSignature) { // There're changes, so annotate this chunk mesh to be rebuilt chunk.lightmapSignature = lightmapSignature; chunk.needsMeshRebuild = true; } chunk.lightmapIsClear = false; }
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); }
/// <summary> /// Paints the terrain inside the chunk defined by its central "position" /// </summary> /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns> public abstract bool PaintChunk(VoxelChunk chunk);
/// <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; visibleDistanceStatus = ChunkVisibleDistanceStatus.Unknown; lightmapIsClear = false; 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; if (mc != null) { mc.enabled = false; } gameObject.SetActive(true); }
void ApplyRule(Vector3 position, VoxelChunk chunk, int voxelIndex, ref VoxelDefinition voxelDefinition, ref Color32 tintColor) { if (rules == null || env == null || voxelDefinition != this.placingVoxel) { return; } VoxelChunk otherChunk; int otherIndex; VoxelDefinition[] otherVoxelDefinitions; for (int k = 0; k < rules.Length; k++) { if (!rules [k].enabled) { continue; } Vector3 pos = position + rules [k].offset; bool res = false; switch (rules [k].condition) { case TileRuleCondition.IsEmpty: res = env.IsEmptyAtPosition(pos); break; case TileRuleCondition.IsOcuppied: res = !env.IsEmptyAtPosition(pos); break; case TileRuleCondition.Equals: if (env.GetVoxelIndex(pos, out otherChunk, out otherIndex, false)) { res = otherChunk.voxels [otherIndex].typeIndex == voxelDefinition.index; } break; case TileRuleCondition.IsAny: otherVoxelDefinitions = rules [k].compareVoxelDefinitionSet; if (otherVoxelDefinitions != null && env.GetVoxelIndex(pos, out otherChunk, out otherIndex, false)) { int otherTypeIndex = otherChunk.voxels [otherIndex].typeIndex; for (int j = 0; j < otherVoxelDefinitions.Length; j++) { if (otherVoxelDefinitions [j].index == otherTypeIndex) { res = true; break; } } } break; case TileRuleCondition.Always: res = true; break; } if (res) { switch (rules [k].action) { case TileRuleAction.CancelPlacement: voxelDefinition = null; break; case TileRuleAction.Replace: voxelDefinition = rules [k].replacementSingle; break; case TileRuleAction.Random: otherVoxelDefinitions = rules [k].replacementSet; if (otherVoxelDefinitions != null && otherVoxelDefinitions.Length > 0) { voxelDefinition = otherVoxelDefinitions [UnityEngine.Random.Range(0, otherVoxelDefinitions.Length)]; } break; case TileRuleAction.Cycle: otherVoxelDefinitions = rules [k].replacementSet; if (otherVoxelDefinitions != null && otherVoxelDefinitions.Length > 0) { voxelDefinition = otherVoxelDefinitions [cycleIndex++]; if (cycleIndex >= otherVoxelDefinitions.Length) { cycleIndex = 0; } } break; } return; // rule executed, exit } } }
/// <summary> /// Internal method that puts a voxel in a given position. This method does not inform to neighbours. Only used by non-contiguous structures, like trees or vegetation. /// For terrain or large scale buildings, use VoxelPlaceFast. /// </summary> /// <param name="position">Position.</param> /// <param name="voxelType">Voxel type.</param> /// <param name="chunk">Chunk.</param> /// <param name="voxelIndex">Voxel index.</param> void VoxelSingleSet(Vector3 position, VoxelDefinition voxelType, out VoxelChunk chunk, out int voxelIndex) { VoxelSingleSet(position, voxelType, out chunk, out voxelIndex, Misc.color32White); }
/// <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 ManageSeeThrough() { SetArrays(false); Camera cam = currentCamera; if (cam == null) { return; } Vector3 camPos = cam.transform.position; if (seeThroughTarget == null) { if (characterController != null) { seeThroughTarget = characterController.gameObject; } if (seeThroughTarget == null) { return; } } Vector3 targetPos = seeThroughTarget.transform.position; // Exclude any voxel above roof from rendering float radiusSqr = seeThroughRadius * seeThroughRadius; Vector3 cylinderAxis; float distToTarget = Vector3.Distance(targetPos, camPos); cylinderAxis.x = (camPos.x - targetPos.x) / distToTarget; cylinderAxis.y = (camPos.y - targetPos.y) / distToTarget; cylinderAxis.z = (camPos.z - targetPos.z) / distToTarget; Shader.SetGlobalVector("_VPSeeThroughData", new Vector4(-cylinderAxis.x, -cylinderAxis.y, -cylinderAxis.z, radiusSqr)); alphaOccludedIndicesCount = 0; // Add surrounding chunks int chunkCount = LineCast(targetPos, camPos, occludedChunks); int flag = Time.frameCount; for (int k = 0; k < chunkCount; k++) { VoxelChunk chunk = occludedChunks [k]; chunk.tempFlag = flag; } int lineChunks = chunkCount; for (int k = 0; k < lineChunks; k++) { VoxelChunk chunk = occludedChunks [k]; VoxelChunk n = chunk.top; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.bottom; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.left; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.right; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.forward; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } n = chunk.back; if (n != null && n.tempFlag != flag) { if (chunkCount >= occludedChunks.Length) { occludedChunks = occludedChunks.Extend(); } occludedChunks [chunkCount++] = n; n.tempFlag = flag; } } float minY = targetPos.y + seeThroughHeightOffset; Vector3 voxelPosition = Misc.vector3zero; for (int k = 0; k < chunkCount; k++) { VoxelChunk chunk = occludedChunks [k]; Vector3 chunkPosBase = chunk.position; chunkPosBase.x = chunkPosBase.x - CHUNK_HALF_SIZE + 0.5f; chunkPosBase.y = chunkPosBase.y - CHUNK_HALF_SIZE + 0.5f; chunkPosBase.z = chunkPosBase.z - CHUNK_HALF_SIZE + 0.5f; for (int voxelIndex = 0; voxelIndex < chunk.voxels.Length; voxelIndex++) { if (chunk.voxels [voxelIndex].hasContent == 1) { int py = voxelIndex / ONE_Y_ROW; voxelPosition.y = py + chunkPosBase.y; if (voxelPosition.y < minY) { continue; } int pz = (voxelIndex - py * ONE_Y_ROW) / ONE_Z_ROW; int px = voxelIndex & (CHUNK_SIZE - 1); voxelPosition.x = px + chunkPosBase.x; voxelPosition.z = pz + chunkPosBase.z; // Check if it's inside the cylinder Vector3 v = voxelPosition; v.x -= targetPos.x; v.y -= targetPos.y; v.z -= targetPos.z; // v is the voxel position from targetPos // cylinderDist is the distance from targetPos to the projected vector v on the cylinder axis float cylinderDist = v.x * cylinderAxis.x + v.y * cylinderAxis.y + v.z * cylinderAxis.z; if (cylinderDist < 1 || cylinderDist > distToTarget) { continue; } v.x -= cylinderDist * cylinderAxis.x; v.y -= cylinderDist * cylinderAxis.y; v.z -= cylinderDist * cylinderAxis.z; // this is the distance between the voxel position and the projected point calculated above = distance from the voxel to the axis of the cylinder float orthDistanceSqr = v.x * v.x + v.y * v.y + v.z * v.z; if (orthDistanceSqr < radiusSqr) { if (alphaOccludedIndicesCount >= alphaOccludedIndices.Length) { alphaOccludedIndices = alphaOccludedIndices.Extend(); } alphaOccludedIndices [alphaOccludedIndicesCount].chunk = chunk; alphaOccludedIndices [alphaOccludedIndicesCount].voxelIndex = voxelIndex; alphaOccludedIndicesCount++; } } } } SetArrays(true); }
void CheckChunksVisibleDistanceLoop(long maxFrameTime) { try { bool eventOut = OnChunkExitVisibleDistance != null; bool eventIn = OnChunkEnterVisibleDistance != null; int max = checkChunksVisibleDistanceIndex + 200; if (max >= chunksPoolLoadIndex) { max = chunksPoolLoadIndex; } float visibleDistanceSqr = (_visibleChunksDistance + 1) * CHUNK_SIZE; visibleDistanceSqr *= visibleDistanceSqr; while (checkChunksVisibleDistanceIndex < max) { VoxelChunk chunk = chunksPool[checkChunksVisibleDistanceIndex]; if (chunk.isRendered && !chunk.isCloud) { float dist = FastVector.SqrMaxDistanceXorZ(ref chunk.position, ref currentAnchorPos); if (dist > visibleDistanceSqr) { if (chunk.visibleDistanceStatus != ChunkVisibleDistanceStatus.OutOfVisibleDistance) { chunk.visibleDistanceStatus = ChunkVisibleDistanceStatus.OutOfVisibleDistance; if (unloadFarChunks || eventOut) { if (unloadFarChunks) { chunk.gameObject.SetActive(false); } if (eventOut) { OnChunkExitVisibleDistance(chunk); } if (stopWatch.ElapsedMilliseconds >= maxFrameTime) { break; } } } } else if (chunk.visibleDistanceStatus != ChunkVisibleDistanceStatus.WithinVisibleDistance) { chunk.visibleDistanceStatus = ChunkVisibleDistanceStatus.WithinVisibleDistance; if (unloadFarChunks || eventOut) { if (unloadFarChunks) { chunk.gameObject.SetActive(true); } if (eventIn) { OnChunkEnterVisibleDistance(chunk); } if (stopWatch.ElapsedMilliseconds >= maxFrameTime) { break; } } } } checkChunksVisibleDistanceIndex++; } if (checkChunksVisibleDistanceIndex >= chunksPoolLoadIndex) { checkChunksVisibleDistanceIndex = -1; } } catch (Exception ex) { ShowExceptionMessage(ex); } }
void SaveGameBinaryFormat_9(BinaryWriter bw) { if (cachedChunks == null) { return; } // Build a table with all voxel definitions used in modified chunks InitSaveGameStructs(); int voxelDefinitionsCount = 0; int itemDefinitionsCount = 0; int numChunks = 0; // Pack used voxel and item definitions foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { if (kv.Value == null) { continue; } VoxelChunk chunk = kv.Value.chunk; if (chunk != null && chunk.modified) { numChunks++; if (chunk.voxels != null) { VoxelDefinition last = null; for (int k = 0; k < chunk.voxels.Length; k++) { VoxelDefinition vd = chunk.voxels [k].type; if (vd == null || vd == last || vd.isDynamic || vd.doNotSave) { continue; } last = vd; if (!saveVoxelDefinitionsDict.ContainsKey(vd)) { saveVoxelDefinitionsDict [vd] = voxelDefinitionsCount++; saveVoxelDefinitionsList.Add(vd.name); } } } if (chunk.items != null) { ItemDefinition last = null; for (int k = 0; k < chunk.items.count; k++) { Item item = chunk.items.values [k]; if (item == null) { continue; } ItemDefinition id = item.itemDefinition; if (id == null || id == last) { continue; } last = id; if (!saveItemDefinitionsDict.ContainsKey(id)) { saveItemDefinitionsDict [id] = itemDefinitionsCount++; saveItemDefinitionsList.Add(id.name); } } } if (chunk.lightSources != null) { ItemDefinition last = null; for (int k = 0; k < chunk.lightSources.Count; k++) { ItemDefinition id = chunk.lightSources [k].itemDefinition; if (id == null || id == last) { continue; } last = id; if (!saveItemDefinitionsDict.ContainsKey(id)) { saveItemDefinitionsDict [id] = itemDefinitionsCount++; saveItemDefinitionsList.Add(id.name); } } } } } // Header bw.Write(SAVE_FILE_CURRENT_FORMAT); // Character controller transform position if (characterController != null) { EncodeVector3Binary(bw, characterController.transform.position); // Character controller transform rotation EncodeVector3Binary(bw, characterController.transform.rotation.eulerAngles); } else { EncodeVector3Binary(bw, Misc.vector3zero); EncodeVector3Binary(bw, Misc.vector3zero); } // Character controller's camera local rotation if (cameraMain != null) { EncodeVector3Binary(bw, cameraMain.transform.localRotation.eulerAngles); } else { EncodeVector3Binary(bw, Misc.vector3zero); } // Add voxel definitions table int vdCount = saveVoxelDefinitionsList.Count; bw.Write((Int16)vdCount); for (int k = 0; k < vdCount; k++) { bw.Write(saveVoxelDefinitionsList [k]); } // Add item definitions table int idCount = saveItemDefinitionsList.Count; bw.Write((Int16)idCount); for (int k = 0; k < idCount; k++) { bw.Write(saveItemDefinitionsList [k]); } // Add modified chunks bw.Write(numChunks); foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { if (kv.Value == null) { continue; } VoxelChunk chunk = kv.Value.chunk; if (chunk != null && chunk.modified) { ToggleHiddenVoxels(chunk, true); WriteChunkData_9(bw, chunk); ToggleHiddenVoxels(chunk, false); } } // Add gameobjects VoxelPlaySaveThis [] gos = FindObjectsOfType <VoxelPlaySaveThis> (); bw.Write((Int16)gos.Length); Dictionary <string, string> data = new Dictionary <string, string> (); for (int k = 0; k < gos.Length; k++) { VoxelPlaySaveThis go = gos [k]; if (string.IsNullOrEmpty(go.prefabResourcesPath)) { continue; } bw.Write(go.prefabResourcesPath); bw.Write(go.name); EncodeVector3Binary(bw, go.transform.position); EncodeVector3Binary(bw, go.transform.eulerAngles); EncodeVector3Binary(bw, go.transform.localScale); data.Clear(); go.SendMessage("OnSaveGame", data); //go.GetData (data); Int16 dataCount = (Int16)data.Count; bw.Write(dataCount); foreach (KeyValuePair <string, string> entry in data) { bw.Write(entry.Key); bw.Write(entry.Value); } } }
void LoadGameBinaryFileFormat_9(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); } } } // 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 LoadSaveBinaryFileFormat_3(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 int chunkX, chunkY, chunkZ; // Get chunk position Vector3 chunkPosition = DecodeVector3Binary(br); GetChunkCoordinates(chunkPosition, out chunkX, out chunkY, out chunkZ); 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(); } 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); } } }
public bool GetChunkNavMeshIsReady(VoxelChunk chunk) { return(chunk.navMeshSourceIndex >= 0); }
/// <summary> /// Toggles on/off hidden voxels. /// </summary> void ToggleHiddenVoxels(VoxelChunk chunk, bool show) { if (chunk == null) { return; } FastHashSet <VoxelExtraData> data = chunk.voxelsExtraData; if (data == null) { return; } int count = data.Count; Voxel[] voxels = chunk.voxels; for (int k = 0; k < count; k++) { int voxelIndex = data.entries [k].key; if (voxelIndex >= 0) { if (data.entries [k].value.hidden) { if (show) { voxels [voxelIndex].typeIndex = data.entries [k].value.hiddenTypeIndex; voxels [voxelIndex].opaque = data.entries [k].value.hiddenOpaque; voxels [voxelIndex].light = data.entries [k].value.hiddenLight; } else { ushort typeIndex = voxels [voxelIndex].typeIndex; data.entries [k].value.hiddenTypeIndex = typeIndex; data.entries [k].value.hiddenOpaque = voxels [voxelIndex].opaque; data.entries [k].value.hiddenLight = voxels [voxelIndex].light; VoxelDefinition vd = voxelDefinitions [typeIndex]; switch (vd.seeThroughMode) { case SeeThroughMode.None: voxels [voxelIndex].typeIndex = 0; // null voxel whose renderType = Empty (but genereate colliders) voxels [voxelIndex].opaque = 0; break; case SeeThroughMode.ReplaceVoxel: voxels [voxelIndex].typeIndex = vd.seeThroughVoxel.index; // null voxel whose renderType = Empty (but genereate colliders) voxels [voxelIndex].opaque = vd.seeThroughVoxel.opaque; break; case SeeThroughMode.Transparency: voxels [voxelIndex].typeIndex = (ushort)vd.seeThroughVoxelTempTransp; // null voxel whose renderType = Empty (but genereate colliders) voxels [voxelIndex].opaque = 0; break; } voxels [voxelIndex].light = 15; } } } } }
/// <summary> /// Picks first chunk out of visible distance and not near to the current desired position /// </summary> void ReuseChunkEntry(Vector3 position) { bool valid = false; float visibleDistance = (_visibleChunksDistance + 1) * 16; // adds one to avoid chunks appearing and disappearing on the border of visible distance float minDistance = 8 * 16; int lastGood = -1; for (int i = 0; i < chunksPool.Length; i++) { if (++chunksPoolCurrentIndex >= chunksPool.Length - 1) { chunksPoolCurrentIndex = chunksPoolFirstReusableIndex; } VoxelChunk chunk = chunksPool [chunksPoolCurrentIndex]; // if chunk has been modified or chunk is marked as non reusable, skip it if (chunk.modified || chunk.cannotBeReused) { continue; } // if chunk is too near from desired position, skip it float dx = position.x - chunk.position.x; float dy = position.y - chunk.position.y; float dz = position.y - chunk.position.z; if (dx >= -minDistance && dx <= minDistance && dy >= -minDistance && dy <= minDistance && dz >= -minDistance && dz <= minDistance) { continue; } lastGood = chunksPoolCurrentIndex; // if chunk is within visible distance, skip it dx = currentCamPos.x - chunk.position.x; dy = currentCamPos.y - chunk.position.y; dz = currentCamPos.z - chunk.position.z; if (dx >= -visibleDistance && dx <= visibleDistance && dy >= -visibleDistance && dy <= visibleDistance && dz >= -visibleDistance && dz <= visibleDistance) { continue; } // check event confirmation bool canUnload = true; if (OnChunkUnload != null) { OnChunkUnload(chunk, ref canUnload); if (!canUnload) { continue; } } // chunk seems good, pick it up! valid = true; break; } if (lastGood >= 0) { chunksPoolCurrentIndex = lastGood; } if (!valid) { ShowMessage("Reusing visible chunks (Chunks Pool Size value must be increased)"); } VoxelChunk bestChunk = chunksPool [chunksPoolCurrentIndex]; lock (lockLastChunkFetch) { if (bestChunk == lastChunkFetch) { lastChunkFetch = null; } } bestChunk.PrepareForReuse(effectiveGlobalIllumination ? (byte)0 : (byte)15); // Remove cached chunk at old position SetChunkOctreeIsDirty(bestChunk.position, true); }
void VoxelDestroyFastSingle(VoxelChunk chunk, int voxelIndex) { chunk.voxels [voxelIndex].Clear(effectiveGlobalIllumination ? (byte)0 : (byte)15); chunk.modified = true; }
//TODO: Painting Chunk /// <summary> /// Paints the terrain inside the chunk defined by its central "position" /// </summary> /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns> public override bool PaintChunk(VoxelChunk chunk) { Vector3 position = chunk.position; if (position.y + VoxelPlayEnvironment.CHUNK_HALF_SIZE < minHeight) { chunk.isAboveSurface = false; return(false); } int bedrockRow = -1; if ((object)bedrockVoxel != null && position.y < minHeight + VoxelPlayEnvironment.CHUNK_HALF_SIZE) { bedrockRow = (int)(minHeight - (position.y - VoxelPlayEnvironment.CHUNK_HALF_SIZE) + 1) * ONE_Y_ROW - 1; } position.x -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; position.y -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; position.z -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; Vector3 pos; int waterLevel = env.waterLevel > 0 ? env.waterLevel : -1; Voxel [] voxels = chunk.voxels; bool hasContent = false; bool isAboveSurface = false; generation++; env.GetHeightMapInfoFast(position.x, position.z, heightChunkData); // iterate 256 slice of chunk (z/x plane = 16*16 positions) for (int arrayIndex = 0; arrayIndex < VoxelPlayEnvironment.CHUNK_SIZE * VoxelPlayEnvironment.CHUNK_SIZE; arrayIndex++) { float groundLevel = heightChunkData [arrayIndex].groundLevel; float surfaceLevel = waterLevel > groundLevel ? waterLevel : groundLevel; if (surfaceLevel < position.y) { // position is above terrain or water isAboveSurface = true; continue; } BiomeDefinition biome = heightChunkData [arrayIndex].biome; if ((object)biome == null) { biome = world.defaultBiome; if ((object)biome == null) { continue; } } int y = (int)(surfaceLevel - position.y); if (y >= VoxelPlayEnvironment.CHUNK_SIZE) { y = VoxelPlayEnvironment.CHUNK_SIZE - 1; } pos.y = position.y + y; //remember: Array is linear pos.x = position.x + (arrayIndex & 0xF); pos.z = position.z + (arrayIndex >> 4); // Place voxels int voxelIndex = y * ONE_Y_ROW + arrayIndex; if (pos.y > groundLevel) { // water above terrain if (pos.y == surfaceLevel) { isAboveSurface = true; } while (pos.y > groundLevel && voxelIndex >= 0) { voxels [voxelIndex].SetFastWater(waterVoxel); voxelIndex -= ONE_Y_ROW; pos.y--; } } else if (pos.y == groundLevel) { isAboveSurface = true; if (voxels [voxelIndex].hasContent == 0) { if (paintShore && pos.y == waterLevel) { // this is on the shore, place a shoreVoxel voxels [voxelIndex].Set(shoreVoxel); } else { // we're at the surface of the biome => draw the voxel top of the biome and also check for random vegetation and trees voxels [voxelIndex].Set(biome.voxelTop); #if UNITY_EDITOR if (!env.draftModeActive) { #endif // Check tree probability if (pos.y > waterLevel) { float rn = WorldRand.GetValue(pos); if (biome.treeDensity > 0 && rn < biome.treeDensity && biome.trees.Length > 0) { // request one tree at this position env.RequestTreeCreation(chunk, pos, env.GetTree(biome.trees, rn / biome.treeDensity)); } else if (biome.vegetationDensity > 0 && rn < biome.vegetationDensity && biome.vegetation.Length > 0) { if (voxelIndex >= (VoxelPlayEnvironment.CHUNK_SIZE - 1) * ONE_Y_ROW) { // request one vegetation voxel one position above which means the chunk above this one env.RequestVegetationCreation(chunk.top, voxelIndex - ONE_Y_ROW * (VoxelPlayEnvironment.CHUNK_SIZE - 1), env.GetVegetation(biome, rn / biome.vegetationDensity)); } else { // directly place a vegetation voxel above this voxel if (env.enableVegetation) { voxels [voxelIndex + ONE_Y_ROW].Set(env.GetVegetation(biome, rn / biome.vegetationDensity)); env.vegetationCreated++; } } } } #if UNITY_EDITOR } #endif } voxelIndex -= ONE_Y_ROW; pos.y--; } } biome.biomeGeneration = generation; // fill hole with water while (voxelIndex >= 0 && voxels [voxelIndex].hasContent == 2 && pos.y <= waterLevel) { voxels [voxelIndex].SetFastWater(waterVoxel); voxelIndex -= ONE_Y_ROW; pos.y--; } // Continue filling down for (; voxelIndex > bedrockRow; voxelIndex -= ONE_Y_ROW, pos.y--) { if (voxels [voxelIndex].hasContent == 0) // avoid holes { voxels [voxelIndex].SetFastOpaque(biome.voxelDirt); } else if (voxels [voxelIndex].hasContent == 2 && pos.y <= waterLevel) // hole under water level -> fill with water { voxels [voxelIndex].SetFastWater(waterVoxel); } } if (bedrockRow >= 0 && voxelIndex >= 0) { voxels [voxelIndex].SetFastOpaque(bedrockVoxel); } hasContent = true; } // Spawn random ore if (addOre) { // Check if there's any ore in this chunk (randomly) float noiseValue = WorldRand.GetValue(chunk.position); for (int b = 0; b < world.biomes.Length; b++) { BiomeDefinition biome = world.biomes [b]; if (biome.biomeGeneration != generation) { continue; } for (int o = 0; o < biome.ores.Length; o++) { if (biome.ores [o].ore == null) { continue; } if (biome.ores [o].probabilityMin <= noiseValue && biome.ores [o].probabilityMax >= noiseValue) { // ore picked; determine the number of veins in this chunk int veinsCount = biome.ores [o].veinsCountMin + (int)(WorldRand.GetValue() * (biome.ores [o].veinsCountMax - biome.ores [o].veinsCountMin + 1f)); for (int vein = 0; vein < veinsCount; vein++) { Vector3 veinPos = chunk.position; veinPos.x += vein; // Determine random vein position in the chunk Vector3 v = WorldRand.GetVector3(veinPos, VoxelPlayEnvironment.CHUNK_SIZE); int px = (int)v.x; int py = (int)v.y; int pz = (int)v.z; veinPos = env.GetVoxelPosition(veinPos, px, py, pz); int oreIndex = py * ONE_Y_ROW + pz * ONE_Z_ROW + px; int veinSize = biome.ores [o].veinMinSize + (oreIndex % (biome.ores [o].veinMaxSize - biome.ores [o].veinMinSize + 1)); // span ore vein SpawnOre(chunk, biome.ores [o].ore, veinPos, px, py, pz, veinSize, biome.ores [o].depthMin, biome.ores [o].depthMax); } break; } } } } // Finish, return chunk.isAboveSurface = isAboveSurface; return(hasContent); }
/// <summary> /// Loads game world from a string /// </summary> /// <returns>True if saveGameData was loaded successfully.</returns> /// <param name="preservePlayerPosition">If set to <c>true</c> preserve player position.</param> /// <param name="clearScene">If set to <c>true</c> existing chunks will be cleared before loading the game. If set to false, only chunks from the saved game will be replaced.</param> public bool LoadGameFromByteArray(byte[] saveGameData, bool preservePlayerPosition, bool clearScene = true) { bool result = false; if (clearScene) { DestroyAllVoxels(); } else { // Remove all modified chunks to ensure only loaded chunks are the modified ones GetChunks(tempChunks, true); int count = tempChunks.Count; for (int k = 0; k < count; k++) { VoxelChunk chunk = tempChunks [k]; if (chunk != null && chunk.modified) { // Restore original contents world.terrainGenerator.PaintChunk(chunk); ChunkRequestRefresh(chunk, true, true); chunk.modified = false; } } } try { if (saveGameData == null) { return(false); } // get version isLoadingGame = true; BinaryReader br = new BinaryReader(new MemoryStream(saveGameData), Encoding.UTF8); byte version = br.ReadByte(); switch (version) { case 3: LoadSaveBinaryFileFormat_3(br, preservePlayerPosition); break; case 4: LoadSaveBinaryFileFormat_4(br, preservePlayerPosition); break; case 5: LoadGameBinaryFileFormat_5(br, preservePlayerPosition); break; case 6: LoadGameBinaryFileFormat_6(br, preservePlayerPosition); break; case 7: LoadGameBinaryFileFormat_7(br, preservePlayerPosition); break; default: throw new ApplicationException("LoadGameFromArray() does not support this file format."); } br.Close(); isLoadingGame = false; saveFileIsLoaded = true; result = true; } catch (Exception ex) { Debug.LogError("Voxel Play: " + ex.Message); result = false; } isLoadingGame = false; shouldCheckChunksInFrustum = true; return(result); }
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); }
private void FixedUpdate() { if (!hasCharacterController) { return; } float speed; GetInput(out speed); Vector3 pos = transform.position; if (isFlying || isInWater) { m_MoveDir = m_Camera.transform.forward * m_Input.y + m_Camera.transform.right * m_Input.x + m_Camera.transform.up * m_Input.z; m_MoveDir *= speed; if (!isFlying) { if (m_MoveDir.y < 0) { m_MoveDir.y += 0.1f * Time.fixedDeltaTime; } if (m_Jump) { // Check if player is next to terrain if (env.CheckCollision(new Vector3(pos.x + m_Camera.transform.forward.x, pos.y, pos.z + m_Camera.transform.forward.z))) { m_MoveDir.y = jumpSpeed * 0.5f; m_Jumping = true; } m_Jump = false; } else { m_MoveDir += Physics.gravity * gravityMultiplier * Time.fixedDeltaTime * 0.5f; } if (pos.y > waterLevelTop && m_MoveDir.y > 0) { m_MoveDir.y = 0; // do not exit water } ProgressSwimCycle(m_CharacterController.velocity, swimSpeed); } } else { // always move along the camera forward as it is the direction that it being aimed at Vector3 desiredMove = transform.forward * m_Input.y + transform.right * m_Input.x; // get a normal for the surface that is being touched to move along it RaycastHit hitInfo; Physics.SphereCast(pos, m_CharacterController.radius, Misc.vector3down, out hitInfo, characterHeight / 2f, Physics.AllLayers, QueryTriggerInteraction.Ignore); desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized; m_MoveDir.x = desiredMove.x * speed; m_MoveDir.z = desiredMove.z * speed; if (m_CharacterController.isGrounded) { m_MoveDir.y = -stickToGroundForce; if (m_Jump) { m_MoveDir.y = jumpSpeed; PlayJumpSound(); m_Jump = false; m_Jumping = true; } } else { m_MoveDir += Physics.gravity * gravityMultiplier * Time.fixedDeltaTime; } UpdateCameraPosition(speed); ProgressStepCycle(m_CharacterController.velocity, speed); } Vector3 finalMove = m_MoveDir * Time.fixedDeltaTime; Vector3 newPos = pos + finalMove; bool canMove = !limitBoundsEnabled || limitBounds.Contains(newPos); if (m_PreviouslyGrounded && !isFlying && isCrouched) { // check if player is beyond the edge Ray ray = new Ray(newPos, Misc.vector3down); canMove = Physics.SphereCast(ray, 0.3f, 1f); // if player can't move, clamp movement along the edge and check again if (!canMove) { if (Mathf.Abs(m_MoveDir.z) > Mathf.Abs(m_MoveDir.x)) { m_MoveDir.x = 0; } else { m_MoveDir.z = 0; } finalMove = m_MoveDir * Time.fixedDeltaTime; newPos = pos + finalMove; ray.origin = newPos; canMove = Physics.SphereCast(ray, 0.3f, 1f); } } // if constructor is enabled, disable any movement if control key is pressed (reserved for special constructor actions) if (env.constructorMode && input.GetButton(InputButtonNames.LeftControl)) { canMove = false; } if (canMove) { // autoclimb Vector3 dir = new Vector3(m_MoveDir.x, 0, m_MoveDir.z); Vector3 basePos = new Vector3(pos.x, pos.y - characterHeight * 0.25f, pos.z); Ray ray = new Ray(basePos, dir); if (Physics.SphereCast(ray, 0.3f, 1f)) { m_CharacterController.stepOffset = 1.1f; } else { m_CharacterController.stepOffset = 0.2f; } m_CollisionFlags = m_CharacterController.Move(finalMove); } isGrounded = m_CharacterController.isGrounded; // Check limits if (orbitMode) { if (FastVector.ClampDistance(ref lookAt, ref pos, minDistance, maxDistance)) { m_CharacterController.transform.position = pos; } } mouseLook.UpdateCursorLock(); if (!isGrounded && !isFlying) { // Check current chunk VoxelChunk chunk = env.GetCurrentChunk(); if (chunk != null && !chunk.isRendered) { WaitForCurrentChunk(); return; } } }
/// <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); }
void SaveGameBinaryFormat_8(BinaryWriter bw) { if (cachedChunks == null) { return; } // Build a table with all voxel definitions used in modified chunks InitSaveGameStructs(); int voxelDefinitionsCount = 0; int itemDefinitionsCount = 0; int numChunks = 0; // Pack used voxel and item definitions foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { if (kv.Value == null) { continue; } VoxelChunk chunk = kv.Value.chunk; if (chunk != null && chunk.modified) { numChunks++; if (chunk.voxels != null) { VoxelDefinition last = null; for (int k = 0; k < chunk.voxels.Length; k++) { VoxelDefinition vd = chunk.voxels [k].type; if (vd == null || vd == last || vd.isDynamic || vd.doNotSave) { continue; } last = vd; if (!saveVoxelDefinitionsDict.ContainsKey(vd)) { saveVoxelDefinitionsDict [vd] = voxelDefinitionsCount++; saveVoxelDefinitionsList.Add(vd.name); } } } if (chunk.items != null) { ItemDefinition last = null; for (int k = 0; k < chunk.items.count; k++) { Item item = chunk.items.values [k]; if (item == null) { continue; } ItemDefinition id = item.itemDefinition; if (id == null || id == last) { continue; } last = id; if (!saveItemDefinitionsDict.ContainsKey(id)) { saveItemDefinitionsDict [id] = itemDefinitionsCount++; saveItemDefinitionsList.Add(id.name); } } } if (chunk.lightSources != null) { ItemDefinition last = null; for (int k = 0; k < chunk.lightSources.Count; k++) { ItemDefinition id = chunk.lightSources [k].itemDefinition; if (id == null || id == last) { continue; } last = id; if (!saveItemDefinitionsDict.ContainsKey(id)) { saveItemDefinitionsDict [id] = itemDefinitionsCount++; saveItemDefinitionsList.Add(id.name); } } } } } // Header bw.Write(SAVE_FILE_CURRENT_FORMAT); // Character controller transform position if (characterController != null) { EncodeVector3Binary(bw, characterController.transform.position); // Character controller transform rotation EncodeVector3Binary(bw, characterController.transform.rotation.eulerAngles); } else { EncodeVector3Binary(bw, Misc.vector3zero); EncodeVector3Binary(bw, Misc.vector3zero); } // Character controller's camera local rotation if (cameraMain != null) { EncodeVector3Binary(bw, cameraMain.transform.localRotation.eulerAngles); } else { EncodeVector3Binary(bw, Misc.vector3zero); } // Add voxel definitions table int vdCount = saveVoxelDefinitionsList.Count; bw.Write((Int16)vdCount); for (int k = 0; k < vdCount; k++) { bw.Write(saveVoxelDefinitionsList [k]); } // Add item definitions table int idCount = saveItemDefinitionsList.Count; bw.Write((Int16)idCount); for (int k = 0; k < idCount; k++) { bw.Write(saveItemDefinitionsList [k]); } // Add modified chunks bw.Write(numChunks); foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { if (kv.Value == null) { continue; } VoxelChunk chunk = kv.Value.chunk; if (chunk != null && chunk.modified) { ToggleHiddenVoxels(chunk, true); WriteChunkData_8(bw, chunk); ToggleHiddenVoxels(chunk, false); } } }
/// <summary> /// Paints the terrain inside the chunk defined by its central "position" /// </summary> /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns> public override bool PaintChunk(VoxelChunk chunk) { Vector3 position = chunk.position; if (position.y + VoxelPlayEnvironment.CHUNK_HALF_SIZE < minHeight) { chunk.isAboveSurface = false; return(false); } int bedrockRow = -1; if ((object)bedrockVoxel != null && position.y < minHeight + VoxelPlayEnvironment.CHUNK_HALF_SIZE) { bedrockRow = (int)(minHeight - (position.y - VoxelPlayEnvironment.CHUNK_HALF_SIZE) + 1) * ONE_Y_ROW; } position.x -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; position.y -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; position.z -= VoxelPlayEnvironment.CHUNK_HALF_SIZE; Vector3 pos; int waterLevel = env.waterLevel > 0 ? env.waterLevel : -1; Voxel[] voxels = chunk.voxels; bool hasContent = false; bool isAboveSurface = false; for (int z = 0; z < VoxelPlayEnvironment.CHUNK_SIZE; z++) { pos.z = position.z + z; int arrayZIndex = z * ONE_Z_ROW; for (int x = 0; x < VoxelPlayEnvironment.CHUNK_SIZE; x++) { pos.x = position.x + x; HeightMapInfo heightMapInfo = env.GetHeightMapInfoFast(pos.x, pos.z); float groundLevel = heightMapInfo.groundLevel; float surfaceLevel = waterLevel > groundLevel ? waterLevel : groundLevel; if (surfaceLevel < position.y) { // position is above terrain or water isAboveSurface = true; continue; } int hindex = GetHeightIndex(pos.x, pos.z); if (hindex < 0) { continue; } VoxelDefinition vd = heights [hindex].terrainVoxelTop; if ((object)vd == null) { continue; } int y = (int)(surfaceLevel - position.y); if (y >= VoxelPlayEnvironment.CHUNK_SIZE) { y = (VoxelPlayEnvironment.CHUNK_SIZE - 1); } pos.y = position.y + y; // Place voxels int voxelIndex = y * ONE_Y_ROW + arrayZIndex + x; if (pos.y > groundLevel) { // water above terrain if (pos.y == surfaceLevel) { isAboveSurface = true; } while (pos.y > groundLevel && voxelIndex >= 0) { voxels [voxelIndex].Set(waterVoxel); voxelIndex -= ONE_Y_ROW; pos.y--; } } else if (pos.y == groundLevel) { isAboveSurface = true; if (voxels [voxelIndex].hasContent == 0) { // surface => draw voxel top, vegetation and trees voxels [voxelIndex].Set(vd); #if UNITY_EDITOR if (!env.draftModeActive) { #endif // Check tree probability if (pos.y > waterLevel) { ModelDefinition treeModel = heights [hindex].treeModel; if (env.enableTrees && treeModel != null) { env.RequestTreeCreation(chunk, pos, treeModel); } else if (env.enableVegetation) { VoxelDefinition vegetation = heights [hindex].vegetationVoxel; if (vegetation != null) { if (voxelIndex >= (VoxelPlayEnvironment.CHUNK_SIZE - 1) * ONE_Y_ROW) { env.RequestVegetationCreation(chunk.top, voxelIndex - ONE_Y_ROW * (VoxelPlayEnvironment.CHUNK_SIZE - 1), vegetation); } else { voxels [voxelIndex + ONE_Y_ROW].Set(vegetation); } env.vegetationCreated++; } } } #if UNITY_EDITOR } #endif } voxelIndex -= ONE_Y_ROW; } // Continue filling down vd = heights [hindex].terrainVoxelDirt; while (voxelIndex > bedrockRow) { if (voxels [voxelIndex].hasContent == 0) { voxels [voxelIndex].SetFastOpaque(vd); } voxelIndex -= ONE_Y_ROW; } if (bedrockRow >= 0) { voxels [voxelIndex].SetFastOpaque(bedrockVoxel); } hasContent = true; } } chunk.isAboveSurface = isAboveSurface; return(hasContent); }
void WriteChunkData_8(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> /// Uses geometry-shader-based materials and mesh to render the chunk /// </summary> void GenerateMeshData_Geo(int jobIndex) { VoxelChunk chunk = meshJobs [jobIndex].chunk; tempChunkVertices = meshJobs [jobIndex].vertices; tempChunkUV0 = meshJobs [jobIndex].uv0; tempChunkUV2 = meshJobs [jobIndex].uv2; tempChunkColors32 = meshJobs [jobIndex].colors; meshColliderVertices = meshJobs [jobIndex].colliderVertices; meshColliderIndices = meshJobs [jobIndex].colliderIndices; navMeshVertices = meshJobs [jobIndex].navMeshVertices; navMeshIndices = meshJobs [jobIndex].navMeshIndices; mivs = meshJobs [jobIndex].mivs; tempChunkVertices.Clear(); tempChunkUV0.Clear(); tempChunkColors32.Clear(); if (enableColliders) { meshColliderIndices.Clear(); meshColliderVertices.Clear(); if (enableNavMesh) { navMeshIndices.Clear(); navMeshVertices.Clear(); } } mivs.Clear(); Voxel[] voxels = chunk.voxels; Vector4 uvAux, uv2Aux; tempChunkUV2.Clear(); int chunkUVIndex = -1; int chunkVoxelCount = 0; Color32 tintColor = Misc.color32White; Vector3 pos = Misc.vector3zero; ModelInVoxel miv = new ModelInVoxel(); const int V_ONE_Y_ROW = 18 * 18; const int V_ONE_Z_ROW = 18; int voxelIndex = 0; int voxelSignature = 1; for (int y = 0; y < 16; y++) { int vy = (y + 1) * 18 * 18; for (int z = 0; z < 16; z++) { int vyz = vy + (z + 1) * 18; for (int x = 0; x < 16; x++, voxelIndex++) { voxels [voxelIndex].lightMesh = voxels [voxelIndex].light; if (voxels [voxelIndex].hasContent != 1) { continue; } int vxyz = vyz + x + 1; int vindex = vxyz - 1; Voxel[] chunk_middle_middle_left = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_middle_left = virtualChunk [vindex].voxelIndex; vindex = vxyz + 1; Voxel[] chunk_middle_middle_right = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_middle_right = virtualChunk [vindex].voxelIndex; vindex = vxyz + V_ONE_Y_ROW; Voxel[] chunk_top_middle_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int top_middle_middle = virtualChunk [vindex].voxelIndex; vindex = vxyz - V_ONE_Y_ROW; Voxel[] chunk_bottom_middle_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_middle_middle = virtualChunk [vindex].voxelIndex; vindex = vxyz + V_ONE_Z_ROW; Voxel[] chunk_middle_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_forward_middle = virtualChunk [vindex].voxelIndex; vindex = vxyz - V_ONE_Z_ROW; Voxel[] chunk_middle_back_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_back_middle = virtualChunk [vindex].voxelIndex; // If voxel is surrounded by material, don't render int v1u = chunk_top_middle_middle [top_middle_middle].opaque; int v1f = chunk_middle_forward_middle [middle_forward_middle].opaque; int v1b = chunk_middle_back_middle [middle_back_middle].opaque; int v1l = chunk_middle_middle_left [middle_middle_left].opaque; int v1r = chunk_middle_middle_right [middle_middle_right].opaque; int v1d = chunk_bottom_middle_middle [bottom_middle_middle].opaque; if (v1u + v1f + v1b + v1l + v1r + v1d == 90) // 90 = 15 * 6 { continue; } // top vindex = vxyz + V_ONE_Y_ROW + V_ONE_Z_ROW - 1; Voxel[] chunk_top_forward_left = chunk9 [virtualChunk [vindex].chunk9Index]; int top_forward_left = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_top_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int top_forward_middle = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_top_forward_right = chunk9 [virtualChunk [vindex].chunk9Index]; int top_forward_right = virtualChunk [vindex].voxelIndex; vindex = vxyz + V_ONE_Y_ROW - 1; Voxel[] chunk_top_middle_left = chunk9 [virtualChunk [vindex].chunk9Index]; int top_middle_left = virtualChunk [vindex].voxelIndex; vindex += 2; Voxel[] chunk_top_middle_right = chunk9 [virtualChunk [vindex].chunk9Index]; int top_middle_right = virtualChunk [vindex].voxelIndex; vindex = vxyz + V_ONE_Y_ROW - V_ONE_Z_ROW - 1; Voxel[] chunk_top_back_left = chunk9 [virtualChunk [vindex].chunk9Index]; int top_back_left = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_top_back_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int top_back_middle = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_top_back_right = chunk9 [virtualChunk [vindex].chunk9Index]; int top_back_right = virtualChunk [vindex].voxelIndex; // middle vindex = vxyz + V_ONE_Z_ROW - 1; Voxel[] chunk_middle_forward_left = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_forward_left = virtualChunk [vindex].voxelIndex; vindex += 2; Voxel[] chunk_middle_forward_right = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_forward_right = virtualChunk [vindex].voxelIndex; vindex = vxyz - V_ONE_Z_ROW - 1; Voxel[] chunk_middle_back_left = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_back_left = virtualChunk [vindex].voxelIndex; vindex += 2; Voxel[] chunk_middle_back_right = chunk9 [virtualChunk [vindex].chunk9Index]; int middle_back_right = virtualChunk [vindex].voxelIndex; // bottom vindex = vxyz - V_ONE_Y_ROW + V_ONE_Z_ROW - 1; Voxel[] chunk_bottom_forward_left = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_forward_left = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_bottom_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_forward_middle = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_bottom_forward_right = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_forward_right = virtualChunk [vindex].voxelIndex; vindex = vxyz - V_ONE_Y_ROW - 1; Voxel[] chunk_bottom_middle_left = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_middle_left = virtualChunk [vindex].voxelIndex; vindex += 2; Voxel[] chunk_bottom_middle_right = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_middle_right = virtualChunk [vindex].voxelIndex; vindex = vxyz - V_ONE_Y_ROW - V_ONE_Z_ROW - 1; Voxel[] chunk_bottom_back_left = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_back_left = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_bottom_back_middle = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_back_middle = virtualChunk [vindex].voxelIndex; vindex++; Voxel[] chunk_bottom_back_right = chunk9 [virtualChunk [vindex].chunk9Index]; int bottom_back_right = virtualChunk [vindex].voxelIndex; pos.y = y - 7.5f; pos.z = z - 7.5f; pos.x = x - 7.5f; voxelSignature += voxelIndex; chunkVoxelCount++; VoxelDefinition type = voxelDefinitions [voxels [voxelIndex].typeIndex]; FastFixedBuffer <int> indices = tempGeoIndices[type.materialBufferIndex]; uvAux.x = type.textureIndexSide; uvAux.y = type.textureIndexTop; uvAux.z = type.textureIndexBottom; uvAux.w = 0; int occlusionX = 0; int occlusionY = 0; int occlusionZ = 0; int occlusionW = 0; int occ = 0; RenderType rt = type.renderType; switch (rt) { case RenderType.Water: { ++chunkUVIndex; indices.Add(chunkUVIndex); uvAux.w = voxels [voxelIndex].light / 15f; int hf = chunk_middle_forward_middle [middle_forward_middle].GetWaterLevel(); int hb = chunk_middle_back_middle [middle_back_middle].GetWaterLevel(); int hr = chunk_middle_middle_right [middle_middle_right].GetWaterLevel(); int hl = chunk_middle_middle_left [middle_middle_left].GetWaterLevel(); // compute water neighbours and account for foam // back occ = chunk_middle_back_middle [middle_back_middle].hasContent; if (occ == 1) { // occlusionX bit = 0 means that face is visible occlusionX |= 1; if (hb == 0) { occlusionY |= 1; } } // front occ = chunk_middle_forward_middle [middle_forward_middle].hasContent; if (occ == 1) { occlusionX |= (1 << 1); if (hf == 0) { occlusionY |= 2; } } int wh = voxels [voxelIndex].GetWaterLevel(); int th = chunk_top_middle_middle [top_middle_middle].GetWaterLevel(); // top (hide only if water level is full or voxel on top is water) if (wh == 15 || th > 0) { occlusionX += ((chunk_top_middle_middle [top_middle_middle].hasContent & 1) << 2); } // down occlusionX += ((chunk_bottom_middle_middle [bottom_middle_middle].hasContent & 1) << 3); // left occ = chunk_middle_middle_left [middle_middle_left].hasContent; if (occ == 1) { occlusionX |= (1 << 4); if (hl == 0) { occlusionY |= 4; } } // right occ = chunk_middle_middle_right [middle_middle_right].hasContent; if (occ == 1) { occlusionX += (1 << 5); if (hr == 0) { occlusionY |= 8; } } // If there's water on top, full size if (th > 0) { occlusionW = 15 + (15 << 4) + (15 << 8) + (15 << 12); // full height occlusionY += (1 << 8) + (1 << 10); // neutral/no flow } else { // Get corners heights int hfr = chunk_middle_forward_right [middle_forward_right].GetWaterLevel(); int hbr = chunk_middle_back_right [middle_back_right].GetWaterLevel(); int hbl = chunk_middle_back_left [middle_back_left].GetWaterLevel(); int hfl = chunk_middle_forward_left [middle_forward_left].GetWaterLevel(); // corner foam if (type.showFoam) { if (hbl == 0) { occlusionY |= chunk_middle_back_left [middle_back_left].hasContent << 4; } if (hfl == 0) { occlusionY |= chunk_middle_forward_left [middle_forward_left].hasContent << 5; } if (hfr == 0) { occlusionY |= chunk_middle_forward_right [middle_forward_right].hasContent << 6; } if (hbr == 0) { occlusionY |= chunk_middle_back_right [middle_back_right].hasContent << 7; } } int tf = chunk_top_forward_middle [top_forward_middle].GetWaterLevel(); int tfr = chunk_top_forward_right [top_forward_right].GetWaterLevel(); int tr = chunk_top_middle_right [top_middle_right].GetWaterLevel(); int tbr = chunk_top_back_right [top_back_right].GetWaterLevel(); int tb = chunk_top_back_middle [top_back_middle].GetWaterLevel(); int tbl = chunk_top_back_left [top_back_left].GetWaterLevel(); int tl = chunk_top_middle_left [top_middle_left].GetWaterLevel(); int tfl = chunk_top_forward_left [top_forward_left].GetWaterLevel(); // forward right corner if (tf * hf + tfr * hfr + tr * hr > 0) { hfr = 15; } else { hfr = wh > hfr ? wh : hfr; if (hf > hfr) { hfr = hf; } if (hr > hfr) { hfr = hr; } } // bottom right corner if (tr * hr + tbr * hbr + tb * hb > 0) { hbr = 15; } else { hbr = wh > hbr ? wh : hbr; if (hr > hbr) { hbr = hr; } if (hb > hbr) { hbr = hb; } } // bottom left corner if (tb * hb + tbl * hbl + tl * hl > 0) { hbl = 15; } else { hbl = wh > hbl ? wh : hbl; if (hb > hbl) { hbl = hb; } if (hl > hbl) { hbl = hl; } } // forward left corner if (tl * hl + tfl * hfl + tf * hf > 0) { hfl = 15; } else { hfl = wh > hfl ? wh : hfl; if (hl > hfl) { hfl = hl; } if (hf > hfl) { hfl = hf; } } occlusionW = hfr + (hbr << 4) + (hbl << 8) + (hfl << 12); // flow int fx = hfr + hbr - hfl - hbl; if (fx > 0) { fx = 2; } else if (fx < 0) { fx = 0; } else { fx = 1; } int fz = hfl + hfr - hbl - hbr; if (fz > 0) { fz = 2; } else if (fz < 0) { fz = 0; } else { fz = 1; } occlusionY += (fx << 8) + (fz << 10); } if (!type.showFoam) { occlusionY &= 0xFF00; } pos.y -= 0.5f; tempChunkVertices.Add(pos); } break; case RenderType.CutoutCross: { ++chunkUVIndex; indices.Add(chunkUVIndex); uvAux.w = voxels [voxelIndex].light / 15f; Vector3 aux = pos; float random = WorldRand.GetValue(pos); uvAux.w *= 1f + (random - 0.45f) * type.colorVariation; // * (1f + random * 0.3f; // adds color variation pos.x += random * 0.5f - 0.25f; aux.x += 1f; random = WorldRand.GetValue(aux); pos.z += random * 0.5f - 0.25f; pos.y -= random * 0.1f; tempChunkVertices.Add(pos); } break; case RenderType.OpaqueNoAO: ++chunkUVIndex; tempChunkVertices.Add(pos); v1b = (v1b + 1) >> 4; // substitutes a comparisson with FULL_OPAQUE (15) v1f = (v1f + 1) >> 4; v1u = (v1u + 1) >> 4; v1d = (v1d + 1) >> 4; v1l = (v1l + 1) >> 4; v1r = (v1r + 1) >> 4; uvAux.w = v1b + (v1f << 1) + (v1u << 2) + (v1d << 3) + (v1l << 4) + (v1r << 5); indices.Add(chunkUVIndex); break; case RenderType.Transp6tex: { ++chunkUVIndex; indices.Add(chunkUVIndex); int rotation = voxels [voxelIndex].GetTextureRotation(); uvAux = type.textureSideIndices [rotation].xyzw; uvAux.w = voxels [voxelIndex].light; // Avoid overlapping faces of same glass material int typeIndex = voxels [voxelIndex].typeIndex; // back if (v1b == FULL_OPAQUE || chunk_middle_back_middle [middle_back_middle].typeIndex == typeIndex) { // occlusionX bit = 0 means that face is visible occlusionX |= 1; } // front if (v1f == FULL_OPAQUE || chunk_middle_forward_middle [middle_forward_middle].typeIndex == typeIndex) { occlusionX |= 2; } // up if (v1u == FULL_OPAQUE || chunk_top_middle_middle [top_middle_middle].typeIndex == typeIndex) { occlusionX |= 4; } // down if (v1d == FULL_OPAQUE || chunk_bottom_middle_middle [bottom_middle_middle].typeIndex == typeIndex) { occlusionX |= 8; } // left if (v1l == FULL_OPAQUE || chunk_middle_middle_left [middle_middle_left].typeIndex == typeIndex) { occlusionX |= 16; } // right if (v1r == FULL_OPAQUE || chunk_middle_middle_right [middle_middle_right].typeIndex == typeIndex) { occlusionX |= 32; } tempChunkVertices.Add(pos); // Pass custom alpha occlusionY = (int)(255 * type.alpha); // Add collider data if (enableColliders) { if (v1b == 0) { greedyCollider.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyCollider.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyCollider.AddQuad(FaceDirection.Top, x, z, y); } if (v1d == 0) { greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y); } if (v1l == 0) { greedyCollider.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyCollider.AddQuad(FaceDirection.Right, z, y, x); } if (enableNavMesh && type.navigatable) { if (v1b == 0) { greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y); } if (v1l == 0) { greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x); } } } } break; default: //case RenderType.Custom: miv.voxelIndex = voxelIndex; miv.vd = type; mivs.Add(miv); continue; case RenderType.Empty: { v1b = (v1b + 1) >> 4; // substitutes a comparison with FULL_OPAQUE (15) v1f = (v1f + 1) >> 4; v1u = (v1u + 1) >> 4; v1d = (v1d + 1) >> 4; v1l = (v1l + 1) >> 4; v1r = (v1r + 1) >> 4; // Add collider data if (enableColliders) { if (v1b == 0) { greedyCollider.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyCollider.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyCollider.AddQuad(FaceDirection.Top, x, z, y); } if (v1d == 0) { greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y); } if (v1l == 0) { greedyCollider.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyCollider.AddQuad(FaceDirection.Right, z, y, x); } if (enableNavMesh && type.navigatable) { if (v1b == 0) { greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y); } if (v1l == 0) { greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x); } } } } continue; // loop case RenderType.Opaque: case RenderType.Opaque6tex: case RenderType.Cutout: // Opaque & Cutout ++chunkUVIndex; tempChunkVertices.Add(pos); if (rt == RenderType.Cutout) { indices.Add(chunkUVIndex); } else { indices.Add(chunkUVIndex); int rotation = voxels [voxelIndex].GetTextureRotation(); uvAux = type.textureSideIndices [rotation].xyzw; } if (denseTrees && rt == RenderType.Cutout) { uvAux.w = 0; } else { v1b = (v1b + 1) >> 4; // substitutes a comparisson with FULL_OPAQUE (15) v1f = (v1f + 1) >> 4; v1u = (v1u + 1) >> 4; v1d = (v1d + 1) >> 4; v1l = (v1l + 1) >> 4; v1r = (v1r + 1) >> 4; uvAux.w = v1b + (v1f << 1) + (v1u << 2) + (v1d << 3) + (v1l << 4) + (v1r << 5); // Add collider data if (enableColliders && rt != RenderType.Cutout) { if (v1b == 0) { greedyCollider.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyCollider.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyCollider.AddQuad(FaceDirection.Top, x, z, y); } if (v1d == 0) { greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y); } if (v1l == 0) { greedyCollider.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyCollider.AddQuad(FaceDirection.Right, z, y, x); } if (enableNavMesh && type.navigatable) { if (v1b == 0) { greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z); } if (v1f == 0) { greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z); } if (v1u == 0) { greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y); } if (v1l == 0) { greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x); } if (v1r == 0) { greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x); } } } } if (rt == RenderType.Cutout) { // Add color variation float random = WorldRand.GetValue(pos); int r = (int)(255f * (1f + (random - 0.45f) * type.colorVariation)); uvAux.w += (r << 6); if (type.windAnimation) { uvAux.w += 65536; //1 << 16; } } int lu = chunk_top_middle_middle [top_middle_middle].light; int ll = chunk_middle_middle_left [middle_middle_left].light; int lf = chunk_middle_forward_middle [middle_forward_middle].light; int lr = chunk_middle_middle_right [middle_middle_right].light; int lb = chunk_middle_back_middle [middle_back_middle].light; int ld = chunk_bottom_middle_middle [bottom_middle_middle].light; #if UNITY_EDITOR if (enableSmoothLighting && !draftModeActive) { #else if (enableSmoothLighting) { #endif int v2r = chunk_top_middle_right [top_middle_right].light; int v2br = chunk_top_back_right [top_back_right].light; int v2b = chunk_top_back_middle [top_back_middle].light; int v2bl = chunk_top_back_left [top_back_left].light; int v2l = chunk_top_middle_left [top_middle_left].light; int v2fl = chunk_top_forward_left [top_forward_left].light; int v2f = chunk_top_forward_middle [top_forward_middle].light; int v2fr = chunk_top_forward_right [top_forward_right].light; int v1fr = chunk_middle_forward_right [middle_forward_right].light; int v1br = chunk_middle_back_right [middle_back_right].light; int v1bl = chunk_middle_back_left [middle_back_left].light; int v1fl = chunk_middle_forward_left [middle_forward_left].light; int v0r = chunk_bottom_middle_right [bottom_middle_right].light; int v0br = chunk_bottom_back_right [bottom_back_right].light; int v0b = chunk_bottom_back_middle [bottom_back_middle].light; int v0bl = chunk_bottom_back_left [bottom_back_left].light; int v0l = chunk_bottom_middle_left [bottom_middle_left].light; int v0fl = chunk_bottom_forward_left [bottom_forward_left].light; int v0f = chunk_bottom_forward_middle [bottom_forward_middle].light; int v0fr = chunk_bottom_forward_right [bottom_forward_right].light; // Backwards face // Vertex 0 occ = ((lb + v0b + v0bl + v1bl) >> 2); occlusionX += occ; // Vertex 1 occ = ((lb + v0b + v0br + v1br) >> 2); occlusionX += occ << 4; // Vertex 2 occ = ((lb + v1bl + v2bl + v2b) >> 2); occlusionX += occ << 8; // Vertex 3 occ = ((lb + v2b + v2br + v1br) >> 2); occlusionX += occ << 12; // Forward face // Vertex 5 occ = ((lf + v0f + v0fr + v1fr) >> 2); occlusionX += occ << 16; // Vertex 4 occ = ((lf + v0f + v0fl + v1fl) >> 2); occlusionX += occ << 20; // Vertex 6 occ = ((lf + v1fr + v2fr + v2f) >> 2); occlusionY += occ; // Vertex 7 occ = ((lf + v2f + v2fl + v1fl) >> 2); occlusionY += occ << 4; // Top face // Vertex 2 occ = ((lu + v2b + v2bl + v2l) >> 2); occlusionY += occ << 8; // Vertex 3 occ = ((lu + v2b + v2br + v2r) >> 2); occlusionY += occ << 12; // Vertex 7 occ = ((lu + v2l + v2fl + v2f)) >> 2; occlusionY += occ << 16; // Vertex 6 occ = ((lu + v2r + v2fr + v2f) >> 2); occlusionY += occ << 20; // Left face // Vertex 0 occ = ((ll + v1bl + v0l + v0bl) >> 2); occlusionZ += occ; // Vertex 4 occ = ((ll + v1fl + v0l + v0fl) >> 2); occlusionZ += occ << 4; // Vertex 2 occ = ((ll + v2l + v2bl + v1bl) >> 2); occlusionZ += occ << 8; // Vertex 7 occ = ((ll + v2l + v2fl + v1fl) >> 2); occlusionZ += occ << 12; // Right face // Vertex 1 occ = ((lr + v1br + v0r + v0br) >> 2); occlusionZ += occ << 16; // Vertex 5 occ = ((lr + v1fr + v0r + v0fr) >> 2); occlusionZ += occ << 20; // Vertex 3 occ = ((lr + v2r + v2br + v1br) >> 2); occlusionW += occ; // Vertex 6 occ = ((lr + v2r + v2fr + v1fr) >> 2); occlusionW += occ << 4; // Bottom face // Vertex 0 occ = ((ld + v0b + v0l + v0bl) >> 2); occlusionW += occ << 8; // Vertex 1 occ = ((ld + v0b + v0r + v0br) >> 2); occlusionW += occ << 12; // Vertex 4 occ = ((ld + v0f + v0l + v0fl) >> 2); occlusionW += occ << 16; // Vertex 5 occ = ((ld + v0f + v0r + v0fr) >> 2); occlusionW += occ << 20; } else { occlusionX = lb; // back occlusionX += lf << 4; // forward occlusionX += lu << 8; // top occlusionX += ll << 12; // // left occlusionX += lr << 16; // right occlusionX += ld << 20; // bottom } break; } tempChunkUV0.Add(uvAux); uv2Aux.x = occlusionX; uv2Aux.y = occlusionY; uv2Aux.z = occlusionZ; uv2Aux.w = occlusionW; tempChunkUV2.Add(uv2Aux); if (enableTinting) { tintColor.r = voxels [voxelIndex].red; tintColor.g = voxels [voxelIndex].green; tintColor.b = voxels [voxelIndex].blue; tempChunkColors32.Add(tintColor); } } } } meshJobs [jobIndex].chunk = chunk; // chunkVoxelCount includes MIVs int nonMivsCount = chunkUVIndex + 1; meshJobs [jobIndex].totalVisibleVoxels = nonMivsCount; if (chunkVoxelCount == 0) { return; } if (voxelSignature != chunk.voxelSignature) { chunk.needsColliderRebuild = true; } chunk.voxelSignature = voxelSignature; if (enableColliders) { if (chunk.needsColliderRebuild) { greedyCollider.FlushTriangles(meshColliderVertices, meshColliderIndices); if (enableNavMesh) { greedyNavMesh.FlushTriangles(navMeshVertices, navMeshIndices); } } else { greedyCollider.Clear(); greedyNavMesh.Clear(); } } // job index 0 lists are used as generic buffers but we need to convert them to array to use SetIndices API :( int subMeshCount = 0; for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++) { if (tempGeoIndices [k].count > 0) { subMeshCount++; meshJobs [jobIndex].buffers [k].indicesArray = tempGeoIndices [k].ToArray(); meshJobs [jobIndex].buffers [k].indicesCount = meshJobs [jobIndex].buffers [k].indicesArray.Length; } } meshJobs [jobIndex].subMeshCount = subMeshCount; meshJobs [jobIndex].colliderVertices = meshColliderVertices; meshJobs [jobIndex].colliderIndices = meshColliderIndices; meshJobs [jobIndex].navMeshVertices = navMeshVertices; meshJobs [jobIndex].navMeshIndices = navMeshIndices; meshJobs [jobIndex].mivs = mivs; } }
/// <summary> /// Clears a chunk /// </summary> void ChunkClearFast(VoxelChunk chunk) { chunk.ClearVoxels(noLightValue); }
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); } } }
void UploadMeshData(int jobIndex) { VoxelChunk chunk = meshJobs [jobIndex].chunk; if (meshJobs [jobIndex].totalVisibleVoxels == 0) { if (chunk.mf.sharedMesh != null) { chunk.mf.sharedMesh.Clear(false); } chunk.mc.enabled = false; chunk.renderState = ChunkRenderState.RenderingComplete; return; } // Create mesh #if !UNITY_EDITOR Mesh mesh; if (isMobilePlatform) { mesh = new Mesh(); // on mobile will be released mesh data upon uploading to the GPU so the mesh is no longer readable; need to recreate it everytime the chunk is rendered } else { mesh = chunk.mf.sharedMesh; if (mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); } } #else Mesh mesh = chunk.mf.sharedMesh; if (mesh == null) { mesh = new Mesh(); chunksDrawn++; } else { voxelsCreatedCount -= mesh.vertexCount; mesh.Clear(); } voxelsCreatedCount += meshJobs [jobIndex].totalVisibleVoxels; #endif // Vertices mesh.SetVertices(meshJobs [jobIndex].vertices); // UVs, normals, colors mesh.SetUVs(0, meshJobs [jobIndex].uv0); if (effectiveUseGeometryShaders) { mesh.SetUVs(1, meshJobs [jobIndex].uv2); } else { mesh.SetNormals(meshJobs [jobIndex].normals); } if (enableTinting) { mesh.SetColors(meshJobs [jobIndex].colors); } // Assign materials and submeshes int subMeshIndex = -1; int matIndex = 0; mesh.subMeshCount = meshJobs [jobIndex].subMeshCount; if (mesh.subMeshCount > 0) { if (effectiveUseGeometryShaders) { for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++) { if (meshJobs [jobIndex].buffers [k].indicesCount > 0) { subMeshIndex++; mesh.SetIndices(meshJobs [jobIndex].buffers [k].indicesArray, MeshTopology.Points, subMeshIndex, false); matIndex += 1 << k; } } } else { for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++) { if (meshJobs [jobIndex].buffers [k].indicesCount > 0) { subMeshIndex++; mesh.SetTriangles(meshJobs [jobIndex].buffers [k].indices, subMeshIndex, false); matIndex += 1 << k; } } } // Compute material array Material[] matArray; if (!materialsDict.TryGetValue(matIndex, out matArray)) { matArray = new Material[mesh.subMeshCount]; for (int k = 0, j = 0; k < MAX_MATERIALS_PER_CHUNK; k++) { if (meshJobs [jobIndex].buffers [k].indicesCount > 0) { matArray [j++] = materials [k]; } } materialsDict [matIndex] = matArray; } chunk.mr.sharedMaterials = matArray; mesh.bounds = enableCurvature ? Misc.bounds16Stretched : Misc.bounds16; chunk.mf.sharedMesh = mesh; #if !UNITY_EDITOR if (isMobilePlatform) { mesh.UploadMeshData(true); } #endif if (!chunk.mr.enabled) { chunk.mr.enabled = true; } } // Update collider? if (enableColliders && chunk.needsColliderRebuild) { int colliderVerticesCount = meshJobs [jobIndex].colliderVertices.Count; Mesh colliderMesh = chunk.mc.sharedMesh; if (colliderVerticesCount == 0 || !applicationIsPlaying) { chunk.mc.enabled = false; } else { if (colliderMesh == null) { colliderMesh = new Mesh(); } else { colliderMesh.Clear(); } colliderMesh.SetVertices(meshJobs [jobIndex].colliderVertices); colliderMesh.SetTriangles(meshJobs [jobIndex].colliderIndices, 0); chunk.mc.sharedMesh = colliderMesh; chunk.mc.enabled = true; } // Update navmesh if (enableNavMesh) { int navMeshVerticesCount = meshJobs [jobIndex].navMeshVertices.Count; Mesh navMesh = chunk.navMesh; if (navMesh == null) { navMesh = new Mesh(); } else { navMesh.Clear(); } navMesh.SetVertices(meshJobs [jobIndex].navMeshVertices); navMesh.SetTriangles(meshJobs [jobIndex].navMeshIndices, 0); chunk.navMesh = navMesh; AddChunkNavMesh(chunk); } } RenderModelsInVoxels(chunk, meshJobs [jobIndex].mivs); if (chunk.renderState != ChunkRenderState.RenderingComplete) { chunk.renderState = ChunkRenderState.RenderingComplete; if (OnChunkAfterFirstRender != null) { OnChunkAfterFirstRender(chunk); } } if (OnChunkRender != null) { OnChunkRender(chunk); } shouldUpdateParticlesLighting = true; }