├── bass.dll
├── icon.ico
├── rsrc.syso
├── bass_fx.dll
├── libbass.so
├── libbass.dylib
├── libbass_fx.so
├── libbass_fx.dylib
├── assets
├── textures
│ ├── cursor.png
│ ├── hit-0.png
│ ├── hit-100.png
│ ├── hit-300.png
│ ├── hit-50.png
│ ├── default-0.png
│ ├── default-1.png
│ ├── default-2.png
│ ├── default-3.png
│ ├── default-4.png
│ ├── default-5.png
│ ├── default-6.png
│ ├── default-7.png
│ ├── default-8.png
│ ├── default-9.png
│ ├── hitcircle.png
│ ├── presskey.png
│ ├── cursor-top.png
│ ├── cursortrail.png
│ ├── dansercoin.png
│ ├── dansercoin16.png
│ ├── dansercoin24.png
│ ├── dansercoin48.png
│ ├── reversearrow.png
│ ├── sliderball.png
│ ├── spinner-top.png
│ ├── approachcircle.png
│ ├── hitcircle-full.png
│ ├── slidergradient.png
│ ├── spinner-bottom.png
│ ├── spinner-circle.png
│ ├── spinner-clear.png
│ ├── spinner-middle.png
│ ├── hitcircleoverlay.png
│ ├── ranking-a-small.png
│ ├── ranking-b-small.png
│ ├── ranking-c-small.png
│ ├── ranking-d-small.png
│ ├── ranking-s-small.png
│ ├── ranking-sh-small.png
│ ├── ranking-x-small.png
│ ├── ranking-xh-small.png
│ ├── sliderscorepoint.png
│ ├── spinner-background.png
│ ├── spinner-approachcircle.png
│ └── skin.ini
├── fonts
│ ├── Roboto-Black.ttf
│ └── Roboto-Bold.ttf
├── sounds
│ ├── drum-hitclap.wav
│ ├── drum-hitfinish.wav
│ ├── drum-hitnormal.wav
│ ├── normal-hitclap.wav
│ ├── soft-hitclap.wav
│ ├── soft-hitfinish.wav
│ ├── soft-hitnormal.wav
│ ├── drum-hitwhistle.wav
│ ├── drum-slidertick.wav
│ ├── normal-hitfinish.wav
│ ├── normal-hitnormal.wav
│ ├── soft-hitwhistle.wav
│ ├── soft-slidertick.wav
│ ├── normal-hitwhistle.wav
│ └── normal-slidertick.wav
└── shaders
│ ├── fbopass.vsh
│ ├── fbopass.fsh
│ ├── slider.vsh
│ ├── slider.fsh
│ ├── sprite.fsh
│ ├── combine.fsh
│ ├── cursortrail.fsh
│ ├── sprite.vsh
│ ├── brightfilter.fsh
│ ├── cursortrail.vsh
│ └── blur.fsh
├── states
└── state.go
├── hitjudge
├── hitresult.go
├── key.go
├── objectresult.go
├── error.go
├── totalresult.go
└── errorio.go
├── score
├── rank.go
├── ppcalculater.go
└── scorecalculater.go
├── bmath
├── curves
│ ├── curve.go
│ ├── linear.go
│ ├── cirarc.go
│ ├── catmull.go
│ └── bezier.go
├── math.go
├── vector2d.go
├── sliders
│ └── sliderAlgo.go
└── camera.go
├── dance
├── movers
│ ├── mover.go
│ ├── linear.go
│ ├── axisaligned.go
│ ├── halfcircle.go
│ ├── bezier.go
│ └── angleoffset.go
└── schedulers
│ ├── scheduler.go
│ ├── sliderprocessor.go
│ └── generic.go
├── prof
└── prof.go
├── replay
├── replayparser.go
└── replayreader.go
├── error.err
├── .gitattributes
├── audio
├── system.go
├── sample.go
├── osuaudio.go
└── music.go
├── main.go
├── .gitignore
├── wheelmodified.md
├── settings
├── dance.go
└── manager.go
├── README.md
├── utils
├── fps.go
├── screenshot.go
├── wintime.go
├── colors.go
└── utils.go
├── storyboard
├── enums.go
├── loop.go
├── transformations.go
├── layer.go
├── commands.go
└── object.go
├── danser.exe.manifest
├── hitjudgetest
└── Genryuu Kaiko.md
├── configui
└── varassign.go
├── beatmap
├── objects
│ ├── pause.go
│ ├── baseobject.go
│ ├── timing.go
│ ├── util.go
│ └── circle.go
├── loader.go
└── beatmap.go
├── LICENSE
├── osuconst
└── osuconst.go
├── resultcache
└── cacheio.go
├── animation
├── glider.go
└── easing
│ └── equations.go
├── render
├── framebuffer
│ └── framebuffer.go
├── fx.go
├── texture
│ ├── single.go
│ ├── texture.go
│ └── atlas.go
├── effects
│ ├── bloom.go
│ └── blur.go
├── slider.go
└── font
│ └── font.go
├── ini
└── ini.go
├── settings-1080.json
├── settings.json
└── database
└── manager.go
/bass.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/bass.dll
--------------------------------------------------------------------------------
/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/icon.ico
--------------------------------------------------------------------------------
/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/rsrc.syso
--------------------------------------------------------------------------------
/bass_fx.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/bass_fx.dll
--------------------------------------------------------------------------------
/libbass.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/libbass.so
--------------------------------------------------------------------------------
/libbass.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/libbass.dylib
--------------------------------------------------------------------------------
/libbass_fx.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/libbass_fx.so
--------------------------------------------------------------------------------
/libbass_fx.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/libbass_fx.dylib
--------------------------------------------------------------------------------
/assets/textures/cursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/cursor.png
--------------------------------------------------------------------------------
/assets/textures/hit-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hit-0.png
--------------------------------------------------------------------------------
/assets/textures/hit-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hit-100.png
--------------------------------------------------------------------------------
/assets/textures/hit-300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hit-300.png
--------------------------------------------------------------------------------
/assets/textures/hit-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hit-50.png
--------------------------------------------------------------------------------
/assets/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/assets/textures/default-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-0.png
--------------------------------------------------------------------------------
/assets/textures/default-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-1.png
--------------------------------------------------------------------------------
/assets/textures/default-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-2.png
--------------------------------------------------------------------------------
/assets/textures/default-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-3.png
--------------------------------------------------------------------------------
/assets/textures/default-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-4.png
--------------------------------------------------------------------------------
/assets/textures/default-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-5.png
--------------------------------------------------------------------------------
/assets/textures/default-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-6.png
--------------------------------------------------------------------------------
/assets/textures/default-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-7.png
--------------------------------------------------------------------------------
/assets/textures/default-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-8.png
--------------------------------------------------------------------------------
/assets/textures/default-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/default-9.png
--------------------------------------------------------------------------------
/assets/textures/hitcircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hitcircle.png
--------------------------------------------------------------------------------
/assets/textures/presskey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/presskey.png
--------------------------------------------------------------------------------
/assets/sounds/drum-hitclap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/drum-hitclap.wav
--------------------------------------------------------------------------------
/assets/sounds/drum-hitfinish.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/drum-hitfinish.wav
--------------------------------------------------------------------------------
/assets/sounds/drum-hitnormal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/drum-hitnormal.wav
--------------------------------------------------------------------------------
/assets/sounds/normal-hitclap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/normal-hitclap.wav
--------------------------------------------------------------------------------
/assets/sounds/soft-hitclap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/soft-hitclap.wav
--------------------------------------------------------------------------------
/assets/sounds/soft-hitfinish.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/soft-hitfinish.wav
--------------------------------------------------------------------------------
/assets/sounds/soft-hitnormal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/soft-hitnormal.wav
--------------------------------------------------------------------------------
/assets/textures/cursor-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/cursor-top.png
--------------------------------------------------------------------------------
/assets/textures/cursortrail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/cursortrail.png
--------------------------------------------------------------------------------
/assets/textures/dansercoin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/dansercoin.png
--------------------------------------------------------------------------------
/assets/textures/dansercoin16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/dansercoin16.png
--------------------------------------------------------------------------------
/assets/textures/dansercoin24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/dansercoin24.png
--------------------------------------------------------------------------------
/assets/textures/dansercoin48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/dansercoin48.png
--------------------------------------------------------------------------------
/assets/textures/reversearrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/reversearrow.png
--------------------------------------------------------------------------------
/assets/textures/sliderball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/sliderball.png
--------------------------------------------------------------------------------
/assets/textures/spinner-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-top.png
--------------------------------------------------------------------------------
/assets/sounds/drum-hitwhistle.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/drum-hitwhistle.wav
--------------------------------------------------------------------------------
/assets/sounds/drum-slidertick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/drum-slidertick.wav
--------------------------------------------------------------------------------
/assets/sounds/normal-hitfinish.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/normal-hitfinish.wav
--------------------------------------------------------------------------------
/assets/sounds/normal-hitnormal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/normal-hitnormal.wav
--------------------------------------------------------------------------------
/assets/sounds/soft-hitwhistle.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/soft-hitwhistle.wav
--------------------------------------------------------------------------------
/assets/sounds/soft-slidertick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/soft-slidertick.wav
--------------------------------------------------------------------------------
/assets/textures/approachcircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/approachcircle.png
--------------------------------------------------------------------------------
/assets/textures/hitcircle-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hitcircle-full.png
--------------------------------------------------------------------------------
/assets/textures/slidergradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/slidergradient.png
--------------------------------------------------------------------------------
/assets/textures/spinner-bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-bottom.png
--------------------------------------------------------------------------------
/assets/textures/spinner-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-circle.png
--------------------------------------------------------------------------------
/assets/textures/spinner-clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-clear.png
--------------------------------------------------------------------------------
/assets/textures/spinner-middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-middle.png
--------------------------------------------------------------------------------
/assets/sounds/normal-hitwhistle.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/normal-hitwhistle.wav
--------------------------------------------------------------------------------
/assets/sounds/normal-slidertick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/sounds/normal-slidertick.wav
--------------------------------------------------------------------------------
/assets/textures/hitcircleoverlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/hitcircleoverlay.png
--------------------------------------------------------------------------------
/assets/textures/ranking-a-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-a-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-b-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-b-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-c-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-c-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-d-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-d-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-s-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-s-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-sh-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-sh-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-x-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-x-small.png
--------------------------------------------------------------------------------
/assets/textures/ranking-xh-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/ranking-xh-small.png
--------------------------------------------------------------------------------
/assets/textures/sliderscorepoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/sliderscorepoint.png
--------------------------------------------------------------------------------
/states/state.go:
--------------------------------------------------------------------------------
1 | package states
2 |
3 | type State interface {
4 | Show()
5 | Hide()
6 | Draw(delta float64)
7 | Dispose()
8 | }
9 |
--------------------------------------------------------------------------------
/assets/textures/spinner-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-background.png
--------------------------------------------------------------------------------
/assets/textures/spinner-approachcircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasupandceacar/osu-vs-player/HEAD/assets/textures/spinner-approachcircle.png
--------------------------------------------------------------------------------
/hitjudge/hitresult.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | type HitResult int
4 |
5 | const (
6 | Hit300 HitResult = 0
7 | Hit100 HitResult = 1
8 | Hit50 HitResult = 2
9 | HitMiss HitResult = 3
10 | )
11 |
--------------------------------------------------------------------------------
/hitjudge/key.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | type Key string
4 |
5 | const (
6 | Key1 Key = "K1"
7 | Key2 Key = "K2"
8 | Mouse1 Key = "M1"
9 | Mouse2 Key = "M2"
10 | NoKey Key = "NK"
11 | )
12 |
--------------------------------------------------------------------------------
/hitjudge/objectresult.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | import "danser/bmath"
4 |
5 | type ObjectResult struct {
6 | JudgePos bmath.Vector2d
7 | JudgeTime int64
8 | Result HitResult
9 | IsBreak bool
10 | }
11 |
--------------------------------------------------------------------------------
/hitjudge/error.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | type Error struct {
4 | ReplayIndex int
5 | ObjectIndex int
6 | Result HitResult
7 | IsBreak bool
8 | MaxComboOffset int
9 | NowComboOffset int
10 | }
11 |
--------------------------------------------------------------------------------
/score/rank.go:
--------------------------------------------------------------------------------
1 | package score
2 |
3 | type Rank int
4 |
5 | const (
6 | SSH Rank = -2
7 | SH Rank = -1
8 | SS Rank = 0
9 | S Rank = 1
10 | A Rank = 2
11 | B Rank = 3
12 | C Rank = 4
13 | D Rank = 5
14 | )
15 |
--------------------------------------------------------------------------------
/assets/shaders/fbopass.vsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec3 in_position;
4 | in vec2 in_tex_coord;
5 |
6 | out vec2 tex_coord;
7 | void main()
8 | {
9 | gl_Position = vec4(in_position, 1);
10 | tex_coord = in_tex_coord;
11 | }
--------------------------------------------------------------------------------
/bmath/curves/curve.go:
--------------------------------------------------------------------------------
1 | package curves
2 |
3 | import "danser/bmath"
4 |
5 | type Curve interface {
6 | PointAt(t float64) bmath.Vector2d
7 | GetStartAngle() float64
8 | GetEndAngle() float64
9 | GetLength() float64
10 | }
11 |
--------------------------------------------------------------------------------
/assets/shaders/fbopass.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2DArray tex;
4 |
5 | in vec2 tex_coord;
6 | out vec4 color;
7 |
8 | void main()
9 | {
10 | vec4 in_color = texture(tex, vec3(tex_coord, 0));
11 | color = in_color;
12 | }
--------------------------------------------------------------------------------
/dance/movers/mover.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | )
7 |
8 | type MultiPointMover interface {
9 | Reset()
10 | SetObjects(objs []objects.BaseObject)
11 | Update(time int64) bmath.Vector2d
12 | GetEndTime() int64
13 | }
14 |
--------------------------------------------------------------------------------
/prof/prof.go:
--------------------------------------------------------------------------------
1 | package prof
2 |
3 | import (
4 | "os"
5 | "runtime/pprof"
6 | )
7 |
8 | func ProfStart() {
9 | f, err := os.OpenFile("vsplayer.prof", os.O_RDWR|os.O_CREATE, 0644)
10 | if err != nil {
11 | panic(err)
12 | }
13 | pprof.StartCPUProfile(f)
14 | }
15 |
16 | func ProfEnd() {
17 | pprof.StopCPUProfile()
18 | }
19 |
--------------------------------------------------------------------------------
/assets/shaders/slider.vsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec3 in_position;
4 | in vec3 center;
5 | in vec2 in_tex_coord;
6 |
7 | uniform mat4 proj;
8 | uniform mat4 trans;
9 |
10 | out vec2 tex_coord;
11 | void main()
12 | {
13 | gl_Position = proj * ((trans * vec4(in_position-center, 1))+vec4(center, 0));
14 | tex_coord = in_tex_coord;
15 | }
--------------------------------------------------------------------------------
/assets/shaders/slider.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2DArray tex;
4 | uniform vec4 col_border;
5 | uniform vec4 col_border1;
6 |
7 | in vec2 tex_coord;
8 | out vec4 color;
9 | void main()
10 | {
11 | vec4 in_color = texture(tex, vec3(tex_coord, 0));
12 |
13 | color = in_color*mix(col_border1, col_border, smoothstep(45.0/512, 60.0/512, tex_coord.x));
14 | }
--------------------------------------------------------------------------------
/hitjudge/totalresult.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | import (
4 | "danser/score"
5 | "github.com/flesnuk/oppai5"
6 | )
7 |
8 | type TotalResult struct {
9 | N300 uint16
10 | N100 uint16
11 | N50 uint16
12 | Misses uint16
13 | Combo uint16
14 | Mods uint32
15 | Acc float64
16 | Rank score.Rank
17 | PP oppai.PPv2
18 | UR float64
19 | }
20 |
--------------------------------------------------------------------------------
/assets/shaders/sprite.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec4 col_tint;
4 | in vec3 tex_coord;
5 | in float additive;
6 |
7 | uniform sampler2DArray tex;
8 |
9 | out vec4 color;
10 |
11 | void main()
12 | {
13 | vec4 in_color = texture(tex, tex_coord);
14 | color = in_color*col_tint;
15 | color.rgb *= color.a;
16 | if (additive == 1) {
17 | color.a = 0;
18 | }
19 | }
--------------------------------------------------------------------------------
/replay/replayparser.go:
--------------------------------------------------------------------------------
1 | package replay
2 |
3 | import (
4 | "github.com/Mempler/rplpa"
5 | "io/ioutil"
6 | )
7 |
8 | func ExtractReplay(name string) *rplpa.Replay {
9 | buf, err := ioutil.ReadFile(name)
10 | if err != nil {
11 | panic(err)
12 | }
13 | replay, err := rplpa.ParseReplay(buf)
14 | if err != nil {
15 | panic(err)
16 | }
17 | return replay
18 | }
19 |
--------------------------------------------------------------------------------
/assets/shaders/combine.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2DArray tex;
4 | uniform sampler2DArray tex2;
5 | uniform float power;
6 |
7 | in vec2 tex_coord;
8 | out vec4 color;
9 |
10 | void main()
11 | {
12 | vec4 in_color = texture(tex, vec3(tex_coord, 0));
13 | vec4 in_color2 = texture(tex2, vec3(tex_coord, 0));
14 | color = in_color + in_color2 * power;
15 | }
--------------------------------------------------------------------------------
/assets/shaders/cursortrail.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2DArray tex;
4 | uniform vec4 col_tint;
5 | uniform float points;
6 |
7 | in vec2 tex_coord;
8 | in float index;
9 |
10 | out vec4 color;
11 |
12 | void main() {
13 | vec4 in_color = texture(tex, vec3(tex_coord, 0));
14 | color = in_color * col_tint * vec4(1, 1, 1, 1-smoothstep(points / 3, points, index));
15 | }
--------------------------------------------------------------------------------
/assets/shaders/sprite.vsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec3 in_position;
4 | in vec3 in_tex_coord;
5 | in vec4 in_color;
6 | in float in_additive;
7 |
8 | uniform mat4 proj;
9 |
10 | out vec4 col_tint;
11 | out vec3 tex_coord;
12 | out float additive;
13 | void main()
14 | {
15 | gl_Position = proj * vec4(in_position, 1);
16 | tex_coord = in_tex_coord;
17 | col_tint = in_color;
18 | additive = in_additive;
19 | }
--------------------------------------------------------------------------------
/bmath/math.go:
--------------------------------------------------------------------------------
1 | package bmath
2 |
3 | import "math"
4 |
5 | func AngleBetween(centre, p1, p2 Vector2d) float64 {
6 | a := centre.Dst(p1)
7 | b := centre.Dst(p2)
8 | c := p1.Dst(p2)
9 | return math.Acos((a*a + b*b - c*c) / (2 * a * b))
10 | }
11 |
12 | func Xor(v1 bool, v2 bool) bool {
13 | return (v1 && v2) != (v1 || v2)
14 | }
15 |
16 | func Fmod(a float64, b float64) float64 {
17 | return a - float64(int(a/b))*b
18 | }
19 |
--------------------------------------------------------------------------------
/error.err:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ReplayIndex": 1,
4 | "ObjectIndex": 497,
5 | "Result": 0,
6 | "IsBreak": false,
7 | "MaxComboOffset": 1,
8 | "NowComboOffset": 1
9 | },
10 | {
11 | "ReplayIndex": 1,
12 | "ObjectIndex": 1043,
13 | "Result": 0,
14 | "IsBreak": false,
15 | "MaxComboOffset": 1,
16 | "NowComboOffset": 1
17 | }
18 | ]
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/assets/shaders/brightfilter.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2DArray tex;
4 | uniform float threshold;
5 |
6 | in vec2 tex_coord;
7 | out vec4 color;
8 |
9 | void main()
10 | {
11 | vec4 in_color = texture(tex, vec3(tex_coord, 0));
12 | float brightness = dot(in_color.rgb, vec3(0.2126, 0.7152, 0.0722));
13 |
14 | if (brightness > threshold) {
15 | color = in_color;
16 | } else {
17 | color = vec4(0.0);
18 | }
19 | }
--------------------------------------------------------------------------------
/audio/system.go:
--------------------------------------------------------------------------------
1 | package audio
2 |
3 | /*
4 | #cgo CFLAGS: -I/usr/include -I.
5 | #cgo LDFLAGS: -L${SRCDIR}/../ -L/usr/lib -Wl,-rpath=\$ORIGIN -lbass -lbass_fx
6 | #include "bass.h"
7 | #include "bass_fx.h"
8 | */
9 | import "C"
10 |
11 | import (
12 | "log"
13 | )
14 |
15 | func Init() {
16 | if C.BASS_Init(C.int(-1), C.DWORD(44100), C.DWORD(0), nil, nil) != 0 {
17 | log.Println("BASS Initialized!")
18 | } else {
19 | log.Println("BASS error", int(C.BASS_ErrorGetCode()))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/assets/shaders/cursortrail.vsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec3 in_position;
4 | in vec3 in_mid;
5 | in vec2 in_tex_coord;
6 | in float in_index;
7 |
8 | uniform mat4 proj;
9 | uniform float scale;
10 | uniform float points;
11 | uniform float endScale;
12 |
13 | out vec2 tex_coord;
14 | out float index;
15 |
16 | void main() {
17 | gl_Position = proj * vec4((in_position - in_mid) * scale * (endScale + (1.0 - endScale) * (points-1-in_index) / points) + in_mid, 1);
18 | tex_coord = in_tex_coord;
19 | index = in_index;
20 | }
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "danser/configui"
5 | "flag"
6 | "log"
7 | "os"
8 | )
9 |
10 | func main() {
11 | stdinLog := flag.Bool("stdinLog", false, "")
12 | noGUI := flag.Bool("noGUI", false, "")
13 |
14 | flag.Parse()
15 |
16 | if !*stdinLog {
17 | // 设置log文件
18 | file, _ := os.OpenFile("vsplayer.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
19 | defer file.Close()
20 | log.SetOutput(file)
21 | log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
22 | }
23 |
24 | configui.UImain(*noGUI)
25 | }
26 |
--------------------------------------------------------------------------------
/dance/schedulers/scheduler.go:
--------------------------------------------------------------------------------
1 | package schedulers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/render"
7 | )
8 |
9 | type Scheduler interface {
10 | Init(objects []objects.BaseObject, cursor *render.Cursor)
11 | //Update(time int64)
12 |
13 | /////////////////////////////////////////////////////////////////////////////////////////////////////
14 | // 添加更多参数
15 | Update(time int64, position bmath.Vector2d)
16 | /////////////////////////////////////////////////////////////////////////////////////////////////////
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail cache files
2 | Thumbs.db
3 | ehthumbs.db
4 | ehthumbs_vista.db
5 |
6 | # Folder config file
7 | Desktop.ini
8 |
9 | # Recycle Bin used on file shares
10 | $RECYCLE.BIN/
11 |
12 | # Windows Installer files
13 | *.cab
14 | *.msi
15 | *.msm
16 | *.msp
17 |
18 | # Windows shortcuts
19 | *.lnk
20 |
21 | # =========================
22 | # Operating System Files
23 | # =========================
24 |
25 | .idea
26 | *.osr
27 | song/
28 | screenshots/
29 | replays*/
30 | cache/
31 | *.exe
32 | skin/
33 | danser.log
34 | vsplayer.log
35 | *.prof
36 | danser.db
37 |
--------------------------------------------------------------------------------
/wheelmodified.md:
--------------------------------------------------------------------------------
1 | - 新版pp算法 oppai的更新
2 | [https://github.com/flesnuk/oppai5/pull/2](https://github.com/flesnuk/oppai5/pull/2) 已merge
3 | - oppai算法转盘开头计算的bug
4 | [https://github.com/flesnuk/oppai5/pull/3](https://github.com/flesnuk/oppai5/pull/3) 已merge
5 | - replay 按键读取错误
6 | [https://github.com/Mempler/rplpa/issues/2](https://github.com/Mempler/rplpa/issues/2) 已merge
7 | - 如何让 go walk 窗口大小固定(SetFixedSize)
8 | [https://studygolang.com/topics/1150](https://studygolang.com/topics/1150)
9 | - 2021 年 2 月份,新的 pp 算法修改
10 | [https://github.com/flesnuk/oppai5/pull/6](https://github.com/flesnuk/oppai5/pull/6) 已merge
--------------------------------------------------------------------------------
/bmath/curves/linear.go:
--------------------------------------------------------------------------------
1 | package curves
2 |
3 | import math2 "danser/bmath"
4 |
5 | type Linear struct {
6 | point1, point2 math2.Vector2d
7 | }
8 |
9 | func NewLinear(pt1, pt2 math2.Vector2d) Linear {
10 | return Linear{pt1, pt2}
11 | }
12 |
13 | func (ln Linear) PointAt(t float64) math2.Vector2d {
14 | return ln.point2.Sub(ln.point1).Scl(t).Add(ln.point1)
15 | }
16 |
17 | func (ln Linear) GetStartAngle() float64 {
18 | return ln.point1.AngleRV(ln.point2)
19 | }
20 |
21 | func (ln Linear) GetEndAngle() float64 {
22 | return ln.point2.AngleRV(ln.point1)
23 | }
24 |
25 | func (ln Linear) GetLength() float64 {
26 | return ln.point1.Dst(ln.point2)
27 | }
28 |
--------------------------------------------------------------------------------
/settings/dance.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | type bezier struct {
4 | Aggressiveness, SliderAggressiveness float64
5 | }
6 |
7 | type flower struct {
8 | UseNewStyle bool
9 | AngleOffset float64
10 | DistanceMult float64
11 | StreamTrigger int64
12 | StreamAngleOffset float64
13 | LongJump int64
14 | LongJumpMult float64
15 | LongJumpOnEqualPos bool
16 | }
17 |
18 | type circular struct {
19 | RadiusMultiplier float64
20 | StreamTrigger int64
21 | }
22 |
23 | type dance struct {
24 | SliderDance bool
25 | TAGSliderDance bool
26 | Bezier *bezier
27 | Flower *flower
28 | HalfCircle *circular
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About this project ##
2 | Learn more in [https://space.bilibili.com/4436914/channel/detail?cid=68983](https://space.bilibili.com/4436914/channel/detail?cid=68983)
3 |
4 | ## Build ##
5 |
6 | - Use ```go get``` to get the missing wheels.
7 | - Look at [wheelmodified.md](https://github.com/wasupandceacar/osu-vs-player/blob/master/wheelmodified.md) to see my code modifications for used wheels. For some modifications I've merged it to original repo, while for others you should learn them and modify the wheels, otherwise NO hope for build success.
8 | - Change the code folder name to ```danser``` —— because I import module using ```danser/xxx```.
9 | - Look at [build.md](https://github.com/wasupandceacar/osu-vs-player/blob/master/build.md) to learn how to build.
10 | - You may encounter rsrc build error, but the built exe will still run.
--------------------------------------------------------------------------------
/utils/fps.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "log"
4 |
5 | type FPSCounter struct {
6 | samples []float64
7 | index int
8 | FPS float64
9 | sum float64
10 | log bool
11 | }
12 |
13 | func NewFPSCounter(samples int, log bool) *FPSCounter {
14 | return &FPSCounter{make([]float64, samples), -1, 0, 0.0, log}
15 | }
16 |
17 | func (prof *FPSCounter) PutSample(fps float64) {
18 | prof.index++
19 | if prof.index >= len(prof.samples) {
20 | prof.index = 0
21 | }
22 | prof.samples[prof.index] = fps
23 | prof.sum += 1.0 / fps
24 | if prof.sum >= 1.0 && prof.log {
25 | log.Println("FPS:", prof.GetFPS())
26 | prof.sum = 0.0
27 | }
28 | }
29 |
30 | func (prof *FPSCounter) GetFPS() float64 {
31 | sum := 0.0
32 | for _, g := range prof.samples {
33 | sum += g
34 | }
35 | return sum / float64(len(prof.samples))
36 | }
37 |
--------------------------------------------------------------------------------
/dance/schedulers/sliderprocessor.go:
--------------------------------------------------------------------------------
1 | package schedulers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "sort"
6 | )
7 |
8 | func objectPreProcess(hitobject objects.BaseObject, sliderDance bool) ([]objects.BaseObject, bool) {
9 | if s1, ok1 := hitobject.(*objects.Slider); ok1 && sliderDance {
10 | return s1.GetAsDummyCircles(), true
11 | }
12 | return nil, false
13 | }
14 |
15 | func PreprocessQueue(index int, queue []objects.BaseObject, sliderDance bool) []objects.BaseObject {
16 | if arr, ok := objectPreProcess(queue[index], sliderDance); ok {
17 | if index < len(queue)-1 {
18 | queue1 := append(queue[:index], append(arr, queue[index+1:]...)...)
19 | sort.Slice(queue1, func(i, j int) bool { return queue1[i].GetBasicData().StartTime < queue1[j].GetBasicData().StartTime })
20 | return queue1
21 | } else {
22 | return append(queue[:index], arr...)
23 | }
24 | }
25 | return queue
26 | }
27 |
--------------------------------------------------------------------------------
/storyboard/enums.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import "danser/bmath"
4 |
5 | var Origin = map[string]bmath.Vector2d{
6 | "0": bmath.NewVec2d(-1, -1),
7 | "TopLeft": bmath.NewVec2d(-1, -1),
8 |
9 | "1": bmath.NewVec2d(0, 0),
10 | "Centre": bmath.NewVec2d(0, 0),
11 |
12 | "2": bmath.NewVec2d(-1, 0),
13 | "CentreLeft": bmath.NewVec2d(-1, 0),
14 |
15 | "3": bmath.NewVec2d(1, -1),
16 | "TopRight": bmath.NewVec2d(1, -1),
17 |
18 | "4": bmath.NewVec2d(0, 1),
19 | "BottomCentre": bmath.NewVec2d(0, 1),
20 |
21 | "5": bmath.NewVec2d(0, -1),
22 | "TopCentre": bmath.NewVec2d(0, -1),
23 |
24 | "7": bmath.NewVec2d(1, 0),
25 | "CentreRight": bmath.NewVec2d(1, 0),
26 |
27 | "8": bmath.NewVec2d(-1, 1),
28 | "BottomLeft": bmath.NewVec2d(-1, 1),
29 |
30 | "9": bmath.NewVec2d(1, 1),
31 | "BottomRight": bmath.NewVec2d(1, 1),
32 | }
33 |
--------------------------------------------------------------------------------
/danser.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2
12 |
13 |
14 | true
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/hitjudgetest/Genryuu Kaiko.md:
--------------------------------------------------------------------------------
1 | | player | 真实(100,50,miss) | Tail+头 | Tail+中 | Tail+尾 | 无Tail+头 | 无Tail+中 | 无Tail+尾 |
2 | | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
3 | | Mathi | 5,0,0 | 4,0,0 | 5,0,0 | 5,0,0 | 3,0,0 | 4,0,0 | 4,0,0
4 | | rustbell | 9,3,2 | 10,3,2 | 9,3,2 | 9,3,2 | 7,3,2 | 7,3,2 | 8,3,2
5 | | idke | 24,1,3 | 27,1,3 | 27,1,3 | 27,1,3 | 20,1,3 | 22,1,3 | 22,1,3
6 | | firebat92 | 9,0,1 | 6,0,1 | 7,0,1 | 7,0,1 | 6,0,1 | 7,0,1 | 7,0,1
7 | | HappyStick | 21,0,6 | 18,0,7 | 18,0,7 | 18,0,7 | 18,0,7 | 18,0,7 | 18,0,7
8 | | BeasttrollMC | 26,0,7 | 23,0,8 | 24,0,8 | 24,0,8 | 22,0,8 | 23,0,8 | 23,0,8
9 | | Toy | 8,2,4 | 4,2,7 | 5,2,7 | 5,2,7 | 4,2,7 | 5,2,7 | 5,2,7
10 | | ThePooN | 25,0,17 | 23,0,19 | 23,0,19 | 23,0,19 | 22,0,19 | 22,0,19 | 22,0,19
11 | | WubWoofWolf | 18,0,4 | 15,0,5 | 13,0,5 | 13,0,5 | 12,0,5 | 11,0,5 | 11,0,5
12 | | _index | 4,0,4 | 3,0,2 | 2,0,2 | 2,0,2 | 2,0,2 | 2,0,2 | 2,0,2
13 |
14 | 结论:暂时使用判定Tail+中?这TM不是完全判不准么,我佛了#呲牙
--------------------------------------------------------------------------------
/configui/varassign.go:
--------------------------------------------------------------------------------
1 | package configui
2 |
3 | import (
4 | "github.com/lxn/walk"
5 | "strconv"
6 | )
7 |
8 | func assign(ivarpt interface{}, ivar interface{}, component interface{}) {
9 | switch ivar.(type) {
10 | case bool:
11 | *ivarpt.(*bool) = component.(*walk.CheckBox).Checked()
12 | break
13 | case string:
14 | *ivarpt.(*string) = component.(*walk.LineEdit).Text()
15 | break
16 | case int:
17 | intvar, err := strconv.Atoi(component.(*walk.LineEdit).Text())
18 | if err != nil {
19 | panic(err)
20 | }
21 | *ivarpt.(*int) = intvar
22 | break
23 | case int64:
24 | intvar, err := strconv.Atoi(component.(*walk.LineEdit).Text())
25 | if err != nil {
26 | panic(err)
27 | }
28 | *ivarpt.(*int64) = int64(intvar)
29 | break
30 | case float64:
31 | float64var, err := strconv.ParseFloat(component.(*walk.LineEdit).Text(), 64)
32 | if err != nil {
33 | panic(err)
34 | }
35 | *ivarpt.(*float64) = float64var
36 | break
37 | }
38 | return
39 | }
40 |
--------------------------------------------------------------------------------
/beatmap/objects/pause.go:
--------------------------------------------------------------------------------
1 | package objects
2 |
3 | import (
4 | "danser/bmath"
5 | . "danser/osuconst"
6 | "strconv"
7 | )
8 |
9 | type Pause struct {
10 | objData *basicData
11 | }
12 |
13 | func NewPause(data []string) *Pause {
14 | pause := &Pause{}
15 | pause.objData = &basicData{}
16 | pause.objData.StartTime, _ = strconv.ParseInt(data[1], 10, 64)
17 | pause.objData.EndTime, _ = strconv.ParseInt(data[2], 10, 64)
18 | pause.objData.StartPos = bmath.NewVec2d(PLAYFIELD_WIDTH/2, PLAYFIELD_HEIGHT/2)
19 | pause.objData.EndPos = pause.objData.StartPos
20 | pause.objData.Number = -1
21 | return pause
22 | }
23 |
24 | func (self Pause) GetBasicData() *basicData {
25 | return self.objData
26 | }
27 |
28 | func (self *Pause) SetDifficulty(preempt, fadeIn float64) {
29 |
30 | }
31 |
32 | func (self *Pause) Update(time int64) bool {
33 | return time >= self.objData.EndTime
34 | }
35 |
36 | func (self *Pause) GetPosition() bmath.Vector2d {
37 | return self.objData.StartPos
38 | }
39 |
40 | func (self *Pause) GetObjectNumber() int64 {
41 | return -1
42 | }
43 |
--------------------------------------------------------------------------------
/storyboard/loop.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | type Loop struct {
8 | start, end, repeats int64
9 | transformations *Transformations
10 | }
11 |
12 | func NewLoop(data []string, object Object) *Loop {
13 | loop := &Loop{transformations: NewTransformations(object)}
14 | loop.start, _ = strconv.ParseInt(data[1], 10, 64)
15 | loop.repeats, _ = strconv.ParseInt(data[2], 10, 64)
16 | return loop
17 | }
18 |
19 | func (loop *Loop) Add(command *Command) {
20 | loop.transformations.Add(command)
21 | loop.end = loop.start + loop.transformations.startTime + loop.repeats*(loop.transformations.endTime-loop.transformations.startTime)
22 | }
23 |
24 | func (loop *Loop) Update(time int64) {
25 | sTime := int64(0)
26 | if time-loop.start > loop.transformations.endTime {
27 | sTime = loop.transformations.startTime
28 | }
29 |
30 | local := (time - loop.start - sTime) % (loop.transformations.endTime - sTime)
31 | if time >= loop.end {
32 | local = loop.transformations.endTime - sTime
33 | }
34 |
35 | loop.transformations.Update(sTime + local)
36 | }
37 |
--------------------------------------------------------------------------------
/hitjudge/errorio.go:
--------------------------------------------------------------------------------
1 | package hitjudge
2 |
3 | import (
4 | "danser/settings"
5 | "encoding/json"
6 | "io/ioutil"
7 | )
8 |
9 | func SaveError(errors []Error) {
10 | oerr := ioutil.WriteFile(settings.VSplayer.ErrorFix.ErrorFixFile, []byte(getErrorCache(errors)), 0666)
11 | if oerr != nil {
12 | panic(oerr)
13 | }
14 | }
15 |
16 | func ReadError() []Error {
17 | oread, _ := ioutil.ReadFile(settings.VSplayer.ErrorFix.ErrorFixFile)
18 | return setErrorCache(oread)
19 | }
20 |
21 | func getErrorCache(errors []Error) string {
22 | data, err := json.MarshalIndent(errors, "", " ")
23 | if err != nil {
24 | panic(err)
25 | }
26 | return string(data)
27 | }
28 |
29 | func setErrorCache(r []byte) []Error {
30 | var errors []Error
31 | if err := json.Unmarshal(r, &errors); err != nil {
32 | panic(err)
33 | }
34 | return errors
35 | }
36 |
37 | // 过滤Error
38 | func FilterError(replayindex int, errors []Error) []Error {
39 | reerror := []Error{}
40 | for _, err := range errors {
41 | if err.ReplayIndex == replayindex {
42 | reerror = append(reerror, err)
43 | }
44 | }
45 | return reerror
46 | }
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Sebastian Krajewski
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/utils/screenshot.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/go-gl/gl/v3.3-core/gl"
5 | "github.com/go-gl/glfw/v3.2/glfw"
6 | "image"
7 | "image/png"
8 | "os"
9 | "runtime"
10 | "strconv"
11 | "time"
12 | "unsafe"
13 | )
14 |
15 | func MakeScreenshot(win glfw.Window) {
16 | w, h := win.GetFramebufferSize()
17 | buff := make([]uint8, w*h*4)
18 | gl.PixelStorei(gl.PACK_ALIGNMENT, int32(1))
19 | gl.ReadPixels(0, 0, int32(w), int32(h), gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&buff[0]))
20 |
21 | go func() {
22 | img := image.NewNRGBA(image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{w, h}})
23 | buff1 := make([]uint8, w*h*4)
24 | for i := h - 1; i >= 0; i-- {
25 | for j := 0; j < w*4; j++ {
26 | if (j+1)%4 == 0 {
27 | buff1[(h-1-i)*w*4+j] = 0xFF
28 | } else {
29 | buff1[(h-1-i)*w*4+j] = buff[i*w*4+j]
30 | }
31 | }
32 | }
33 | runtime.KeepAlive(buff)
34 | img.Pix = buff1
35 | os.Mkdir("screenshots", 0644)
36 | f, _ := os.OpenFile("screenshots/"+strconv.FormatInt(time.Now().UnixNano(), 10)+".png", os.O_WRONLY|os.O_CREATE, 0644)
37 | defer f.Close()
38 | png.Encode(f, img)
39 |
40 | }()
41 | }
42 |
--------------------------------------------------------------------------------
/assets/shaders/blur.fsh:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | #define INVSQ2PI 0.398942
4 |
5 | uniform sampler2DArray tex;
6 |
7 | uniform vec2 kernelSize;
8 | uniform vec2 direction;
9 | uniform vec2 sigma;
10 | uniform vec2 size;
11 |
12 | in vec2 tex_coord;
13 |
14 | out vec4 color;
15 |
16 | float gauss(float x, float sigma) {
17 | return INVSQ2PI * exp(-0.5 * x * x / (sigma * sigma)) / sigma;
18 | }
19 |
20 | void main() {
21 | float tSigma = length(direction*sigma);
22 |
23 | float gs = gauss(0, tSigma);
24 |
25 | vec4 inc = texture(tex, vec3(tex_coord, 0));
26 |
27 | color = inc*gs;
28 |
29 | float totalGauss = gs;
30 |
31 | int kSize = int(length(kernelSize*direction));
32 |
33 | for (int i = 2; i < 200; i+=2) {
34 | float fac = float(i) - 0.5;
35 |
36 | gs = gauss(i, tSigma)*2.0;
37 | totalGauss += 2.0*gs;
38 |
39 | vec2 mv = fac * direction / size;
40 |
41 | color += texture(tex, vec3(tex_coord + mv, 0)) * gs;
42 | color += texture(tex, vec3(tex_coord - mv, 0)) * gs;
43 |
44 | if (i >= kSize) {
45 | break;
46 | }
47 |
48 | }
49 |
50 | color /= totalGauss;
51 | }
--------------------------------------------------------------------------------
/utils/wintime.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "runtime"
5 | "time"
6 | )
7 |
8 | /*
9 | #ifdef _WIN32
10 | #include
11 |
12 | int started = 0;
13 | double PCFreq = 0.0;
14 | __int64 CounterStart = 0;
15 |
16 | void startCounter() {
17 | LARGE_INTEGER li;
18 | QueryPerformanceFrequency(&li);
19 |
20 | PCFreq = (double)(li.QuadPart) / 1000000000.0;
21 |
22 | QueryPerformanceCounter(&li);
23 | CounterStart = li.QuadPart;
24 | }
25 |
26 | double getTime() {
27 | if (!started) {
28 | startCounter();
29 | started = 1;
30 | }
31 | LARGE_INTEGER li;
32 | QueryPerformanceCounter(&li);
33 | return (double)(li.QuadPart-CounterStart)/PCFreq;
34 | }
35 |
36 | void resetTime() {
37 | started = 0;
38 | PCFreq = 0.0;
39 | CounterStart = 0;
40 | }
41 |
42 | long long getNanoTime() {
43 | return (long long)(getTime());
44 | }
45 | #else
46 | long long getNanoTime() {
47 | return 0;
48 | }
49 | #endif
50 | */
51 | import "C"
52 |
53 | func GetNanoTime() int64 {
54 | if runtime.GOOS == "windows" {
55 | return int64(C.getNanoTime())
56 | }
57 | return time.Now().UnixNano()
58 | }
59 |
60 | func ResetTime() {
61 | if runtime.GOOS == "windows" {
62 | C.resetTime()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/dance/movers/linear.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/bmath/curves"
7 | "math"
8 | )
9 |
10 | /////////////////////////////////////////////////////////////////////////////////////////////////////
11 | // 直线移动,auto
12 |
13 | type LinearMover struct {
14 | bz curves.Bezier
15 | beginTime, endTime int64
16 | }
17 |
18 | func NewLinearMover() MultiPointMover {
19 | return &LinearMover{}
20 | }
21 |
22 | func (bm *LinearMover) Reset() {
23 |
24 | }
25 |
26 | func (bm *LinearMover) SetObjects(objs []objects.BaseObject) {
27 | end, start := objs[0], objs[1]
28 | endPos := end.GetBasicData().EndPos
29 | endTime := end.GetBasicData().EndTime
30 | startPos := start.GetBasicData().StartPos
31 | startTime := start.GetBasicData().StartTime
32 |
33 | bm.bz = curves.NewBezier([]bmath.Vector2d{endPos, startPos})
34 | bm.endTime = endTime
35 | bm.beginTime = startTime
36 | }
37 |
38 | func (bm LinearMover) Update(time int64) bmath.Vector2d {
39 | t := float64(time-bm.endTime) / float64(bm.beginTime-bm.endTime)
40 | t = math.Max(0.0, math.Min(1.0, t))
41 | return bm.bz.NPointAt(math.Sin(t * math.Pi / 2))
42 | }
43 |
44 | func (bm *LinearMover) GetEndTime() int64 {
45 | return bm.beginTime
46 | }
47 |
--------------------------------------------------------------------------------
/score/ppcalculater.go:
--------------------------------------------------------------------------------
1 | package score
2 |
3 | import (
4 | "danser/settings"
5 | "github.com/flesnuk/oppai5"
6 | "math"
7 | "os"
8 | )
9 |
10 | // 部分载入map
11 | func LoadMapbyNum(filename string, objnum int) *oppai.Map {
12 | f, _ := os.Open(filename)
13 | return oppai.ParsebyNum(f, objnum)
14 | }
15 |
16 | // 部分载入map,并计算难度信息
17 | func CalculateDiffbyNum(filename string, objnum int, mods uint32) oppai.PP {
18 | beatmap := LoadMapbyNum(filename, objnum)
19 | return oppai.PPInfo(beatmap, &oppai.Parameters{
20 | Combo: uint16(beatmap.MaxCombo),
21 | Mods: mods,
22 | N300: uint16(objnum),
23 | N100: 0,
24 | N50: 0,
25 | Misses: 0,
26 | })
27 | }
28 |
29 | // 计算每帧实时数值(PP、UR)
30 | func CalculateRealtimeValue(firstvalue float64, secondvalue float64, firsttime int64, secondtime int64, nowtime float64) (realvalue float64) {
31 | deltavalue := secondvalue - firstvalue
32 | deltatime := math.Min(float64(secondtime-firsttime), settings.VSplayer.PlayerInfoUI.RealTimePPGap)
33 | realvalue = firstvalue + deltavalue*math.Max(math.Min(math.Min(nowtime-float64(firsttime+settings.VSplayer.PlayerFieldUI.HitFadeTime), settings.VSplayer.PlayerInfoUI.RealTimePPGap)/deltatime, 1), 0)
34 | if math.IsNaN(realvalue) {
35 | realvalue = 0.0
36 | }
37 | return realvalue
38 | }
39 |
--------------------------------------------------------------------------------
/replay/replayreader.go:
--------------------------------------------------------------------------------
1 | package replay
2 |
3 | import (
4 | "danser/settings"
5 | "io/ioutil"
6 | "log"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | func GetOsrFiles() (files []string, err error) {
12 | dir, err := ioutil.ReadDir(settings.VSplayer.ReplayandCache.ReplayDir)
13 | if err != nil {
14 | return nil, err
15 | }
16 |
17 | if settings.VSplayer.PlayerInfo.SpecifiedPlayers {
18 | specifiedplayers := strings.Split(settings.VSplayer.PlayerInfo.SpecifiedLine, ",")
19 | specifiedplayerindex := []int{}
20 | for _, player := range specifiedplayers {
21 | pl, _ := strconv.Atoi(player)
22 | if pl <= 0 {
23 | log.Panic("指定player的字符串有误,请重新检查设定")
24 | } else {
25 | specifiedplayerindex = append(specifiedplayerindex, pl)
26 | }
27 | }
28 | for i, fi := range dir {
29 | ok := strings.HasSuffix(fi.Name(), ".osr")
30 | if ok {
31 | for _, pl := range specifiedplayerindex {
32 | if i+1 == pl {
33 | files = append(files, settings.VSplayer.ReplayandCache.ReplayDir+fi.Name())
34 | }
35 | }
36 | }
37 | }
38 | } else {
39 | for _, fi := range dir {
40 | ok := strings.HasSuffix(fi.Name(), ".osr")
41 | if ok {
42 | files = append(files, settings.VSplayer.ReplayandCache.ReplayDir+fi.Name())
43 | }
44 | }
45 | }
46 | return files, nil
47 | }
48 |
--------------------------------------------------------------------------------
/audio/sample.go:
--------------------------------------------------------------------------------
1 | package audio
2 |
3 | /*
4 | #include "bass.h"
5 | */
6 | import "C"
7 |
8 | import (
9 | "danser/settings"
10 | "os"
11 | "unsafe"
12 | )
13 |
14 | type Sample struct {
15 | channel C.DWORD
16 | }
17 |
18 | func NewSample(path string) *Sample {
19 | f, err := os.Open(path)
20 |
21 | if os.IsNotExist(err) {
22 | return nil
23 | }
24 | f.Close()
25 |
26 | player := &Sample{}
27 | han := C.BASS_SampleLoad(0, unsafe.Pointer(C.CString(path)), 0, 0, 32, 0)
28 | player.channel = han
29 | return player
30 | }
31 |
32 | func (wv *Sample) Play() {
33 | channel := C.BASS_SampleGetChannel(C.DWORD(wv.channel), 0)
34 | C.BASS_ChannelSetAttribute(channel, C.BASS_ATTRIB_VOL, C.float(settings.Audio.GeneralVolume*settings.Audio.SampleVolume))
35 | C.BASS_ChannelPlay(channel, 1)
36 | }
37 |
38 | func (wv *Sample) PlayV(volume float64) {
39 | channel := C.BASS_SampleGetChannel(C.DWORD(wv.channel), 0)
40 | C.BASS_ChannelSetAttribute(channel, C.BASS_ATTRIB_VOL, C.float(volume))
41 | C.BASS_ChannelPlay(channel, 1)
42 | }
43 |
44 | func (wv *Sample) PlayRV(volume float64) {
45 | channel := C.BASS_SampleGetChannel(C.DWORD(wv.channel), 0)
46 | C.BASS_ChannelSetAttribute(channel, C.BASS_ATTRIB_VOL, C.float(settings.Audio.GeneralVolume*settings.Audio.SampleVolume*volume))
47 | C.BASS_ChannelPlay(channel, 1)
48 | }
49 |
--------------------------------------------------------------------------------
/dance/movers/axisaligned.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/bmath/sliders"
7 | "math"
8 | )
9 |
10 | type AxisMover struct {
11 | bz sliders.SliderAlgo
12 | beginTime, endTime int64
13 | }
14 |
15 | func NewAxisMover() MultiPointMover {
16 | return &AxisMover{}
17 | }
18 |
19 | func (bm *AxisMover) Reset() {
20 |
21 | }
22 |
23 | func (bm *AxisMover) SetObjects(objs []objects.BaseObject) {
24 | end, start := objs[0], objs[1]
25 | endPos := end.GetBasicData().EndPos
26 | endTime := end.GetBasicData().EndTime
27 | startPos := start.GetBasicData().StartPos
28 | startTime := start.GetBasicData().StartTime
29 |
30 | var midP bmath.Vector2d
31 |
32 | if math.Abs(startPos.Sub(endPos).X) < math.Abs(startPos.Sub(endPos).X) {
33 | midP = bmath.NewVec2d(endPos.X, startPos.Y)
34 | } else {
35 | midP = bmath.NewVec2d(startPos.X, endPos.Y)
36 | }
37 |
38 | bm.bz = sliders.NewSliderAlgo("L", []bmath.Vector2d{endPos, midP, startPos}, endPos.Dst(midP)+midP.Dst(startPos))
39 | bm.endTime = endTime
40 | bm.beginTime = startTime
41 | }
42 |
43 | func (bm AxisMover) Update(time int64) bmath.Vector2d {
44 | t := float64(time-bm.endTime) / float64(bm.beginTime-bm.endTime)
45 | tr := math.Max(0.0, math.Min(1.0, math.Sin(t*math.Pi/2)))
46 | return bm.bz.PointAt(tr)
47 | }
48 |
49 | func (bm *AxisMover) GetEndTime() int64 {
50 | return bm.beginTime
51 | }
52 |
--------------------------------------------------------------------------------
/beatmap/loader.go:
--------------------------------------------------------------------------------
1 | package beatmap
2 |
3 | import (
4 | "danser/settings"
5 | "log"
6 | "os"
7 | "path/filepath"
8 | "strings"
9 | "sync"
10 | )
11 |
12 | func LoadBeatmaps() []*BeatMap {
13 | searchDir := settings.General.OsuSongsDir
14 |
15 | log.Println("Loading beatmaps...")
16 |
17 | var candidates []string
18 | var beatmaps []*BeatMap
19 |
20 | _, err := os.Open(searchDir)
21 | if os.IsNotExist(err) {
22 | log.Println(searchDir + " does not exist!")
23 | return beatmaps
24 | }
25 |
26 | filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
27 | if strings.HasSuffix(f.Name(), ".osu") {
28 | candidates = append(candidates, path)
29 | }
30 | return nil
31 | })
32 |
33 | channel := make(chan string, 20)
34 | channelB := make(chan *BeatMap, len(candidates))
35 | var wg sync.WaitGroup
36 |
37 | for i := 0; i < 20; i++ {
38 | wg.Add(1)
39 | go func() {
40 | defer wg.Done()
41 | for {
42 | path, ok := <-channel
43 | if !ok {
44 | break
45 | }
46 | if f, err := os.Open(path); err == nil {
47 | if bMap := ParseBeatMap(f); bMap != nil {
48 | channelB <- bMap
49 | }
50 | f.Close()
51 | }
52 | }
53 | }()
54 | }
55 |
56 | for _, path := range candidates {
57 | channel <- path
58 | }
59 |
60 | close(channel)
61 | wg.Wait()
62 |
63 | for len(channelB) > 0 {
64 | beatmap := <-channelB
65 | beatmaps = append(beatmaps, beatmap)
66 | }
67 | return beatmaps
68 | }
69 |
--------------------------------------------------------------------------------
/osuconst/osuconst.go:
--------------------------------------------------------------------------------
1 | package osuconst
2 |
3 | // osu判定区域相对大小
4 | const PLAYFIELD_WIDTH = 512.0
5 | const PLAYFIELD_HEIGHT = 384.0
6 |
7 | // 原始皮肤基准大小
8 | const DEFAULT_SKIN_SIZE = 667.0
9 |
10 | // 各个object的判定大小倍数
11 | const CIRCLE_JUDGE_SCALE = 1.0
12 | const TICK_JUDGE_SCALE = 2.4
13 |
14 | // 初始acc、pp、ur
15 | const DEFAULT_ACC = 100.0
16 | const DEFAULT_PP = 0.0
17 | const DEFAULT_UR = 0.0
18 |
19 | // MOD参数
20 | const MOD_NF = 1
21 | const MOD_EZ = 2
22 | const MOD_TD = 4
23 | const MOD_HD = 8
24 | const MOD_HR = 16
25 | const MOD_SD = 32
26 | const MOD_DT = 64
27 | const MOD_HT = 256
28 | const MOD_NC = 512
29 | const MOD_FL = 1024
30 | const MOD_SO = 4096
31 | const MOD_PF = 16384
32 |
33 | // OD参数
34 | const OD_300_BASE = 79
35 | const OD_100_BASE = 139
36 | const OD_50_BASE = 199
37 | const OD_MISS_BASE = 399
38 |
39 | const OD_300_MULT = 6
40 | const OD_100_MULT = 8
41 | const OD_50_MULT = 10
42 |
43 | const OD_PRECISION_FIX = 0.5
44 |
45 | const OD_HR_HENSE = 1.4
46 | const OD_EZ_HENSE = 0.5
47 |
48 | const OD_MAX = 10.0
49 |
50 | // CS 参数
51 | const CS_HR_HENSE = 1.3
52 | const CS_EZ_HENSE = 0.5
53 |
54 | const CS_MAX = 10.0
55 |
56 | const NO_USE_CS_OFFSET = -1.0
57 |
58 | // AR参数
59 | const AR_HR_HENSE = 1.4
60 | const AR_EZ_HENSE = 0.5
61 |
62 | const AR_MAX = 10.0
63 |
64 | // HD参数
65 | const FADE_IN_DURATION_MULTIPLIER = 0.4
66 | const FADE_OUT_DURATION_MULTIPLIER = 0.3
67 |
68 | // stackleniency参数
69 | const BASE_STACK_OFFSET = -6.4
70 |
71 | // replay时间结束默认时间
72 | const REPLAY_END_TIME = -12345
73 |
--------------------------------------------------------------------------------
/dance/movers/halfcircle.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/bmath/curves"
7 | "danser/settings"
8 | "math"
9 | )
10 |
11 | type HalfCircleMover struct {
12 | ca curves.Curve
13 | startTime, endTime int64
14 | invert float64
15 | }
16 |
17 | func NewHalfCircleMover() MultiPointMover {
18 | return &HalfCircleMover{invert: -1}
19 | }
20 |
21 | func (bm *HalfCircleMover) Reset() {
22 | bm.invert = -1
23 | }
24 |
25 | func (bm *HalfCircleMover) SetObjects(objs []objects.BaseObject) {
26 | end := objs[0]
27 | start := objs[1]
28 |
29 | endPos := end.GetBasicData().EndPos
30 | startPos := start.GetBasicData().StartPos
31 | bm.endTime = end.GetBasicData().EndTime
32 | bm.startTime = start.GetBasicData().StartTime
33 |
34 | if settings.Dance.HalfCircle.StreamTrigger < 0 || (bm.startTime-bm.endTime) < settings.Dance.HalfCircle.StreamTrigger {
35 | bm.invert = -1 * bm.invert
36 | }
37 |
38 | if endPos == startPos {
39 | bm.ca = curves.NewLinear(endPos, startPos)
40 | return
41 | }
42 |
43 | point := endPos.Mid(startPos)
44 | p := point.Sub(endPos).Rotate(bm.invert * math.Pi / 2).Scl(settings.Dance.HalfCircle.RadiusMultiplier).Add(point)
45 | bm.ca = curves.NewCirArc(endPos, p, startPos)
46 | }
47 |
48 | func (bm *HalfCircleMover) Update(time int64) bmath.Vector2d {
49 | return bm.ca.PointAt(float64(time-bm.endTime) / float64(bm.startTime-bm.endTime))
50 | }
51 |
52 | func (bm *HalfCircleMover) GetEndTime() int64 {
53 | return bm.startTime
54 | }
55 |
--------------------------------------------------------------------------------
/score/scorecalculater.go:
--------------------------------------------------------------------------------
1 | package score
2 |
3 | import . "danser/osuconst"
4 |
5 | func CalculateAccuracy(hits []int64) float64 {
6 | sum := int64(0)
7 | for _, value := range hits {
8 | sum += value
9 | }
10 | return 100 * float64(sum) / float64(300*len(hits))
11 | }
12 |
13 | func CalculateRank(hits []int64, mods uint32) Rank {
14 | countall := len(hits)
15 | count300 := 0
16 | count100 := 0
17 | count50 := 0
18 | countmiss := 0
19 | for _, value := range hits {
20 | switch value {
21 | case 300:
22 | count300 += 1
23 | break
24 | case 100:
25 | count100 += 1
26 | break
27 | case 50:
28 | count50 += 1
29 | break
30 | case 0:
31 | countmiss += 1
32 | break
33 | }
34 | }
35 | if count300 == countall {
36 | if IsSilver(mods) {
37 | // SSH
38 | return SSH
39 | } else {
40 | // SS
41 | return SS
42 | }
43 | } else if ((float64(count300) / float64(countall)) > 0.9) && ((float64(count50) / float64(countall)) < 0.01) && (countmiss == 0) {
44 | if IsSilver(mods) {
45 | // SH
46 | return SH
47 | } else {
48 | // S
49 | return S
50 | }
51 | } else if ((float64(count300) / float64(countall)) > 0.9) || (((float64(count300) / float64(countall)) > 0.8) && (countmiss == 0)) {
52 | // A
53 | return A
54 | } else if ((float64(count300) / float64(countall)) > 0.8) || (((float64(count300) / float64(countall)) > 0.7) && (countmiss == 0)) {
55 | // B
56 | return B
57 | } else if (float64(count300) / float64(countall)) > 0.6 {
58 | // C
59 | return C
60 | } else {
61 | // D
62 | return D
63 | }
64 | }
65 |
66 | func IsSilver(mods uint32) bool {
67 | return (mods&MOD_HD > 0) || (mods&MOD_FL > 0)
68 | }
69 |
--------------------------------------------------------------------------------
/resultcache/cacheio.go:
--------------------------------------------------------------------------------
1 | package resultcache
2 |
3 | import (
4 | "danser/build"
5 | "danser/hitjudge"
6 | "danser/settings"
7 | "encoding/json"
8 | "github.com/Mempler/rplpa"
9 | "io/ioutil"
10 | "log"
11 | )
12 |
13 | type Cache struct {
14 | ObjectResults []hitjudge.ObjectResult
15 | TotalResults []hitjudge.TotalResult
16 | Version string
17 | }
18 |
19 | func CacheResult(objectResults []hitjudge.ObjectResult, totalResults []hitjudge.TotalResult, rep *rplpa.Replay) {
20 | err := ioutil.WriteFile(settings.VSplayer.ReplayandCache.CacheDir+rep.ReplayMD5+".oac", marshalCache(Cache{ObjectResults: objectResults, TotalResults: totalResults, Version: build.CACHE_VERSION}), 0666)
21 | if err != nil {
22 | panic(err)
23 | }
24 | }
25 |
26 | func GetResult(rep *rplpa.Replay) ([]hitjudge.ObjectResult, []hitjudge.TotalResult, bool) {
27 | bytes, err := ioutil.ReadFile(settings.VSplayer.ReplayandCache.CacheDir + rep.ReplayMD5 + ".oac")
28 | if err != nil {
29 | log.Printf("Could not find ot could not access cache file for %v's replay, MD5 = %v", rep.Username, rep.ReplayMD5)
30 | return nil, nil, false
31 | }
32 | cache := unmarshalCache(bytes)
33 | if cache.Version != build.CACHE_VERSION {
34 | log.Printf("Detected unmatched CACHE_VERSION in %v.oac (%v), overwriting...", rep.ReplayMD5, rep.Username)
35 | return nil, nil, false
36 | }
37 | return cache.ObjectResults, cache.TotalResults, true
38 | }
39 |
40 | func marshalCache(cache Cache) []byte {
41 | data, err := json.Marshal(cache)
42 | if err != nil {
43 | panic(err)
44 | }
45 | return data
46 | }
47 |
48 | func unmarshalCache(r []byte) Cache {
49 | var cache Cache
50 | if err := json.Unmarshal(r, &cache); err != nil {
51 | panic(err)
52 | }
53 | return cache
54 | }
55 |
--------------------------------------------------------------------------------
/animation/glider.go:
--------------------------------------------------------------------------------
1 | package animation
2 |
3 | import (
4 | "danser/animation/easing"
5 | )
6 |
7 | type event struct {
8 | startTime, endTime, targetValue float64
9 | hasStartValue bool
10 | startValue float64
11 | }
12 |
13 | type Glider struct {
14 | eventqueue []event
15 | time, value, startValue float64
16 | current event
17 | easing func(float64) float64
18 | }
19 |
20 | func NewGlider(value float64) *Glider {
21 | return &Glider{value: value, startValue: value, current: event{-1, 0, value, false, 0}, easing: easing.Linear}
22 | }
23 |
24 | func (glider *Glider) SetEasing(easing func(float64) float64) {
25 | glider.easing = easing
26 | }
27 |
28 | func (glider *Glider) AddEvent(startTime, endTime, targetValue float64) {
29 | glider.eventqueue = append(glider.eventqueue, event{startTime, endTime, targetValue, false, 0})
30 | }
31 |
32 | func (glider *Glider) AddEventS(startTime, endTime, startValue, targetValue float64) {
33 | glider.eventqueue = append(glider.eventqueue, event{startTime, endTime, targetValue, true, startValue})
34 | }
35 |
36 | func (glider *Glider) Update(time float64) {
37 | glider.time = time
38 | if len(glider.eventqueue) > 0 {
39 | if e := glider.eventqueue[0]; e.startTime <= time {
40 | glider.current = e
41 | glider.eventqueue = glider.eventqueue[1:]
42 | }
43 | }
44 |
45 | if time <= glider.current.endTime {
46 | e := glider.current
47 | t := (time - e.startTime) / (e.endTime - e.startTime)
48 | glider.value = glider.startValue + glider.easing(t)*(e.targetValue-glider.startValue)
49 | } else {
50 | glider.value = glider.current.targetValue
51 | glider.startValue = glider.value
52 | }
53 | }
54 |
55 | func (glider *Glider) UpdateD(delta float64) {
56 | glider.Update(glider.time + delta)
57 | }
58 | func (glider *Glider) SetValue(value float64) {
59 | glider.value = value
60 | glider.current.targetValue = value
61 | glider.startValue = value
62 | }
63 |
64 | func (glider *Glider) GetValue() float64 {
65 | return glider.value
66 | }
67 |
--------------------------------------------------------------------------------
/utils/colors.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/lucasb-eyer/go-colorful"
6 | )
7 |
8 | func GetColors(baseHue, hueShift float64, times int, alpha float64) []mgl32.Vec4 {
9 | return GetColorsSV(baseHue, hueShift, times, 1, 1, alpha)
10 | }
11 |
12 | func GetColor(H, S, V, alpha float64) mgl32.Vec4 {
13 | color := colorful.Hsv(H, S, V)
14 | return mgl32.Vec4{float32(color.R), float32(color.G), float32(color.B), float32(alpha)}
15 | }
16 |
17 | func GetColorsSV(baseHue, hueShift float64, times int, S, V, alpha float64) []mgl32.Vec4 {
18 | colors := make([]mgl32.Vec4, times)
19 |
20 | for baseHue < 0.0 {
21 | baseHue += 360.0
22 | }
23 |
24 | for baseHue >= 360.0 {
25 | baseHue -= 360.0
26 | }
27 |
28 | for i := 0; i < times; i++ {
29 | hue := baseHue + float64(i)*hueShift
30 |
31 | for hue < 0.0 {
32 | hue += 360.0
33 | }
34 |
35 | for hue >= 360.0 {
36 | hue -= 360.0
37 | }
38 |
39 | colors[i] = GetColor(hue, S, V, alpha)
40 | }
41 |
42 | return colors
43 | }
44 |
45 | func GetColorsSVT(baseHue, hueShift, tagShift float64, times, tag int, S, V, alpha float64) []mgl32.Vec4 {
46 | colors := make([]mgl32.Vec4, 0)
47 |
48 | for baseHue < 0.0 {
49 | baseHue += 360.0
50 | }
51 |
52 | for baseHue >= 360.0 {
53 | baseHue -= 360.0
54 | }
55 |
56 | for i := 0; i < times; i++ {
57 | hue := baseHue + float64(i)*hueShift
58 |
59 | for hue < 0.0 {
60 | hue += 360.0
61 | }
62 |
63 | for hue >= 360.0 {
64 | hue -= 360.0
65 | }
66 |
67 | colors = append(colors, GetColorsSV(hue, tagShift, tag, S, V, alpha)...)
68 | }
69 |
70 | return colors
71 | }
72 |
73 | func GetColorShifted(color mgl32.Vec4, hueOffset float64) mgl32.Vec4 {
74 | tohsv := colorful.Color{float64(color[0]), float64(color[1]), float64(color[2])}
75 | h, s, v := tohsv.Hsv()
76 | h += hueOffset
77 |
78 | for h < 0 {
79 | h += 360.0
80 | }
81 |
82 | for h > 360.0 {
83 | h -= 360.0
84 | }
85 |
86 | col2 := colorful.Hsv(h, s, v)
87 | return mgl32.Vec4{float32(col2.R), float32(col2.G), float32(col2.B), color.W()}
88 | }
89 |
--------------------------------------------------------------------------------
/render/framebuffer/framebuffer.go:
--------------------------------------------------------------------------------
1 | package framebuffer
2 |
3 | import (
4 | "runtime"
5 |
6 | "danser/render/texture"
7 | "github.com/faiface/mainthread"
8 | "github.com/go-gl/gl/v3.3-core/gl"
9 | )
10 |
11 | // Framebuffer is a fixed resolution texture that you can draw on.
12 | type Framebuffer struct {
13 | obj uint32
14 | last int32
15 | tex *texture.TextureSingle
16 | }
17 |
18 | // NewFrame creates a new fully transparent Framebuffer with given dimensions in pixels.
19 | func NewFrame(width, height int, smooth, depth bool) *Framebuffer {
20 | f := new(Framebuffer)
21 |
22 | f.tex = texture.NewTextureSingle(width, height, 0)
23 |
24 | gl.GenFramebuffers(1, &f.obj)
25 |
26 | f.Begin()
27 | f.tex.Bind(0)
28 | gl.FramebufferTextureLayerARB(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, f.tex.GetID(), 0, 0)
29 |
30 | if depth {
31 | var depthRenderBuffer uint32
32 | gl.GenRenderbuffers(1, &depthRenderBuffer)
33 | gl.BindRenderbuffer(gl.RENDERBUFFER, depthRenderBuffer)
34 | gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT, int32(width), int32(height))
35 | gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRenderBuffer)
36 | }
37 |
38 | f.End()
39 |
40 | runtime.SetFinalizer(f, (*Framebuffer).delete)
41 |
42 | return f
43 | }
44 |
45 | func (f *Framebuffer) delete() {
46 | mainthread.CallNonBlock(func() {
47 | gl.DeleteFramebuffers(1, &f.obj)
48 | })
49 | }
50 |
51 | // ID returns the OpenGL framebuffer ID of this Framebuffer.
52 | func (f *Framebuffer) ID() uint32 {
53 | return f.obj
54 | }
55 |
56 | // Begin binds the Framebuffer. All draw operations will target this Framebuffer until End is called.
57 | func (f *Framebuffer) Begin() {
58 | gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f.last)
59 | gl.BindFramebuffer(gl.FRAMEBUFFER, f.obj)
60 | }
61 |
62 | // End unbinds the Framebuffer. All draw operations will go to whatever was bound before this Framebuffer.
63 | func (f *Framebuffer) End() {
64 | gl.BindFramebuffer(gl.FRAMEBUFFER, uint32(f.last))
65 | }
66 |
67 | // Texture returns the Framebuffer's underlying Texture that the Framebuffer draws on.
68 | func (f *Framebuffer) Texture() texture.Texture {
69 | return f.tex
70 | }
71 |
--------------------------------------------------------------------------------
/render/fx.go:
--------------------------------------------------------------------------------
1 | package render
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/wieku/glhf"
6 | )
7 |
8 | var fxshader *glhf.Shader = nil
9 |
10 | func setupFx() {
11 | fxVertexFormat := glhf.AttrFormat{
12 | {Name: "in_position", Type: glhf.Vec3},
13 | }
14 |
15 | fxUniformFormat := glhf.AttrFormat{
16 | {Name: "in_color", Type: glhf.Vec4},
17 | {Name: "transform", Type: glhf.Mat4},
18 | }
19 | var err error
20 | fxshader, err = glhf.NewShader(fxVertexFormat, fxUniformFormat, fxvertex, fxfragment)
21 |
22 | if err != nil {
23 | panic("Fx: " + err.Error())
24 | }
25 |
26 | }
27 |
28 | type FxBatch struct {
29 | color mgl32.Vec4
30 | transform mgl32.Mat4
31 | }
32 |
33 | func NewFxBatch() *FxBatch {
34 | if fxshader == nil {
35 | setupFx()
36 | }
37 | return &FxBatch{mgl32.Vec4{1, 1, 1, 1}, mgl32.Ident4()}
38 | }
39 |
40 | func (batch *FxBatch) Begin() {
41 | fxshader.Begin()
42 | fxshader.SetUniformAttr(0, batch.color)
43 | fxshader.SetUniformAttr(1, batch.transform)
44 | }
45 |
46 | func (batch *FxBatch) CreateVao(length int) *glhf.VertexSlice {
47 | return glhf.MakeVertexSlice(fxshader, length, length)
48 | }
49 |
50 | func (batch *FxBatch) SetColor(r, g, b, a float64) {
51 | batch.color = mgl32.Vec4{float32(r), float32(g), float32(b), float32(a)}
52 | fxshader.SetUniformAttr(0, batch.color)
53 | }
54 |
55 | func (batch *FxBatch) SetColorM(color mgl32.Vec4) {
56 | batch.color = color
57 | fxshader.SetUniformAttr(0, batch.color)
58 | }
59 |
60 | func (batch *FxBatch) ResetTransform() {
61 | batch.transform = mgl32.Ident4()
62 | fxshader.SetUniformAttr(1, batch.transform)
63 | }
64 |
65 | func (batch *FxBatch) End() {
66 | fxshader.End()
67 | }
68 |
69 | func (batch *FxBatch) SetTransform(dz mgl32.Mat4) {
70 | batch.transform = dz
71 | fxshader.SetUniformAttr(1, dz)
72 | }
73 |
74 | const fxvertex = `
75 | #version 330
76 |
77 | in vec3 in_position;
78 |
79 | uniform mat4 transform;
80 |
81 | void main()
82 | {
83 | gl_Position = transform * vec4(in_position, 1);
84 | }
85 | `
86 |
87 | const fxfragment = `
88 | #version 330
89 |
90 | uniform vec4 in_color;
91 | out vec4 color;
92 |
93 | void main()
94 | {
95 | color = in_color;
96 | }
97 | `
98 |
--------------------------------------------------------------------------------
/render/texture/single.go:
--------------------------------------------------------------------------------
1 | package texture
2 |
3 | import (
4 | "github.com/faiface/mainthread"
5 | "github.com/go-gl/gl/v3.3-core/gl"
6 | "image"
7 | "runtime"
8 | )
9 |
10 | type TextureSingle struct {
11 | store *textureStore
12 | defRegion TextureRegion
13 | }
14 |
15 | func NewTextureSingle(width, height, mipmaps int) *TextureSingle {
16 | texture := new(TextureSingle)
17 | texture.store = newStore(1, width, height, mipmaps)
18 | texture.defRegion = TextureRegion{texture, 0, 1, 0, 1, int32(width), int32(height), 0}
19 |
20 | runtime.SetFinalizer(texture, (*TextureSingle).Dispose)
21 |
22 | return texture
23 | }
24 |
25 | func LoadTextureSingle(img *image.NRGBA, mipmaps int) *TextureSingle {
26 | texture := NewTextureSingle(img.Bounds().Dx(), img.Bounds().Dy(), mipmaps)
27 | texture.SetData(0, 0, img.Bounds().Dx(), img.Bounds().Dy(), img.Pix)
28 | return texture
29 | }
30 |
31 | func (texture *TextureSingle) SetData(x, y, width, height int, data []uint8) {
32 | if len(data) != width*height*4 {
33 | panic("Wrong number of pixels given!")
34 | }
35 |
36 | gl.TexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, int32(x), int32(y), 0, int32(width), int32(height), 1, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(data))
37 | if texture.store.mipmaps > 1 {
38 | gl.GenerateMipmap(gl.TEXTURE_2D_ARRAY)
39 | }
40 | }
41 |
42 | func (texture *TextureSingle) GetID() uint32 {
43 | return texture.store.id
44 | }
45 |
46 | func (texture *TextureSingle) GetWidth() int32 {
47 | return texture.store.width
48 | }
49 |
50 | func (texture *TextureSingle) GetHeight() int32 {
51 | return texture.store.height
52 | }
53 |
54 | func (texture *TextureSingle) GetRegion() TextureRegion {
55 | return texture.defRegion
56 | }
57 |
58 | func (texture *TextureSingle) GetLayers() int32 {
59 | return 1
60 | }
61 |
62 | func (texture *TextureSingle) SetFiltering(min, mag Filter) {
63 | texture.store.SetFiltering(min, mag)
64 | }
65 |
66 | func (texture *TextureSingle) Bind(loc uint) {
67 | texture.store.Bind(loc)
68 | }
69 |
70 | func (texture *TextureSingle) GetLocation() uint {
71 | return texture.store.binding
72 | }
73 |
74 | func (texture *TextureSingle) Dispose() {
75 | mainthread.CallNonBlock(func() {
76 | texture.store.Dispose()
77 | })
78 | }
79 |
--------------------------------------------------------------------------------
/bmath/curves/cirarc.go:
--------------------------------------------------------------------------------
1 | package curves
2 |
3 | import (
4 | math2 "danser/bmath"
5 | "math"
6 | )
7 |
8 | type CirArc struct {
9 | pt1, pt2, pt3 math2.Vector2d
10 | centre math2.Vector2d
11 | startAngle, totalAngle, r, dir float64
12 | Unstable bool
13 | }
14 |
15 | func NewCirArc(pt1, pt2, pt3 math2.Vector2d) CirArc {
16 | arc := &CirArc{pt1: pt1, pt2: pt2, pt3: pt3}
17 |
18 | aSq := pt2.DstSq(pt3)
19 | bSq := pt1.DstSq(pt3)
20 | cSq := pt1.DstSq(pt2)
21 |
22 | if math.Abs(aSq) < 0.001 || math.Abs(bSq) < 0.001 || math.Abs(cSq) < 0.001 {
23 | arc.Unstable = true
24 | }
25 |
26 | s := aSq * (bSq + cSq - aSq)
27 | t := bSq * (aSq + cSq - bSq)
28 | u := cSq * (aSq + bSq - cSq)
29 |
30 | sum := s + t + u
31 |
32 | if math.Abs(sum) < 0.001 {
33 | arc.Unstable = true
34 | }
35 |
36 | centre := pt1.Scl(s).Add(pt2.Scl(t)).Add(pt3.Scl(u)).Scl(1 / sum)
37 |
38 | dA := pt1.Sub(centre)
39 | dC := pt3.Sub(centre)
40 |
41 | r := dA.Len()
42 |
43 | start := math.Atan2(dA.Y, dA.X)
44 | end := math.Atan2(dC.Y, dC.X)
45 |
46 | for end < start {
47 | end += 2 * math.Pi
48 | }
49 |
50 | dir := 1
51 | totalAngle := end - start
52 |
53 | aToC := pt3.Sub(pt1)
54 | aToC = math2.NewVec2d(aToC.Y, -aToC.X)
55 | if aToC.Dot(pt2.Sub(pt1)) < 0 {
56 | dir = -dir
57 | totalAngle = 2*math.Pi - totalAngle
58 | }
59 |
60 | arc.totalAngle = totalAngle
61 | arc.dir = float64(dir)
62 | arc.startAngle = start
63 | arc.centre = centre
64 | arc.r = r
65 |
66 | return *arc
67 | }
68 |
69 | func (ln CirArc) PointAt(t float64) math2.Vector2d {
70 | return math2.NewVec2dRad(ln.startAngle+ln.dir*t*ln.totalAngle, ln.r).Add(ln.centre)
71 | }
72 |
73 | func (ln CirArc) GetLength() float64 {
74 | return ln.r * ln.totalAngle
75 | }
76 |
77 | func (ln CirArc) GetStartAngle() float64 {
78 | return ln.pt1.AngleRV(ln.PointAt(1.0 / ln.GetLength()))
79 | }
80 |
81 | func (ln CirArc) GetEndAngle() float64 {
82 | return ln.pt3.AngleRV(ln.PointAt((ln.GetLength() - 1.0) / ln.GetLength()))
83 | }
84 |
85 | func (ln CirArc) GetPoints(num int) []math2.Vector2d {
86 | t0 := 1 / float64(num-1)
87 |
88 | points := make([]math2.Vector2d, num)
89 | t := 0.0
90 | for i := 0; i < num; i += 1 {
91 | points[i] = ln.PointAt(t)
92 | t += t0
93 | }
94 |
95 | return points
96 | }
97 |
--------------------------------------------------------------------------------
/storyboard/transformations.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import (
4 | "math"
5 | "sort"
6 | )
7 |
8 | type Transformations struct {
9 | object Object
10 | commands []*Command
11 | queue []*Command
12 | processed []*Command
13 | startTime, endTime, lastTime int64
14 | }
15 |
16 | func NewTransformations(obj Object) *Transformations {
17 | return &Transformations{object: obj, startTime: math.MaxInt64, endTime: math.MinInt64}
18 | }
19 |
20 | func (trans *Transformations) Add(command *Command) {
21 |
22 | if command.command != "P" {
23 | exists := false
24 |
25 | for _, e := range trans.queue {
26 | if e.command == command.command && e.start < command.start {
27 | exists = true
28 | break
29 | }
30 | }
31 |
32 | if !exists {
33 | if trans.object != nil {
34 | command.Init(trans.object)
35 | }
36 | }
37 | }
38 |
39 | trans.commands = append(trans.commands, command)
40 | trans.queue = append(trans.queue, command)
41 |
42 | if command.start < trans.startTime {
43 | trans.startTime = command.start
44 | }
45 |
46 | if command.end > trans.endTime {
47 | trans.endTime = command.end
48 | }
49 | }
50 |
51 | func (trans *Transformations) Finalize() {
52 | sort.Slice(trans.queue, func(i, j int) bool {
53 | return trans.queue[i].start < trans.queue[j].start
54 | })
55 |
56 | sort.Slice(trans.commands, func(i, j int) bool {
57 | return trans.commands[i].start < trans.commands[j].start
58 | })
59 | }
60 |
61 | func (trans *Transformations) Update(time int64) {
62 |
63 | if time < trans.lastTime {
64 | trans.queue = make([]*Command, len(trans.commands))
65 | copy(trans.queue, trans.commands)
66 | trans.processed = make([]*Command, 0)
67 | }
68 |
69 | for i := 0; i < len(trans.queue); i++ {
70 | c := trans.queue[i]
71 | if c.start <= time {
72 | trans.processed = append(trans.processed, c)
73 | trans.queue = append(trans.queue[:i], trans.queue[i+1:]...)
74 | i--
75 | }
76 | }
77 |
78 | for i := 0; i < len(trans.processed); i++ {
79 | c := trans.processed[i]
80 | c.Update(time)
81 | if trans.object != nil {
82 | c.Apply(trans.object)
83 | }
84 |
85 | if time > c.end {
86 | trans.processed = append(trans.processed[:i], trans.processed[i+1:]...)
87 | i--
88 | }
89 | }
90 |
91 | trans.lastTime = time
92 | }
93 |
--------------------------------------------------------------------------------
/bmath/curves/catmull.go:
--------------------------------------------------------------------------------
1 | package curves
2 |
3 | import (
4 | "danser/bmath"
5 | math2 "danser/bmath"
6 | "math"
7 | )
8 |
9 | type Catmull struct {
10 | points []math2.Vector2d
11 | ApproxLength float64
12 | }
13 |
14 | func NewCatmull(points []math2.Vector2d) Catmull {
15 |
16 | if len(points) != 4 {
17 | panic("4 points are needed to create centripetal catmull rom")
18 | }
19 |
20 | cm := &Catmull{points: points}
21 |
22 | pointLength := points[1].Dst(points[2])
23 |
24 | pointLength = math.Ceil(pointLength)
25 |
26 | for i := 1; i <= int(pointLength); i++ {
27 | cm.ApproxLength += cm.NPointAt(float64(i) / pointLength).Dst(cm.NPointAt(float64(i-1) / pointLength))
28 | }
29 |
30 | return *cm
31 | }
32 |
33 | func (cm Catmull) NPointAt(t float64) math2.Vector2d {
34 | return findPoint(cm.points[0], cm.points[1], cm.points[2], cm.points[3], t)
35 | }
36 |
37 | func findPoint(vec1, vec2, vec3, vec4 bmath.Vector2d, t float64) bmath.Vector2d {
38 | t2 := t * t
39 | t3 := t * t2
40 |
41 | return bmath.NewVec2d(0.5*(2*vec2.X+(-vec1.X+vec3.X)*t+(2*vec1.X-5*vec2.X+4*vec3.X-vec4.X)*t2+(-vec1.X+3*vec2.X-3*vec3.X+vec4.X)*t3),
42 | 0.5*(2*vec2.Y+(-vec1.Y+vec3.Y)*t+(2*vec1.Y-5*vec2.Y+4*vec3.Y-vec4.Y)*t2+(-vec1.Y+3*vec2.Y-3*vec3.Y+vec4.Y)*t3))
43 | }
44 |
45 | //It's not a neat solution, but it works
46 | //This calculates point on catmull curve with constant velocity
47 | func (cm Catmull) PointAt(t float64) math2.Vector2d {
48 | desiredWidth := cm.ApproxLength * t
49 | width := 0.0
50 | pos := cm.points[1]
51 | c := 0.0
52 | for width < desiredWidth {
53 | pt := cm.NPointAt(c)
54 | width += pt.Dst(pos)
55 | if width > desiredWidth {
56 | return pos
57 | }
58 | pos = pt
59 | c += 1.0 / float64(cm.ApproxLength*2-1)
60 | }
61 |
62 | return pos
63 | }
64 |
65 | func (cm Catmull) GetLength() float64 {
66 | return cm.ApproxLength
67 | }
68 |
69 | func (cm Catmull) GetStartAngle() float64 {
70 | return cm.points[0].AngleRV(cm.NPointAt(1.0 / cm.ApproxLength))
71 | }
72 |
73 | func (cm Catmull) GetEndAngle() float64 {
74 | return cm.points[len(cm.points)-1].AngleRV(cm.NPointAt((cm.ApproxLength - 1) / cm.ApproxLength))
75 | }
76 |
77 | func (ln Catmull) GetPoints(num int) []math2.Vector2d {
78 | t0 := 1 / float64(num-1)
79 |
80 | points := make([]math2.Vector2d, num)
81 | t := 0.0
82 | for i := 0; i < num; i += 1 {
83 | points[i] = ln.PointAt(t)
84 | t += t0
85 | }
86 |
87 | return points
88 | }
89 |
--------------------------------------------------------------------------------
/storyboard/layer.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import (
4 | "danser/render"
5 | "sort"
6 | "sync"
7 | )
8 |
9 | type StoryboardLayer struct {
10 | spriteQueue []Object
11 | spriteProcessed []Object
12 | drawArray []Object
13 | visibleObjects int
14 | allSprites int
15 | mutex *sync.Mutex
16 | }
17 |
18 | func NewStoryboardLayer() *StoryboardLayer {
19 | return &StoryboardLayer{mutex: &sync.Mutex{}}
20 | }
21 |
22 | func (layer *StoryboardLayer) Add(object Object) {
23 | layer.spriteQueue = append(layer.spriteQueue, object)
24 | }
25 |
26 | func (layer *StoryboardLayer) FinishLoading() {
27 | sort.Slice(layer.spriteQueue, func(i, j int) bool {
28 | return layer.spriteQueue[i].GetStartTime() < layer.spriteQueue[j].GetStartTime()
29 | })
30 | layer.allSprites = len(layer.spriteQueue)
31 | layer.drawArray = make([]Object, len(layer.spriteQueue))
32 | }
33 |
34 | func (layer *StoryboardLayer) Update(time int64) {
35 | toRemove := 0
36 |
37 | for i := 0; i < len(layer.spriteQueue); i++ {
38 | c := layer.spriteQueue[i]
39 | if c.GetStartTime() > time {
40 | break
41 | }
42 |
43 | toRemove++
44 | }
45 |
46 | if toRemove > 0 {
47 | layer.spriteProcessed = append(layer.spriteProcessed, layer.spriteQueue[:toRemove]...)
48 | layer.spriteQueue = layer.spriteQueue[toRemove:]
49 | sort.Slice(layer.spriteProcessed, func(i, j int) bool {
50 | return layer.spriteProcessed[i].GetZIndex() < layer.spriteProcessed[j].GetZIndex()
51 | })
52 | }
53 |
54 | layer.mutex.Lock()
55 |
56 | for i := 0; i < len(layer.spriteProcessed); i++ {
57 | c := layer.spriteProcessed[i]
58 | c.Update(time)
59 |
60 | if time >= c.GetEndTime() {
61 | layer.spriteProcessed = append(layer.spriteProcessed[:i], layer.spriteProcessed[i+1:]...)
62 | i--
63 | }
64 | }
65 |
66 | layer.visibleObjects = len(layer.spriteProcessed)
67 | copy(layer.drawArray, layer.spriteProcessed)
68 |
69 | layer.mutex.Unlock()
70 | }
71 |
72 | func (layer *StoryboardLayer) GetLoad() (sum float64) {
73 | for i := 0; i < layer.visibleObjects; i++ {
74 | if layer.drawArray[i] != nil {
75 | sum += layer.drawArray[i].GetLoad()
76 | }
77 | }
78 | return
79 | }
80 |
81 | func (layer *StoryboardLayer) Draw(time int64, batch *render.SpriteBatch) {
82 | layer.mutex.Lock()
83 | for i := 0; i < layer.visibleObjects; i++ {
84 | if layer.drawArray[i] != nil {
85 | layer.drawArray[i].Draw(time, batch)
86 | }
87 | }
88 |
89 | layer.mutex.Unlock()
90 | }
91 |
--------------------------------------------------------------------------------
/render/texture/texture.go:
--------------------------------------------------------------------------------
1 | package texture
2 |
3 | import (
4 | "github.com/go-gl/gl/v3.3-core/gl"
5 | )
6 |
7 | type Filter int32
8 |
9 | var Filtering = struct {
10 | Nearest,
11 | Linear,
12 | MipMap,
13 | MipMapNearestNearest,
14 | MipMapLinearNearest,
15 | MipMapNearestLinear,
16 | MipMapLinearLinear Filter
17 | }{gl.NEAREST, gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR, gl.NEAREST_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR, gl.LINEAR_MIPMAP_LINEAR}
18 |
19 | type Texture interface {
20 | GetID() uint32
21 | GetWidth() int32
22 | GetHeight() int32
23 | GetRegion() TextureRegion
24 | GetLayers() int32
25 | SetFiltering(min, mag Filter)
26 | Bind(loc uint)
27 | GetLocation() uint
28 | Dispose()
29 | }
30 |
31 | type TextureRegion struct {
32 | Texture Texture
33 | U1, U2, V1, V2 float32
34 | Width, Height int32
35 | Layer int32
36 | }
37 |
38 | type textureStore struct {
39 | id uint32
40 | binding uint
41 | layers, width, height, mipmaps int32
42 | }
43 |
44 | func newStore(layerNum, width, height, mipmaps int) *textureStore {
45 | store := new(textureStore)
46 | gl.GenTextures(1, &store.id)
47 |
48 | store.layers = int32(layerNum)
49 | store.width = int32(width)
50 | store.height = int32(height)
51 |
52 | if mipmaps < 1 {
53 | mipmaps = 1
54 | }
55 | store.mipmaps = int32(mipmaps)
56 |
57 | store.Bind(0)
58 | gl.TexStorage3D(gl.TEXTURE_2D_ARRAY, store.mipmaps, gl.RGBA8, store.width, store.height, store.layers)
59 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_BASE_LEVEL, 0)
60 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAX_LEVEL, store.mipmaps-1)
61 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
62 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
63 |
64 | if mipmaps > 1 {
65 | store.SetFiltering(Filtering.MipMap, Filtering.Linear)
66 | } else {
67 | store.SetFiltering(Filtering.Linear, Filtering.Linear)
68 | }
69 |
70 | return store
71 | }
72 |
73 | func (store *textureStore) Bind(loc uint) {
74 | store.binding = loc
75 | gl.ActiveTexture(gl.TEXTURE0 + uint32(loc))
76 | gl.BindTexture(gl.TEXTURE_2D_ARRAY, store.id)
77 | }
78 |
79 | func (store *textureStore) SetFiltering(min, mag Filter) {
80 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, int32(min))
81 | gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, int32(mag))
82 | }
83 |
84 | func (store *textureStore) Dispose() {
85 | gl.DeleteTextures(1, &store.id)
86 | }
87 |
--------------------------------------------------------------------------------
/beatmap/objects/baseobject.go:
--------------------------------------------------------------------------------
1 | package objects
2 |
3 | import (
4 | om "danser/bmath"
5 | . "danser/osuconst"
6 | "danser/render"
7 | "danser/settings"
8 | "github.com/go-gl/mathgl/mgl32"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | type BaseObject interface {
14 | GetBasicData() *basicData
15 | Update(time int64) bool
16 | GetPosition() om.Vector2d
17 | SetDifficulty(preempt, fadeIn float64)
18 | }
19 |
20 | type Renderable interface {
21 | Draw(time int64, preempt float64, fadeIn float64, color mgl32.Vec4, batch *render.SpriteBatch) bool
22 | DrawApproach(time int64, preempt float64, fadeIn float64, color mgl32.Vec4, batch *render.SpriteBatch)
23 | GetObjectNumber() int64
24 | }
25 |
26 | type basicData struct {
27 | StartPos, EndPos om.Vector2d
28 | StartTime, EndTime int64
29 | StackOffset om.Vector2d
30 | StackIndex int64
31 | // 一个combo内的object序号
32 | Number int64
33 | // 总的obejct序号
34 | ObjectNumber int64
35 | SliderPoint bool
36 |
37 | // 物件的判定时间
38 | // note:开始时间
39 | // 滑条:结束时间减滑条尾偏移
40 | // 转盘:结束时间
41 | JudgeTime int64
42 |
43 | sampleSet int
44 | additionSet int
45 | customIndex int
46 | customVolume float64
47 | }
48 |
49 | func commonParse(data []string, number int64) *basicData {
50 | x, _ := strconv.ParseFloat(data[0], 64)
51 | y, _ := strconv.ParseFloat(data[1], 64)
52 | if settings.VSplayer.Mods.EnableHR {
53 | y = PLAYFIELD_HEIGHT - y
54 | }
55 | time, _ := strconv.ParseInt(data[2], 10, 64)
56 | return &basicData{StartPos: om.NewVec2d(x, y), StartTime: time, Number: number}
57 | }
58 |
59 | func commonParsebyPath(data []string, number int64, isHR bool) *basicData {
60 | x, _ := strconv.ParseFloat(data[0], 64)
61 | y, _ := strconv.ParseFloat(data[1], 64)
62 | if isHR {
63 | y = PLAYFIELD_HEIGHT - y
64 | }
65 | time, _ := strconv.ParseInt(data[2], 10, 64)
66 | return &basicData{StartPos: om.NewVec2d(x, y), StartTime: time, Number: number}
67 | }
68 |
69 | func (bData *basicData) parseExtras(data []string, extraIndex int) {
70 | if extraIndex < len(data) {
71 | extras := strings.Split(data[extraIndex], ":")
72 | sampleSet, _ := strconv.ParseInt(extras[0], 10, 64)
73 | additionSet, _ := strconv.ParseInt(extras[1], 10, 64)
74 | index, _ := strconv.ParseInt(extras[2], 10, 64)
75 | if len(extras) > 3 {
76 | volume, _ := strconv.ParseInt(extras[3], 10, 64)
77 | bData.customVolume = float64(volume) / 100.0
78 | } else {
79 | bData.customVolume = 0
80 | }
81 |
82 | bData.sampleSet = int(sampleSet)
83 | bData.additionSet = int(additionSet)
84 | bData.customIndex = int(index)
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/beatmap/objects/timing.go:
--------------------------------------------------------------------------------
1 | package objects
2 |
3 | import (
4 | "log"
5 | "math"
6 | )
7 |
8 | type TimingPoint struct {
9 | Time int64
10 | BaseBpm, Bpm float64
11 | SampleSet int
12 | SampleIndex int
13 | SampleVolume float64
14 | }
15 |
16 | func (t TimingPoint) GetRatio() float64 {
17 | return t.Bpm / t.BaseBpm
18 | }
19 |
20 | type Timings struct {
21 | Points []TimingPoint
22 | queue []TimingPoint
23 | SliderMult float64
24 | Current TimingPoint
25 | fullBPM, partBPM float64
26 | BaseSet int
27 | LastSet int
28 | TickRate float64
29 | }
30 |
31 | func NewTimings() *Timings {
32 | return &Timings{BaseSet: 1, LastSet: 1}
33 | }
34 |
35 | func (tim *Timings) AddPoint(time int64, bpm float64, sampleset, sampleindex int, samplevolume float64) {
36 | point := TimingPoint{Time: time, Bpm: bpm, SampleSet: sampleset, SampleIndex: sampleindex, SampleVolume: samplevolume}
37 | if point.Bpm > 0 {
38 | tim.fullBPM = point.Bpm
39 | } else {
40 | point.Bpm = tim.fullBPM / math.Max(0.1, -100.0/point.Bpm)
41 | }
42 | point.BaseBpm = tim.fullBPM
43 | tim.Points = append(tim.Points, point)
44 | tim.queue = append(tim.queue, point)
45 | }
46 |
47 | func (tim *Timings) Update(time int64) {
48 | if len(tim.queue) > 0 {
49 | p := tim.queue[0]
50 | if p.Time <= time {
51 | tim.queue = tim.queue[1:]
52 | tim.partBPM = p.Bpm
53 | tim.Current = p
54 | }
55 | }
56 | }
57 |
58 | func clamp(a int, min int, max int) int {
59 | if a > max {
60 | return max
61 | }
62 | if a < min {
63 | return min
64 | }
65 | return a
66 | }
67 |
68 | func clampF(a float64, min float64, max float64) float64 {
69 | if a > max {
70 | return max
71 | }
72 | if a < min {
73 | return min
74 | }
75 | return a
76 | }
77 |
78 | func (tim *Timings) GetPoint(time int64) TimingPoint {
79 | for i, pt := range tim.Points {
80 | if time < pt.Time {
81 | return tim.Points[clamp(i-1, 0, len(tim.Points)-1)]
82 | }
83 | }
84 | return tim.Points[len(tim.Points)-1]
85 | }
86 |
87 | func (tim Timings) GetSliderTimeS(time int64, pixelLength float64) int64 {
88 | return int64(tim.GetPoint(time).Bpm * pixelLength / (100.0 * tim.SliderMult))
89 | }
90 |
91 | func (tim Timings) GetSliderTime(pixelLength float64) int64 {
92 | return int64(tim.partBPM * pixelLength / (100.0 * tim.SliderMult))
93 | }
94 |
95 | func (tim Timings) GetSliderTimeP(point TimingPoint, pixelLength float64) int64 {
96 | return int64((point.Bpm * pixelLength / (100.0 * tim.SliderMult)))
97 | }
98 |
99 | func (tim *Timings) Reset() {
100 | tim.queue = make([]TimingPoint, len(tim.Points))
101 | copy(tim.queue, tim.Points)
102 | tim.Current = tim.queue[0]
103 | }
104 |
105 | func (tim *Timings) Log() {
106 | log.Println(len(tim.Points))
107 | }
108 |
--------------------------------------------------------------------------------
/bmath/curves/bezier.go:
--------------------------------------------------------------------------------
1 | package curves
2 |
3 | import (
4 | math2 "danser/bmath"
5 | "math"
6 | )
7 |
8 | type Bezier struct {
9 | points []math2.Vector2d
10 | ApproxLength float64
11 | lengthCalculated bool
12 | }
13 |
14 | func NewBezier(points []math2.Vector2d) Bezier {
15 | bz := &Bezier{points: points}
16 |
17 | pointLength := 0.0
18 | for i := 1; i < len(points); i++ {
19 | pointLength += points[i].Dst(points[i-1])
20 | }
21 |
22 | pointLength = math.Ceil(pointLength)
23 |
24 | for i := 1; i <= int(pointLength); i++ {
25 | bz.ApproxLength += bz.NPointAt(float64(i) / pointLength).Dst(bz.NPointAt(float64(i-1) / pointLength))
26 | }
27 |
28 | return *bz
29 | }
30 |
31 | func (bz Bezier) NPointAt(t float64) math2.Vector2d {
32 | x := 0.0
33 | y := 0.0
34 | n := len(bz.points) - 1
35 | for i := 0; i <= n; i++ {
36 | b := bernstein(int64(i), int64(n), t)
37 | x += bz.points[i].X * b
38 | y += bz.points[i].Y * b
39 | }
40 | return math2.NewVec2d(x, y)
41 | }
42 |
43 | //It's not a neat solution, but it works
44 | //This calculates point on bezier with constant velocity
45 | func (bz Bezier) PointAt(t float64) math2.Vector2d {
46 | desiredWidth := bz.ApproxLength * t
47 | width := 0.0
48 | pos := bz.points[0]
49 | c := 0.0
50 | for width < desiredWidth {
51 | pt := bz.NPointAt(c)
52 | width += pt.Dst(pos)
53 | if width > desiredWidth {
54 | return pos
55 | }
56 | pos = pt
57 | c += 1.0 / float64(bz.ApproxLength*2-1)
58 | }
59 |
60 | return pos
61 | }
62 |
63 | func (bz Bezier) GetLength() float64 {
64 | return bz.ApproxLength
65 | }
66 |
67 | func (bz Bezier) GetStartAngle() float64 {
68 | return bz.points[0].AngleRV(bz.NPointAt(1.0 / bz.ApproxLength))
69 | }
70 |
71 | func (bz Bezier) GetEndAngle() float64 {
72 | return bz.points[len(bz.points)-1].AngleRV(bz.NPointAt((bz.ApproxLength - 1) / bz.ApproxLength))
73 | }
74 |
75 | func min(a, b int64) int64 {
76 | if a < b {
77 | return a
78 | }
79 | return b
80 | }
81 |
82 | func BinomialCoefficient(n, k int64) int64 {
83 | if k < 0 || k > n {
84 | return 0
85 | }
86 | if k == 0 || k == n {
87 | return 1
88 | }
89 | k = min(k, n-k)
90 | var c int64 = 1
91 | var i int64 = 0
92 | for ; i < k; i++ {
93 | c = c * (n - i) / (i + 1)
94 | }
95 |
96 | return c
97 | }
98 |
99 | func bernstein(i, n int64, t float64) float64 {
100 | return float64(BinomialCoefficient(n, i)) * math.Pow(t, float64(i)) * math.Pow(1.0-t, float64(n-i))
101 | }
102 |
103 | func calcLength() {
104 |
105 | }
106 |
107 | func (ln Bezier) GetPoints(num int) []math2.Vector2d {
108 | t0 := 1 / float64(num-1)
109 |
110 | points := make([]math2.Vector2d, num)
111 | t := 0.0
112 | for i := 0; i < num; i += 1 {
113 | points[i] = ln.PointAt(t)
114 | t += t0
115 | }
116 |
117 | return points
118 | }
119 |
--------------------------------------------------------------------------------
/dance/schedulers/generic.go:
--------------------------------------------------------------------------------
1 | package schedulers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/render"
7 | )
8 |
9 | //type GenericScheduler struct {
10 | // cursor *render.Cursor
11 | // queue []objects.BaseObject
12 | // mover movers.MultiPointMover
13 | //}
14 | //
15 | //func NewGenericScheduler(mover func() movers.MultiPointMover) Scheduler {
16 | // return &GenericScheduler{mover: mover()}
17 | //}
18 | //
19 | //func (sched *GenericScheduler) Init(objs []objects.BaseObject, cursor *render.Cursor) {
20 | // sched.cursor = cursor
21 | // sched.queue = objs
22 | // sched.mover.Reset()
23 | // sched.queue = PreprocessQueue(0, sched.queue, settings.Dance.SliderDance)
24 | // /////////////////////////////////////////////////////////////////////////////////////////////////////
25 | // // 初始位置 (100, 100)
26 | // sched.mover.SetObjects([]objects.BaseObject{objects.DummyCircle(bmath.NewVec2d(100, 100), 0), sched.queue[0]})
27 | // /////////////////////////////////////////////////////////////////////////////////////////////////////
28 | //}
29 | //
30 | //func (sched *GenericScheduler) Update(time int64) {
31 | // if len(sched.queue) > 0 {
32 | // move := true
33 | // for i := 0; i < len(sched.queue); i++ {
34 | // g := sched.queue[i]
35 | // if g.GetBasicData().StartTime > time {
36 | // break
37 | // }
38 | //
39 | // move = false
40 | //
41 | // if time >= g.GetBasicData().StartTime && time <= g.GetBasicData().EndTime {
42 | //
43 | // sched.cursor.SetPos(g.GetPosition())
44 | // } else if time > g.GetBasicData().EndTime {
45 | // if i < len(sched.queue)-1 {
46 | // sched.queue = append(sched.queue[:i], sched.queue[i+1:]...)
47 | // } else if i < len(sched.queue) {
48 | // sched.queue = sched.queue[:i]
49 | // }
50 | // i--
51 | //
52 | // if len(sched.queue) > 0 {
53 | // sched.queue = PreprocessQueue(i+1, sched.queue, settings.Dance.SliderDance)
54 | // sched.mover.SetObjects([]objects.BaseObject{g, sched.queue[i+1]})
55 | // }
56 | //
57 | // move = true
58 | // }
59 | // }
60 | //
61 | // if move && sched.mover.GetEndTime() >= time {
62 | // sched.cursor.SetPos(sched.mover.Update(time))
63 | // }
64 | //
65 | // }
66 | // //log.Println(time, sched.cursor.Position)
67 | //}
68 |
69 | /////////////////////////////////////////////////////////////////////////////////////////////////////
70 | // 修改Scheduler
71 | type ReplayScheduler struct {
72 | cursor *render.Cursor
73 | }
74 |
75 | func NewReplayScheduler() Scheduler {
76 | return &ReplayScheduler{}
77 | }
78 |
79 | func (sched *ReplayScheduler) Init(objs []objects.BaseObject, cursor *render.Cursor) {
80 | sched.cursor = cursor
81 | }
82 |
83 | func (sched *ReplayScheduler) Update(time int64, position bmath.Vector2d) {
84 | sched.cursor.SetPos(position)
85 | }
86 |
87 | /////////////////////////////////////////////////////////////////////////////////////////////////////
88 |
--------------------------------------------------------------------------------
/ini/ini.go:
--------------------------------------------------------------------------------
1 | package ini
2 |
3 | /**
4 | * 截入配置文件并读取其中内容
5 | * @Auther QiuXiangCheng
6 | * @Date 2018/05/08
7 | *
8 | * 与INI配置文件风格一样 根据顺序读取文件和每一行 如果在行首出现了;号,则认为是配置文件的注释
9 | * 当INI不规范时 如[mysql] 的注释被写为[mysql 则会返回错误
10 | * 本包的配置文件是严格区分大小写的 需要禁止区分大小写 将在后期加入或自行加入
11 | */
12 |
13 | import (
14 | "bufio"
15 | "errors"
16 | "net/url"
17 | "os"
18 | "strconv"
19 | "strings"
20 | )
21 |
22 | // 配置文件
23 | type Config struct {
24 | conf map[string]url.Values
25 | }
26 |
27 | // 将指定的配置以字符串返回
28 | func (c *Config) String(tag string) string {
29 | spl := strings.Split(tag, ".")
30 | key := strings.Join(spl[1:], "_")
31 | if len(spl) < 2 || spl[1] == "" {
32 | return ""
33 | }
34 |
35 | return c.conf[spl[0]].Get(key)
36 | }
37 |
38 | // 返回一个Int类型的配置值
39 | func (c *Config) Int(tag string) (int, error) {
40 | return strconv.Atoi(c.String(tag))
41 | }
42 |
43 | // 返回一个int64配置值
44 | func (c *Config) Int64(tag string) (int64, error) {
45 | return strconv.ParseInt(c.String(tag), 10, 64)
46 | }
47 |
48 | // 返回一个float64配置值
49 | func (c *Config) Float64(tag string) (float64, error) {
50 | return strconv.ParseFloat(c.String(tag), 64)
51 | }
52 |
53 | // 初始化一个文件配置句柄
54 | func NewFileConf(filePath string) (*Config, error) {
55 |
56 | cf := &Config{
57 | conf: make(map[string]url.Values, 10),
58 | }
59 |
60 | f, err := NewFileReader(filePath)
61 | if err != nil {
62 | return nil, errors.New("Error:can not read file \"" + filePath + "\"")
63 | }
64 | defer f.Close()
65 |
66 | tag := ""
67 | buf := bufio.NewReader(f)
68 | replacer := strings.NewReplacer(" ", "")
69 |
70 | for {
71 | lstr, err := buf.ReadString('\n')
72 | if err != nil && err != errors.New("EOF") {
73 | break
74 | }
75 |
76 | if lstr == "" {
77 | break
78 | }
79 |
80 | lstr = strings.TrimSpace(lstr)
81 | if lstr == "" {
82 | continue
83 | }
84 |
85 | if idx := strings.Index(lstr, "["); idx != -1 {
86 | if lstr[len(lstr)-1:] != "]" {
87 | return nil, errors.New("Error:field to parse this symbol style:\"" + lstr + "\"")
88 | }
89 | tag = lstr[1 : len(lstr)-1]
90 | cf.conf[tag] = url.Values{}
91 | } else {
92 | lstr = replacer.Replace(lstr)
93 | // 修改分隔符为 :
94 | spl := strings.Split(lstr, ":")
95 |
96 | // 增加注释符 //
97 | if lstr[0:1] == ";" || lstr[0:2] == "//" {
98 | continue
99 | }
100 |
101 | if len(spl) < 2 {
102 | return nil, errors.New("error:" + lstr)
103 | }
104 | cf.conf[tag].Set(strings.Replace(spl[0], ".", "_", -1), spl[1])
105 | }
106 | }
107 |
108 | return cf, nil
109 | }
110 |
111 | // 打开一个文件句柄
112 | func NewFileReader(filePath string) (*os.File, error) {
113 | if !PathExists(filePath) {
114 | return nil, errors.New("Error:File not exists:" + filePath)
115 | }
116 |
117 | f, err := os.Open(filePath)
118 | if err != nil {
119 | return nil, err
120 | }
121 |
122 | return f, nil
123 | }
124 |
125 | // 检查文件或文件夹是否存在
126 | func PathExists(path string) bool {
127 | _, err := os.Stat(path)
128 | if err == nil {
129 | return true
130 | }
131 |
132 | if os.IsNotExist(err) {
133 | return false
134 | }
135 |
136 | return false
137 | }
138 |
--------------------------------------------------------------------------------
/bmath/vector2d.go:
--------------------------------------------------------------------------------
1 | package bmath
2 |
3 | import (
4 | "fmt"
5 | "github.com/go-gl/mathgl/mgl32"
6 | "math"
7 | )
8 |
9 | type Vector2d struct {
10 | X, Y float64
11 | }
12 |
13 | func NewVec2d(x, y float64) Vector2d {
14 | return Vector2d{x, y}
15 | }
16 |
17 | func NewVec2dP(x, y float64) *Vector2d {
18 | return &Vector2d{x, y}
19 | }
20 |
21 | func NewVec2dRad(rad, length float64) Vector2d {
22 | return Vector2d{math.Cos(rad) * length, math.Sin(rad) * length}
23 | }
24 |
25 | func (v Vector2d) X32() float32 {
26 | return float32(v.X)
27 | }
28 |
29 | func (v Vector2d) Y32() float32 {
30 | return float32(v.Y)
31 | }
32 |
33 | func (v Vector2d) AsVec3() mgl32.Vec3 {
34 | return mgl32.Vec3{float32(v.X), float32(v.Y), 0}
35 | }
36 |
37 | func (v Vector2d) AsVec4() mgl32.Vec4 {
38 | return mgl32.Vec4{float32(v.X), float32(v.Y), 0, 1}
39 | }
40 |
41 | func (v *Vector2d) Set(x, y float64) {
42 | v.X = x
43 | v.Y = y
44 | }
45 |
46 | func (v *Vector2d) SetRad(rad, length float64) {
47 | v.X = math.Cos(rad) * length
48 | v.Y = math.Sin(rad) * length
49 | }
50 |
51 | func (v Vector2d) printOut() {
52 | fmt.Println("[", v.X, ":", v.Y, "]")
53 | }
54 |
55 | func (v Vector2d) Add(v1 Vector2d) Vector2d {
56 | return Vector2d{v.X + v1.X, v.Y + v1.Y}
57 | }
58 |
59 | func (v Vector2d) AddS(x, y float64) Vector2d {
60 | return Vector2d{v.X + x, v.Y + y}
61 | }
62 |
63 | func (v Vector2d) Sub(v1 Vector2d) Vector2d {
64 | return Vector2d{v.X - v1.X, v.Y - v1.Y}
65 | }
66 |
67 | func (v Vector2d) Mult(v1 Vector2d) Vector2d {
68 | return Vector2d{v.X * v1.X, v.Y * v1.Y}
69 | }
70 |
71 | func (v Vector2d) Mid(v1 Vector2d) Vector2d {
72 | return Vector2d{(v.X + v1.X) / 2, (v.Y + v1.Y) / 2}
73 | }
74 |
75 | func (v Vector2d) Dot(v1 Vector2d) float64 {
76 | return v.X*v1.X + v.Y*v1.Y
77 | }
78 |
79 | func (v Vector2d) Dst(v1 Vector2d) float64 {
80 | return math.Sqrt(math.Pow(v1.X-v.X, 2) + math.Pow(v1.Y-v.Y, 2))
81 | }
82 |
83 | func (v Vector2d) DstSq(v1 Vector2d) float64 {
84 | return math.Pow(v1.X-v.X, 2) + math.Pow(v1.Y-v.Y, 2)
85 | }
86 |
87 | func (v Vector2d) Angle() float64 {
88 | return v.AngleR() * 180 / math.Pi
89 | }
90 |
91 | func (v Vector2d) AngleR() float64 {
92 | return math.Atan2(v.Y, v.X)
93 | }
94 |
95 | func (v Vector2d) Nor() Vector2d {
96 | len := v.Len()
97 | return Vector2d{v.X / len, v.Y / len}
98 | }
99 |
100 | func (v Vector2d) AngleRV(v1 Vector2d) float64 {
101 | return math.Atan2(v.Y-v1.Y, v.X-v1.X)
102 | }
103 |
104 | func (v Vector2d) Rotate(rad float64) Vector2d {
105 | cos := math.Cos(rad)
106 | sin := math.Sin(rad)
107 | return Vector2d{v.X*cos - v.Y*sin, v.X*sin + v.Y*cos}
108 | }
109 |
110 | func (v Vector2d) Len() float64 {
111 | return math.Sqrt(v.X*v.X + v.Y*v.Y)
112 | }
113 |
114 | func (v Vector2d) Scl(mag float64) Vector2d {
115 | return Vector2d{v.X * mag, v.Y * mag}
116 | }
117 |
118 | func (v Vector2d) Abs() Vector2d {
119 | return NewVec2d(math.Abs(v.X), math.Abs(v.Y))
120 | }
121 |
122 | func (v Vector2d) Copy() Vector2d {
123 | return Vector2d{v.X, v.Y}
124 | }
125 |
126 | func GetX(v Vector2d) float64 {
127 | return v.X
128 | }
129 |
130 | func GetY(v Vector2d) float64 {
131 | return v.Y
132 | }
133 |
--------------------------------------------------------------------------------
/dance/movers/bezier.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/bmath/curves"
7 | . "danser/osuconst"
8 | "danser/settings"
9 | "math"
10 | )
11 |
12 | type BezierMover struct {
13 | pt bmath.Vector2d
14 | bz curves.Bezier
15 | beginTime, endTime int64
16 | previousSpeed float64
17 | invert float64
18 | }
19 |
20 | func NewBezierMover() MultiPointMover {
21 | bm := &BezierMover{invert: 1}
22 | bm.pt = bmath.NewVec2d(PLAYFIELD_WIDTH/2, PLAYFIELD_HEIGHT/2)
23 | bm.previousSpeed = -1
24 | return bm
25 | }
26 |
27 | func (bm *BezierMover) Reset() {
28 | bm.pt = bmath.NewVec2d(PLAYFIELD_WIDTH/2, PLAYFIELD_HEIGHT/2)
29 | bm.invert = 1
30 | bm.previousSpeed = -1
31 | }
32 |
33 | func (bm *BezierMover) SetObjects(objs []objects.BaseObject) {
34 | end := objs[0]
35 | start := objs[1]
36 | endPos := end.GetBasicData().EndPos
37 | endTime := end.GetBasicData().EndTime
38 | startPos := start.GetBasicData().StartPos
39 | startTime := start.GetBasicData().StartTime
40 |
41 | dst := endPos.Dst(startPos)
42 |
43 | if bm.previousSpeed < 0 {
44 | bm.previousSpeed = dst / float64(startTime-endTime)
45 | }
46 |
47 | s1, ok1 := end.(*objects.Slider)
48 | s2, ok2 := start.(*objects.Slider)
49 |
50 | var points []bmath.Vector2d
51 |
52 | genScale := bm.previousSpeed
53 |
54 | aggressiveness := settings.Dance.Bezier.Aggressiveness
55 | sliderAggressiveness := settings.Dance.Bezier.SliderAggressiveness
56 |
57 | if endPos == startPos {
58 | points = []bmath.Vector2d{endPos, startPos}
59 | } else if ok1 && ok2 {
60 | endAngle := s1.GetEndAngle()
61 | startAngle := s2.GetStartAngle()
62 | bm.pt = bmath.NewVec2dRad(endAngle, s1.GetPointAt(endTime-10).Dst(endPos)*aggressiveness*sliderAggressiveness/10).Add(endPos)
63 | pt2 := bmath.NewVec2dRad(startAngle, s2.GetPointAt(startTime+10).Dst(startPos)*aggressiveness*sliderAggressiveness/10).Add(startPos)
64 | points = []bmath.Vector2d{endPos, bm.pt, pt2, startPos}
65 | } else if ok1 {
66 | endAngle := s1.GetEndAngle()
67 | pt1 := bmath.NewVec2dRad(endAngle, s1.GetPointAt(endTime-10).Dst(endPos)*aggressiveness*sliderAggressiveness/10).Add(endPos)
68 | bm.pt = bmath.NewVec2dRad(startPos.AngleRV(bm.pt), genScale*aggressiveness).Add(startPos)
69 | points = []bmath.Vector2d{endPos, pt1, bm.pt, startPos}
70 | } else if ok2 {
71 | startAngle := s2.GetStartAngle()
72 | bm.pt = bmath.NewVec2dRad(endPos.AngleRV(bm.pt), genScale*aggressiveness).Add(endPos)
73 | pt1 := bmath.NewVec2dRad(startAngle, s2.GetPointAt(startTime+10).Dst(startPos)*aggressiveness*sliderAggressiveness/10).Add(startPos)
74 | points = []bmath.Vector2d{endPos, bm.pt, pt1, startPos}
75 | } else {
76 | angle := endPos.AngleRV(bm.pt)
77 | if math.IsNaN(angle) {
78 | angle = 0
79 | }
80 | bm.pt = bmath.NewVec2dRad(angle, bm.previousSpeed*aggressiveness).Add(endPos)
81 |
82 | points = []bmath.Vector2d{endPos, bm.pt, startPos}
83 | }
84 |
85 | bm.bz = curves.NewBezier(points)
86 |
87 | bm.endTime = endTime
88 | bm.beginTime = startTime
89 | bm.previousSpeed = (dst + 1.0) / float64(startTime-endTime)
90 | }
91 |
92 | func (bm *BezierMover) Update(time int64) bmath.Vector2d {
93 | return bm.bz.NPointAt(float64(time-bm.endTime) / float64(bm.beginTime-bm.endTime))
94 | }
95 |
96 | func (bm *BezierMover) GetEndTime() int64 {
97 | return bm.beginTime
98 | }
99 |
--------------------------------------------------------------------------------
/beatmap/beatmap.go:
--------------------------------------------------------------------------------
1 | package beatmap
2 |
3 | import (
4 | "danser/audio"
5 | "danser/beatmap/objects"
6 | "strconv"
7 | "strings"
8 | "time"
9 | )
10 |
11 | type BeatMap struct {
12 | Artist, ArtistUnicode, Name, NameUnicode, Difficulty, Creator, Source, Tags string
13 | // 加入OD
14 | SliderMultiplier, StackLeniency, CircleSize, AR, ARms, FadeIn, OD, OD300, OD100, OD50, ODMiss float64
15 | Dir, File, Audio, Bg, MD5, PausesText, TimingPoints string
16 |
17 | LastModified, TimeAdded, PlayCount, LastPlayed, PreviewTime int64
18 |
19 | Timings *objects.Timings
20 | HitObjects []objects.BaseObject
21 | Pauses []objects.BaseObject
22 | Queue []objects.BaseObject
23 | }
24 |
25 | func NewBeatMap() *BeatMap {
26 | return &BeatMap{Timings: objects.NewTimings(), AR: 8.0, StackLeniency: 0.7}
27 | }
28 |
29 | func (b *BeatMap) Reset() {
30 | b.Queue = make([]objects.BaseObject, len(b.HitObjects))
31 | copy(b.Queue, b.HitObjects)
32 | b.Timings.Reset()
33 | for _, o := range b.HitObjects {
34 | o.SetDifficulty(b.ARms, b.FadeIn)
35 | }
36 | }
37 |
38 | func (b *BeatMap) Update(time int64) {
39 | b.Timings.Update(time)
40 | if len(b.Queue) > 0 {
41 | for i := 0; i < len(b.Queue); i++ {
42 | g := b.Queue[i]
43 | if g.GetBasicData().StartTime > time {
44 | break
45 | }
46 |
47 | if isDone := g.Update(time); isDone {
48 | if i < len(b.Queue)-1 {
49 | b.Queue = append(b.Queue[:i], b.Queue[i+1:]...)
50 | } else if i < len(b.Queue) {
51 | b.Queue = b.Queue[:i]
52 | }
53 | i--
54 | }
55 | }
56 | }
57 |
58 | }
59 | func (beatMap *BeatMap) GetObjectsCopy() []objects.BaseObject {
60 | queue := make([]objects.BaseObject, len(beatMap.HitObjects))
61 | copy(queue, beatMap.HitObjects)
62 | return queue
63 | }
64 |
65 | func (beatMap *BeatMap) LoadTimingPoints() {
66 |
67 | points := strings.Split(beatMap.TimingPoints, "|")
68 |
69 | if len(points) == 1 && points[0] == "" {
70 | return
71 | }
72 |
73 | for _, point := range points {
74 | line := strings.Split(point, ",")
75 | time, _ := strconv.ParseInt(line[0], 10, 64)
76 | bpm, _ := strconv.ParseFloat(line[1], 64)
77 | if len(line) > 3 {
78 | sampleset, _ := strconv.ParseInt(line[3], 10, 64)
79 | sampleindex, _ := strconv.ParseInt(line[4], 10, 64)
80 |
81 | samplevolume := int64(100)
82 |
83 | if len(line) > 5 {
84 | samplevolume, _ = strconv.ParseInt(line[5], 10, 64)
85 | }
86 |
87 | beatMap.Timings.LastSet = int(sampleset)
88 | beatMap.Timings.AddPoint(time, bpm, int(sampleset), int(sampleindex), float64(samplevolume)/100)
89 | } else {
90 | beatMap.Timings.AddPoint(time, bpm, beatMap.Timings.LastSet, 1, 1)
91 | }
92 | }
93 | }
94 |
95 | func (beatMap *BeatMap) LoadCustomSamples() {
96 | audio.LoadBeatmapSamples(beatMap.Dir)
97 | }
98 |
99 | func (beatMap *BeatMap) LoadPauses() {
100 | points := strings.Split(beatMap.PausesText, ",")
101 |
102 | if len(points) < 2 {
103 | return
104 | }
105 |
106 | for i := 0; i < len(points); i += 2 {
107 | line := []string{"2", points[i], points[i+1]}
108 | beatMap.Pauses = append(beatMap.Pauses, objects.NewPause(line))
109 | }
110 | }
111 |
112 | func (beatMap *BeatMap) UpdatePlayStats() {
113 | beatMap.PlayCount += 1
114 | beatMap.LastPlayed = time.Now().UnixNano() / 1000000
115 | }
116 |
--------------------------------------------------------------------------------
/settings/manager.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | import (
4 | "encoding/json"
5 | "os"
6 | "strconv"
7 | )
8 |
9 | var fileStorage *fileformat
10 | var fileName string
11 |
12 | func initDefaults() {
13 | Version = SETTINGSVERSION
14 | General = &general{os.Getenv("localappdata") + string(os.PathSeparator) + "osu!" + string(os.PathSeparator) + "Songs" + string(os.PathSeparator)}
15 | VSplayer = &vsplayer{
16 | &playerinfo{
17 | 1,
18 | false,
19 | "",
20 | "",
21 | },
22 | &playerinfoUI{
23 | 6,
24 | 14,
25 | 696,
26 | false,
27 | false,
28 | true,
29 | 1000,
30 | false,
31 | false,
32 | false,
33 | 1.3,
34 | 0.6,
35 | },
36 | &recordinfoUI{
37 | "who are you",
38 | "1970.01.01",
39 | 1019,
40 | 35,
41 | 18,
42 | 0.5,
43 | },
44 | &diffinfoUI{
45 | true,
46 | 1049,
47 | 678,
48 | 18,
49 | 0.8,
50 | },
51 | &playerfieldUI{
52 | 200,
53 | 25,
54 | 13,
55 | false,
56 | },
57 | &mapinfo{
58 | "title",
59 | "difficulty",
60 | },
61 | &mods{
62 | false,
63 | false,
64 | false,
65 | false,
66 | false,
67 | },
68 | &knockout{
69 | true,
70 | false,
71 | 2000,
72 | 36,
73 | 1.5,
74 | },
75 | &replayandcache{
76 | "replays\\",
77 | "cache\\",
78 | true,
79 | false,
80 | },
81 | &errorfix{
82 | false,
83 | "error.err",
84 | },
85 | &skin{
86 | false,
87 | "skin\\",
88 | 0,
89 | },
90 | }
91 | Graphics = &graphics{1920, 1080, 1280, 720, true, false, 1000, 16}
92 | Audio = &audio{0.5, 0.5, 0.5, 0, false, false}
93 | Beat = &beat{1.2}
94 | Cursor = &cursor{&color{true, 8, &hsv{0, 1.0, 1.0}, false, 0, false, 0, 0}, true, -36, true, true, -36.0, false, 18, true, true, false, 0.4, 0.5, 2000, 1, 0.4, 0.9}
95 | Objects = &objects{5, 0.3, true, true, &color{true, 8, &hsv{0, 1.0, 1.0}, false, 0, true, 100.0, 0}, -1, 1.2, true, 30, 50, true, true, true, true, true, 0.0, false, &color{false, 8, &hsv{0, 0.0, 1.0}, false, 0, true, 100.0, 0}, true, 18, true}
96 | Playfield = &playfield{5, 2, 5, 0, 0.95, 0.95, true, 0, 0.6, 0.6, 0, 1, 0, true, 1, true, true, 0.8, 1.1, 0, false, 2, true, true, 0.3, &bloom{0.0, 0.6, 0.7}}
97 | Dance = &dance{Bezier: &bezier{60, 3}, Flower: &flower{true, 90, 0.666, 130, 90, -1, 0.7, false}, HalfCircle: &circular{1, 130}}
98 | fileStorage = &fileformat{&Version, General, VSplayer, Graphics, Audio, Beat, Cursor, Objects, Playfield, Dance}
99 | }
100 |
101 | func LoadSettings(version int) bool {
102 | initDefaults()
103 | fileName = "settings"
104 |
105 | if version > 0 {
106 | fileName += "-" + strconv.FormatInt(int64(version), 10)
107 | }
108 | fileName += ".json"
109 |
110 | file, err := os.Open(fileName)
111 | defer file.Close()
112 | if os.IsNotExist(err) {
113 | saveSettings(fileName)
114 | return true
115 | } else if err != nil {
116 | panic(err)
117 | } else {
118 | load(file)
119 | saveSettings(fileName) //this is done to save additions from the current format
120 | }
121 |
122 | return false
123 | }
124 |
125 | func load(file *os.File) {
126 | decoder := json.NewDecoder(file)
127 | decoder.Decode(fileStorage)
128 | }
129 |
130 | func Save() {
131 | saveSettings(fileName)
132 | }
133 |
134 | func saveSettings(path string) {
135 | file, err := os.Create(path)
136 | defer file.Close()
137 |
138 | if err != nil && !os.IsExist(err) {
139 | panic(err)
140 | }
141 |
142 | Version = SETTINGSVERSION
143 | encoder := json.NewEncoder(file)
144 | encoder.SetIndent("", "\t")
145 | encoder.Encode(fileStorage)
146 | }
147 |
--------------------------------------------------------------------------------
/utils/utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "danser/render/texture"
5 | _ "golang.org/x/image/bmp"
6 | "image"
7 | "image/draw"
8 | _ "image/jpeg"
9 | _ "image/png"
10 | "log"
11 | "os"
12 | "sort"
13 | )
14 |
15 | func LoadImage(path string) (*image.NRGBA, error) {
16 | file, err := os.Open(path)
17 | log.Println("Loading texture: ", path)
18 | if err != nil {
19 | log.Println("er1")
20 | return nil, err
21 | }
22 | img, _, err := image.Decode(file)
23 | if err != nil {
24 | log.Println("er2")
25 | return nil, err
26 | }
27 | bounds := img.Bounds()
28 | nrgba := image.NewNRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
29 | draw.Draw(nrgba, nrgba.Bounds(), img, bounds.Min, draw.Src)
30 | return nrgba, nil
31 | }
32 |
33 | /*func LoadTexture(path string) (*texture.Texture, error) {
34 | img, err := LoadImage(path)
35 | if err == nil {
36 | tex := glhf.NewTexture(
37 | img.Bounds().Dx(),
38 | img.Bounds().Dy(),
39 | 4,
40 | true,
41 | img.Pix,
42 | )
43 |
44 | tex.Begin()
45 | tex.SetWrap(glhf.CLAMP_TO_EDGE)
46 | tex.End()
47 |
48 | return tex, nil
49 | }
50 | return nil, err
51 | }*/
52 |
53 | func LoadTexture(path string) (*texture.TextureSingle, error) {
54 | img, err := LoadImage(path)
55 | if err == nil {
56 | tex := texture.LoadTextureSingle(img, 4)
57 |
58 | return tex, nil
59 | }
60 | return nil, err
61 | }
62 |
63 | func LoadTextureToAtlas(atlas *texture.TextureAtlas, path string) (*texture.TextureRegion, error) {
64 | img, err := LoadImage(path)
65 | if err == nil {
66 | return atlas.AddTexture(path, img.Bounds().Dx(), img.Bounds().Dy(), img.Pix), nil
67 | }
68 | log.Println(err)
69 | return nil, err
70 | }
71 |
72 | /*func LoadTextureU(path string) (*glhf.Texture, error) {
73 | img, err := LoadImage(path)
74 | if err == nil {
75 | tex := glhf.NewTexture(
76 | img.Bounds().Dx(),
77 | img.Bounds().Dy(),
78 | 0,
79 | true,
80 | img.Pix,
81 | )
82 |
83 | tex.Begin()
84 | tex.SetWrap(glhf.CLAMP_TO_EDGE)
85 | tex.End()
86 |
87 | return tex, nil
88 | }
89 | return nil, err
90 | }*/
91 |
92 | func PathExists(path string) (bool, error) {
93 | _, err := os.Stat(path)
94 | if err == nil {
95 | return true, nil
96 | }
97 | if os.IsNotExist(err) {
98 | return false, nil
99 | }
100 | return false, err
101 | }
102 |
103 | // 对数组排序,获得其序号(正序)
104 | func SortRankLowToHigh(array []float64) (rank []int) {
105 | var sortarray []float64
106 | sortarray = make([]float64, len(array))
107 | copy(sortarray, array)
108 | sort.Float64s(sortarray)
109 | rank = make([]int, len(array))
110 | for i, ar := range array {
111 | rank[i] = firstindexof(sortarray, ar) + 1
112 | }
113 | return rank
114 | }
115 |
116 | // 对数组排序,获得其序号(反序)
117 | func SortRankHighToLow(array []float64) (rank []int) {
118 | var sortarray []float64
119 | sortarray = make([]float64, len(array))
120 | copy(sortarray, array)
121 | sort.Float64s(sortarray)
122 | rank = make([]int, len(array))
123 | for i, ar := range array {
124 | rank[i] = len(array) - lastindexof(sortarray, ar)
125 | }
126 | return rank
127 | }
128 |
129 | // 查找第一个指定元素返回的下标
130 | func firstindexof(array []float64, ar float64) int {
131 | error := 0.1
132 | for i, a := range array {
133 | if (ar >= a-error) && (ar <= a+error) {
134 | return i
135 | }
136 | }
137 | return -1
138 | }
139 |
140 | // 查找最后一个指定元素返回的下标
141 | func lastindexof(array []float64, ar float64) int {
142 | error := 0.1
143 | for i := len(array) - 1; i >= 0; i-- {
144 | a := array[i]
145 | if (ar >= a-error) && (ar <= a+error) {
146 | return i
147 | }
148 | }
149 | return -1
150 | }
151 |
--------------------------------------------------------------------------------
/render/effects/bloom.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "danser/render/framebuffer"
5 | "github.com/go-gl/gl/v3.3-core/gl"
6 | "github.com/wieku/glhf"
7 | "io/ioutil"
8 | )
9 |
10 | type BloomEffect struct {
11 | colFilter *glhf.Shader
12 | combineShader *glhf.Shader
13 | fbo *framebuffer.Framebuffer
14 |
15 | blurEffect *BlurEffect
16 |
17 | blur, threshold, power float64
18 | fboSlice *glhf.VertexSlice
19 | }
20 |
21 | func NewBloomEffect(width, height int) *BloomEffect {
22 | effect := &BloomEffect{}
23 | vertexFormat := glhf.AttrFormat{
24 | {Name: "in_position", Type: glhf.Vec3},
25 | {Name: "in_tex_coord", Type: glhf.Vec2},
26 | }
27 |
28 | uniformFormat := glhf.AttrFormat{
29 | {Name: "tex", Type: glhf.Int},
30 | {Name: "threshold", Type: glhf.Float},
31 | }
32 |
33 | var err error
34 | vert, _ := ioutil.ReadFile("assets/shaders/fbopass.vsh")
35 | frag, _ := ioutil.ReadFile("assets/shaders/brightfilter.fsh")
36 | effect.colFilter, err = glhf.NewShader(vertexFormat, uniformFormat, string(vert), string(frag))
37 |
38 | if err != nil {
39 | panic("BloomFilter: " + err.Error())
40 | }
41 |
42 | uniformFormat = glhf.AttrFormat{
43 | {Name: "tex", Type: glhf.Int},
44 | {Name: "tex2", Type: glhf.Int},
45 | {Name: "power", Type: glhf.Float},
46 | }
47 | frag, _ = ioutil.ReadFile("assets/shaders/combine.fsh")
48 | effect.combineShader, err = glhf.NewShader(vertexFormat, uniformFormat, string(vert), string(frag))
49 |
50 | if err != nil {
51 | panic("BloomCombine: " + err.Error())
52 | }
53 |
54 | effect.fboSlice = glhf.MakeVertexSlice(effect.colFilter, 6, 6)
55 | effect.fboSlice.Begin()
56 | effect.fboSlice.SetVertexData([]float32{
57 | -1, -1, 0, 0, 0,
58 | 1, -1, 0, 1, 0,
59 | -1, 1, 0, 0, 1,
60 | 1, -1, 0, 1, 0,
61 | 1, 1, 0, 1, 1,
62 | -1, 1, 0, 0, 1,
63 | })
64 | effect.fboSlice.End()
65 |
66 | effect.fbo = framebuffer.NewFrame(width, height, true, false)
67 |
68 | effect.threshold = 0.7
69 | effect.blur = 0.3
70 | effect.power = 1.2
71 |
72 | effect.blurEffect = NewBlurEffect(width, height)
73 | effect.blurEffect.SetBlur(effect.blur, effect.blur)
74 | return effect
75 | }
76 |
77 | func (effect *BloomEffect) SetThreshold(threshold float64) {
78 | effect.threshold = threshold
79 | }
80 |
81 | func (effect *BloomEffect) SetBlur(blur float64) {
82 | effect.blur = blur
83 | effect.blurEffect.SetBlur(blur, blur)
84 | }
85 |
86 | func (effect *BloomEffect) SetPower(power float64) {
87 | effect.power = power
88 | }
89 |
90 | func (effect *BloomEffect) Begin() {
91 | effect.fbo.Begin()
92 | glhf.Clear(0, 0, 0, 0)
93 | }
94 |
95 | func (effect *BloomEffect) EndAndRender() {
96 | effect.fbo.End()
97 |
98 | effect.blurEffect.Begin()
99 | glhf.Clear(0, 0, 0, 0)
100 |
101 | effect.colFilter.Begin()
102 | effect.colFilter.SetUniformAttr(0, int32(0))
103 | effect.colFilter.SetUniformAttr(1, float32(effect.threshold))
104 |
105 | effect.fbo.Texture().Bind(0)
106 |
107 | effect.fboSlice.Begin()
108 | effect.fboSlice.Draw()
109 | effect.fboSlice.End()
110 |
111 | effect.colFilter.End()
112 |
113 | gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
114 |
115 | texture := effect.blurEffect.EndAndProcess()
116 |
117 | effect.combineShader.Begin()
118 | effect.combineShader.SetUniformAttr(0, int32(0))
119 | effect.combineShader.SetUniformAttr(1, int32(1))
120 | effect.combineShader.SetUniformAttr(2, float32(effect.power))
121 |
122 | effect.fbo.Texture().Bind(0)
123 |
124 | texture.Bind(1)
125 |
126 | effect.fboSlice.Begin()
127 | effect.fboSlice.Draw()
128 | effect.fboSlice.End()
129 |
130 | effect.combineShader.End()
131 |
132 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
133 | }
134 |
--------------------------------------------------------------------------------
/audio/osuaudio.go:
--------------------------------------------------------------------------------
1 | package audio
2 |
3 | import (
4 | "danser/settings"
5 | "os"
6 | "path/filepath"
7 | "strconv"
8 | "strings"
9 | "unicode"
10 | )
11 |
12 | var Samples [3][5]*Sample
13 | var MapSamples [3][5]map[int]*Sample
14 |
15 | var sets = map[string]int{
16 | "normal": 1,
17 | "soft": 2,
18 | "drum": 3,
19 | }
20 |
21 | var hitsounds = map[string]int{
22 | "hitnormal": 1,
23 | "hitwhistle": 2,
24 | "hitfinish": 3,
25 | "hitclap": 4,
26 | "slidertick": 5,
27 | }
28 |
29 | func LoadSamples() {
30 | Samples[0][0] = NewSample("assets/sounds/normal-hitnormal.wav")
31 | Samples[0][1] = NewSample("assets/sounds/normal-hitwhistle.wav")
32 | Samples[0][2] = NewSample("assets/sounds/normal-hitfinish.wav")
33 | Samples[0][3] = NewSample("assets/sounds/normal-hitclap.wav")
34 | Samples[0][4] = NewSample("assets/sounds/normal-slidertick.wav")
35 |
36 | Samples[1][0] = NewSample("assets/sounds/soft-hitnormal.wav")
37 | Samples[1][1] = NewSample("assets/sounds/soft-hitwhistle.wav")
38 | Samples[1][2] = NewSample("assets/sounds/soft-hitfinish.wav")
39 | Samples[1][3] = NewSample("assets/sounds/soft-hitclap.wav")
40 | Samples[1][4] = NewSample("assets/sounds/soft-slidertick.wav")
41 |
42 | Samples[2][0] = NewSample("assets/sounds/drum-hitnormal.wav")
43 | Samples[2][1] = NewSample("assets/sounds/drum-hitwhistle.wav")
44 | Samples[2][2] = NewSample("assets/sounds/drum-hitfinish.wav")
45 | Samples[2][3] = NewSample("assets/sounds/drum-hitclap.wav")
46 | Samples[2][4] = NewSample("assets/sounds/drum-slidertick.wav")
47 | }
48 |
49 | func PlaySample(sampleSet, additionSet, hitsound, index int, volume float64) {
50 | playSample(sampleSet, 0, index, volume)
51 |
52 | if additionSet == 0 {
53 | additionSet = sampleSet
54 | }
55 |
56 | if hitsound&2 > 0 {
57 | playSample(additionSet, 1, index, volume)
58 | }
59 | if hitsound&4 > 0 {
60 | playSample(additionSet, 2, index, volume)
61 | }
62 | if hitsound&8 > 0 {
63 | playSample(additionSet, 3, index, volume)
64 | }
65 | }
66 |
67 | func playSample(sampleSet int, hitsoundIndex, index int, volume float64) {
68 | if settings.Audio.IgnoreBeatmapSampleVolume {
69 | volume = 1.0
70 | }
71 |
72 | if sample := MapSamples[sampleSet-1][hitsoundIndex][index]; sample != nil {
73 | sample.PlayRV(volume)
74 | } else {
75 | Samples[sampleSet-1][hitsoundIndex].PlayRV(volume)
76 | }
77 | }
78 |
79 | func PlaySliderTick(sampleSet, index int, volume float64) {
80 | playSample(sampleSet, 4, index, volume)
81 | }
82 |
83 | func LoadBeatmapSamples(dir string) {
84 | splitBeforeDigit := func(name string) []string {
85 | for i, r := range name {
86 | if unicode.IsDigit(r) {
87 | return []string{name[:i], name[i:]}
88 | }
89 | }
90 | return []string{name}
91 | }
92 |
93 | fullPath := settings.General.OsuSongsDir + string(os.PathSeparator) + dir
94 |
95 | filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
96 | if !strings.HasSuffix(info.Name(), ".wav") {
97 | return nil
98 | }
99 |
100 | rawName := strings.TrimSuffix(info.Name(), ".wav")
101 |
102 | if separated := strings.Split(rawName, "-"); len(separated) == 2 {
103 |
104 | setID := sets[separated[0]]
105 |
106 | if setID == 0 {
107 | return nil
108 | }
109 |
110 | subSeparated := splitBeforeDigit(separated[1])
111 |
112 | hitSoundIndex := 1
113 |
114 | if len(subSeparated) > 1 {
115 | index, err := strconv.ParseInt(subSeparated[1], 10, 32)
116 |
117 | if err != nil {
118 | return nil
119 | }
120 |
121 | hitSoundIndex = int(index)
122 | }
123 |
124 | hitSoundID := hitsounds[subSeparated[0]]
125 |
126 | if hitSoundID == 0 {
127 | return nil
128 | }
129 |
130 | if MapSamples[setID-1][hitSoundID-1] == nil {
131 | MapSamples[setID-1][hitSoundID-1] = make(map[int]*Sample)
132 | }
133 |
134 | MapSamples[setID-1][hitSoundID-1][hitSoundIndex] = NewSample(path)
135 |
136 | }
137 |
138 | return nil
139 | })
140 | }
141 |
--------------------------------------------------------------------------------
/render/effects/blur.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "danser/render/framebuffer"
5 | "danser/render/texture"
6 | "danser/settings"
7 | "github.com/go-gl/gl/v3.3-core/gl"
8 | "github.com/go-gl/mathgl/mgl32"
9 | "github.com/wieku/glhf"
10 | "io/ioutil"
11 | "math"
12 | )
13 |
14 | type BlurEffect struct {
15 | blurShader *glhf.Shader
16 | fbo1 *framebuffer.Framebuffer
17 | fbo2 *framebuffer.Framebuffer
18 | kernelSize mgl32.Vec2
19 | sigma mgl32.Vec2
20 | size mgl32.Vec2
21 | fboSlice *glhf.VertexSlice
22 | }
23 |
24 | func NewBlurEffect(width, height int) *BlurEffect {
25 | effect := &BlurEffect{}
26 | vertexFormat := glhf.AttrFormat{
27 | {Name: "in_position", Type: glhf.Vec3},
28 | {Name: "in_tex_coord", Type: glhf.Vec2},
29 | }
30 |
31 | uniformFormat := glhf.AttrFormat{
32 | {Name: "tex", Type: glhf.Int},
33 | {Name: "kernelSize", Type: glhf.Vec2},
34 | {Name: "direction", Type: glhf.Vec2},
35 | {Name: "sigma", Type: glhf.Vec2},
36 | {Name: "size", Type: glhf.Vec2},
37 | }
38 |
39 | var err error
40 | vert, _ := ioutil.ReadFile("assets/shaders/fbopass.vsh")
41 | frag, _ := ioutil.ReadFile("assets/shaders/blur.fsh")
42 | effect.blurShader, err = glhf.NewShader(vertexFormat, uniformFormat, string(vert), string(frag))
43 |
44 | if err != nil {
45 | panic("Blur: " + err.Error())
46 | }
47 |
48 | effect.fboSlice = glhf.MakeVertexSlice(effect.blurShader, 6, 6)
49 | effect.fboSlice.Begin()
50 | effect.fboSlice.SetVertexData([]float32{
51 | -1, -1, 0, 0, 0,
52 | 1, -1, 0, 1, 0,
53 | -1, 1, 0, 0, 1,
54 | 1, -1, 0, 1, 0,
55 | 1, 1, 0, 1, 1,
56 | -1, 1, 0, 0, 1,
57 | })
58 | effect.fboSlice.End()
59 |
60 | effect.fbo1 = framebuffer.NewFrame(width, height, true, false)
61 | effect.fbo2 = framebuffer.NewFrame(width, height, true, false)
62 | effect.SetBlur(0, 0)
63 | effect.size = mgl32.Vec2{float32(width), float32(height)}
64 | return effect
65 | }
66 |
67 | func (effect *BlurEffect) SetBlur(blurX, blurY float64) {
68 | sigmaX, sigmaY := float32(blurX)*25, float32(blurY)*25
69 | kX := kernelSize(sigmaX)
70 | if kX == 0 {
71 | sigmaX = 1.0
72 | }
73 | kY := kernelSize(sigmaY)
74 | if kY == 0 {
75 | sigmaY = 1.0
76 | }
77 | effect.kernelSize = mgl32.Vec2{float32(kX), float32(kY)}
78 | effect.sigma = mgl32.Vec2{sigmaX, sigmaY}
79 | }
80 |
81 | func gauss(x int, sigma float32) float32 {
82 | factor := float32(0.398942)
83 | return factor * float32(math.Exp(-0.5*float64(x*x)/float64(sigma*sigma))) / sigma
84 | }
85 |
86 | func kernelSize(sigma float32) int {
87 | if sigma == 0 {
88 | return 0
89 | }
90 | baseG := gauss(0, sigma) * 0.1
91 | max := 200
92 |
93 | for i := 1; i <= max; i++ {
94 | if gauss(i, sigma) < baseG {
95 | return i - 1
96 | }
97 | }
98 | return max
99 | }
100 |
101 | func (effect *BlurEffect) Begin() {
102 | effect.fbo1.Begin()
103 | glhf.Clear(0, 0, 0, 1)
104 | gl.Viewport(0, 0, int32(effect.fbo1.Texture().GetWidth()), int32(effect.fbo1.Texture().GetHeight()))
105 | }
106 |
107 | func (effect *BlurEffect) EndAndProcess() texture.Texture {
108 | effect.fbo1.End()
109 |
110 | effect.blurShader.Begin()
111 | effect.blurShader.SetUniformAttr(0, int32(0))
112 | effect.blurShader.SetUniformAttr(1, effect.kernelSize)
113 | effect.blurShader.SetUniformAttr(2, mgl32.Vec2{1, 0})
114 | effect.blurShader.SetUniformAttr(3, effect.sigma)
115 | effect.blurShader.SetUniformAttr(4, effect.size)
116 |
117 | effect.fboSlice.Begin()
118 |
119 | effect.fbo2.Begin()
120 | glhf.Clear(0, 0, 0, 0)
121 |
122 | effect.fbo1.Texture().Bind(0)
123 |
124 | effect.fboSlice.Draw()
125 |
126 | effect.fbo2.End()
127 |
128 | effect.fbo1.Begin()
129 | glhf.Clear(0, 0, 0, 0)
130 |
131 | effect.fbo2.Texture().Bind(0)
132 |
133 | effect.blurShader.SetUniformAttr(2, mgl32.Vec2{0, 1})
134 | effect.fboSlice.Draw()
135 |
136 | effect.fbo1.End()
137 |
138 | effect.fboSlice.End()
139 | effect.blurShader.End()
140 | gl.Viewport(0, 0, int32(settings.Graphics.GetWidth()), int32(settings.Graphics.GetHeight()))
141 | return effect.fbo1.Texture()
142 | }
143 |
--------------------------------------------------------------------------------
/beatmap/objects/util.go:
--------------------------------------------------------------------------------
1 | package objects
2 |
3 | import (
4 | "danser/bmath"
5 | "danser/render"
6 | "strconv"
7 | )
8 |
9 | func GetObject(data []string, number int64) (BaseObject, int64) {
10 | objType, _ := strconv.ParseInt(data[3], 10, 64)
11 | objType = objType % 16
12 | newnumber := number
13 | if (objType & CIRCLE) > 0 {
14 | if objType == NEWCIRCLE {
15 | // 新的combo
16 | newnumber = 1
17 | } else {
18 | // 继续combo
19 | newnumber += 1
20 | }
21 | return NewCircle(data, newnumber), newnumber
22 | } else if (objType & SLIDER) > 0 {
23 | if objType == NEWSLIDER {
24 | // 新的combo
25 | newnumber = 1
26 | } else {
27 | // 继续combo
28 | newnumber += 1
29 | }
30 | sl := NewSlider(data, newnumber)
31 | if sl == nil {
32 | return nil, newnumber
33 | } else {
34 | return sl, newnumber
35 | }
36 | } else if (objType & SPINNNER) > 0 {
37 | if objType == NEWSPINNNER {
38 | // 新的combo
39 | newnumber = 1
40 | } else {
41 | // 继续combo
42 | newnumber += 1
43 | }
44 | return NewSpinner(data, newnumber), newnumber
45 | }
46 | return nil, newnumber
47 | }
48 |
49 | func GetObjectbyPath(data []string, number int64, isHR bool) (BaseObject, int64) {
50 | objType, _ := strconv.ParseInt(data[3], 10, 64)
51 | objType = objType % 16
52 | newnumber := number
53 | if (objType & CIRCLE) > 0 {
54 | if objType == NEWCIRCLE {
55 | // 新的combo
56 | newnumber = 1
57 | } else {
58 | // 继续combo
59 | newnumber += 1
60 | }
61 | return NewCirclebyPath(data, newnumber, isHR), newnumber
62 | } else if (objType & SLIDER) > 0 {
63 | if objType == NEWSLIDER {
64 | // 新的combo
65 | newnumber = 1
66 | } else {
67 | // 继续combo
68 | newnumber += 1
69 | }
70 | sl := NewSliderbyPath(data, newnumber, isHR)
71 | if sl == nil {
72 | return nil, newnumber
73 | } else {
74 | return sl, newnumber
75 | }
76 | } else if (objType & SPINNNER) > 0 {
77 | if objType == NEWSPINNNER {
78 | // 新的combo
79 | newnumber = 1
80 | } else {
81 | // 继续combo
82 | newnumber += 1
83 | }
84 | return NewSpinner(data, newnumber), newnumber
85 | }
86 | return nil, newnumber
87 | }
88 |
89 | // 绘制圈内数字
90 | func DrawHitCircleNumber(number int64, position bmath.Vector2d, batch *render.SpriteBatch) {
91 | switch number {
92 | case 0:
93 | batch.DrawUnitN(*render.Circle0, position)
94 | break
95 | case 1:
96 | batch.DrawUnitN(*render.Circle1, position)
97 | break
98 | case 2:
99 | batch.DrawUnitN(*render.Circle2, position)
100 | break
101 | case 3:
102 | batch.DrawUnitN(*render.Circle3, position)
103 | break
104 | case 4:
105 | batch.DrawUnitN(*render.Circle4, position)
106 | break
107 | case 5:
108 | batch.DrawUnitN(*render.Circle5, position)
109 | break
110 | case 6:
111 | batch.DrawUnitN(*render.Circle6, position)
112 | break
113 | case 7:
114 | batch.DrawUnitN(*render.Circle7, position)
115 | break
116 | case 8:
117 | batch.DrawUnitN(*render.Circle8, position)
118 | break
119 | case 9:
120 | batch.DrawUnitN(*render.Circle9, position)
121 | break
122 | }
123 | }
124 |
125 | // 获取圈内数字宽度
126 | func GetHitCircleNumberWidth(number int64) int32 {
127 | switch number {
128 | case 0:
129 | return render.Circle0.Width
130 | case 1:
131 | return render.Circle1.Width
132 | case 2:
133 | return render.Circle2.Width
134 | case 3:
135 | return render.Circle3.Width
136 | case 4:
137 | return render.Circle4.Width
138 | case 5:
139 | return render.Circle5.Width
140 | case 6:
141 | return render.Circle6.Width
142 | case 7:
143 | return render.Circle7.Width
144 | case 8:
145 | return render.Circle8.Width
146 | case 9:
147 | return render.Circle9.Width
148 | }
149 | return 0
150 | }
151 |
152 | // 限制范围
153 | func Clamp(value float64, min float64, max float64) float64 {
154 | if value < min {
155 | return min
156 | } else if value > max {
157 | return max
158 | } else {
159 | return value
160 | }
161 | }
162 |
163 | const (
164 | CIRCLE int64 = 1
165 | SLIDER int64 = 2
166 | SPINNNER int64 = 8
167 | NEWCIRCLE int64 = 5
168 | NEWSLIDER int64 = 6
169 | NEWSPINNNER int64 = 12
170 | )
171 |
--------------------------------------------------------------------------------
/bmath/sliders/sliderAlgo.go:
--------------------------------------------------------------------------------
1 | package sliders
2 |
3 | import (
4 | "danser/bmath"
5 | m2 "danser/bmath"
6 | "danser/bmath/curves"
7 | . "danser/osuconst"
8 | "math"
9 | )
10 |
11 | type SliderAlgo struct {
12 | curves []curves.Curve
13 | sections []float64
14 | length float64
15 | scale float64
16 | typ string
17 | startPos bmath.Vector2d
18 | endPos bmath.Vector2d
19 | }
20 |
21 | func NewSliderAlgo(typ string, points []m2.Vector2d, desiredLength float64) SliderAlgo {
22 | // 另外保存开始点和结束点
23 | startPos := points[0]
24 | endPos := points[len(points)-1]
25 |
26 | var curveList []curves.Curve
27 |
28 | var length = 0.0
29 | if len(points) < 3 {
30 | typ = "L"
31 | }
32 | switch typ {
33 | case "P":
34 | c := curves.NewCirArc(points[0], points[1], points[2])
35 | if !c.Unstable {
36 | curveList = append(curveList, c)
37 | length += c.GetLength()
38 | break
39 | }
40 | fallthrough
41 | case "L":
42 | for i := 1; i < len(points); i++ {
43 | c := curves.NewLinear(points[i-1], points[i])
44 | curveList = append(curveList, c)
45 | length += c.GetLength()
46 | }
47 | break
48 | case "B":
49 | lastIndex := 0
50 | for i, p := range points {
51 | if (i == len(points)-1 && p != points[i-1]) || (i < len(points)-1 && points[i+1] == p) {
52 | pts := points[lastIndex : i+1]
53 | var c curves.Curve
54 | if len(pts) > 2 {
55 | c = curves.NewBezier(pts)
56 | } else if len(pts) == 1 {
57 | c = curves.NewLinear(pts[0], pts[0])
58 | } else {
59 | c = curves.NewLinear(pts[0], pts[1])
60 | }
61 |
62 | curveList = append(curveList, c)
63 | length += c.GetLength()
64 | lastIndex = i + 1
65 | }
66 | }
67 | break
68 | case "C":
69 |
70 | if points[0] != points[1] {
71 | points = append([]bmath.Vector2d{points[0]}, points...)
72 | }
73 |
74 | if points[len(points)-1] != points[len(points)-2] {
75 | points = append(points, points[len(points)-1])
76 | }
77 |
78 | for i := 0; i < len(points)-3; i++ {
79 | c := curves.NewCatmull(points[i : i+4])
80 | curveList = append(curveList, c)
81 | length += c.GetLength()
82 | }
83 | break
84 | }
85 |
86 | scale := -1.0
87 |
88 | if length > desiredLength {
89 | scale = desiredLength / length
90 | } else if desiredLength > length {
91 | last := curveList[len(curveList)-1]
92 | p2 := last.PointAt(1)
93 | p3 := bmath.NewVec2dRad(last.GetEndAngle()+math.Pi, desiredLength-length).Add(p2)
94 | c := curves.NewLinear(p2, p3)
95 | curveList = append(curveList, c)
96 | length += c.GetLength()
97 | }
98 |
99 | sections := make([]float64, len(curveList)+1)
100 | sections[0] = 0.0
101 | prev := 0.0
102 | if len(curveList) > 1 {
103 | for i := 0; i < len(curveList); i++ {
104 | prev += curveList[i].GetLength() / length
105 | sections[i+1] = prev
106 | }
107 | }
108 |
109 | if typ == "P" {
110 |
111 | }
112 |
113 | return SliderAlgo{curveList, sections, length, scale, typ, startPos, endPos}
114 | }
115 |
116 | func (sa *SliderAlgo) PointAt(t float64) m2.Vector2d {
117 | if sa.scale > -0.5 {
118 | t *= sa.scale
119 | }
120 | if len(sa.curves) == 1 {
121 | return sa.curves[0].PointAt(t)
122 | } else {
123 | t = sa.sections[len(sa.sections)-1] * t
124 | for i := 1; i < len(sa.sections); i++ {
125 | if t <= sa.sections[i] || i == len(sa.sections)-1 {
126 | prc := (t - sa.sections[i-1]) / (sa.sections[i] - sa.sections[i-1])
127 | return sa.curves[i-1].PointAt(prc)
128 | }
129 | }
130 | }
131 | return m2.NewVec2d(PLAYFIELD_WIDTH/2, PLAYFIELD_HEIGHT/2)
132 | }
133 |
134 | func (sa *SliderAlgo) PointAtTail(t float64) m2.Vector2d {
135 | if len(sa.curves) == 1 {
136 | return sa.curves[0].PointAt(t)
137 | } else {
138 | t = sa.sections[len(sa.sections)-1] * t
139 | for i := 1; i < len(sa.sections); i++ {
140 | if t <= sa.sections[i] || i == len(sa.sections)-1 {
141 | prc := (t - sa.sections[i-1]) / (sa.sections[i] - sa.sections[i-1])
142 | return sa.curves[i-1].PointAt(prc)
143 | }
144 | }
145 | }
146 | return m2.NewVec2d(PLAYFIELD_WIDTH/2, PLAYFIELD_HEIGHT/2)
147 | }
148 |
149 | func (sa *SliderAlgo) GetLength() float64 {
150 | if sa.scale > -0.5 {
151 | return sa.length * sa.scale
152 | }
153 | return sa.length
154 | }
155 |
156 | func (sa *SliderAlgo) GetPointsLen() int {
157 | return len(sa.curves)
158 | }
159 |
--------------------------------------------------------------------------------
/assets/textures/skin.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | Name: AngeLMegumin's private skin Strato's remake
3 | Author: tarora
4 | Version: 2.2
5 |
6 |
7 |
8 |
9 | SliderBallFlip: 1
10 | CursorRotate: 0
11 | //CursorTrailRotate: 0
12 | CursorExpand: 1
13 | CursorCentre: 1
14 | SliderBallFrames: 6
15 | SpinnerFadePlayfield: 1
16 | ComboBurstRandom: 0
17 | HitCircleOverlayAboveNumer: 75
18 | AnimationFramerate: 7
19 | //CustomComboBurstSounds: 30,60,100,150,200,250,300
20 | //SliderStyle: 1
21 |
22 | [Colours]
23 | Combo1: 255,0,0
24 | Combo2: 255,120,0
25 | Combo3: 255,255,255
26 |
27 | InputOverlayText: 255,255,255
28 | SliderBorder: 179, 109, 255
29 | SliderTrackOverride: 0,0,0
30 | //SpinnerApproachCircle: 77,139,217
31 | SongSelectActiveText: 255,182,193
32 | //SongSelectInactiveText: 255,255,255
33 | //StarBreakAdditive: 0,0,0
34 | MenuGlow: 0,78,155
35 |
36 | [Fonts]
37 | HitCirclePrefix: default
38 | HitCircleOverlap: 50
39 |
40 | ScorePrefix: score
41 | ScoreOverlap: -5
42 |
43 |
44 | [Mania]
45 | Keys: 1
46 | //Mania skin config
47 | ColumnStart: 275
48 | HitPosition: 390
49 | UpsideDown: 0
50 | JudgementLine: 1
51 | ScorePosition: 230
52 | ComboPosition: 160
53 | LightFramePerSecond: 60
54 | ColumnWidth: 64
55 | //Colours
56 | //images
57 | //Keys
58 | [Mania]
59 | Keys: 2
60 | //Mania skin config
61 | ColumnStart: 275
62 | HitPosition: 390
63 | UpsideDown: 0
64 | JudgementLine: 1
65 | ScorePosition: 230
66 | ComboPosition: 160
67 | LightFramePerSecond: 60
68 | ColumnWidth: 30,30
69 | //Colours
70 | //images
71 | //Keys
72 | [Mania]
73 | Keys: 3
74 | //Mania skin config
75 | ColumnStart: 275
76 | HitPosition: 390
77 | UpsideDown: 0
78 | JudgementLine: 1
79 | ScorePosition: 230
80 | ComboPosition: 160
81 | LightFramePerSecond: 60
82 | ColumnWidth: 30,30,30
83 | //Colours
84 | //images
85 | //Keys
86 | [Mania]
87 | Keys: 4
88 | //Mania skin config
89 | ColumnStart: 345
90 | HitPosition: 390
91 | UpsideDown: 0
92 | JudgementLine: 1
93 | ScorePosition: 230
94 | ComboPosition: 160
95 | LightFramePerSecond: 24
96 | ColumnWidth: 50,50,50,50
97 | //Colours
98 | Colour1: 0,0,0,230
99 | Colour2: 0,0,0,230
100 | Colour3: 0,0,0,230
101 | Colour4: 0,0,0,230
102 | ColourHold: 255,255,255,255
103 | //Keys
104 | ColumnLineWidth: 0,0,0,0,0
105 |
106 | //Скин кукизи в 2к16 xDddDDdDdDDDD
107 | [Mania]
108 | Keys: 5
109 | //Mania skin config
110 | ColumnStart: 320
111 | HitPosition: 390
112 | UpsideDown: 0
113 | JudgementLine: 1
114 | ScorePosition: 230
115 | ComboPosition: 160
116 | LightFramePerSecond: 24
117 | ColumnWidth: 45,45,45,45,45
118 | //Colours
119 | Colour1: 0,0,0,255
120 | Colour2: 0,0,0,255
121 | Colour3: 0,0,0,255
122 | Colour4: 0,0,0,255
123 | Colour5: 0,0,0,255
124 | ColourHold: 255,255,255,255
125 | //Keys
126 | ColumnLineWidth: 0,0,0,0,0,0
127 | [Mania]
128 | Keys: 6
129 | //Mania skin config
130 | ColumnStart: 275
131 | HitPosition: 390
132 | UpsideDown: 0
133 | JudgementLine: 1
134 | ScorePosition: 230
135 | ComboPosition: 160
136 | LightFramePerSecond: 60
137 | ColumnWidth: 44,44,44,44,44,44
138 | //Colours
139 | Colour1: 0,0,0,255
140 | Colour2: 0,0,0,255
141 | Colour3: 0,0,0,255
142 | Colour4: 0,0,0,255
143 | Colour5: 0,0,0,255
144 | Colour6: 0,0,0,255
145 | ColourHold: 255,255,255,255
146 | //Keys
147 | ColumnLineWidth: 0,0,0,0,0,0,0
148 | [Mania]
149 | Keys: 7
150 | //Mania skin config
151 | ColumnStart: 275
152 | HitPosition: 390
153 | UpsideDown: 0
154 | JudgementLine: 0
155 | ScorePosition: 285
156 | ComboPosition: 170
157 | LightFramePerSecond: 24
158 | ColumnWidth: 44,44,44,44,44,44,44
159 | //Colours
160 | Colour1: 0,0,0,230
161 | Colour2: 0,0,0,230
162 | Colour3: 0,0,0,230
163 | Colour4: 0,0,0,230
164 | Colour5: 0,0,0,230
165 | Colour6: 0,0,0,230
166 | Colour7: 0,0,0,230
167 | ColourHold: 255,255,255,255
168 | ColourBarline: 255,255,255,150
169 | //images
170 | //Keys
171 | ColumnLineWidth: 0,0,0,0,0,0,0,0
172 | [Mania]
173 | Keys: 8
174 | //Mania skin config
175 | ColumnStart: 170
176 | HitPosition: 390
177 | UpsideDown: 0
178 | JudgementLine: 1
179 | ScorePosition: 260
180 | ComboPosition: 111
181 | LightFramePerSecond: 60
182 | ColumnWidth: 64,64,64,64,64,64,64,64
183 | //Colours
184 | Colour1: 0,0,0,255
185 | Colour2: 0,0,0,255
186 | Colour3: 0,0,0,255
187 | Colour4: 0,0,0,255
188 | Colour5: 0,0,0,255
189 | Colour6: 0,0,0,255
190 | Colour7: 0,0,0,255
191 | Colour8: 0,0,0,255
192 | //Keys
193 | ColumnLineWidth: 0,0,0,0,0,0,0,0,0
194 | [Mania]
195 | Keys: 9
196 | //Mania skin config
197 | ColumnStart: 275
198 | HitPosition: 390
199 | UpsideDown: 0
200 | JudgementLine: 1
201 | ScorePosition: 325
202 | ComboPosition: 111
203 | LightFramePerSecond: 60
204 | ColumnWidth: 30,30,30,30,30,30,30,30,30
205 | //Colours
206 | //images
207 | //Keys
208 |
--------------------------------------------------------------------------------
/audio/music.go:
--------------------------------------------------------------------------------
1 | package audio
2 |
3 | /*
4 | #include "bass.h"
5 | #include "bass_fx.h"
6 |
7 | extern void musicCallback(DWORD);
8 |
9 | static inline void SyncFunc(HSYNC handle, DWORD channel, DWORD data, void *user) {
10 | musicCallback(channel);
11 | }
12 |
13 | static inline void setSync(HCHANNEL channel) {
14 | BASS_ChannelSetSync(channel, BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, SyncFunc, 0);
15 | }
16 | */
17 | import "C"
18 | import (
19 | "unsafe"
20 | //"log"
21 | "danser/settings"
22 | "math"
23 | )
24 |
25 | const (
26 | MUSIC_STOPPED = 0
27 | MUSIC_PLAYING = 1
28 | MUSIC_STALLED = 2
29 | MUSIC_PAUSED = 3
30 | )
31 |
32 | type Callback func()
33 |
34 | var callbacks = make(map[C.DWORD][]Callback)
35 |
36 | //export musicCallback
37 | func musicCallback(channel C.DWORD) {
38 | for _, f := range callbacks[channel] {
39 | f()
40 | }
41 | }
42 |
43 | func registerEndCallback(channel C.DWORD, f func()) {
44 | arr := callbacks[channel]
45 | arr = append(arr, Callback(f))
46 | callbacks[channel] = arr
47 | C.setSync(channel)
48 | }
49 |
50 | func unregisterEndCallback(channel C.DWORD, f func()) {
51 | arr := callbacks[channel]
52 | var cb Callback = f
53 | for i, v := range arr {
54 | if &v == &cb {
55 | arr = append(arr[:i], arr[i+1:]...)
56 | break
57 | }
58 | }
59 | callbacks[channel] = arr
60 | }
61 |
62 | type Music struct {
63 | channel C.DWORD
64 | fft []float32
65 | beat float64
66 | peak float64
67 | }
68 |
69 | func NewMusic(path string) *Music {
70 | player := new(Music)
71 | channel := C.BASS_StreamCreateFile(0, unsafe.Pointer(C.CString(path)), 0, 0, C.BASS_ASYNCFILE|C.BASS_STREAM_DECODE)
72 | player.channel = C.BASS_FX_TempoCreate(channel, C.BASS_FX_FREESOURCE)
73 | player.fft = make([]float32, 512)
74 | return player
75 | }
76 |
77 | func (wv *Music) Play() {
78 | wv.SetVolume(settings.Audio.GeneralVolume * settings.Audio.MusicVolume)
79 | C.BASS_ChannelPlay(C.DWORD(wv.channel), 1)
80 | }
81 |
82 | func (wv *Music) PlayV(volume float64) {
83 | wv.SetVolume(volume)
84 | C.BASS_ChannelPlay(C.DWORD(wv.channel), 0)
85 | }
86 |
87 | func (wv *Music) RegisterCallback(f func()) {
88 | registerEndCallback(wv.channel, f)
89 | }
90 |
91 | func (wv *Music) UnregisterCallback(f func()) {
92 | unregisterEndCallback(wv.channel, f)
93 | }
94 |
95 | func (wv *Music) Pause() {
96 | C.BASS_ChannelPause(C.DWORD(wv.channel))
97 | }
98 |
99 | func (wv *Music) Resume() {
100 | C.BASS_ChannelPlay(C.DWORD(wv.channel), 0)
101 | }
102 |
103 | func (wv *Music) Stop() {
104 | C.BASS_ChannelStop(C.DWORD(wv.channel))
105 | }
106 |
107 | func (wv *Music) SetVolume(vol float64) {
108 | C.BASS_ChannelSetAttribute(C.DWORD(wv.channel), C.BASS_ATTRIB_VOL, C.float(vol))
109 | }
110 |
111 | func (wv *Music) SetVolumeRelative(vol float64) {
112 | C.BASS_ChannelSetAttribute(C.DWORD(wv.channel), C.BASS_ATTRIB_VOL, C.float(settings.Audio.GeneralVolume*settings.Audio.MusicVolume*vol))
113 | }
114 |
115 | func (wv *Music) GetLength() float64 {
116 | return float64(C.BASS_ChannelBytes2Seconds(wv.channel, C.BASS_ChannelGetLength(wv.channel, C.BASS_POS_BYTE)))
117 | }
118 |
119 | func (wv *Music) SetPosition(pos float64) {
120 | C.BASS_ChannelSetPosition(wv.channel, C.BASS_ChannelSeconds2Bytes(wv.channel, C.double(pos)), C.BASS_POS_BYTE)
121 | }
122 |
123 | func (wv *Music) GetPosition() float64 {
124 | return float64(C.BASS_ChannelBytes2Seconds(wv.channel, C.BASS_ChannelGetPosition(wv.channel, C.BASS_POS_BYTE)))
125 | }
126 |
127 | func (wv *Music) SetTempo(tempo float64) {
128 | C.BASS_ChannelSetAttribute(C.DWORD(wv.channel), C.BASS_ATTRIB_TEMPO, C.float((tempo-1.0)*100))
129 | }
130 |
131 | func (wv *Music) SetPitch(tempo float64) {
132 | C.BASS_ChannelSetAttribute(C.DWORD(wv.channel), C.BASS_ATTRIB_TEMPO_PITCH, C.float((tempo-1.0)*12))
133 | }
134 |
135 | func (wv *Music) GetState() int {
136 | return int(C.BASS_ChannelIsActive(wv.channel))
137 | }
138 |
139 | func (wv *Music) Update() {
140 | C.BASS_ChannelGetData(wv.channel, unsafe.Pointer(&wv.fft[0]), C.BASS_DATA_FFT1024)
141 | toPeak := -1.0
142 | beatAv := 0.0
143 | for i, g := range wv.fft {
144 | h := math.Abs(float64(g))
145 | if toPeak < h {
146 | toPeak = h
147 | }
148 | if i > 0 && i < 5 {
149 | beatAv = math.Max(beatAv, float64(g))
150 | }
151 | //toAv += math.Abs(float64(g))
152 | }
153 | //beatAv /= 5.0
154 | //toAv /= 512
155 | wv.beat = beatAv
156 | wv.peak = toPeak
157 | }
158 |
159 | func (wv *Music) GetFFT() []float32 {
160 | return wv.fft
161 | }
162 |
163 | func (wv *Music) GetPeak() float64 {
164 | return wv.peak
165 | }
166 |
167 | func (wv *Music) GetBeat() float64 {
168 | return wv.beat
169 | }
170 |
--------------------------------------------------------------------------------
/dance/movers/angleoffset.go:
--------------------------------------------------------------------------------
1 | package movers
2 |
3 | import (
4 | "danser/beatmap/objects"
5 | "danser/bmath"
6 | "danser/bmath/curves"
7 | "danser/settings"
8 | "math"
9 | )
10 |
11 | type AngleOffsetMover struct {
12 | lastAngle float64
13 | lastPoint bmath.Vector2d
14 | bz curves.Bezier
15 | startTime, endTime int64
16 | invert float64
17 | }
18 |
19 | func NewAngleOffsetMover() MultiPointMover {
20 | return &AngleOffsetMover{lastAngle: 0, invert: 1}
21 | }
22 |
23 | func (bm *AngleOffsetMover) Reset() {
24 | bm.lastAngle = 0
25 | bm.invert = 1
26 | bm.lastPoint = bmath.NewVec2d(0, 0)
27 | }
28 |
29 | func (bm *AngleOffsetMover) SetObjects(objs []objects.BaseObject) {
30 | end := objs[0]
31 | start := objs[1]
32 |
33 | endPos := end.GetBasicData().EndPos
34 | endTime := end.GetBasicData().EndTime
35 | startPos := start.GetBasicData().StartPos
36 | startTime := start.GetBasicData().StartTime
37 |
38 | distance := endPos.Dst(startPos)
39 |
40 | s1, ok1 := end.(*objects.Slider)
41 | s2, ok2 := start.(*objects.Slider)
42 |
43 | var points []bmath.Vector2d
44 |
45 | scaledDistance := distance * settings.Dance.Flower.DistanceMult
46 | newAngle := settings.Dance.Flower.AngleOffset * math.Pi / 180.0
47 |
48 | if end.GetBasicData().StartTime > 0 && settings.Dance.Flower.LongJump >= 0 && (startTime-endTime) > settings.Dance.Flower.LongJump {
49 | scaledDistance = float64(startTime-endTime) * settings.Dance.Flower.LongJumpMult
50 | }
51 |
52 | if endPos == startPos {
53 | if settings.Dance.Flower.LongJumpOnEqualPos {
54 | scaledDistance = float64(startTime-endTime) * settings.Dance.Flower.LongJumpMult
55 | bm.lastAngle += math.Pi
56 |
57 | pt1 := bmath.NewVec2dRad(bm.lastAngle, scaledDistance).Add(endPos)
58 |
59 | if ok1 {
60 | pt1 = bmath.NewVec2dRad(s1.GetEndAngle(), scaledDistance).Add(endPos)
61 | }
62 |
63 | if !ok2 {
64 | angle := bm.lastAngle - newAngle*bm.invert
65 | pt2 := bmath.NewVec2dRad(angle, scaledDistance).Add(startPos)
66 | bm.lastAngle = angle
67 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
68 | } else {
69 | pt2 := bmath.NewVec2dRad(s2.GetStartAngle(), scaledDistance).Add(startPos)
70 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
71 | }
72 |
73 | } else {
74 | points = []bmath.Vector2d{endPos, startPos}
75 | }
76 | } else if ok1 && ok2 {
77 | bm.invert = -1 * bm.invert
78 |
79 | pt1 := bmath.NewVec2dRad(s1.GetEndAngle(), scaledDistance).Add(endPos)
80 | pt2 := bmath.NewVec2dRad(s2.GetStartAngle(), scaledDistance).Add(startPos)
81 |
82 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
83 | } else if ok1 {
84 | bm.invert = -1 * bm.invert
85 | bm.lastAngle = endPos.AngleRV(startPos) - newAngle*bm.invert
86 |
87 | pt1 := bmath.NewVec2dRad(s1.GetEndAngle(), scaledDistance).Add(endPos)
88 | pt2 := bmath.NewVec2dRad(bm.lastAngle, scaledDistance).Add(startPos)
89 |
90 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
91 | } else if ok2 {
92 | bm.lastAngle += math.Pi
93 |
94 | pt1 := bmath.NewVec2dRad(bm.lastAngle, scaledDistance).Add(endPos)
95 | pt2 := bmath.NewVec2dRad(s2.GetStartAngle(), scaledDistance).Add(startPos)
96 |
97 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
98 | } else {
99 | if settings.Dance.Flower.UseNewStyle {
100 | if bmath.AngleBetween(endPos, bm.lastPoint, startPos) >= settings.Dance.Flower.AngleOffset*math.Pi/180.0 {
101 | bm.invert = -1 * bm.invert
102 | newAngle = settings.Dance.Flower.StreamAngleOffset * math.Pi / 180.0
103 | }
104 | } else if startTime-endTime < settings.Dance.Flower.StreamTrigger {
105 | newAngle = settings.Dance.Flower.StreamAngleOffset * math.Pi / 180.0
106 | }
107 |
108 | angle := endPos.AngleRV(startPos) - newAngle*bm.invert
109 |
110 | pt1 := bmath.NewVec2dRad(bm.lastAngle+math.Pi, scaledDistance).Add(endPos)
111 | pt2 := bmath.NewVec2dRad(angle, scaledDistance).Add(startPos)
112 |
113 | bm.lastAngle = angle
114 |
115 | if !settings.Dance.Flower.UseNewStyle && startTime-endTime < settings.Dance.Flower.StreamTrigger && !(start.GetBasicData().SliderPoint && end.GetBasicData().SliderPoint) {
116 | bm.invert = -1 * bm.invert
117 | }
118 |
119 | points = []bmath.Vector2d{endPos, pt1, pt2, startPos}
120 | }
121 |
122 | bm.bz = curves.NewBezier(points)
123 | bm.endTime = endTime
124 | bm.startTime = startTime
125 | bm.lastPoint = endPos
126 | }
127 |
128 | func (bm *AngleOffsetMover) Update(time int64) bmath.Vector2d {
129 | t := float64(time-bm.endTime) / float64(bm.startTime-bm.endTime)
130 | t = math.Max(0.0, math.Min(1.0, t))
131 | return bm.bz.NPointAt(t)
132 | }
133 |
134 | func (bm *AngleOffsetMover) GetEndTime() int64 {
135 | return bm.startTime
136 | }
137 |
--------------------------------------------------------------------------------
/storyboard/commands.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import (
4 | "danser/animation/easing"
5 | "danser/bmath"
6 | "github.com/go-gl/mathgl/mgl32"
7 | "log"
8 | "math"
9 | "strconv"
10 | )
11 |
12 | type Command struct {
13 | start, end int64
14 | command string
15 | easing func(float64) float64
16 | val []float64
17 | numSections int64
18 | sectionTime int64
19 | sections [][]float64
20 | custom string
21 | constant bool
22 | }
23 |
24 | func NewCommand(data []string) *Command {
25 | command := &Command{}
26 | command.command = data[0]
27 |
28 | easingID, err := strconv.ParseInt(data[1], 10, 32)
29 |
30 | if err != nil {
31 | log.Println(err)
32 | }
33 |
34 | command.easing = easing.GetEasing(easingID)
35 |
36 | command.start, err = strconv.ParseInt(data[2], 10, 64)
37 |
38 | if err != nil {
39 | panic(err)
40 | }
41 |
42 | if data[3] == "" {
43 | command.end = command.start
44 | } else {
45 | command.end, err = strconv.ParseInt(data[3], 10, 64)
46 |
47 | if err != nil {
48 | panic(err)
49 | }
50 | }
51 |
52 | if command.end < command.start {
53 | command.end = command.start
54 | }
55 |
56 | arguments := 0
57 |
58 | switch command.command {
59 | case "F", "R", "S", "MX", "MY":
60 | arguments = 1
61 | break
62 | case "M", "V":
63 | arguments = 2
64 | break
65 | case "C":
66 | arguments = 3
67 | break
68 | }
69 |
70 | parameters := data[4:]
71 |
72 | if arguments == 0 {
73 | command.custom = parameters[0]
74 | command.val = make([]float64, 1)
75 | return command
76 | }
77 |
78 | numSections := len(parameters) / arguments
79 | command.numSections = int64(numSections) - 1
80 | command.sectionTime = command.end - command.start
81 |
82 | if command.numSections > 1 {
83 | command.end = command.start + command.sectionTime*command.numSections
84 | }
85 |
86 | command.sections = make([][]float64, numSections)
87 | command.val = make([]float64, arguments)
88 |
89 | for i := 0; i < numSections; i++ {
90 | command.sections[i] = make([]float64, arguments)
91 | for j := 0; j < arguments; j++ {
92 | var err error
93 | command.sections[i][j], err = strconv.ParseFloat(parameters[arguments*i+j], 64)
94 |
95 | if command.command == "C" {
96 | command.sections[i][j] /= 255
97 | }
98 |
99 | if err != nil {
100 | log.Println(err)
101 | }
102 | }
103 | }
104 |
105 | copy(command.val, command.sections[0])
106 |
107 | if numSections == 1 {
108 | command.constant = true
109 | }
110 |
111 | return command
112 | }
113 |
114 | func (command *Command) Update(time int64) {
115 |
116 | if command.command == "P" {
117 | if command.start != command.end {
118 | if time >= command.start && time <= command.end {
119 | command.val[0] = 1
120 | } else {
121 | command.val[0] = 0
122 | }
123 | } else {
124 | command.val[0] = 1
125 | }
126 |
127 | return
128 | }
129 |
130 | if command.constant {
131 | copy(command.val, command.sections[0])
132 | } else {
133 |
134 | section := int64(0)
135 |
136 | if command.sectionTime > 0 {
137 | section = (time - command.start) / command.sectionTime
138 | }
139 |
140 | if section >= command.numSections {
141 | section = command.numSections - 1
142 | }
143 |
144 | t := command.easing(math.Min(math.Max(float64((time-command.start)-section*command.sectionTime)/float64(command.sectionTime), 0), 1))
145 |
146 | for i := range command.val {
147 | command.val[i] = command.sections[section][i] + t*(command.sections[section+1][i]-command.sections[section][i])
148 | }
149 | }
150 | }
151 |
152 | func (command *Command) Apply(obj Object) {
153 | switch command.command {
154 | case "F":
155 | obj.SetAlpha(command.val[0])
156 | break
157 | case "R":
158 | obj.SetRotation(command.val[0])
159 | break
160 | case "S":
161 | obj.SetScale(bmath.NewVec2d(command.val[0], command.val[0]))
162 | break
163 | case "V":
164 | obj.SetScale(bmath.NewVec2d(command.val[0], command.val[1]))
165 | break
166 | case "M":
167 | obj.SetPosition(bmath.NewVec2d(command.val[0], command.val[1]))
168 | break
169 | case "MX":
170 | obj.SetPosition(bmath.NewVec2d(command.val[0], obj.GetPosition().Y))
171 | break
172 | case "MY":
173 | obj.SetPosition(bmath.NewVec2d(obj.GetPosition().X, command.val[0]))
174 | break
175 | case "C":
176 | obj.SetColor(mgl32.Vec3{float32(command.val[0]), float32(command.val[1]), float32(command.val[2])})
177 | break
178 | case "P":
179 | switch command.custom {
180 | case "H":
181 | obj.SetHFlip(command.val[0] == 1)
182 | break
183 | case "V":
184 | obj.SetVFlip(command.val[0] == 1)
185 | break
186 | case "A":
187 | obj.SetAdditive(command.val[0] == 1)
188 | break
189 | }
190 | break
191 | }
192 | }
193 |
194 | func (command *Command) Init(obj Object) {
195 |
196 | if command.command == "P" {
197 | return
198 | }
199 |
200 | copy(command.val, command.sections[0])
201 | command.Apply(obj)
202 | }
203 |
204 | //TODO: TRIGGER command
205 |
--------------------------------------------------------------------------------
/render/slider.go:
--------------------------------------------------------------------------------
1 | package render
2 |
3 | import (
4 | "danser/bmath"
5 | "danser/render/framebuffer"
6 | "danser/settings"
7 | "danser/utils"
8 | "github.com/go-gl/gl/v3.3-core/gl"
9 | "github.com/go-gl/mathgl/mgl32"
10 | "github.com/wieku/glhf"
11 | _ "image/png"
12 | "io/ioutil"
13 | "log"
14 | "math"
15 | )
16 |
17 | var sliderShader *glhf.Shader = nil
18 | var fboShader *glhf.Shader
19 | var fboSlice *glhf.VertexSlice
20 | var sliderVertexFormat glhf.AttrFormat
21 | var cam mgl32.Mat4
22 | var fbo *framebuffer.Framebuffer
23 | var fboUnit int32
24 | var CS float64
25 |
26 | func SetupSlider() {
27 |
28 | sliderVertexFormat = glhf.AttrFormat{
29 | {Name: "in_position", Type: glhf.Vec3},
30 | {Name: "center", Type: glhf.Vec3},
31 | {Name: "in_tex_coord", Type: glhf.Vec2},
32 | }
33 | var err error
34 |
35 | svert, _ := ioutil.ReadFile("assets/shaders/slider.vsh")
36 | sfrag, _ := ioutil.ReadFile("assets/shaders/slider.fsh")
37 | sliderShader, err = glhf.NewShader(sliderVertexFormat, glhf.AttrFormat{{Name: "col_border", Type: glhf.Vec4}, {Name: "tex", Type: glhf.Int}, {Name: "proj", Type: glhf.Mat4}, {Name: "trans", Type: glhf.Mat4}, {Name: "col_border1", Type: glhf.Vec4}}, string(svert), string(sfrag))
38 | if err != nil {
39 | log.Println(err)
40 | }
41 |
42 | fvert, _ := ioutil.ReadFile("assets/shaders/fbopass.vsh")
43 | ffrag, _ := ioutil.ReadFile("assets/shaders/fbopass.fsh")
44 | fboShader, err = glhf.NewShader(glhf.AttrFormat{
45 | {Name: "in_position", Type: glhf.Vec3},
46 | {Name: "in_tex_coord", Type: glhf.Vec2},
47 | }, glhf.AttrFormat{{Name: "tex", Type: glhf.Int}}, string(fvert), string(ffrag))
48 |
49 | if err != nil {
50 | log.Println("FboPass: " + err.Error())
51 | }
52 |
53 | fbo = framebuffer.NewFrame(int(settings.Graphics.GetWidth()), int(settings.Graphics.GetHeight()), true, true)
54 | fbo.Texture().Bind(29)
55 | fboUnit = 29
56 |
57 | fboSlice = glhf.MakeVertexSlice(fboShader, 6, 6)
58 | fboSlice.Begin()
59 | fboSlice.SetVertexData([]float32{
60 | -1, -1, 0, 0, 0,
61 | 1, -1, 0, 1, 0,
62 | -1, 1, 0, 0, 1,
63 | 1, -1, 0, 1, 0,
64 | 1, 1, 0, 1, 1,
65 | -1, 1, 0, 0, 1,
66 | })
67 | fboSlice.End()
68 |
69 | }
70 |
71 | type SliderRenderer struct{}
72 |
73 | func NewSliderRenderer() *SliderRenderer {
74 | return &SliderRenderer{}
75 | }
76 |
77 | func (sr *SliderRenderer) Begin() {
78 |
79 | gl.Disable(gl.BLEND)
80 | gl.Enable(gl.DEPTH_TEST)
81 | gl.DepthMask(true)
82 | gl.DepthFunc(gl.LESS)
83 |
84 | fbo.Begin()
85 |
86 | gl.ClearColor(0.0, 0.0, 0.0, 0.0)
87 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
88 |
89 | sliderShader.Begin()
90 |
91 | gl.ActiveTexture(gl.TEXTURE0)
92 | SliderGradient.Bind(0)
93 | sliderShader.SetUniformAttr(1, int32(0))
94 | sliderShader.SetUniformAttr(2, cam)
95 | }
96 |
97 | func (sr *SliderRenderer) SetColor(color mgl32.Vec4, prev mgl32.Vec4) {
98 | sliderShader.SetUniformAttr(0, color)
99 | if settings.Objects.EnableCustomSliderBorderGradientOffset {
100 | sliderShader.SetUniformAttr(4, utils.GetColorShifted(color, settings.Objects.SliderBorderGradientOffset))
101 | } else {
102 | sliderShader.SetUniformAttr(4, prev)
103 | }
104 | }
105 |
106 | func (sr *SliderRenderer) SetScale(scale float64) {
107 | sliderShader.SetUniformAttr(3, mgl32.Scale3D(float32(scale), float32(scale), 1))
108 | }
109 |
110 | func (sr *SliderRenderer) EndAndRender() {
111 |
112 | sliderShader.End()
113 | fbo.End()
114 | gl.Disable(gl.DEPTH_TEST)
115 | gl.DepthMask(false)
116 | gl.Enable(gl.BLEND)
117 |
118 | gl.BlendEquation(gl.FUNC_ADD)
119 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
120 |
121 | fboShader.Begin()
122 | fboShader.SetUniformAttr(0, int32(fboUnit))
123 | fboSlice.BeginDraw()
124 | fboSlice.Draw()
125 | fboSlice.EndDraw()
126 | fboShader.End()
127 | }
128 |
129 | func (self *SliderRenderer) SetCamera(camera mgl32.Mat4) {
130 | cam = camera
131 | sliderShader.SetUniformAttr(2, cam)
132 | }
133 |
134 | func (self *SliderRenderer) GetShape(curve []bmath.Vector2d) ([]float32, int) {
135 | return createMesh(curve), int(settings.Objects.SliderLOD)
136 | }
137 |
138 | func (self *SliderRenderer) UploadMesh(mesh []float32) *glhf.VertexSlice {
139 | slice := glhf.MakeVertexSlice(sliderShader, len(mesh)/8, len(mesh)/8)
140 | slice.Begin()
141 | slice.SetVertexData(mesh)
142 | slice.End()
143 | return slice
144 | }
145 |
146 | func createMesh(curve []bmath.Vector2d) []float32 {
147 | vertices := make([]float32, 8*3*int(settings.Objects.SliderLOD)*len(curve))
148 | num := 0
149 | iter := 0
150 | for _, v := range curve {
151 | tab := createCircle(v.X, v.Y, 64*CS, int(settings.Objects.SliderLOD))
152 | for j := range tab {
153 | if j >= 2 {
154 | p1, p2, p3 := tab[j-1], tab[j], tab[0]
155 | set(vertices, iter, float32(p1.X), float32(p1.Y), 1.0, float32(p3.X), float32(p3.Y), 0.0, 0.0, 0.0, float32(p2.X), float32(p2.Y), 1.0, float32(p3.X), float32(p3.Y), 0.0, 0.0, 0.0, float32(p3.X), float32(p3.Y), 0.0, float32(p3.X), float32(p3.Y), 0.0, 1.0, 0.0)
156 | iter += 24
157 | }
158 | }
159 | num += len(tab)
160 | }
161 |
162 | return vertices
163 | }
164 |
165 | func set(array []float32, index int, data ...float32) {
166 | copy(array[index:index+len(data)], data)
167 | }
168 |
169 | func createCircle(x, y, radius float64, segments int) []bmath.Vector2d {
170 | points := make([]bmath.Vector2d, segments+2)
171 | points[0] = bmath.NewVec2d(x, y)
172 |
173 | for i := 0; i < segments; i++ {
174 | points[i+1] = bmath.NewVec2dRad(float64(i)/float64(segments)*2*math.Pi, radius).AddS(x, y)
175 | }
176 |
177 | points[segments+1] = points[1]
178 | return points
179 | }
180 |
--------------------------------------------------------------------------------
/render/font/font.go:
--------------------------------------------------------------------------------
1 | package font
2 |
3 | import (
4 | "danser/bmath"
5 | "danser/render"
6 | "danser/render/texture"
7 | "danser/settings"
8 | "github.com/go-gl/gl/v3.3-core/gl"
9 | "github.com/golang/freetype"
10 | "github.com/golang/freetype/truetype"
11 | font2 "golang.org/x/image/font"
12 | "golang.org/x/image/math/fixed"
13 | "image"
14 | "image/draw"
15 | "io"
16 | "io/ioutil"
17 | "log"
18 | )
19 |
20 | var fonts map[string]*Font
21 |
22 | func init() {
23 | fonts = make(map[string]*Font)
24 | }
25 |
26 | func GetFont(name string) *Font {
27 | return fonts[name]
28 | }
29 |
30 | type glyphData struct {
31 | region texture.TextureRegion
32 | advance, bearingX, bearingY float64
33 | }
34 |
35 | type Font struct {
36 | face font2.Face
37 | atlas *texture.TextureAtlas
38 | glyphs []*glyphData
39 | min, max rune
40 | initialSize float64
41 | }
42 |
43 | func LoadFont(reader io.Reader, loc uint) *Font {
44 | data, err := ioutil.ReadAll(reader)
45 |
46 | if err != nil {
47 | panic("Error reading font: " + err.Error())
48 | }
49 |
50 | ttf, err := truetype.Parse(data)
51 |
52 | if err != nil {
53 | panic("Error reading font: " + err.Error())
54 | }
55 |
56 | font := new(Font)
57 | font.min = rune(32)
58 | font.max = rune(127)
59 | font.initialSize = 64.0
60 | font.glyphs = make([]*glyphData, font.max-font.min+1)
61 |
62 | font.atlas = texture.NewTextureAtlas(4096, 4)
63 |
64 | font.atlas.Bind(loc)
65 |
66 | fc := truetype.NewFace(ttf, &truetype.Options{Size: font.initialSize, DPI: 72, Hinting: font2.HintingFull})
67 | font.face = fc
68 | context := freetype.NewContext()
69 | context.SetFont(ttf)
70 | context.SetFontSize(font.initialSize)
71 | context.SetDPI(72)
72 | context.SetHinting(font2.HintingFull)
73 |
74 | for i := font.min; i <= font.max; i++ {
75 |
76 | gBnd, gAdv, ok := fc.GlyphBounds(i)
77 | if ok != true {
78 | continue
79 | }
80 |
81 | gh := int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
82 | gw := int32((gBnd.Max.X - gBnd.Min.X) >> 6)
83 |
84 | //if glyph has no dimensions set to a max value
85 | if gw == 0 || gh == 0 {
86 | gBnd = ttf.Bounds(fixed.Int26_6(20))
87 | gw = int32((gBnd.Max.X - gBnd.Min.X) >> 6)
88 | gh = int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
89 |
90 | //above can sometimes yield 0 for font smaller than 48pt, 1 is minimum
91 | if gw == 0 || gh == 0 {
92 | gw = 1
93 | gh = 1
94 | }
95 | }
96 |
97 | //The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y.
98 | gAscent := int(-gBnd.Min.Y) >> 6
99 | //gdescent := int(gBnd.Max.Y) >> 6
100 |
101 | fg, bg := image.White, image.Transparent
102 | rect := image.Rect(0, 0, int(gw), int(gh))
103 | rgba := image.NewNRGBA(rect)
104 | draw.Draw(rgba, rgba.Bounds(), bg, image.Point{}, draw.Src)
105 |
106 | context.SetClip(rect)
107 | context.SetDst(rgba)
108 | context.SetSrc(fg)
109 |
110 | px := 0 - (int(gBnd.Min.X) >> 6)
111 | py := gAscent
112 | pt := freetype.Pt(px, py)
113 |
114 | // Draw the text from mask to image
115 | _, err = context.DrawString(string(i), pt)
116 | if err != nil {
117 | log.Println(err)
118 | continue
119 | }
120 |
121 | //res := font.toSDF(rgba)
122 |
123 | newPix := make([]uint8, len(rgba.Pix))
124 | height := rgba.Bounds().Dy()
125 |
126 | for i := 0; i < height; i++ {
127 | copy(newPix[i*rgba.Stride:(i+1)*rgba.Stride], rgba.Pix[(height-1-i)*rgba.Stride:(height-i)*rgba.Stride])
128 | }
129 |
130 | region := font.atlas.AddTexture(string(i), rgba.Bounds().Dx(), rgba.Bounds().Dy(), newPix)
131 |
132 | //set w,h and adv, bearing V and bearing H in char
133 | advance := float64(gAdv) / 64
134 | bearingV := float64(gBnd.Max.Y) / 64
135 | bearingH := float64(gBnd.Min.X) / 64
136 | font.glyphs[i-font.min] = &glyphData{*region, advance, bearingH, bearingV}
137 | }
138 |
139 | log.Println(ttf.Name(truetype.NameIDFontFullName), "loaded!")
140 | fonts[ttf.Name(truetype.NameIDFontFullName)] = font
141 | return font
142 | }
143 |
144 | func (font *Font) Draw(renderer *render.SpriteBatch, x, y float64, size float64, text string) {
145 | font.DrawAndGetLastPosition(renderer, x, y, size, text)
146 | }
147 |
148 | func (font *Font) DrawAndGetLastPosition(renderer *render.SpriteBatch, x, y float64, size float64, text string) float64 {
149 | xpad := x
150 | scale := size / font.initialSize
151 |
152 | for i, c := range text {
153 | if c-font.min < 0 || c-font.min > font.max || int(c) > 127 {
154 | //log.Println("Warning! A non-ASCII or unprintable character is presented in text! Skipping")
155 | continue
156 | }
157 | char := font.glyphs[c-font.min]
158 | if char == nil {
159 | continue
160 | }
161 |
162 | kerning := 0.0
163 |
164 | if i > 0 {
165 | kerning = float64(font.face.Kern(rune(text[i-1]), c)) / 64
166 | }
167 |
168 | renderer.SetScale(scale, scale)
169 | renderer.SetTranslation(bmath.NewVec2d(xpad+(char.bearingX-kerning+float64(char.region.Width)/2)*scale, y+(float64(char.region.Height)/2-char.bearingY)*scale))
170 | renderer.DrawTexture(char.region)
171 | xpad += scale * (char.advance - kerning)
172 |
173 | }
174 | return xpad
175 | }
176 |
177 | type Word struct {
178 | X float64
179 | Size float64
180 | Text string
181 | }
182 |
183 | func (font *Font) DrawAll(renderer *render.SpriteBatch, words []Word) {
184 | // 清除之前的文字,否则会重叠
185 | gl.ClearColor(0, 0, 0, 1)
186 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
187 | //获得初始Y坐标
188 | currentY := float64(settings.Graphics.GetHeight() - 40)
189 | for _, word := range words {
190 | font.Draw(renderer, word.X, currentY, word.Size, word.Text)
191 | // 扩大1.7倍size的间距
192 | currentY -= word.Size * 1.7
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/settings-1080.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "v1",
3 | "General": {
4 | "OsuSongsDir": "song\\"
5 | },
6 | "VSplayer": {
7 | "PlayerInfo": {
8 | "Players": 50,
9 | "SpecifiedPlayers": false,
10 | "SpecifiedLine": "2"
11 | },
12 | "PlayerInfoUI": {
13 | "BaseSize": 9,
14 | "BaseX": 18,
15 | "BaseY": 1037,
16 | "ShowMouse1": true,
17 | "ShowMouse2": true,
18 | "ShowRealTimePP": true,
19 | "RealTimePPGap": 800,
20 | "ShowRealTimeUR": false,
21 | "ShowPPAndURRank": false,
22 | "Rank1Highlight": true,
23 | "HighlightMult": 1.3,
24 | "LineGapMult": 0
25 | },
26 | "RecordInfoUI": {
27 | "Recorder": "wasupandceacar",
28 | "RecordTime": "2019.2.21",
29 | "RecordBaseX": 1609,
30 | "RecordBaseY": 40,
31 | "RecordBaseSize": 22,
32 | "RecordAlpha": 0.4
33 | },
34 | "DiffInfoUI": {
35 | "ShowDiffInfo": true,
36 | "DiffBaseX": 1649,
37 | "DiffBaseY": 1035,
38 | "DiffBaseSize": 22,
39 | "DiffAlpha": 0.8
40 | },
41 | "PlayerFieldUI": {
42 | "HitFadeTime": 200,
43 | "CursorColorNum": 13,
44 | "CursorColorSkipNum": 13,
45 | "ShowHitCircleNumber": true
46 | },
47 | "MapInfo": {
48 | "Title": "Exit This Earth's Atomosphere",
49 | "Difficulty": "Evolution"
50 | },
51 | "Mods": {
52 | "EnableDT": false,
53 | "EnableHT": false,
54 | "EnableEZ": false,
55 | "EnableHR": false,
56 | "EnableHD": false
57 | },
58 | "Knockout": {
59 | "EnableKnockout": true,
60 | "ShowTrueMiss": false,
61 | "PlayerFadeTime": 1500,
62 | "SameTimeOffset": 22,
63 | "MissMult": 1
64 | },
65 | "ReplayandCache": {
66 | "ReplayDir": "replays\\",
67 | "CacheDir": "cache\\",
68 | "SaveResultCache": true,
69 | "ReadResultCache": true,
70 | "ReplayDebug": false
71 | },
72 | "ErrorFix": {
73 | "EnableErrorFix": true,
74 | "ErrorFixFile": "error.err"
75 | },
76 | "Skin": {
77 | "EnableSkin": true,
78 | "SkinDir": "skin\\lain1\\",
79 | "NumberOffset": 0
80 | }
81 | },
82 | "Graphics": {
83 | "Width": 1920,
84 | "Height": 1080,
85 | "WindowWidth": 1920,
86 | "WindowHeight": 1080,
87 | "Fullscreen": false,
88 | "VSync": false,
89 | "FPSCap": 1000,
90 | "MSAA": 16
91 | },
92 | "Audio": {
93 | "GeneralVolume": 0.5,
94 | "MusicVolume": 0.5,
95 | "SampleVolume": 0.5,
96 | "Offset": 0,
97 | "IgnoreBeatmapSamples": false,
98 | "IgnoreBeatmapSampleVolume": false
99 | },
100 | "Beat": {
101 | "BeatScale": 1.2
102 | },
103 | "Cursor": {
104 | "Colors": {
105 | "EnableRainbow": false,
106 | "RainbowSpeed": 8,
107 | "BaseColor": {
108 | "Hue": 0,
109 | "Saturation": 1,
110 | "Value": 1
111 | },
112 | "EnableCustomHueOffset": false,
113 | "HueOffset": 0,
114 | "FlashToTheBeat": false,
115 | "FlashAmplitude": 0
116 | },
117 | "EnableCustomTagColorOffset": true,
118 | "TagColorOffset": -36,
119 | "EnableTrailGlow": true,
120 | "EnableCustomTrailGlowOffset": true,
121 | "TrailGlowOffset": -36,
122 | "ScaleToCS": false,
123 | "CursorSize": 8.5,
124 | "ScaleToTheBeat": false,
125 | "ShowCursorsOnBreaks": true,
126 | "BounceOnEdges": false,
127 | "TrailEndScale": 0.4,
128 | "TrailDensity": 1,
129 | "TrailMaxLength": 2000,
130 | "TrailRemoveSpeed": 1,
131 | "GlowEndScale": 0.4,
132 | "InnerLengthMult": 0.9
133 | },
134 | "Objects": {
135 | "MandalaTexturesTrigger": 5,
136 | "MandalaTexturesAlpha": 0.3,
137 | "ForceSliderBallTexture": true,
138 | "DrawApproachCircles": true,
139 | "Colors": {
140 | "EnableRainbow": false,
141 | "RainbowSpeed": 8,
142 | "BaseColor": {
143 | "Hue": 0,
144 | "Saturation": 1,
145 | "Value": 1
146 | },
147 | "EnableCustomHueOffset": false,
148 | "HueOffset": 0,
149 | "FlashToTheBeat": false,
150 | "FlashAmplitude": 100
151 | },
152 | "ObjectsSize": -1,
153 | "CSMult": 1,
154 | "ScaleToTheBeat": false,
155 | "SliderLOD": 30,
156 | "SliderPathLOD": 50,
157 | "SliderSnakeIn": true,
158 | "SliderSnakeOut": true,
159 | "SliderMerge": true,
160 | "DrawFollowPoints": true,
161 | "WhiteFollowPoints": true,
162 | "FollowPointColorOffset": 0,
163 | "EnableCustomSliderBorderColor": false,
164 | "CustomSliderBorderColor": {
165 | "EnableRainbow": false,
166 | "RainbowSpeed": 8,
167 | "BaseColor": {
168 | "Hue": 0,
169 | "Saturation": 0,
170 | "Value": 1
171 | },
172 | "EnableCustomHueOffset": false,
173 | "HueOffset": 0,
174 | "FlashToTheBeat": false,
175 | "FlashAmplitude": 100
176 | },
177 | "EnableCustomSliderBorderGradientOffset": true,
178 | "SliderBorderGradientOffset": 18,
179 | "StackEnabled": true
180 | },
181 | "Playfield": {
182 | "LeadInTime": 5,
183 | "LeadInHold": 2,
184 | "FadeOutTime": 20,
185 | "BackgroundInDim": 0,
186 | "BackgroundDim": 0.7,
187 | "BackgroundDimBreaks": 0.1,
188 | "BlurEnable": true,
189 | "BackgroundInBlur": 0,
190 | "BackgroundBlur": 0.6,
191 | "BackgroundBlurBreaks": 0.6,
192 | "SpectrumInDim": 0,
193 | "SpectrumDim": 1,
194 | "SpectrumDimBreaks": 0,
195 | "StoryboardEnabled": true,
196 | "Scale": 1,
197 | "FlashToTheBeat": true,
198 | "UnblurToTheBeat": true,
199 | "UnblurFill": 0.8,
200 | "KiaiFactor": 1.1,
201 | "BaseRotation": 0,
202 | "RotationEnabled": false,
203 | "RotationSpeed": 2,
204 | "BloomEnabled": true,
205 | "BloomToTheBeat": false,
206 | "BloomBeatAddition": 0.3,
207 | "Bloom": {
208 | "Threshold": 0,
209 | "Blur": 0.6,
210 | "Power": 0.7
211 | }
212 | },
213 | "Dance": {
214 | "SliderDance": false,
215 | "TAGSliderDance": false,
216 | "Bezier": {
217 | "Aggressiveness": 60,
218 | "SliderAggressiveness": 3
219 | },
220 | "Flower": {
221 | "UseNewStyle": true,
222 | "AngleOffset": 90,
223 | "DistanceMult": 0.666,
224 | "StreamTrigger": 130,
225 | "StreamAngleOffset": 90,
226 | "LongJump": -1,
227 | "LongJumpMult": 0.7,
228 | "LongJumpOnEqualPos": false
229 | },
230 | "HalfCircle": {
231 | "RadiusMultiplier": 1,
232 | "StreamTrigger": 130
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "v1",
3 | "General": {
4 | "OsuSongsDir": "song\\"
5 | },
6 | "VSplayer": {
7 | "PlayerInfo": {
8 | "Players": 1,
9 | "SpecifiedPlayers": false,
10 | "SpecifiedLine": "6",
11 | "SpecifiedColor": "WhiteCat:255,255,255|YakumoYukari:0,0,0"
12 | },
13 | "PlayerInfoUI": {
14 | "BaseSize": 6,
15 | "BaseX": 14,
16 | "BaseY": 696,
17 | "ShowMouse1": false,
18 | "ShowMouse2": false,
19 | "ShowRealTimePP": true,
20 | "RealTimePPGap": 800,
21 | "ShowRealTimeUR": true,
22 | "ShowPPAndURRank": false,
23 | "Rank1Highlight": false,
24 | "HighlightMult": 1.3,
25 | "LineGapMult": 0
26 | },
27 | "RecordInfoUI": {
28 | "Recorder": "wasupandceacar",
29 | "RecordTime": "2019.2.21",
30 | "RecordBaseX": 1019,
31 | "RecordBaseY": 35,
32 | "RecordBaseSize": 18,
33 | "RecordAlpha": 0.4
34 | },
35 | "DiffInfoUI": {
36 | "ShowDiffInfo": false,
37 | "DiffBaseX": 1049,
38 | "DiffBaseY": 678,
39 | "DiffBaseSize": 18,
40 | "DiffAlpha": 0.8
41 | },
42 | "PlayerFieldUI": {
43 | "HitFadeTime": 200,
44 | "CursorColorNum": 25,
45 | "CursorColorSkipNum": 13,
46 | "ShowHitCircleNumber": true
47 | },
48 | "MapInfo": {
49 | "Title": "Graces of Heaven",
50 | "Difficulty": "Debug"
51 | },
52 | "Mods": {
53 | "EnableDT": false,
54 | "EnableHT": false,
55 | "EnableEZ": false,
56 | "EnableHR": false,
57 | "EnableHD": false
58 | },
59 | "Knockout": {
60 | "EnableKnockout": true,
61 | "ShowTrueMiss": false,
62 | "PlayerFadeTime": 1500,
63 | "SameTimeOffset": 12,
64 | "MissMult": 0.8
65 | },
66 | "ReplayandCache": {
67 | "ReplayDir": "replays\\",
68 | "CacheDir": "cache\\",
69 | "UseCacheSystem": true,
70 | "ReplayDebug": true
71 | },
72 | "ErrorFix": {
73 | "EnableErrorFix": false,
74 | "ErrorFixFile": "error.err"
75 | },
76 | "Skin": {
77 | "EnableSkin": true,
78 | "SkinDir": "skin\\rafis1\\",
79 | "NumberOffset": 30
80 | }
81 | },
82 | "Graphics": {
83 | "Width": 1920,
84 | "Height": 1080,
85 | "WindowWidth": 1280,
86 | "WindowHeight": 720,
87 | "Fullscreen": false,
88 | "VSync": false,
89 | "FPSCap": 1000,
90 | "MSAA": 16
91 | },
92 | "Audio": {
93 | "GeneralVolume": 0.5,
94 | "MusicVolume": 0.5,
95 | "SampleVolume": 0.5,
96 | "Offset": 0,
97 | "IgnoreBeatmapSamples": false,
98 | "IgnoreBeatmapSampleVolume": false
99 | },
100 | "Beat": {
101 | "BeatScale": 1.2
102 | },
103 | "Cursor": {
104 | "Colors": {
105 | "EnableRainbow": false,
106 | "RainbowSpeed": 8,
107 | "BaseColor": {
108 | "Hue": 0,
109 | "Saturation": 1,
110 | "Value": 1
111 | },
112 | "EnableCustomHueOffset": false,
113 | "HueOffset": 0,
114 | "FlashToTheBeat": false,
115 | "FlashAmplitude": 0
116 | },
117 | "EnableCustomTagColorOffset": true,
118 | "TagColorOffset": -36,
119 | "EnableTrailGlow": true,
120 | "EnableCustomTrailGlowOffset": true,
121 | "TrailGlowOffset": -36,
122 | "ScaleToCS": false,
123 | "CursorSize": 8,
124 | "ScaleToTheBeat": false,
125 | "ShowCursorsOnBreaks": true,
126 | "BounceOnEdges": false,
127 | "TrailEndScale": 0.4,
128 | "TrailDensity": 1,
129 | "TrailMaxLength": 2000,
130 | "TrailRemoveSpeed": 1,
131 | "GlowEndScale": 0.4,
132 | "InnerLengthMult": 0.9
133 | },
134 | "Objects": {
135 | "MandalaTexturesTrigger": 5,
136 | "MandalaTexturesAlpha": 0.3,
137 | "ForceSliderBallTexture": true,
138 | "DrawApproachCircles": true,
139 | "Colors": {
140 | "EnableRainbow": false,
141 | "RainbowSpeed": 8,
142 | "BaseColor": {
143 | "Hue": 0,
144 | "Saturation": 1,
145 | "Value": 1
146 | },
147 | "EnableCustomHueOffset": false,
148 | "HueOffset": 0,
149 | "FlashToTheBeat": false,
150 | "FlashAmplitude": 100
151 | },
152 | "ObjectsSize": -1,
153 | "CSMult": 1,
154 | "ScaleToTheBeat": false,
155 | "SliderLOD": 30,
156 | "SliderPathLOD": 50,
157 | "SliderSnakeIn": true,
158 | "SliderSnakeOut": true,
159 | "SliderMerge": false,
160 | "DrawFollowPoints": true,
161 | "WhiteFollowPoints": true,
162 | "FollowPointColorOffset": 0,
163 | "EnableCustomSliderBorderColor": false,
164 | "CustomSliderBorderColor": {
165 | "EnableRainbow": false,
166 | "RainbowSpeed": 8,
167 | "BaseColor": {
168 | "Hue": 0,
169 | "Saturation": 0,
170 | "Value": 1
171 | },
172 | "EnableCustomHueOffset": false,
173 | "HueOffset": 0,
174 | "FlashToTheBeat": false,
175 | "FlashAmplitude": 100
176 | },
177 | "EnableCustomSliderBorderGradientOffset": true,
178 | "SliderBorderGradientOffset": 18,
179 | "StackEnabled": true
180 | },
181 | "Playfield": {
182 | "LeadInTime": 5,
183 | "LeadInHold": 2,
184 | "FadeOutTime": 20,
185 | "BackgroundInDim": 0,
186 | "BackgroundDim": 0.7,
187 | "BackgroundDimBreaks": 0.1,
188 | "BlurEnable": true,
189 | "BackgroundInBlur": 0,
190 | "BackgroundBlur": 0,
191 | "BackgroundBlurBreaks": 0,
192 | "SpectrumInDim": 0,
193 | "SpectrumDim": 1,
194 | "SpectrumDimBreaks": 0,
195 | "StoryboardEnabled": false,
196 | "Scale": 1,
197 | "FlashToTheBeat": true,
198 | "UnblurToTheBeat": true,
199 | "UnblurFill": 0.8,
200 | "KiaiFactor": 1.1,
201 | "BaseRotation": 0,
202 | "RotationEnabled": false,
203 | "RotationSpeed": 2,
204 | "BloomEnabled": true,
205 | "BloomToTheBeat": false,
206 | "BloomBeatAddition": 0.3,
207 | "Bloom": {
208 | "Threshold": 0,
209 | "Blur": 0.6,
210 | "Power": 0.7
211 | }
212 | },
213 | "Dance": {
214 | "SliderDance": false,
215 | "TAGSliderDance": false,
216 | "Bezier": {
217 | "Aggressiveness": 60,
218 | "SliderAggressiveness": 3
219 | },
220 | "Flower": {
221 | "UseNewStyle": true,
222 | "AngleOffset": 90,
223 | "DistanceMult": 0.666,
224 | "StreamTrigger": 130,
225 | "StreamAngleOffset": 90,
226 | "LongJump": -1,
227 | "LongJumpMult": 0.7,
228 | "LongJumpOnEqualPos": false
229 | },
230 | "HalfCircle": {
231 | "RadiusMultiplier": 1,
232 | "StreamTrigger": 130
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/bmath/camera.go:
--------------------------------------------------------------------------------
1 | package bmath
2 |
3 | import (
4 | . "danser/osuconst"
5 | "github.com/go-gl/mathgl/mgl32"
6 | )
7 |
8 | type Rectangle struct {
9 | MinX, MinY, MaxX, MaxY float64
10 | }
11 |
12 | type Camera struct {
13 | screenRect Rectangle
14 | projection mgl32.Mat4
15 | view mgl32.Mat4
16 | projectionView mgl32.Mat4
17 | invProjectionView mgl32.Mat4
18 |
19 | viewDirty bool
20 | origin Vector2d
21 | position Vector2d
22 | rotation float64
23 | scale Vector2d
24 |
25 | rebuildCache bool
26 | cache []mgl32.Mat4
27 | }
28 |
29 | func NewCamera() *Camera {
30 | return &Camera{scale: NewVec2d(1, 1)}
31 | }
32 |
33 | func (camera *Camera) SetViewport(width, height int, yDown bool) {
34 | camera.screenRect.MinX = -float64(width) / 2
35 | camera.screenRect.MaxX = float64(width) / 2
36 |
37 | if yDown {
38 | camera.screenRect.MinY = float64(height) / 2
39 | camera.screenRect.MaxY = -float64(height) / 2
40 | } else {
41 | camera.screenRect.MinY = -float64(height) / 2
42 | camera.screenRect.MaxY = float64(height) / 2
43 | }
44 |
45 | if yDown {
46 | camera.projection = mgl32.Ortho(float32(camera.screenRect.MinX), float32(camera.screenRect.MaxX), float32(camera.screenRect.MinY), float32(camera.screenRect.MaxY), 1, -1)
47 | } else {
48 | camera.projection = mgl32.Ortho(float32(camera.screenRect.MinX), float32(camera.screenRect.MaxX), float32(camera.screenRect.MinY), float32(camera.screenRect.MaxY), -1, 1)
49 | }
50 |
51 | camera.rebuildCache = true
52 | camera.viewDirty = true
53 | }
54 |
55 | func (camera *Camera) SetOsuViewport(width, height int) {
56 | osuAspect := PLAYFIELD_WIDTH / PLAYFIELD_HEIGHT
57 | screenAspect := float64(width) / float64(height)
58 |
59 | if screenAspect > osuAspect {
60 | sh := (PLAYFIELD_HEIGHT - PLAYFIELD_HEIGHT*900.0/1080.0) / 2
61 | sw := (PLAYFIELD_WIDTH*screenAspect*900.0/1080.0 - PLAYFIELD_WIDTH) / 2
62 | camera.screenRect.MinX = -sw
63 | camera.screenRect.MaxX = PLAYFIELD_WIDTH + sw
64 |
65 | camera.screenRect.MinY = PLAYFIELD_HEIGHT + sh
66 | camera.screenRect.MaxY = -sh
67 | }
68 |
69 | camera.projection = mgl32.Ortho(float32(camera.screenRect.MinX), float32(camera.screenRect.MaxX), float32(camera.screenRect.MinY), float32(camera.screenRect.MaxY), 1, -1)
70 | camera.rebuildCache = true
71 | camera.viewDirty = true
72 | }
73 |
74 | func (camera *Camera) SetViewportF(x, y, width, height int) {
75 | camera.screenRect.MinX = float64(x)
76 | camera.screenRect.MaxX = float64(width)
77 | camera.screenRect.MinY = float64(y)
78 | camera.screenRect.MaxY = float64(height)
79 |
80 | camera.projection = mgl32.Ortho(float32(camera.screenRect.MinX), float32(camera.screenRect.MaxX), float32(camera.screenRect.MinY), float32(camera.screenRect.MaxY), 1, -1)
81 | camera.rebuildCache = true
82 | camera.viewDirty = true
83 | }
84 |
85 | func (camera *Camera) calculateView() {
86 | camera.view = mgl32.Translate3D(camera.position.X32(), camera.position.Y32(), 0).Mul4(mgl32.HomogRotate3DZ(float32(camera.rotation))).Mul4(mgl32.Scale3D(camera.scale.X32(), camera.scale.Y32(), 1)).Mul4(mgl32.Translate3D(camera.origin.X32(), camera.origin.Y32(), 0))
87 | }
88 |
89 | func (camera *Camera) SetPosition(pos Vector2d) {
90 | camera.position = pos
91 | camera.viewDirty = true
92 | }
93 |
94 | func (camera *Camera) SetOrigin(pos Vector2d) {
95 | camera.origin = pos.Scl(-1)
96 | camera.viewDirty = true
97 | }
98 |
99 | func (camera *Camera) SetScale(scale Vector2d) {
100 | camera.scale = scale
101 | camera.viewDirty = true
102 | }
103 |
104 | func (camera *Camera) SetRotation(rad float64) {
105 | camera.rotation = rad
106 | camera.viewDirty = true
107 | }
108 |
109 | func (camera *Camera) Rotate(rad float64) {
110 | camera.rotation += rad
111 | camera.viewDirty = true
112 | }
113 |
114 | func (camera *Camera) Translate(pos Vector2d) {
115 | camera.position = camera.position.Add(pos)
116 | camera.viewDirty = true
117 | }
118 |
119 | func (camera *Camera) Scale(scale Vector2d) {
120 | camera.scale = camera.scale.Mult(scale)
121 | camera.viewDirty = true
122 | }
123 |
124 | func (camera *Camera) Update() {
125 | if camera.viewDirty {
126 | camera.calculateView()
127 | camera.projectionView = camera.projection.Mul4(camera.view)
128 | camera.invProjectionView = camera.projectionView.Inv()
129 | camera.rebuildCache = true
130 | camera.viewDirty = false
131 | }
132 | }
133 |
134 | func (camera *Camera) GenRotated(rotations int, rotOffset float64) []mgl32.Mat4 {
135 |
136 | if len(camera.cache) != rotations || camera.rebuildCache {
137 | if len(camera.cache) != rotations {
138 | camera.cache = make([]mgl32.Mat4, rotations)
139 | }
140 |
141 | for i := 0; i < rotations; i++ {
142 | camera.cache[i] = camera.projection.Mul4(mgl32.HomogRotate3DZ(float32(i) * float32(rotOffset))).Mul4(camera.view)
143 | }
144 | camera.rebuildCache = false
145 | }
146 |
147 | return camera.cache
148 | }
149 |
150 | func (camera *Camera) GenRotatedX(rotations int, rotOffset float64) []mgl32.Mat4 {
151 |
152 | if len(camera.cache) != rotations || camera.rebuildCache {
153 | if len(camera.cache) != rotations {
154 | camera.cache = make([]mgl32.Mat4, rotations)
155 | }
156 |
157 | for i := 0; i < rotations; i++ {
158 | camera.cache[i] = camera.projection.Mul4(mgl32.HomogRotate3DX(float32(i) * float32(rotOffset))).Mul4(camera.view)
159 | }
160 | camera.rebuildCache = false
161 | }
162 |
163 | return camera.cache
164 | }
165 |
166 | func (camera Camera) GetProjectionView() mgl32.Mat4 {
167 | return camera.projectionView
168 | }
169 |
170 | func (camera Camera) Unproject(screenPos Vector2d) Vector2d {
171 | res := camera.invProjectionView.Mul4x1(mgl32.Vec4{float32((screenPos.X + camera.screenRect.MinX) / camera.screenRect.MaxX), -float32((screenPos.Y + camera.screenRect.MaxY) / camera.screenRect.MinY), 0.0, 1.0})
172 | return NewVec2d(float64(res[0]), float64(res[1]))
173 | }
174 |
175 | func (camera Camera) GetWorldRect() Rectangle {
176 | res := camera.invProjectionView.Mul4x1(mgl32.Vec4{-1.0, 1.0, 0.0, 1.0})
177 | var rectangle Rectangle
178 | rectangle.MinX = float64(res[0])
179 | rectangle.MinY = float64(res[1])
180 | res = camera.invProjectionView.Mul4x1(mgl32.Vec4{1.0, -1.0, 0.0, 1.0})
181 | rectangle.MaxX = float64(res[0])
182 | rectangle.MaxY = float64(res[1])
183 | if rectangle.MinY > rectangle.MaxY {
184 | a := rectangle.MinY
185 | rectangle.MinY, rectangle.MaxY = rectangle.MaxY, a
186 | }
187 | return rectangle
188 | }
189 |
--------------------------------------------------------------------------------
/animation/easing/equations.go:
--------------------------------------------------------------------------------
1 | package easing
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | var easings = []func(float64) float64{
8 | Linear,
9 | OutQuad,
10 | InQuad,
11 | InQuad,
12 | OutQuad,
13 | InOutQuad,
14 | InCubic,
15 | OutCubic,
16 | InOutCubic,
17 | InQuart,
18 | OutQuart,
19 | InOutQuart,
20 | InQuint,
21 | OutQuint,
22 | InOutQuint,
23 | InSine,
24 | OutSine,
25 | InOutSine,
26 | InExpo,
27 | OutExpo,
28 | InOutExpo,
29 | InCirc,
30 | OutCirc,
31 | InOutCirc,
32 | InElastic,
33 | OutElastic,
34 | OutHalfElastic,
35 | OutQuartElastic,
36 | InOutElastic,
37 | InBack,
38 | OutBack,
39 | InOutBack,
40 | InBounce,
41 | OutBounce,
42 | InOutBounce,
43 | }
44 |
45 | func GetEasing(easingID int64) func(float64) float64 {
46 | if easingID < 0 || easingID >= int64(len(easings)) {
47 | easingID = 0
48 | }
49 | return easings[easingID]
50 | }
51 |
52 | /* ========================
53 | Using equations from: https://github.com/fogleman/ease/blob/master/ease.go
54 | ========================*/
55 |
56 | func Linear(t float64) float64 {
57 | return t
58 | }
59 |
60 | func InQuad(t float64) float64 {
61 | return t * t
62 | }
63 |
64 | func OutQuad(t float64) float64 {
65 | return -t * (t - 2)
66 | }
67 |
68 | func InOutQuad(t float64) float64 {
69 | if t < 0.5 {
70 | return 2 * t * t
71 | } else {
72 | t = 2*t - 1
73 | return -0.5 * (t*(t-2) - 1)
74 | }
75 | }
76 |
77 | func InCubic(t float64) float64 {
78 | return t * t * t
79 | }
80 |
81 | func OutCubic(t float64) float64 {
82 | t -= 1
83 | return t*t*t + 1
84 | }
85 |
86 | func InOutCubic(t float64) float64 {
87 | t *= 2
88 | if t < 1 {
89 | return 0.5 * t * t * t
90 | } else {
91 | t -= 2
92 | return 0.5 * (t*t*t + 2)
93 | }
94 | }
95 |
96 | func InQuart(t float64) float64 {
97 | return t * t * t * t
98 | }
99 |
100 | func OutQuart(t float64) float64 {
101 | t -= 1
102 | return -(t*t*t*t - 1)
103 | }
104 |
105 | func InOutQuart(t float64) float64 {
106 | t *= 2
107 | if t < 1 {
108 | return 0.5 * t * t * t * t
109 | } else {
110 | t -= 2
111 | return -0.5 * (t*t*t*t - 2)
112 | }
113 | }
114 |
115 | func InQuint(t float64) float64 {
116 | return t * t * t * t * t
117 | }
118 |
119 | func OutQuint(t float64) float64 {
120 | t -= 1
121 | return t*t*t*t*t + 1
122 | }
123 |
124 | func InOutQuint(t float64) float64 {
125 | t *= 2
126 | if t < 1 {
127 | return 0.5 * t * t * t * t * t
128 | } else {
129 | t -= 2
130 | return 0.5 * (t*t*t*t*t + 2)
131 | }
132 | }
133 |
134 | func InSine(t float64) float64 {
135 | return -1*math.Cos(t*math.Pi/2) + 1
136 | }
137 |
138 | func OutSine(t float64) float64 {
139 | return math.Sin(t * math.Pi / 2)
140 | }
141 |
142 | func InOutSine(t float64) float64 {
143 | return -0.5 * (math.Cos(math.Pi*t) - 1)
144 | }
145 |
146 | func InExpo(t float64) float64 {
147 | if t == 0 {
148 | return 0
149 | } else {
150 | return math.Pow(2, 10*(t-1))
151 | }
152 | }
153 |
154 | func OutExpo(t float64) float64 {
155 | if t == 1 {
156 | return 1
157 | } else {
158 | return 1 - math.Pow(2, -10*t)
159 | }
160 | }
161 |
162 | func InOutExpo(t float64) float64 {
163 | if t == 0 {
164 | return 0
165 | } else if t == 1 {
166 | return 1
167 | } else {
168 | if t < 0.5 {
169 | return 0.5 * math.Pow(2, (20*t)-10)
170 | } else {
171 | return 1 - 0.5*math.Pow(2, (-20*t)+10)
172 | }
173 | }
174 | }
175 |
176 | func InCirc(t float64) float64 {
177 | return -1 * (math.Sqrt(1-t*t) - 1)
178 | }
179 |
180 | func OutCirc(t float64) float64 {
181 | t -= 1
182 | return math.Sqrt(1 - (t * t))
183 | }
184 |
185 | func InOutCirc(t float64) float64 {
186 | t *= 2
187 | if t < 1 {
188 | return -0.5 * (math.Sqrt(1-t*t) - 1)
189 | } else {
190 | t = t - 2
191 | return 0.5 * (math.Sqrt(1-t*t) + 1)
192 | }
193 | }
194 |
195 | func InElastic(t float64) float64 {
196 | return InElasticFunction(0.5)(t)
197 | }
198 |
199 | func OutElastic(t float64) float64 {
200 | return OutElasticFunction(0.5, 1)(t)
201 | }
202 |
203 | func OutHalfElastic(t float64) float64 {
204 | return OutElasticFunction(0.5, 0.5)(t)
205 | }
206 |
207 | func OutQuartElastic(t float64) float64 {
208 | return OutElasticFunction(0.5, 0.25)(t)
209 | }
210 |
211 | func InOutElastic(t float64) float64 {
212 | return InOutElasticFunction(0.5)(t)
213 | }
214 |
215 | func InElasticFunction(period float64) func(float64) float64 {
216 | p := period
217 | return func(t float64) float64 {
218 | t -= 1
219 | return -1 * (math.Pow(2, 10*t) * math.Sin((t-p/4)*(2*math.Pi)/p))
220 | }
221 | }
222 |
223 | func OutElasticFunction(period, mod float64) func(float64) float64 {
224 | p := period
225 | return func(t float64) float64 {
226 | return math.Pow(2, -10*t)*math.Sin((mod*t-p/4)*(2*math.Pi/p)) + 1
227 | }
228 | }
229 |
230 | func InOutElasticFunction(period float64) func(float64) float64 {
231 | p := period
232 | return func(t float64) float64 {
233 | t *= 2
234 | if t < 1 {
235 | t -= 1
236 | return -0.5 * (math.Pow(2, 10*t) * math.Sin((t-p/4)*2*math.Pi/p))
237 | } else {
238 | t -= 1
239 | return math.Pow(2, -10*t)*math.Sin((t-p/4)*2*math.Pi/p)*0.5 + 1
240 | }
241 | }
242 | }
243 |
244 | func InBack(t float64) float64 {
245 | s := 1.70158
246 | return t * t * ((s+1)*t - s)
247 | }
248 |
249 | func OutBack(t float64) float64 {
250 | s := 1.70158
251 | t -= 1
252 | return t*t*((s+1)*t+s) + 1
253 | }
254 |
255 | func InOutBack(t float64) float64 {
256 | s := 1.70158
257 | t *= 2
258 | if t < 1 {
259 | s *= 1.525
260 | return 0.5 * (t * t * ((s+1)*t - s))
261 | } else {
262 | t -= 2
263 | s *= 1.525
264 | return 0.5 * (t*t*((s+1)*t+s) + 2)
265 | }
266 | }
267 |
268 | func InBounce(t float64) float64 {
269 | return 1 - OutBounce(1-t)
270 | }
271 |
272 | func OutBounce(t float64) float64 {
273 | if t < 4/11.0 {
274 | return (121 * t * t) / 16.0
275 | } else if t < 8/11.0 {
276 | return (363 / 40.0 * t * t) - (99 / 10.0 * t) + 17/5.0
277 | } else if t < 9/10.0 {
278 | return (4356 / 361.0 * t * t) - (35442 / 1805.0 * t) + 16061/1805.0
279 | } else {
280 | return (54 / 5.0 * t * t) - (513 / 25.0 * t) + 268/25.0
281 | }
282 | }
283 |
284 | func InOutBounce(t float64) float64 {
285 | if t < 0.5 {
286 | return InBounce(2*t) * 0.5
287 | } else {
288 | return OutBounce(2*t-1)*0.5 + 0.5
289 | }
290 | }
291 |
292 | func InSquare(t float64) float64 {
293 | if t < 1 {
294 | return 0
295 | } else {
296 | return 1
297 | }
298 | }
299 |
300 | func OutSquare(t float64) float64 {
301 | if t > 0 {
302 | return 1
303 | } else {
304 | return 0
305 | }
306 | }
307 |
308 | func InOutSquare(t float64) float64 {
309 | if t < 0.5 {
310 | return 0
311 | } else {
312 | return 1
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/render/texture/atlas.go:
--------------------------------------------------------------------------------
1 | package texture
2 |
3 | import (
4 | "github.com/faiface/mainthread"
5 | "github.com/go-gl/gl/v3.3-core/gl"
6 | "log"
7 | "runtime"
8 | )
9 |
10 | type rectangle struct {
11 | x, y, width, height int
12 | }
13 |
14 | func (rect rectangle) area() int {
15 | return rect.width * rect.height
16 | }
17 |
18 | type TextureAtlas struct {
19 | store *textureStore
20 | defRegion TextureRegion
21 | min, mag Filter
22 | padding int
23 | subTextures map[string]*TextureRegion
24 | emptySpaces map[int32][]rectangle
25 | }
26 |
27 | func NewTextureAtlas(size, mipmaps int) *TextureAtlas {
28 | texture := new(TextureAtlas)
29 | texture.subTextures = make(map[string]*TextureRegion)
30 | texture.emptySpaces = make(map[int32][]rectangle)
31 | texture.emptySpaces[0] = []rectangle{{0, 0, size, size}}
32 |
33 | var siz int32
34 | gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &siz)
35 | if int(siz) < size {
36 | log.Printf("WARNING: GPU supports only %dx%d textures\n", siz, siz)
37 | size = int(siz)
38 | }
39 |
40 | texture.store = newStore(1, size, size, mipmaps)
41 | texture.defRegion = TextureRegion{texture, 0, 1, 0, 1, int32(size), int32(size), 0}
42 | texture.padding = 1 << uint(texture.store.mipmaps)
43 |
44 | runtime.SetFinalizer(texture, (*TextureAtlas).Dispose)
45 |
46 | return texture
47 | }
48 |
49 | func (texture *TextureAtlas) AddTexture(name string, width, height int, data []uint8) *TextureRegion {
50 | texture.Bind(texture.store.binding)
51 | if len(data) != width*height*4 {
52 | panic("Wrong number of pixels given!")
53 | }
54 |
55 | if int(texture.GetWidth()) < width || int(texture.GetHeight()) < height {
56 | log.Panicf("Texture is too big! Atlas size: %dx%d, texture size: %dx%d", texture.GetWidth(), texture.GetHeight(), width, height)
57 | }
58 |
59 | imBounds := rectangle{0, 0, width + texture.padding, height + texture.padding}
60 | for layer := int32(0); layer < texture.store.layers; layer++ {
61 | j := -1
62 | smallest := rectangle{0, 0, int(texture.store.width), int(texture.store.height)}
63 |
64 | for i := range texture.emptySpaces[layer] {
65 | space := texture.emptySpaces[layer][i]
66 | if imBounds.width <= space.width && imBounds.height <= space.height {
67 | if space.area() <= smallest.area() {
68 | j = i
69 | smallest = space
70 | }
71 | }
72 | }
73 |
74 | if j == -1 {
75 | if layer == texture.store.layers-1 {
76 | texture.newLayer()
77 | }
78 | continue
79 | } else {
80 | dw := smallest.width - imBounds.width
81 | dh := smallest.height - imBounds.height
82 |
83 | var rect1 rectangle
84 | var rect2 rectangle
85 |
86 | if dh > dw {
87 | rect1 = rectangle{smallest.x + imBounds.width, smallest.y, smallest.width - imBounds.width, imBounds.height}
88 | rect2 = rectangle{smallest.x, smallest.y + imBounds.height, smallest.width, smallest.height - imBounds.height}
89 | } else {
90 | rect1 = rectangle{smallest.x + imBounds.width, smallest.y, smallest.width - imBounds.width, smallest.height}
91 | rect2 = rectangle{smallest.x, smallest.y + imBounds.height, imBounds.width, smallest.height - imBounds.height}
92 | }
93 |
94 | texture.emptySpaces[layer] = append(texture.emptySpaces[layer][:j], texture.emptySpaces[layer][j+1:]...)
95 | texture.emptySpaces[layer] = append(texture.emptySpaces[layer], rect1, rect2)
96 |
97 | gl.TexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, int32(smallest.x), int32(smallest.y), int32(layer), int32(width), int32(height), 1, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(data))
98 | gl.GenerateMipmap(gl.TEXTURE_2D_ARRAY)
99 |
100 | region := TextureRegion{Texture: texture, Width: int32(width), Height: int32(height), Layer: int32(layer)}
101 | region.U1 = (float32(smallest.x) + 0.5) / float32(texture.store.width)
102 | region.V1 = (float32(smallest.y) + 0.5) / float32(texture.store.height)
103 | region.U2 = region.U1 + float32(width-1)/float32(texture.store.width)
104 | region.V2 = region.V1 + float32(height-1)/float32(texture.store.height)
105 | texture.subTextures[name] = ®ion
106 | return ®ion
107 | }
108 | }
109 |
110 | return nil
111 | }
112 |
113 | func (texture *TextureAtlas) GetTexture(name string) *TextureRegion {
114 | return texture.subTextures[name]
115 | }
116 |
117 | func (texture *TextureAtlas) newLayer() {
118 | texture.emptySpaces[texture.store.layers] = []rectangle{{0, 0, int(texture.store.width), int(texture.store.height)}}
119 |
120 | layers := texture.store.layers + 1
121 |
122 | var fbo uint32
123 | gl.GenFramebuffers(1, &fbo)
124 | gl.BindFramebuffer(gl.READ_FRAMEBUFFER, fbo)
125 |
126 | dstStore := newStore(int(layers), int(texture.store.width), int(texture.store.height), int(texture.store.mipmaps))
127 | dstStore.SetFiltering(texture.min, texture.mag)
128 | dstStore.Bind(texture.store.binding)
129 |
130 | for layer := int32(0); layer < layers-1; layer++ {
131 | for level := int32(0); level < texture.store.mipmaps; level++ {
132 | gl.FramebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.store.id, level, layer)
133 |
134 | div := int32(1 << uint(level))
135 | gl.CopyTexSubImage3D(gl.TEXTURE_2D_ARRAY, level, 0, 0, layer, 0, 0, texture.store.width/div, texture.store.height/div)
136 | }
137 | }
138 |
139 | gl.DeleteFramebuffers(1, &fbo)
140 | texture.store.Dispose()
141 |
142 | texture.store = dstStore
143 | }
144 |
145 | func (texture *TextureAtlas) SetData(x, y, width, height, layer int, data []uint8) {
146 | if len(data) != width*height*4 {
147 | panic("Wrong number of pixels given!")
148 | }
149 |
150 | gl.TexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, int32(x), int32(y), int32(layer), int32(width), int32(height), 1, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(data))
151 | if texture.store.mipmaps > 1 {
152 | gl.GenerateMipmap(gl.TEXTURE_2D_ARRAY)
153 | }
154 | }
155 |
156 | func (texture *TextureAtlas) GetID() uint32 {
157 | return texture.store.id
158 | }
159 |
160 | func (texture *TextureAtlas) GetWidth() int32 {
161 | return texture.store.width
162 | }
163 |
164 | func (texture *TextureAtlas) GetHeight() int32 {
165 | return texture.store.height
166 | }
167 |
168 | func (texture *TextureAtlas) GetRegion() TextureRegion {
169 | return texture.defRegion
170 | }
171 |
172 | func (texture *TextureAtlas) GetLayers() int32 {
173 | return texture.store.layers
174 | }
175 |
176 | func (texture *TextureAtlas) SetFiltering(min, mag Filter) {
177 | texture.min, texture.mag = min, mag
178 | texture.store.SetFiltering(min, mag)
179 | }
180 |
181 | func (texture *TextureAtlas) Bind(loc uint) {
182 | texture.store.Bind(loc)
183 | }
184 |
185 | func (texture *TextureAtlas) GetLocation() uint {
186 | return texture.store.binding
187 | }
188 |
189 | func (texture *TextureAtlas) Dispose() {
190 | mainthread.CallNonBlock(func() {
191 | texture.store.Dispose()
192 | })
193 | }
194 |
--------------------------------------------------------------------------------
/database/manager.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "danser/settings"
5 | "database/sql"
6 | "log"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 |
11 | "crypto/md5"
12 | "danser/beatmap"
13 | "encoding/hex"
14 | _ "github.com/mattn/go-sqlite3"
15 | "io"
16 | "strconv"
17 | "time"
18 | )
19 |
20 | var dbFile *sql.DB
21 |
22 | const databaseVersion = 20180814
23 |
24 | func Init() {
25 | var err error
26 | dbFile, err = sql.Open("sqlite3", "danser.db")
27 |
28 | if err != nil {
29 | panic(err)
30 | }
31 |
32 | _, err = dbFile.Exec(`CREATE TABLE IF NOT EXISTS beatmaps (dir TEXT, file TEXT, lastModified INTEGER, title TEXT, titleUnicode TEXT, artist TEXT, artistUnicode TEXT, creator TEXT, version TEXT, source TEXT, tags TEXT, cs REAL, ar REAL, sliderMultiplier REAL, sliderTickRate REAL, audioFile TEXT, previewTime INTEGER, sampleSet INTEGER, stackLeniency REAL, mode INTEGER, bg TEXT, pauses TEXT, timingPoints TEXT, md5 TEXT, dateAdded INTEGER, playCount INTEGER, lastPlayed INTEGER);
33 | CREATE INDEX IF NOT EXISTS idx ON beatmaps (dir, file);
34 | CREATE TABLE IF NOT EXISTS info (key TEXT NOT NULL UNIQUE, value TEXT);`)
35 |
36 | if err != nil {
37 | panic(err)
38 | }
39 |
40 | _, err = dbFile.Exec("REPLACE INTO info (key, value) VALUES ('version', ?)", strconv.FormatInt(databaseVersion, 10))
41 | if err != nil {
42 | log.Println(err)
43 | }
44 | }
45 |
46 | func LoadBeatmaps() []*beatmap.BeatMap {
47 | log.Println("Checking database...")
48 |
49 | searchDir := settings.General.OsuSongsDir
50 |
51 | _, err := os.Open(searchDir)
52 | if os.IsNotExist(err) {
53 | log.Println(searchDir + " does not exist!")
54 | return nil
55 | }
56 |
57 | mod := getLastModified()
58 |
59 | newBeatmaps := make([]*beatmap.BeatMap, 0)
60 | cachedBeatmaps := make([]*beatmap.BeatMap, 0)
61 |
62 | filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
63 | if strings.HasSuffix(f.Name(), ".osu") {
64 | cachedTime := mod[filepath.Base(filepath.Dir(path))+"/"+f.Name()]
65 | if cachedTime != f.ModTime().UnixNano()/1000000 {
66 | removeBeatmap(filepath.Base(filepath.Dir(path)), f.Name())
67 |
68 | file, err := os.Open(path)
69 |
70 | if err == nil {
71 | defer file.Close()
72 |
73 | if bMap := beatmap.ParseBeatMap(file); bMap != nil {
74 | bMap.Dir = filepath.Base(filepath.Dir(path))
75 | bMap.File = f.Name()
76 | bMap.LastModified = f.ModTime().UnixNano() / 1000000
77 | bMap.TimeAdded = time.Now().UnixNano() / 1000000
78 | log.Println("Importing:", bMap.File)
79 |
80 | hash := md5.New()
81 | if _, err := io.Copy(hash, file); err == nil {
82 | bMap.MD5 = hex.EncodeToString(hash.Sum(nil))
83 | newBeatmaps = append(newBeatmaps, bMap)
84 | }
85 | }
86 | }
87 | } else {
88 | bMap := beatmap.NewBeatMap()
89 | bMap.Dir = filepath.Base(filepath.Dir(path))
90 | bMap.File = f.Name()
91 | cachedBeatmaps = append(cachedBeatmaps, bMap)
92 | }
93 | }
94 | return nil
95 | })
96 |
97 | log.Println("Imported", len(newBeatmaps), "new beatmaps.")
98 |
99 | updateBeatmaps(newBeatmaps)
100 |
101 | log.Println("Found", len(cachedBeatmaps), "cached beatmaps. Loading...")
102 |
103 | loadBeatmaps(cachedBeatmaps)
104 |
105 | allMaps := append(newBeatmaps, cachedBeatmaps...)
106 |
107 | log.Println("Loaded", len(allMaps), "total.")
108 |
109 | return allMaps
110 | }
111 |
112 | func UpdatePlayStats(beatmap *beatmap.BeatMap) {
113 | _, err := dbFile.Exec("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?", beatmap.PlayCount, beatmap.LastPlayed, beatmap.Dir, beatmap.File)
114 | if err != nil {
115 | log.Println(err)
116 | }
117 | }
118 |
119 | func removeBeatmap(dir, file string) {
120 | dbFile.Exec("DELETE FROM beatmaps WHERE dir = ? AND file = ?", dir, file)
121 | }
122 |
123 | func loadBeatmaps(bMaps []*beatmap.BeatMap) {
124 |
125 | beatmaps := make(map[string]int)
126 |
127 | for i, bMap := range bMaps {
128 | beatmaps[bMap.Dir+"/"+bMap.File] = i + 1
129 | }
130 |
131 | res, _ := dbFile.Query("SELECT * FROM beatmaps")
132 |
133 | for res.Next() {
134 | beatmap := beatmap.NewBeatMap()
135 | var mode int
136 | res.Scan(
137 | &beatmap.Dir,
138 | &beatmap.File,
139 | &beatmap.LastModified,
140 | &beatmap.Name,
141 | &beatmap.NameUnicode,
142 | &beatmap.Artist,
143 | &beatmap.ArtistUnicode,
144 | &beatmap.Creator,
145 | &beatmap.Difficulty,
146 | &beatmap.Source,
147 | &beatmap.Tags,
148 | &beatmap.CircleSize,
149 | &beatmap.AR,
150 | &beatmap.Timings.SliderMult,
151 | &beatmap.Timings.TickRate,
152 | &beatmap.Audio,
153 | &beatmap.PreviewTime,
154 | &beatmap.Timings.BaseSet,
155 | &beatmap.StackLeniency,
156 | &mode,
157 | &beatmap.Bg,
158 | &beatmap.PausesText,
159 | &beatmap.TimingPoints,
160 | &beatmap.MD5,
161 | &beatmap.TimeAdded,
162 | &beatmap.PlayCount,
163 | &beatmap.LastPlayed)
164 |
165 | if beatmap.Name+beatmap.Artist+beatmap.Creator == "" || beatmap.TimingPoints == "" {
166 | log.Println("Corrupted cached beatmap found. Removing from database:", beatmap.File)
167 | removeBeatmap(beatmap.Dir, beatmap.File)
168 | continue
169 | }
170 |
171 | key := beatmap.Dir + "/" + beatmap.File
172 |
173 | if beatmaps[key] > 0 {
174 | bMaps[beatmaps[key]-1] = beatmap
175 | beatmap.LoadPauses()
176 | beatmap.LoadTimingPoints()
177 | }
178 |
179 | }
180 |
181 | }
182 |
183 | func updateBeatmaps(bMaps []*beatmap.BeatMap) {
184 | tx, err := dbFile.Begin()
185 |
186 | if err == nil {
187 | var st *sql.Stmt
188 | st, err = tx.Prepare("INSERT INTO beatmaps VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
189 |
190 | if err == nil {
191 | for _, bMap := range bMaps {
192 | _, err1 := st.Exec(bMap.Dir,
193 | bMap.File,
194 | bMap.LastModified,
195 | bMap.Name,
196 | bMap.NameUnicode,
197 | bMap.Artist,
198 | bMap.ArtistUnicode,
199 | bMap.Creator,
200 | bMap.Difficulty,
201 | bMap.Source,
202 | bMap.Tags,
203 | bMap.CircleSize,
204 | bMap.AR,
205 | bMap.SliderMultiplier,
206 | bMap.Timings.TickRate,
207 | bMap.Audio,
208 | bMap.PreviewTime,
209 | bMap.Timings.BaseSet,
210 | bMap.StackLeniency,
211 | 0,
212 | bMap.Bg,
213 | bMap.PausesText,
214 | bMap.TimingPoints,
215 | bMap.MD5,
216 | bMap.TimeAdded,
217 | bMap.PlayCount,
218 | bMap.LastPlayed)
219 |
220 | if err1 != nil {
221 | log.Println(err1)
222 | }
223 | }
224 | }
225 |
226 | st.Close()
227 | tx.Commit()
228 | }
229 |
230 | if err != nil {
231 | log.Println(err)
232 | }
233 |
234 | }
235 |
236 | func getLastModified() map[string]int64 {
237 | res, _ := dbFile.Query("SELECT dir, file, lastModified FROM beatmaps")
238 |
239 | mod := make(map[string]int64)
240 |
241 | for res.Next() {
242 | var dir string
243 | var file string
244 | var lastModified int64
245 |
246 | res.Scan(&dir, &file, &lastModified)
247 | mod[dir+"/"+file] = lastModified
248 | }
249 |
250 | return mod
251 | }
252 |
--------------------------------------------------------------------------------
/beatmap/objects/circle.go:
--------------------------------------------------------------------------------
1 | package objects
2 |
3 | import (
4 | "danser/audio"
5 | "danser/bmath"
6 | . "danser/osuconst"
7 | "danser/render"
8 | "danser/settings"
9 | "github.com/go-gl/mathgl/mgl32"
10 | "math"
11 | "strconv"
12 | )
13 |
14 | type Circle struct {
15 | objData *basicData
16 | sample int
17 | Timings *Timings
18 | }
19 |
20 | func NewCircle(data []string, number int64) *Circle {
21 | circle := &Circle{}
22 | circle.objData = commonParse(data, number)
23 | f, _ := strconv.ParseInt(data[4], 10, 64)
24 | circle.sample = int(f)
25 | circle.objData.EndTime = circle.objData.StartTime
26 | circle.objData.EndPos = circle.objData.StartPos
27 | circle.objData.parseExtras(data, 5)
28 | return circle
29 | }
30 |
31 | func NewCirclebyPath(data []string, number int64, isHR bool) *Circle {
32 | circle := &Circle{}
33 | circle.objData = commonParsebyPath(data, number, isHR)
34 | f, _ := strconv.ParseInt(data[4], 10, 64)
35 | circle.sample = int(f)
36 | circle.objData.EndTime = circle.objData.StartTime
37 | circle.objData.EndPos = circle.objData.StartPos
38 | circle.objData.parseExtras(data, 5)
39 | return circle
40 | }
41 |
42 | func DummyCircle(pos bmath.Vector2d, time int64) *Circle {
43 | return DummyCircleInherit(pos, time, false)
44 | }
45 |
46 | func DummyCircleInherit(pos bmath.Vector2d, time int64, inherit bool) *Circle {
47 | circle := &Circle{objData: &basicData{}}
48 | circle.objData.StartPos = pos
49 | circle.objData.EndPos = pos
50 | circle.objData.StartTime = time
51 | circle.objData.EndTime = time
52 | circle.objData.EndPos = circle.objData.StartPos
53 | circle.objData.SliderPoint = inherit
54 | return circle
55 | }
56 |
57 | func (self Circle) GetBasicData() *basicData {
58 | return self.objData
59 | }
60 |
61 | func (self *Circle) Update(time int64) bool {
62 |
63 | index := self.objData.customIndex
64 |
65 | if index == 0 {
66 | index = self.Timings.Current.SampleIndex
67 | }
68 |
69 | if self.objData.sampleSet == 0 {
70 | audio.PlaySample(self.Timings.Current.SampleSet, self.objData.additionSet, self.sample, index, self.Timings.Current.SampleVolume)
71 | } else {
72 | audio.PlaySample(self.objData.sampleSet, self.objData.additionSet, self.sample, index, self.Timings.Current.SampleVolume)
73 | }
74 |
75 | return true
76 | }
77 |
78 | func (self *Circle) SetTiming(timings *Timings) {
79 | self.Timings = timings
80 | self.objData.JudgeTime = self.objData.StartTime
81 | }
82 |
83 | func (self *Circle) GetPosition() bmath.Vector2d {
84 | return self.objData.StartPos
85 | }
86 |
87 | func (self *Circle) Draw(time int64, preempt float64, fadeIn float64, color mgl32.Vec4, batch *render.SpriteBatch) bool {
88 | alpha := 1.0
89 | fadeInStart := float64(self.objData.StartTime) - preempt
90 | fadeInEnd := math.Min(float64(self.objData.StartTime), fadeInStart+fadeIn)
91 |
92 | if settings.VSplayer.Mods.EnableHD {
93 | hiddenFadeInStart := float64(self.objData.StartTime) - preempt
94 | hiddenFadeInEnd := hiddenFadeInStart + preempt*FADE_IN_DURATION_MULTIPLIER
95 |
96 | hiddenFadeOutStart := hiddenFadeInEnd
97 | hiddenFadeOutEnd := hiddenFadeOutStart + preempt*FADE_OUT_DURATION_MULTIPLIER
98 | if float64(time) < hiddenFadeInEnd && float64(time) >= hiddenFadeInStart {
99 | alpha = Clamp(1.0-(hiddenFadeInEnd-float64(time))/(hiddenFadeInEnd-hiddenFadeInStart), 0.0, 1.0)
100 | } else if float64(time) >= hiddenFadeOutStart {
101 | alpha = Clamp((hiddenFadeOutEnd-float64(time))/(hiddenFadeOutEnd-hiddenFadeOutStart), 0.0, 1.0)
102 | } else {
103 | alpha = float64(color[3])
104 | }
105 | } else {
106 | if time < self.objData.StartTime && float64(time) >= fadeInStart {
107 | alpha = Clamp(1.0-(fadeInEnd-float64(time))/fadeIn, 0.0, 1.0)
108 | } else if time >= self.objData.StartTime {
109 | alpha = Clamp(1.0-float64(time-self.objData.StartTime)/(preempt/2), 0.0, 1.0)
110 | } else {
111 | alpha = float64(color[3])
112 | }
113 | }
114 |
115 | batch.SetTranslation(self.objData.StartPos)
116 |
117 | if time >= self.objData.StartTime {
118 | batch.SetSubScale(1+(1.0-alpha)*0.5, 1+(1.0-alpha)*0.5)
119 | }
120 |
121 | if settings.DIVIDES >= settings.Objects.MandalaTexturesTrigger {
122 | alpha *= settings.Objects.MandalaTexturesAlpha
123 | }
124 |
125 | batch.SetColor(float64(color[0]), float64(color[1]), float64(color[2]), alpha)
126 | if settings.DIVIDES >= settings.Objects.MandalaTexturesTrigger {
127 | batch.DrawUnit(*render.CircleFull)
128 | } else {
129 | batch.DrawUnit(*render.Circle)
130 | }
131 |
132 | if settings.VSplayer.PlayerFieldUI.ShowHitCircleNumber {
133 | // 绘制圈内数字
134 | widthratio := float64(render.Circle0.Width) / float64(render.Circle.Width)
135 | heightratio := float64(render.Circle0.Height) / float64(render.Circle.Height)
136 | batch.SetNumberScale(widthratio, heightratio)
137 | batch.SetColor(1, 1, 1, alpha)
138 |
139 | if self.objData.Number < 10 {
140 | // 编号一位数
141 | DrawHitCircleNumber(self.objData.Number, self.objData.StartPos, batch)
142 | } else {
143 | // 只考虑编号两位数的情况
144 |
145 | // 计算十位数和个位数
146 | tenDigit := self.objData.Number / 10
147 | unitDigit := self.objData.Number % 10
148 |
149 | // 计算十位数和个位数的位置
150 | screenratio := PLAYFIELD_HEIGHT / 600
151 | baseX := self.objData.StartPos.X
152 | baseY := self.objData.StartPos.Y
153 | tenDigitWidth := int64(GetHitCircleNumberWidth(tenDigit))
154 | unitDigitWidth := int64(GetHitCircleNumberWidth(unitDigit))
155 | tenBaseX := baseX + float64(render.HitCircleOverlap-tenDigitWidth)/2*screenratio
156 | unitBaseX := baseX - float64(render.HitCircleOverlap-unitDigitWidth)/2*screenratio
157 |
158 | DrawHitCircleNumber(tenDigit, bmath.Vector2d{tenBaseX, baseY}, batch)
159 | DrawHitCircleNumber(unitDigit, bmath.Vector2d{unitBaseX, baseY}, batch)
160 | }
161 | }
162 |
163 | if settings.DIVIDES < settings.Objects.MandalaTexturesTrigger {
164 | batch.SetColor(1, 1, 1, alpha)
165 | batch.DrawUnit(*render.CircleOverlay)
166 | }
167 |
168 | batch.SetSubScale(1, 1)
169 |
170 | if time >= self.objData.StartTime+int64(preempt/2) {
171 | return true
172 | }
173 | return false
174 | }
175 |
176 | func (self *Circle) SetDifficulty(preempt, fadeIn float64) {
177 |
178 | }
179 |
180 | func (self *Circle) DrawApproach(time int64, preempt float64, fadeIn float64, color mgl32.Vec4, batch *render.SpriteBatch) {
181 | alpha := 1.0
182 | arr := float64(self.objData.StartTime-time) / preempt
183 |
184 | approachCircleFadeInStart := float64(self.objData.StartTime) - preempt
185 | approachCircleFadeInEnd := math.Min(float64(self.objData.StartTime), approachCircleFadeInStart+2*fadeIn)
186 |
187 | if time < self.objData.StartTime && float64(time) >= approachCircleFadeInStart {
188 | alpha = Clamp(1.0-(approachCircleFadeInEnd-float64(time))/fadeIn, 0.0, 1.0)
189 | } else if time >= self.objData.StartTime {
190 | alpha = Clamp(1.0-float64(time-self.objData.StartTime)/(preempt/2), 0.0, 1.0)
191 | } else {
192 | alpha = float64(color[3])
193 | }
194 |
195 | batch.SetTranslation(self.objData.StartPos)
196 |
197 | if settings.Objects.DrawApproachCircles && time <= self.objData.StartTime {
198 | batch.SetColor(float64(color[0]), float64(color[1]), float64(color[2]), alpha)
199 | batch.SetSubScale(1.0+arr*2, 1.0+arr*2)
200 | batch.DrawUnitFix(*render.ApproachCircle, float64(128*render.ApproachCircle2x), float64(128*render.ApproachCircle2x))
201 | }
202 |
203 | batch.SetSubScale(1, 1)
204 | }
205 |
206 | func (self *Circle) GetObjectNumber() int64 {
207 | return self.objData.ObjectNumber
208 | }
209 |
--------------------------------------------------------------------------------
/storyboard/object.go:
--------------------------------------------------------------------------------
1 | package storyboard
2 |
3 | import (
4 | "danser/bmath"
5 | "danser/render"
6 | "danser/render/texture"
7 | "github.com/go-gl/mathgl/mgl32"
8 | "math"
9 | "strings"
10 | "unicode"
11 | )
12 |
13 | const (
14 | storyboardArea = 640.0 * 480.0
15 | maxLoad = 1.3328125 //480*480*(16/9)/(640*480)
16 | )
17 |
18 | type color struct {
19 | R, G, B, A float64
20 | }
21 |
22 | type Object interface {
23 | Update(time int64)
24 | Draw(time int64, batch *render.SpriteBatch)
25 | GetLoad() float64
26 | GetStartTime() int64
27 | GetEndTime() int64
28 | GetZIndex() int64
29 |
30 | GetPosition() bmath.Vector2d
31 | SetPosition(vec bmath.Vector2d)
32 |
33 | GetScale() bmath.Vector2d
34 | SetScale(vec bmath.Vector2d)
35 |
36 | GetRotation() float64
37 | SetRotation(rad float64)
38 |
39 | GetColor() mgl32.Vec3
40 | SetColor(color mgl32.Vec3)
41 |
42 | GetAlpha() float64
43 | SetAlpha(alpha float64)
44 |
45 | SetHFlip(on bool)
46 | SetVFlip(on bool)
47 |
48 | SetAdditive(on bool)
49 | }
50 |
51 | type Sprite struct {
52 | texture []*texture.TextureRegion
53 | frameDelay float64
54 | loopForever bool
55 | currentFrame int
56 | transform *Transformations
57 | loopQueue []*Loop
58 | loopProcessed []*Loop
59 | startTime, endTime, zIndex int64
60 | position bmath.Vector2d
61 | origin bmath.Vector2d
62 | scale bmath.Vector2d
63 | flip bmath.Vector2d
64 | rotation float64
65 | color color
66 | dirty bool
67 | additive bool
68 | firstupdate bool
69 | }
70 |
71 | func cutWhites(text string) (string, int) {
72 | for i, c := range text {
73 | if unicode.IsLetter(c) || unicode.IsNumber(c) {
74 | return text[i:], i
75 | }
76 | }
77 |
78 | return text, 0
79 | }
80 |
81 | func NewSprite(texture []*texture.TextureRegion, frameDelay float64, loopForever bool, zIndex int64, position bmath.Vector2d, origin bmath.Vector2d, subCommands []string) *Sprite {
82 | sprite := &Sprite{texture: texture, frameDelay: frameDelay, loopForever: loopForever, zIndex: zIndex, position: position, origin: origin, scale: bmath.NewVec2d(1, 1), flip: bmath.NewVec2d(1, 1), color: color{1, 1, 1, 1}}
83 | sprite.transform = NewTransformations(sprite)
84 |
85 | var currentLoop *Loop = nil
86 | loopDepth := -1
87 |
88 | for _, subCommand := range subCommands {
89 | command := strings.Split(subCommand, ",")
90 | var removed int
91 | command[0], removed = cutWhites(command[0])
92 |
93 | if command[0] == "T" {
94 | continue
95 | }
96 |
97 | if removed == 1 {
98 | if currentLoop != nil {
99 | sprite.loopQueue = append(sprite.loopQueue, currentLoop)
100 | loopDepth = -1
101 | }
102 | if command[0] != "L" {
103 | sprite.transform.Add(NewCommand(command))
104 | }
105 | }
106 |
107 | if command[0] == "L" {
108 |
109 | currentLoop = NewLoop(command, sprite)
110 |
111 | loopDepth = removed + 1
112 |
113 | } else if removed == loopDepth {
114 | currentLoop.Add(NewCommand(command))
115 | }
116 | }
117 |
118 | if currentLoop != nil {
119 | sprite.loopQueue = append(sprite.loopQueue, currentLoop)
120 | loopDepth = -1
121 | }
122 |
123 | sprite.transform.Finalize()
124 |
125 | sprite.startTime = sprite.transform.startTime
126 | sprite.endTime = sprite.transform.endTime
127 |
128 | for _, loop := range sprite.loopQueue {
129 | if loop.start < sprite.startTime {
130 | sprite.startTime = loop.start
131 | }
132 |
133 | if loop.end > sprite.endTime {
134 | sprite.endTime = loop.end
135 | }
136 | }
137 |
138 | return sprite
139 | }
140 |
141 | func (sprite *Sprite) Update(time int64) {
142 | sprite.currentFrame = 0
143 |
144 | if len(sprite.texture) > 1 {
145 | frame := int(math.Floor(float64(time-sprite.startTime) / sprite.frameDelay))
146 | if !sprite.loopForever {
147 | if frame >= len(sprite.texture) {
148 | frame = len(sprite.texture) - 1
149 | }
150 | sprite.currentFrame = frame
151 | } else {
152 | sprite.currentFrame = frame % len(sprite.texture)
153 | }
154 | }
155 |
156 | sprite.transform.Update(time)
157 |
158 | for i := 0; i < len(sprite.loopQueue); i++ {
159 | c := sprite.loopQueue[i]
160 | if c.start <= time {
161 | sprite.loopProcessed = append(sprite.loopProcessed, c)
162 | sprite.loopQueue = append(sprite.loopQueue[:i], sprite.loopQueue[i+1:]...)
163 | i--
164 | }
165 | }
166 |
167 | for i := 0; i < len(sprite.loopProcessed); i++ {
168 | c := sprite.loopProcessed[i]
169 | c.Update(time)
170 |
171 | if time > c.end {
172 | sprite.loopProcessed = append(sprite.loopProcessed[:i], sprite.loopProcessed[i+1:]...)
173 | i--
174 | }
175 | }
176 |
177 | sprite.firstupdate = true
178 | }
179 |
180 | func (sprite *Sprite) Draw(time int64, batch *render.SpriteBatch) {
181 | if !sprite.firstupdate || sprite.color.A < 0.01 {
182 | return
183 | }
184 |
185 | alpha := sprite.color.A
186 | if alpha > 1.001 {
187 | alpha -= math.Ceil(sprite.color.A) - 1
188 | }
189 | batch.DrawStObject(sprite.position, sprite.origin, sprite.scale.Abs(), sprite.flip, sprite.rotation, mgl32.Vec4{float32(sprite.color.R), float32(sprite.color.G), float32(sprite.color.B), float32(alpha)}, sprite.additive, *sprite.texture[sprite.currentFrame], true)
190 | }
191 |
192 | func (sprite *Sprite) GetPosition() bmath.Vector2d {
193 | return sprite.position
194 | }
195 |
196 | func (sprite *Sprite) SetPosition(vec bmath.Vector2d) {
197 | sprite.position = vec
198 | sprite.dirty = true
199 | }
200 |
201 | func (sprite *Sprite) GetScale() bmath.Vector2d {
202 | return sprite.scale
203 | }
204 |
205 | func (sprite *Sprite) SetScale(vec bmath.Vector2d) {
206 | sprite.scale = vec
207 | sprite.dirty = true
208 | }
209 |
210 | func (sprite *Sprite) GetRotation() float64 {
211 | return sprite.rotation
212 | }
213 |
214 | func (sprite *Sprite) SetRotation(rad float64) {
215 | sprite.rotation = rad
216 | sprite.dirty = true
217 | }
218 |
219 | func (sprite *Sprite) GetColor() mgl32.Vec3 {
220 | return mgl32.Vec3{float32(sprite.color.R), float32(sprite.color.G), float32(sprite.color.B)}
221 | }
222 |
223 | func (sprite *Sprite) SetColor(color mgl32.Vec3) {
224 | sprite.color.R, sprite.color.G, sprite.color.B = float64(color[0]), float64(color[1]), float64(color[2])
225 | sprite.dirty = true
226 | }
227 |
228 | func (sprite *Sprite) GetAlpha() float64 {
229 | return sprite.color.A
230 | }
231 |
232 | func (sprite *Sprite) SetAlpha(alpha float64) {
233 | sprite.color.A = alpha
234 | sprite.dirty = true
235 | }
236 |
237 | func (sprite *Sprite) SetHFlip(on bool) {
238 | j := 1.0
239 | if on {
240 | j = -1
241 | }
242 | sprite.flip.X = j
243 | sprite.dirty = true
244 | }
245 |
246 | func (sprite *Sprite) SetVFlip(on bool) {
247 | j := 1.0
248 | if on {
249 | j = -1
250 | }
251 | sprite.flip.Y = j
252 | sprite.dirty = true
253 | }
254 |
255 | func (sprite *Sprite) SetAdditive(on bool) {
256 | sprite.additive = on
257 | sprite.dirty = true
258 | }
259 |
260 | func (sprite *Sprite) GetStartTime() int64 {
261 | return sprite.startTime
262 | }
263 |
264 | func (sprite *Sprite) GetEndTime() int64 {
265 | return sprite.endTime
266 | }
267 |
268 | func (sprite *Sprite) GetZIndex() int64 {
269 | return sprite.zIndex
270 | }
271 |
272 | func (sprite *Sprite) GetLoad() float64 {
273 | if sprite.color.A >= 0.01 {
274 | return math.Min((float64(sprite.texture[0].Width)*sprite.scale.X*float64(sprite.texture[0].Height)*sprite.scale.Y)/storyboardArea, maxLoad)
275 | }
276 | return 0
277 | }
278 |
--------------------------------------------------------------------------------