public override void createGeoChunk(out Geo geo, BoundingBox bbox, Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { BarGeo barGeo = new BarGeo(); geo = barGeo; List <Midi.Note> noteList = midiTrack.Notes; if (noteList.Count == 0) { return; } float halfNoteHeight = Project.Props.NoteHeight / 2; int instanceIndex = 0; for (int n = 0; n < noteList.Count; n++) { Midi.Note note = noteList[n]; if (note.start > Project.Notes.SongLengthT) //only if audio ends before the notes end { continue; } Vector2 noteStart = Project.getScreenPos(note.start, note.pitch); Vector2 noteEnd = Project.getScreenPos(note.stop, note.pitch); //Create bounding boxes Vector3 boxMin = new Vector3(noteStart.X, noteStart.Y - halfNoteHeight, 0); Vector3 boxMax = new Vector3(noteEnd.X, noteEnd.Y + halfNoteHeight, 0); geo.bboxes.Add(new BoundingBoxEx(boxMin, boxMax)); //Create inctance data Vector2 topLeft_world = new Vector2(noteStart.X, noteStart.Y - halfNoteHeight); Vector2 size_world = new Vector2(noteEnd.X - noteStart.X, halfNoteHeight * 2 - 0.001f); Vector2 topLeft_tex = topLeft_world; Vector2 size_tex = size_world; Texture2D texture = texMaterial.TexProps.Texture; if (texture != null) { Vector2 texSize = new Vector2(texture.Width, texture.Height); calcRectTexCoords(out topLeft_tex, out size_tex, texSize, topLeft_world, size_world, texMaterial); } instanceVerts[instanceIndex].destRect = new Vector4(topLeft_world.X, topLeft_world.Y, size_world.X, size_world.Y); instanceVerts[instanceIndex].srcRect = new Vector4(topLeft_tex.X, topLeft_tex.Y, size_tex.X, size_tex.Y); if (++instanceIndex >= MaxInstances - 1) { createVb(ref instanceIndex, barGeo); } } if (instanceIndex > 0) { createVb(ref instanceIndex, barGeo); } }
void drawHighLights(Midi.Track midiTrack, TrackProps trackProps, float songPosP) { List <Midi.Note> noteList = midiTrack.Notes; int hlNoteIndex = midiTrack.getLastNoteIndexAtTime((int)(Project.SongPosT - Project.PlaybackOffsetT)); if (hlNoteIndex < 0) { return; } Midi.Note note = noteList[hlNoteIndex], nextNote; if (note.start > Project.Notes.SongLengthT) //only if audio ends before the notes end { return; } if (hlNoteIndex < noteList.Count - 1) { nextNote = noteList[hlNoteIndex + 1]; } else { nextNote = note; } Vector3 noteStart = new Vector3(Project.getScreenPos(note.start, note.pitch), 0); float noteEnd = Project.getScreenPos(note.stop, note.pitch).X; Vector3 nextNoteStart = new Vector3(Project.getScreenPos(nextNote.start, nextNote.pitch), 0); if (noteEnd > nextNoteStart.X && hlNoteIndex < noteList.Count - 1) { noteEnd = nextNoteStart.X; } if ((float)(nextNote.start - note.stop) > Qn_gapThreshold * Project.Notes.TicksPerBeat || note == nextNote) { if (nextNoteStart.X != noteStart.X) { nextNoteStart.Y = MathHelper.Lerp(noteStart.Y, nextNoteStart.Y, (float)(noteEnd - noteStart.X) / (nextNoteStart.X - noteStart.X)); } nextNoteStart.X = noteEnd; } Vector3 hlPos = noteStart; float noteLength = (noteEnd - noteStart.X); float normPos = (songPosP - noteStart.X) / noteLength; if ((bool)MovingHl) { float poweredNormPos = (float)Math.Pow(normPos, (double)HlMovementPow); hlPos.X = noteStart.X + poweredNormPos * noteLength; hlPos.Y = Project.getScreenPosY(trackProps.TrackView.Curve.Evaluate((float)Project.pixelsToTicks(hlPos.X))); } //Set common fx params--------------------- //For shrinking highlights float shrinkPercent = normPos * 1.0001f; if (!(bool)ShrinkingHl) { shrinkPercent = 0; if ((bool)HlBorder) { shrinkPercent = 1; } } fx.Parameters["ClipPercent"].SetValue(shrinkPercent); float innerHlSize = VpHlSize * 0.5f * (1 - shrinkPercent); fx.Parameters["InnerHlSize"].SetValue(innerHlSize); //Vector4 hlColor; //Texture2D hlTexture; //getMaterial(trackProps, true, out hlColor, out hlTexture); //Vector4 hlColor = fx.Parameters["HlColor"].GetValueVector4(); //fx.Parameters["HlColor"].SetValue(SongPanel.HSLA2RGBA(hlColor).ToVector4()); fx.Parameters["Border"].SetValue((bool)HlBorder); //----------------------------------------------- if (HlType == LineHlType.Arrow) { float arrowLength; Vector3 arrowDir; Vector3 arrowNormal; if (!(bool)MovingHl) //Non-moving arrow { Vector3 nextNoteOffset = nextNoteStart - noteStart; arrowLength = nextNoteOffset.Length(); arrowDir = nextNoteOffset;; } else //Moving arrow { float x1 = hlPos.X; float y1 = hlPos.Y; float x2 = x1 + 0.001f; float pitch2 = trackProps.TrackView.Curve.Evaluate((float)Project.pixelsToTicks(x2)); float y2 = Project.getScreenPosY(pitch2); arrowDir = new Vector3(x2 - x1, y2 - y1, 0); arrowLength = VpHlSize * 1.25f; //Make arrow 25% longer than wide } arrowDir.Normalize(); arrowNormal = new Vector3(-arrowDir.Y, arrowDir.X, 0); arrowNormal.Normalize(); float halfArrowWidth = VpHlSize * 0.5f; lineHlVerts[0].pos = hlPos + arrowNormal * halfArrowWidth; lineHlVerts[1].pos = hlPos - arrowNormal * halfArrowWidth; lineHlVerts[2].pos = hlPos + arrowDir * arrowLength; fx.Parameters["ArrowDir"].SetValue(arrowDir); //lineFx.Parameters["ArrowLength"].SetValue(nextNoteOffsetLength); fx.Parameters["ArrowStart"].SetValue(hlPos); fx.Parameters["ArrowEnd"].SetValue(lineHlVerts[2].pos); //Is used to calc distance from the two "sides" of the triangle (not the bottom) since they share this point Vector3 side1Tangent = lineHlVerts[2].pos - lineHlVerts[0].pos; Vector3 side1Normal = new Vector3(-side1Tangent.Y, side1Tangent.X, 0); side1Normal.Normalize(); fx.Parameters["Side1Normal"].SetValue(side1Normal); Vector3 side2Tangent = lineHlVerts[2].pos - lineHlVerts[1].pos; Vector3 side2Normal = new Vector3(-side2Tangent.Y, side2Tangent.X, 0); side2Normal.Normalize(); fx.Parameters["Side2Normal"].SetValue(side2Normal); //Calc shortest dist to incenter from border, ie. the inscribed circle's radius float a = (lineHlVerts[0].pos - lineHlVerts[1].pos).Length(); float b = (lineHlVerts[0].pos - lineHlVerts[2].pos).Length(); float c = (lineHlVerts[1].pos - lineHlVerts[2].pos).Length(); float k = (a + b + c) / 2.0f; float icRadius = (float)Math.Sqrt(k * (k - a) * (k - b) * (k - c)) / k; fx.Parameters["DistToCenter"].SetValue(icRadius); fx.CurrentTechnique = fx.Techniques["Arrow"]; fx.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, lineHlVerts, 0, 1); } else if (HlType == LineHlType.Circle) { setHlCirclePos(hlPos); fx.CurrentTechnique = fx.Techniques["Circle"]; fx.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, lineHlVerts, 0, 2); } }
//static string GetName<T>(T item) where T : class //{ // return typeof(T).GetProperties()[0].Name; //} public override void createGeoChunk(out Geo geo, BoundingBox bbox, Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { LineGeo lineGeo = new LineGeo(); geo = lineGeo; if (LineWidth == 0) { return; } List <Midi.Note> noteList = midiTrack.Notes; //List<Midi.Note> noteList = getNotes(0, midiTrack, songDrawProps); if (noteList.Count == 0) { return; } int vertIndex = 3; int hLineVertIndex = 0; int completeNoteListIndex = midiTrack.Notes.IndexOf(noteList[0]); float vpLineWidth = VpLineWidth; float maxNumBboxesPerScreenWidth = 1000; float bboxMinSqLength = (float)Math.Pow(Project.Props.Camera.ViewportSize.X / maxNumBboxesPerScreenWidth, 2); //for (int i = 0; i < 100; i++) //{ // vertIndex = 30000; // createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); //} //return; for (int n = 0; n < noteList.Count; n++) { //Get current note Midi.Note note = noteList[n], nextNote; if (note.start > Project.Notes.SongLengthT) //only if audio ends before the notes end { continue; } if (n < noteList.Count - 1) { nextNote = noteList[n + 1]; } else if (completeNoteListIndex < midiTrack.Notes.Count - 1) { nextNote = midiTrack.Notes[completeNoteListIndex + 1]; } else { nextNote = note; } Vector2 noteStart = Project.getScreenPos(note.start, note.pitch); float noteEnd = Project.getScreenPos(note.stop, note.pitch).X; Vector2 nextNoteStart = Project.getScreenPos(nextNote.start, nextNote.pitch); if (noteEnd > nextNoteStart.X && completeNoteListIndex < midiTrack.Notes.Count - 1) { noteEnd = nextNoteStart.X; } bool endOfSegment = false; if ((float)(nextNote.start - note.stop) > Qn_gapThreshold * Project.Notes.TicksPerBeat || note == nextNote) { if (nextNoteStart.X != noteStart.X) { nextNoteStart.Y = (int)MathHelper.Lerp(noteStart.Y, nextNoteStart.Y, (float)(noteEnd - noteStart.X) / (nextNoteStart.X - noteStart.X)); } nextNoteStart.X = noteEnd; endOfSegment = true; } float startDraw = noteStart.X; float endDraw = nextNoteStart.X; //Don't draw if length is 0 if (startDraw == endDraw) { continue; } float curvature = calcLinearLineAngle(n, trackProps.TrackView.Curve); //if (n > 0) //{ // float prevCurvature = calcLinearLineAngle(n - 1, trackProps.TrackView.Curve); // if (prevCurvature > curvature) // curvature = (prevCurvature + curvature) / 2; //Prevent too big jumps between levels of tesselation which will cause sharp bends //} if (n < noteList.Count - 1) { curvature = Math.Max(curvature, calcLinearLineAngle(n + 1, trackProps.TrackView.Curve)); } //curvature /= (float)Math.PI; //Map to [0,1] curvature = Math.Min(curvature, 0.97f * (float)Math.PI); //Clip below 180 degrees to avoid extreme tesselation curvature = (float)Math.Pow(curvature, 2.25) * 1000; float step; if (curvature == 0) { step = (endDraw - startDraw) * 0.999f; //Only one point for the note } else { step = 1 / curvature; } if (step >= endDraw - startDraw) { step = (endDraw - startDraw);// * 0.999f; } int iterations = (int)((endDraw - startDraw) / step); if (iterations < 0) { throw new OverflowException($"Too many vertices for note {n}, track {trackProps.TrackView.TrackNumber}."); } step = (endDraw - startDraw) / (iterations + 1); Vector3 bboxStart = Vector3.Zero; for (float x = startDraw; x <= endDraw + 0.00001f; x += step) { Vector3 center, normal, vertexOffset; getCurvePoint(out center, out normal, out vertexOffset, step, x, trackProps, vpLineWidth); //normal.X = curvature/10f; lineVerts[vertIndex].normal = lineVerts[vertIndex + 1].normal = normal; //Create vertices //adjustCurvePoint(center, vertexOffset, -1, trackProps, lineVerts, vertIndex, step, vpLineWidth); //adjustCurvePoint(center, vertexOffset, 1, trackProps, lineVerts, vertIndex + 1, step, vpLineWidth); //curvature = trackProps.TrackView.Curve.EvaluateCurvature(Project.getTimeT(x)); //center.Y = curvature / 10; lineVerts[vertIndex].pos = center - vertexOffset; lineVerts[vertIndex + 1].pos = center + vertexOffset; lineVerts[vertIndex].center = lineVerts[vertIndex + 1].center = center; float normStepFromNoteStart = (x - startDraw) / (nextNoteStart.X - noteStart.X); //lineVerts[vertIndex].normStepFromNoteStart = lineVerts[vertIndex + 1].normStepFromNoteStart = normStepFromNoteStart; lineVerts[vertIndex].normPos = new Vector2(normStepFromNoteStart, 0); lineVerts[vertIndex + 1].normPos = new Vector2(normStepFromNoteStart, 1); if (texMaterial.TexProps.Texture != null) { calcTexCoords(out lineVerts[vertIndex].texCoords, out lineVerts[vertIndex + 1].texCoords, texMaterial.TexProps, x - startDraw, normStepFromNoteStart, vpLineWidth, lineVerts[vertIndex].pos, lineVerts[vertIndex + 1].pos); } if (LineType == VisualMusic.LineType.Ribbon) { float hLineStart = center.X; float hLineEnd = hLineStart; do { hLineEnd += step; } while ((int)center.Y == (int)Project.getCurveScreenY((float)hLineEnd + step, trackProps.TrackView.Curve) && hLineEnd < endDraw); if (hLineEnd > hLineStart + vpLineWidth / 2) { hLineVerts[hLineVertIndex++] = lineVerts[vertIndex]; hLineVerts[hLineVertIndex++] = lineVerts[vertIndex + 1]; } //float horizontalFactor = Math.Abs(normal.Y); //float horizontalLimit = 0.98f; //float minThickness = 0.001f; //if (horizontalFactor > horizontalLimit) //{ //todo use vertexoffset for non-ribbon instead of normal // horizontalFactor = (horizontalFactor - horizontalLimit) / (1 - horizontalLimit); //[horizontalFactor, 1] -> [0, 1] // lineVerts[vertIndex].pos.Y -= minThickness * horizontalFactor * Math.Sign(normal.X); //} } if (x == startDraw && vertIndex > 3) { lineVerts[vertIndex].pos = lineVerts[vertIndex - 2].pos; lineVerts[vertIndex + 1].pos = lineVerts[vertIndex - 1].pos; } //Create bounding box if (bboxStart == Vector3.Zero) { bboxStart = lineVerts[vertIndex].center; } Vector3 bboxEnd = lineVerts[vertIndex].center; if (Vector3.DistanceSquared(bboxStart, bboxEnd) > bboxMinSqLength) { Vector3 bboxCenter = (bboxStart + bboxEnd) / 2; geo.bboxes.Add(new BoundingBoxEx(bboxCenter, bboxEnd - bboxCenter, bboxEnd - lineVerts[vertIndex].pos, new Vector3(0, 0, 0))); bboxStart = bboxEnd; } vertIndex += 2; if (vertIndex >= MaxLineVerts - 2 || hLineVertIndex >= MaxHLineVerts - 2) { createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); x -= step; } //break; } if (!(bool)Continuous) { endOfSegment = true; //One draw call per note. Can be used to avoid glitches between notes because of instant IN.normStepFromNoteStart interpolation from 1 to 0. } if (endOfSegment) { createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); } completeNoteListIndex++; } createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); }