public void CalculateMirrorPlanes() { // clear old contents planeCenters.Clear(); planeNormals.Clear(); // determine triangle centers and normals in world coordinates Mesh room_mesh = GetComponent <MeshFilter>().mesh; Vector3[] room_n = room_mesh.normals; Vector3[] room_vertices = room_mesh.vertices; int[] room_tri = room_mesh.triangles; Matrix4x4 M_scene = transform.localToWorldMatrix; Matrix4x4 N_scene = Matrix4x4.Transpose(Matrix4x4.Inverse(M_scene)); for (var i_tri = 0; i_tri < room_tri.Length; i_tri += 3) { Vector3 new_center = (Vector3)(M_scene * ((room_vertices[room_tri[i_tri]] + room_vertices[room_tri[i_tri + 1]] + room_vertices[room_tri[i_tri + 2]]) / 3)) + transform.position; Vector3 new_normal = ((Vector3)(N_scene * ((room_n[room_tri[i_tri]] + room_n[room_tri[i_tri + 1]] + room_n[room_tri[i_tri + 2]]) / 3))).normalized; // check for duplicate planes bool new_plane = true; for (var i_plane = 0; i_plane < planeCenters.Count; ++i_plane) { if (ISMMath.PlaneEQ(planeCenters[i_plane], planeNormals[i_plane], new_center, new_normal)) { new_plane = false; break; } } if (new_plane) { // Add plane if not a duplicate planeCenters.Add(new_center); planeNormals.Add(new_normal); } } }
/// <summary> /// Apply Image Source Method (ISM) to calculate specular reflections /// </summary> void ISMUpdate() { // Clear old data hitPaths.Clear(); System.Array.Clear(ir, 0, ir.Length); // Check if the image source positions must be updated if (SourceHasMoved() || renderSettings.IRUpdateRequested) { // Clear old image sources imageSources.Clear(); // === E1: Add direct sound === // Add the original source to the image sources list // (E1) YOUR CODE HERE ImageSource OriginalSource = new ImageSource(transform.position); imageSources.Add(OriginalSource); // For each order of reflection int i_end = 0; for (var i_refl = 0; i_refl < renderSettings.NumberOfISMReflections; ++i_refl) { // === E4: Higher order reflections === // (E4) YOUR CODE HERE: Update parent interval int i_begin = 0; i_end = imageSources.Count; // i_end = ... Mathf.Min(1, imageSources.Count // For each parent to reflect = i_begin; i_parent < i_end; /* <-- (E4) YOUR CODE HERE: use i_begin and i_end to go through the parent image sources */ for (var i_parent = i_begin; i_parent < i_end; ++i_parent) { // === E2: Calculate image source positions === // Parent source on this iteration ImageSource parentSource = imageSources[i_parent]; // For each mirroring plane for (var i_child = 0; i_child < renderSettings.PlaneCenters.Length; ++i_child) { // Get the current mirroring plane Vector3 p_plane = renderSettings.PlaneCenters[i_child]; Vector3 n_plane = renderSettings.PlaneNormals[i_child]; // (E2) YOUR CODE HERE: calculate the distance from the plane to the source Plane wall_plane = new Plane(); wall_plane.SetNormalAndPosition(n_plane, p_plane); //Debug.Log(wall_plane.GetDistanceToPoint(transform.position)); float sourcePlaneDistance = wall_plane.GetDistanceToPoint(imageSources[i_parent].pos); // Is the parent source in front of the plane? if (sourcePlaneDistance > 0) /* <-- (E2) YOUR CODE HERE */ { // Parent source is in front of the plane, // calculate mirrored position Vector3 mirroredPosition = imageSources[i_parent].pos + 2 * sourcePlaneDistance * (-n_plane); // Add the image source // (E2) YOUR CODE HERE ImageSource IMGSource = new ImageSource(mirroredPosition, p_plane, n_plane, i_parent); imageSources.Add(IMGSource); } } } } } // === E3: Cast rays === // A mask for game objects using ISMCollider (You define this "User Layer") int ism_colliders_only = LayerMask.GetMask("ISM colliders"); // For each image source for (var i = 0; i < imageSources.Count; ++i) { // Calculate path length float pathLength = Vector3.Distance(imageSources[i].pos, ListenerPosition);/* <-- (E3) YOUR CODE HERE */ // Check that the path can contribute to the impulse response if (pathLength < renderSettings.MaximumRayLength) { // Create a container for this path RaycastHitPath path = new RaycastHitPath(pathLength); // (E3) YOUR CODE HERE: Set the listener as the starting point for // the ray Vector3 origin = ListenerPosition; //Debug.Log(ListenerPosition.x); Vector3 originNormal = imageSources[i].pos - origin; int i_next = i; bool isValidPath = true; // Loop until we have either processed the original source or // found the path invalid while (i_next != -1 && isValidPath) { // Get the current source ImageSource imageSource = imageSources[i_next]; // (E3) YOUR CODE HERE: Determine ray direction and length Vector3 dir = originNormal; float max_length = Vector3.Distance(origin, imageSource.pos); //I OR I NEXT? //Debug.Log(max_length); // Trace the ray RaycastHit hit; //Physics.Raycast(origin, dir, out hit, max_length, ism_colliders_only); Physics.Raycast(origin, dir, out hit, max_length); //Debug.Log(hit.point); //Debug.Log(hit.distance); //Debug.Log(hit.collider); if (imageSource.i_parent == -1) { // Handle the real source // (E3) YOUR CODE HERE: check that the path is not obstructed //if (Mathf.Abs(max_length - hit.distance) < 0.2) if (Mathf.Abs(max_length - hit.distance) < 0.2) { isValidPath = true; //Physics.Raycast(origin, dir); Debug.Log("path is not obstructed"); } else { isValidPath = false; Debug.Log("path is invalid"); } } else { // Handle image sources // (E3) YOUR CODE HERE: check that the ray hits a wall on mirroring plane isValidPath = ISMMath.PlaneEQ(hit, imageSource); } // (E3) Are there more checks needed? This depends on your previous implementation. // Add the traced path if it is still valid if (isValidPath) { // Path is valid, add the hit point to the ray path // (E3) YOUR CODE HERE path.points.Add(hit.point); Debug.Log("path is valid, add hit point"); // Prepare to send the ray towards the next image source // it that okay? origin = hit.point; i_next = imageSource.i_parent; //originNormal = imageSources[i_next].pos - origin; if (i_next != -1) { originNormal = imageSources[i_next].pos - origin; } } } // Add the traced path if it is still valid if (isValidPath) { // (E3) YOUR CODE HERE Debug.Log("path added"); hitPaths.Add(path); } } } // === E5: create image source impulse response === foreach (var path in hitPaths) { // Calculate the sample that the ray path contributes to int i_path = Mathf.RoundToInt( AudioSettings.outputSampleRate * path.totalPathLength / ISMRenderSettings.speedOfSound); // <-- (E5) YOUR CODE HERE if (i_path < ir.Length) { float abs = renderSettings.Absorption; float diff = renderSettings.DiffuseProportion; float num = path.points.Count; float eps = Mathf.Pow(10, -6); float p_ray = (Mathf.Pow((1 - abs) * (1 - diff), num / 2) / (path.totalPathLength + eps)); // (E5) YOUR CODE HERE: Determine the signal magnitude w.r.t. // the amount of wall hits in the path ir[i_path] += p_ray; } } }
/// <summary> /// Apply Image Source Method (ISM) to calculate specular reflections /// </summary> void ISMUpdate() { // Clear old data hitPaths.Clear(); System.Array.Clear(ir, 0, ir.Length); // Check if the image source positions must be updated if (SourceHasMoved() || renderSettings.IRUpdateRequested) { // Clear old image sources imageSources.Clear(); // === E1: Add direct sound === // (E1) YOUR CODE HERE // Add the original source to the image sources list imageSources.Add(new ImageSource(SourcePosition)); // For each order of reflection int i_begin; int i_end = 0; for (var i_refl = 0; i_refl < renderSettings.NumberOfISMReflections; ++i_refl) { // === E4: Higher order reflections === // (E4) YOUR CODE HERE: Update parent interval i_begin = i_end; i_end = imageSources.Count; // For each parent to reflect for (var i_parent = i_begin; i_parent < i_end; ++i_parent) // <-- (E4) YOUR CODE HERE { // === E2: Calculate image source positions === // Parent source on this iteration ImageSource parentSource = imageSources[i_parent]; // For each mirroring plane for (var i_child = 0; i_child < renderSettings.PlaneCenters.Length; ++i_child) { // Get the current mirroring plane Vector3 p_plane = renderSettings.PlaneCenters[i_child]; Vector3 n_plane = renderSettings.PlaneNormals[i_child]; // (E2) YOUR CODE HERE: calculate the distance from the plane to the source float sourcePlaneDistance = Vector3.Dot(parentSource.pos - p_plane, n_plane); // Is the parent source in front of the plane? if (sourcePlaneDistance > 0.0) { // Parent source is in front of the plane, calculate mirrored position Vector3 N = transform.TransformDirection(n_plane); Vector3 mirroredPosition = parentSource.pos - 2 * n_plane * sourcePlaneDistance; // (E2) YOUR CODE HERE // Add the image source imageSources.Add(new ImageSource(mirroredPosition, p_plane, n_plane, i_parent)); } } } } } // === E3: Cast rays === // A mask for game objects using ISMCollider int ism_colliders_only = LayerMask.GetMask("ISM colliders"); // For each image source for (var i = 0; i < imageSources.Count; ++i) { // Calculate path length float pathLength = Vector3.Distance(imageSources[i].pos, ListenerPosition); // Check that the path can contribute to the impulse response if (pathLength < renderSettings.MaximumRayLength) { // Create a container for this path RaycastHitPath path = new RaycastHitPath(pathLength); // (E3) YOUR CODE HERE: Set the listener as the starting point for // the ray Vector3 origin = ListenerPosition; Vector3 originNormal = imageSources[i].pos - origin; int i_next = i; bool isValidPath = true; // Loop until we have either processed the original source or // found the path invalid while (i_next != -1 && isValidPath) { // Get the current source ImageSource imageSource = imageSources[i_next]; // (E3) YOUR CODE HERE: Determine ray direction and length Vector3 dir = originNormal; float max_length = Vector3.Distance(origin, imageSource.pos); // Trace the ray RaycastHit hit; // First, check that the outgoing ray is reflected from the wall if (!Physics.Raycast(origin, dir, out hit, max_length, ism_colliders_only)) { // The ray is sent in the wall, so the path is invalid isValidPath = false; } else if (imageSource.i_parent == -1) { // (E3) YOUR CODE HERE // Handle the real source: check that the path is not obstructed if (Mathf.Abs(max_length - hit.distance) < 0.2) { isValidPath = true; } else { isValidPath = false; } } else { // Handle image sources // (E3) YOUR CODE HERE: check that the ray hits a wall on mirroring plane isValidPath = ISMMath.PlaneEQ(hit, imageSource); } // Add the traced path if it is still valid if (isValidPath) { // Path is valid, add the hit point to the ray path // (E3) YOUR CODE HERE path.points.Add(hit.point); // Prepare to send the ray towards the next image source i_next = imageSource.i_parent; origin = hit.point; if (i_next != -1) { originNormal = imageSources[i_next].pos - origin; } } } // Add the traced path if it is still valid if (isValidPath) { // (E3) YOUR CODE HERE hitPaths.Add(path); } } } // === E5: create image source impulse response === foreach (var path in hitPaths) { // Calculate the sample that the ray path contributes to int i_path = Mathf.RoundToInt( // <-- (E5) YOUR CODE HERE AudioSettings.outputSampleRate * path.totalPathLength / ISMRenderSettings.speedOfSound ); if (i_path < ir.Length) { // (E5) YOUR CODE HERE: Determine the signal magnitude w.r.t. // the amount of wall hits in the path ir[i_path] += Mathf.Pow( (1 - renderSettings.Absorption) * (1 - renderSettings.DiffuseProportion), path.points.Count / 2 ) / (path.totalPathLength + float.Epsilon); } } }