├── mockup_ffmpeg ├── src │ ├── mockup_ffmpeg.nim │ └── mockup_ffmpeg │ │ ├── results.nim │ │ └── h264.nim ├── .gitignore ├── tests │ ├── config.nims │ ├── testdata │ │ └── cloud.mp4 │ └── test_h264.nim ├── mockup_ffmpeg.nimble └── README.md ├── develop.sh ├── nginx ├── Dockerfile └── nginx.conf ├── tests ├── config.nims └── test_decode_video.nim ├── muml ├── tests │ └── config.nims ├── .gitignore ├── src │ ├── muml │ │ ├── builtin │ │ │ ├── text.nim │ │ │ ├── triangle.nim │ │ │ ├── rectangle.nim │ │ │ ├── commonTypes.nim │ │ │ └── video.nim │ │ ├── types.nim │ │ ├── header.nim │ │ ├── deserializer.nim │ │ ├── utils.nim │ │ ├── builder.nim │ │ └── parserAST.nim │ └── muml.nim ├── .github │ └── workflows │ │ ├── main.yml │ │ └── docs.yml ├── muml.nimble └── README.md ├── nagu ├── tests │ ├── config.nims │ └── test_position.nim ├── src │ ├── nagu │ │ ├── opengl.nim │ │ ├── triangle.nim │ │ ├── utils.nim │ │ ├── mvp_matrix.nim │ │ ├── color.nim │ │ ├── old_vbo.nim │ │ ├── old_vao.nim │ │ ├── vao.nim │ │ ├── shader.nim │ │ ├── glfw.nim │ │ ├── vbo.nim │ │ ├── old_shape.nim │ │ ├── position.nim │ │ ├── types │ │ │ └── texture.nim │ │ ├── old_triangle.nim │ │ ├── shape.nim │ │ ├── texture.nim │ │ └── program.nim │ └── nagu.nim ├── .gitignore ├── example │ ├── example.nimble │ └── src │ │ └── example.nim ├── .github │ └── workflows │ │ ├── main.yml │ │ └── docs.yml ├── nagu.nimble ├── README-ja.md └── README.md ├── .vscode └── settings.json ├── mockup ├── tests │ ├── config.nims │ └── test1.nim ├── src │ ├── mockup.nim │ └── mockup │ │ └── submodule.nim └── mockup.nimble ├── mockup_opengl ├── tests │ ├── config.nims │ └── test1.nim ├── mockup_opengl.nimble └── src │ ├── mockup_opengl.nim │ └── mockup_opengl │ └── submodule.nim ├── mockup.png ├── experiment ├── src │ ├── experiment.nim │ ├── encode_video.nim │ └── encode_mp4.nim ├── experiment.nimble └── index.html ├── README-ja.md ├── shaders ├── shapes │ └── id │ │ ├── id.frag │ │ └── id.vert ├── triangles │ ├── fragment │ │ └── idFilter.glsl │ ├── vertex_shader.glsl │ ├── fragment_shader.glsl │ └── vertex │ │ └── idFilter.glsl └── textures │ ├── fragment │ ├── idFilter.glsl │ └── colorInversionFilter.glsl │ └── vertex │ ├── colorInversionFilter.glsl │ └── idFilter.glsl ├── mockmedia ├── packets.nim ├── types.nim ├── scaler.nim ├── codecs.nim └── frames.nim ├── docker-compose.yml ├── .gitignore ├── .github └── workflows │ └── main.yaml ├── utils └── link_ffmpeg.nims ├── src ├── mockuppkg │ ├── triangle.nim │ ├── render_buffer.nim │ ├── textures.nim │ ├── utils.nim │ ├── opengl.nim │ ├── fonts.nim │ ├── frames.nim │ ├── shaders.nim │ ├── encode_mp4.nim │ ├── streaming.nim │ └── videos.nim └── mockup.nim ├── Dockerfile ├── mockup.nimble └── README.md /mockup_ffmpeg/src/mockup_ffmpeg.nim: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /develop.sh: -------------------------------------------------------------------------------- 1 | nimble develop -p mockup_ffmpeg -------------------------------------------------------------------------------- /mockup_ffmpeg/.gitignore: -------------------------------------------------------------------------------- 1 | /tests/testdata/out -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tiangolo/nginx-rtmp:latest 2 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /muml/tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /nagu/tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nuxt.isNuxtApp": false 3 | } -------------------------------------------------------------------------------- /mockup/tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /mockup_ffmpeg/tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /mockup_opengl/tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momeemt/mock-up/HEAD/mockup.png -------------------------------------------------------------------------------- /mockup/src/mockup.nim: -------------------------------------------------------------------------------- 1 | import mockup_ffmpeg 2 | 3 | echo openMp4("", 0, 0, 0) -------------------------------------------------------------------------------- /experiment/src/experiment.nim: -------------------------------------------------------------------------------- 1 | import encode_mp4 2 | 3 | when isMainModule: 4 | encodeMp4("./out/out.mp4") 5 | -------------------------------------------------------------------------------- /muml/.gitignore: -------------------------------------------------------------------------------- 1 | # Nim 2 | nimcache/ 3 | nimblecache/ 4 | htmldocs/ 5 | /docs 6 | 7 | # macOS 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 | # mock up: 動画編集ソフトウェアフレームワーク 2 | ![](/mockup.png) 3 | 4 | mock upは現在開発中の動画編集ソフトウェアを開発するためのフレームワークです。 5 | 6 | -------------------------------------------------------------------------------- /mockup_ffmpeg/tests/testdata/cloud.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momeemt/mock-up/HEAD/mockup_ffmpeg/tests/testdata/cloud.mp4 -------------------------------------------------------------------------------- /shaders/shapes/id/id.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec4 color; 4 | out vec4 fragColor; 5 | 6 | void main () { 7 | fragColor = color; 8 | } 9 | -------------------------------------------------------------------------------- /shaders/triangles/fragment/idFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | out vec4 frag_color; 3 | in vec4 color; 4 | 5 | void main(){ 6 | frag_color = vec4(color.rgb, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /tests/test_decode_video.nim: -------------------------------------------------------------------------------- 1 | import std/unittest 2 | import mockuppkg/videos 3 | 4 | test "seek video": 5 | var video = newVideo("assets/mockup.mp4", 0) 6 | video.seek(100) -------------------------------------------------------------------------------- /nagu/src/nagu/opengl.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/opengl.nim defines common types and procedures for OpenGL. 2 | 3 | type 4 | OpenGLDefect* = object of Defect 5 | ## Base defect type for exceptions for OpenGL 6 | -------------------------------------------------------------------------------- /mockmedia/packets.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | 3 | type 4 | Packet* = object 5 | ffmpeg_packet: ptr AVPacket 6 | 7 | proc init* (_: typedesc[Packet]): Packet = 8 | result = Packet() 9 | result.ffmpeg_packet = av_packet_alloc() -------------------------------------------------------------------------------- /nagu/src/nagu/triangle.nim: -------------------------------------------------------------------------------- 1 | import shape 2 | import glm 3 | 4 | type 5 | naguTriangleObj [binded: static bool] = ShapeObj[binded, 3, 9, 12] 6 | naguTriangle* = ref naguTriangleObj[false] 7 | naguBindedTriangle* = ref naguTriangleObj[true] 8 | -------------------------------------------------------------------------------- /muml/src/muml/builtin/text.nim: -------------------------------------------------------------------------------- 1 | import commonTypes, ../builder 2 | 3 | type 4 | Text* = ref object of mumlRootElement 5 | position*: Animation[mumlPosition] 6 | text*: string 7 | # color*: Animation[Color] 8 | 9 | mumlBuilder(Text) 10 | -------------------------------------------------------------------------------- /shaders/textures/fragment/idFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform sampler2D frameTex; 4 | in vec2 texCoord; 5 | out vec4 fragColor; 6 | 7 | void main () { 8 | fragColor = texture(frameTex, texCoord); 9 | fragColor = vec4(fragColor.xyz, 1.0); 10 | } -------------------------------------------------------------------------------- /shaders/triangles/vertex_shader.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 vertex; 4 | in vec2 texCoord0; 5 | uniform mat4 mvpMatrix; 6 | out vec2 texCoord; 7 | 8 | void main() { 9 | gl_Position = mvpMatrix * vec4(vertex, 1.0); 10 | texCoord = texCoord0; 11 | } -------------------------------------------------------------------------------- /nagu/src/nagu.nim: -------------------------------------------------------------------------------- 1 | import nagu/[color, opengl, position, program, shader, shape, triangle, vao, vbo, glfw, texture] 2 | import nagu/types/texture as textureType 3 | export color, opengl, position, program, shader, shape, triangle, vao, vbo, glfw, texture 4 | export textureType 5 | -------------------------------------------------------------------------------- /shaders/textures/vertex/colorInversionFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 vertex; 4 | in vec2 texCoord0; 5 | uniform mat4 mvpMatrix; 6 | out vec2 texCoord; 7 | 8 | void main() { 9 | gl_Position = mvpMatrix * vec4(vertex, 1.0); 10 | texCoord = texCoord0; 11 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | nginx: 4 | build: ./nginx 5 | ports: 6 | - "8080:8080" # HLS配信 7 | - "1935:1935" # RTMP 8 | volumes: 9 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf 10 | - ./nginx/hls:/var/local/www/hls/ 11 | -------------------------------------------------------------------------------- /mockup_opengl/mockup_opengl.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "momeemt" 5 | description = "opengl utility for mock-up" 6 | license = "Apache-2.0" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.6.8" 13 | -------------------------------------------------------------------------------- /nagu/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | nimcache 3 | /docs 4 | /testresults 5 | 6 | # Assets used for experiments under development 7 | /assets 8 | /experiments 9 | 10 | # MacOS 11 | .DS_Store 12 | 13 | # Test executable files 14 | tests/* 15 | !tests/*/ 16 | !tests/*.* 17 | 18 | example/example 19 | -------------------------------------------------------------------------------- /shaders/textures/fragment/colorInversionFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform sampler2D frameTex; 4 | in vec2 texCoord; 5 | out vec4 fragColor; 6 | 7 | void main () { 8 | fragColor = texture(frameTex, texCoord); 9 | fragColor = vec4(vec3(1.0, 1.0, 1.0) - fragColor.xyz, 1.0); 10 | } -------------------------------------------------------------------------------- /shaders/triangles/fragment_shader.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | uniform sampler2D frameTex; 3 | in vec2 texCoord; 4 | out vec4 fragColor; 5 | void main() { 6 | fragColor = texture(frameTex, texCoord); 7 | float ave = (fragColor.x + fragColor.y + fragColor.z) / 3; 8 | fragColor = vec4(ave, ave, ave, 1.0); 9 | } -------------------------------------------------------------------------------- /mockup_opengl/src/mockup_opengl.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. A typical library package 2 | # exports the main API in this file. Note that you cannot rename this file 3 | # but you can remove it if you wish. 4 | 5 | proc add*(x, y: int): int = 6 | ## Adds two files together. 7 | return x + y 8 | -------------------------------------------------------------------------------- /muml/src/muml/builtin/triangle.nim: -------------------------------------------------------------------------------- 1 | import commonTypes, ../builder 2 | 3 | type 4 | Triangle* = ref object of mumlRootElement 5 | position*: Animation[mumlPosition] 6 | scale*: Animation[mumlScale] 7 | rotate*: Animation[float] 8 | opacity*: Animation[float] 9 | 10 | mumlBuilder(Triangle) 11 | -------------------------------------------------------------------------------- /mockup_ffmpeg/mockup_ffmpeg.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "momeemt" 5 | description = "ffmpeg utility for mock-up" 6 | license = "LGPL-3.0-or-later" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.6.8" 13 | requires "ffmpeg >= 0.5.5" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nim 2 | nimcache/ 3 | nimblecache/ 4 | htmldocs/ 5 | /docs 6 | 7 | # macOS 8 | .DS_Store 9 | 10 | # binary 11 | bin 12 | 13 | # database 14 | /db 15 | 16 | # Assets directory for testing 17 | assets 18 | 19 | # Movie output directory for testing 20 | movies 21 | 22 | # HTTP Live Streaming File 23 | /nginx/hls 24 | -------------------------------------------------------------------------------- /mockup_ffmpeg/README.md: -------------------------------------------------------------------------------- 1 | # mockup_ffmpeg 2 | mock-upのためにFFmpegの各種APIをNim風に記述できるようにするライブラリ 3 | 4 | ## 優先 5 | 1. H264のデコード・エンコードのための実装 6 | 1. 画像ファイル(JPEG)のデコード・エンコード 7 | 1. 音声のデコード・エンコード 8 | 1. 動画ファイルのデコード・エンコードの一般化 9 | 10 | ## tests 11 | - 動画 12 | - 画像 13 | - 音声 14 | 15 | の3つについてサンプルファイルを用意してテストを行う。 16 | GitHub Actionsを回す。 17 | -------------------------------------------------------------------------------- /shaders/triangles/vertex/idFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | layout(location = 0) in vec3 vertexPosition_modelspace; 4 | layout(location = 1) in vec4 vertexColor; 5 | uniform mat4 mvpMatrix; 6 | out vec4 color; 7 | 8 | void main() { 9 | gl_Position = mvpMatrix * vec4(vertexPosition_modelspace, 1.0); 10 | color = vertexColor; 11 | } -------------------------------------------------------------------------------- /experiment/experiment.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Mutsuha Asada" 5 | description = "A new awesome nimble package" 6 | license = "MIT" 7 | srcDir = "src" 8 | bin = @["experiment"] 9 | binDir = "bin" 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.6.6" 14 | requires "ffmpeg >= 0.5.3" -------------------------------------------------------------------------------- /mockup/mockup.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "momeemt" 5 | description = "a core library of mock-up" 6 | license = "LGPL-3.0-or-later" 7 | srcDir = "src" 8 | binDir = "bin" 9 | bin = @["mockup"] 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.6.8" 14 | requires "mockup_ffmpeg#head" -------------------------------------------------------------------------------- /nagu/example/example.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Mutsuha Asada" 5 | description = "A new awesome nimble package" 6 | license = "MIT" 7 | srcDir = "src" 8 | bin = @["example"] 9 | 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.6.4" 14 | requires "nagu" 15 | requires "pnm >= 2.1.1" 16 | -------------------------------------------------------------------------------- /muml/src/muml/builtin/rectangle.nim: -------------------------------------------------------------------------------- 1 | import commonTypes, ../builder 2 | 3 | type 4 | Rectangle* = ref object of mumlRootElement 5 | position*: Animation[mumlPosition] 6 | width*: Animation[float] 7 | height*: Animation[float] 8 | scale*: Animation[mumlScale] 9 | rotate*: Animation[float] 10 | opacity*: Animation[float] 11 | # color*: Animation[Color] 12 | 13 | mumlBuilder(Rectangle) 14 | -------------------------------------------------------------------------------- /muml/src/muml/builtin/commonTypes.nim: -------------------------------------------------------------------------------- 1 | import uuids 2 | 3 | type 4 | Animation* [T] = seq[T] 5 | 6 | mumlRootElement* = ref object of RootObj 7 | id*: UUID 8 | layer: int 9 | frame: Animation[int] 10 | 11 | mumlFilter* = ref object of RootObj 12 | 13 | mumlPosition* = object 14 | x*: float 15 | y*: float 16 | z*: float 17 | 18 | mumlScale* = object 19 | width*: float 20 | height*: float 21 | -------------------------------------------------------------------------------- /mockup/tests/test1.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. You may wish to put all of your 2 | # tests into a single file, or separate them into multiple `test1`, `test2` 3 | # etc. files (better names are recommended, just make sure the name starts with 4 | # the letter 't'). 5 | # 6 | # To run these tests, simply execute `nimble test`. 7 | 8 | import unittest 9 | 10 | import mockup 11 | test "can add": 12 | check add(5, 5) == 10 13 | -------------------------------------------------------------------------------- /mockup_opengl/tests/test1.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. You may wish to put all of your 2 | # tests into a single file, or separate them into multiple `test1`, `test2` 3 | # etc. files (better names are recommended, just make sure the name starts with 4 | # the letter 't'). 5 | # 6 | # To run these tests, simply execute `nimble test`. 7 | 8 | import unittest 9 | 10 | import mockup_opengl 11 | test "can add": 12 | check add(5, 5) == 10 13 | -------------------------------------------------------------------------------- /mockup/src/mockup/submodule.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. Users of your library will 2 | # import this file by writing ``import mockup/submodule``. Feel free to rename or 3 | # remove this file altogether. You may create additional modules alongside 4 | # this file as required. 5 | 6 | type 7 | Submodule* = object 8 | name*: string 9 | 10 | proc initSubmodule*(): Submodule = 11 | ## Initialises a new ``Submodule`` object. 12 | Submodule(name: "Anonymous") 13 | -------------------------------------------------------------------------------- /nagu/src/nagu/utils.nim: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | proc getFileNameAndLine: tuple[path: string, line: int] = 4 | let 5 | stack_trace = getStackTraceEntries() 6 | last_stack_trace = stack_trace[stack_trace.high-2] 7 | result = ($last_stack_trace.filename, last_stack_trace.line) 8 | 9 | template debugOpenGLStatement* (body: untyped): untyped = 10 | when defined(debuggingOpenGL): 11 | let (path, line) = getFileNameAndLine() 12 | stdout.write path & "(" & $line & ") " 13 | body -------------------------------------------------------------------------------- /muml/src/muml/builtin/video.nim: -------------------------------------------------------------------------------- 1 | import commonTypes, ../builder 2 | 3 | type 4 | mumlVideoFrame* = object 5 | start*: int 6 | stop*: int 7 | 8 | Video* = ref object of mumlRootElement 9 | path*: string 10 | videoFrame*: mumlVideoFrame 11 | position*: Animation[mumlPosition] 12 | scale*: Animation[mumlScale] 13 | rotate*: Animation[float] 14 | opacity*: Animation[float] 15 | # filters*: seq[mumlFilter] 16 | # audio*: mumlAudio 17 | 18 | mumlBuilder(Video) 19 | -------------------------------------------------------------------------------- /mockup_opengl/src/mockup_opengl/submodule.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. Users of your library will 2 | # import this file by writing ``import mockup_opengl/submodule``. Feel free to rename or 3 | # remove this file altogether. You may create additional modules alongside 4 | # this file as required. 5 | 6 | type 7 | Submodule* = object 8 | name*: string 9 | 10 | proc initSubmodule*(): Submodule = 11 | ## Initialises a new ``Submodule`` object. 12 | Submodule(name: "Anonymous") 13 | -------------------------------------------------------------------------------- /nagu/src/nagu/mvp_matrix.nim: -------------------------------------------------------------------------------- 1 | import vbo 2 | 3 | type 4 | ModelMatrixVector* [V: static int] = VBO[V, float32] 5 | BindedModelMatrixVector* [V: static int] = BindedVBO[V, float32] 6 | 7 | ModelMatrix* [V: static int] = array[4, ModelMatrixVector[V]] 8 | 9 | proc init* [V: static int] (_: typedesc[ModelMatrix[V]]): ModelMatrix[V] = 10 | result = [ 11 | ModelMatrixVector[V].init(), 12 | ModelMatrixVector[V].init(), 13 | ModelMatrixVector[V].init(), 14 | ModelMatrixVector[V].init() 15 | ] 16 | -------------------------------------------------------------------------------- /shaders/shapes/id/id.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 vertexPositions; 4 | in vec4 vertexColors; 5 | 6 | in vec4 modelMatrixVec1; 7 | in vec4 modelMatrixVec2; 8 | in vec4 modelMatrixVec3; 9 | in vec4 modelMatrixVec4; 10 | mat4 modelMatrix; 11 | 12 | uniform mat4 mvpMatrix; 13 | out vec4 color; 14 | 15 | void main() { 16 | modelMatrix = mat4(modelMatrixVec1, modelMatrixVec2, modelMatrixVec3, modelMatrixVec4); 17 | gl_Position = mvpMatrix * modelMatrix * vec4(vertexPositions, 1.0); 18 | color = vertexColors; 19 | } -------------------------------------------------------------------------------- /shaders/textures/vertex/idFilter.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 vertex; 4 | in vec2 texCoord0; 5 | 6 | in vec4 modelMatrixVec1; 7 | in vec4 modelMatrixVec2; 8 | in vec4 modelMatrixVec3; 9 | in vec4 modelMatrixVec4; 10 | mat4 modelMatrix; 11 | 12 | uniform mat4 mvpMatrix; 13 | out vec2 texCoord; 14 | 15 | void main() { 16 | modelMatrix = mat4(modelMatrixVec1, modelMatrixVec2, modelMatrixVec3, modelMatrixVec4); 17 | gl_Position = mvpMatrix * modelMatrix * vec4(vertex, 1.0); 18 | texCoord = texCoord0; 19 | } 20 | -------------------------------------------------------------------------------- /muml/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Cache choosenim 11 | id: cache-choosenim 12 | uses: actions/cache@v1 13 | with: 14 | path: ~/.choosenim 15 | key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} 16 | - name: Cache nimble 17 | id: cache-nimble 18 | uses: actions/cache@v1 19 | with: 20 | path: ~/.nimble 21 | key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} 22 | - uses: jiro4989/setup-nim-action@v1.1.4 23 | - run: nimble mainCi 24 | -------------------------------------------------------------------------------- /nagu/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Cache choosenim 11 | id: cache-choosenim 12 | uses: actions/cache@v1 13 | with: 14 | path: ~/.choosenim 15 | key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} 16 | - name: Cache nimble 17 | id: cache-nimble 18 | uses: actions/cache@v1 19 | with: 20 | path: ~/.nimble 21 | key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} 22 | - uses: jiro4989/setup-nim-action@v1.1.4 23 | - run: nimble mainCi 24 | -------------------------------------------------------------------------------- /muml/src/muml/types.nim: -------------------------------------------------------------------------------- 1 | import std/json 2 | 3 | type 4 | mumlNode* = object 5 | header: JsonNode 6 | contents: JsonNode 7 | 8 | mumlHeader* = object 9 | projectName: string 10 | width: int 11 | height: int 12 | frameCount: int 13 | ## プロジェクト全体のフレーム数 14 | outputPath: string 15 | fps: float 16 | 17 | func header* (muml: mumlNode): JsonNode = 18 | result = muml.header 19 | 20 | func contents* (muml: mumlNode): JsonNode = 21 | result = muml.contents 22 | 23 | proc `header=`* (muml: var mumlNode, header: JsonNode) = 24 | muml.header = header 25 | 26 | proc `contents=`* (muml: var mumlNode, contents: JsonNode) = 27 | muml.contents = contents 28 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | # name: build 2 | 3 | # on: [push] 4 | 5 | # jobs: 6 | # test: 7 | # runs-on: ubuntu-latest 8 | # steps: 9 | # - uses: actions/checkout@v1 10 | # - name: Cache choosenim 11 | # id: cache-choosenim 12 | # uses: actions/cache@v1 13 | # with: 14 | # path: ~/.choosenim 15 | # key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} 16 | # - name: Cache nimble 17 | # id: cache-nimble 18 | # uses: actions/cache@v1 19 | # with: 20 | # path: ~/.nimble 21 | # key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} 22 | # - uses: jiro4989/setup-nim-action@v1.0.1 23 | # - run: nimble ci 24 | -------------------------------------------------------------------------------- /muml/muml.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.0.1" 4 | author = "Mutsuha Asada" 5 | description = "mock up markup language parser" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.4.8" 13 | requires "Palette >= 0.2.1" 14 | requires "uuids >= 0.1.11" 15 | 16 | # Tasks 17 | task docs, "Generate documents": 18 | rmDir "docs" 19 | exec "nimble doc --project --index:on -o:docs src/muml.nim" 20 | 21 | task docsCi, "Run Docs CI": 22 | exec "nim -v" 23 | exec "nimble -v" 24 | exec "nimble check" 25 | exec "nimble install -Y" 26 | exec "nimble docs -Y" 27 | 28 | task mainCi, "Run CI": 29 | exec "nimble docsCi" 30 | exec "nimble test -Y" 31 | -------------------------------------------------------------------------------- /mockmedia/types.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | 3 | type 4 | VideoCodecKind* = enum 5 | vck_mpeg4 = AV_CODEC_ID_MPEG4 6 | vck_H264 = AV_CODEC_ID_H264 7 | 8 | VideoEncoder* = object 9 | ffmpeg_codec: ptr AVCodec 10 | ffmpeg_codec_context: ptr AVCodecContext 11 | 12 | VideoFrameFormat* {.pure.} = enum 13 | vff_unknown_or_unset = AV_PIX_FMT_NONE 14 | vff_YUV420P = AV_PIX_FMT_YUV420P 15 | vff_YUYV422 = AV_PIX_FMT_YUYV422 16 | vff_RGB24 = AV_PIX_FMT_RGB24 17 | vff_BGR24 = AV_PIX_FMT_BGR24 18 | vff_YUV422P = AV_PIX_FMT_YUV422P 19 | 20 | vff_RGBA = AV_PIX_FMT_RGBA 21 | # WIP 22 | 23 | VideoFrame* = object 24 | ffmpeg_frame: ptr AVFrame 25 | 26 | FFmpegDefect* = object of Defect -------------------------------------------------------------------------------- /muml/.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docs: 10 | env: 11 | NIM_VERSION: latest 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Cache choosenim 16 | id: cache-choosenim 17 | uses: actions/cache@v1 18 | with: 19 | path: ~/.choosenim 20 | key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} 21 | - uses: jiro4989/setup-nim-action@v1.1.4 22 | - run: nimble docsCi 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | if: ${{ github.ref == 'refs/heads/main' }} 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./docs 29 | -------------------------------------------------------------------------------- /nagu/.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docs: 10 | env: 11 | NIM_VERSION: latest 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Cache choosenim 16 | id: cache-choosenim 17 | uses: actions/cache@v1 18 | with: 19 | path: ~/.choosenim 20 | key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} 21 | - uses: jiro4989/setup-nim-action@v1.1.4 22 | - run: nimble docsCi 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | if: ${{ github.ref == 'refs/heads/main' }} 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./docs 29 | -------------------------------------------------------------------------------- /muml/src/muml.nim: -------------------------------------------------------------------------------- 1 | import std/[json] 2 | import muml/[utils, builder, deserializer, types, header] 3 | import muml/builtin/[triangle, rectangle, video, text, commonTypes] 4 | export utils, video, rectangle, text, triangle, builder, deserializer, types, commonTypes, header 5 | 6 | proc readMuml* (json: JsonNode): mumlNode = 7 | if not json.hasKey("muml"): 8 | raise newException(Exception, "no muml") 9 | if not json["muml"].hasKey("header"): 10 | raise newException(Exception, "no header") 11 | if not json["muml"].hasKey("contents"): 12 | raise newException(Exception, "no contents") 13 | 14 | result.header = json["muml"]["header"] 15 | result.contents = json["muml"]["contents"] 16 | 17 | proc readMuml* (path: string): mumlNode = 18 | result = readmuml(parseFile(path)) 19 | -------------------------------------------------------------------------------- /utils/link_ffmpeg.nims: -------------------------------------------------------------------------------- 1 | import strutils, strformat 2 | 3 | const 4 | FFmpegDylibDirPath = "/opt/homebrew/Cellar/ffmpeg/5.0.1_3/lib" 5 | LocalLibDirPath = "/usr/local/lib" 6 | LibNames = @[ 7 | "libavcodec.59.18.100", 8 | "libavdevice.59.4.100", 9 | "libavfilter.8.24.100", 10 | "libavformat.59.16.100", 11 | "libavutil.57.17.100", 12 | "libpostproc.56.3.100", 13 | "libswresample.4.3.100", 14 | "libswscale.6.4.100" 15 | ] 16 | 17 | proc unlinkAndLink (full_lib_name: string) = 18 | let lib_name = full_lib_name.split(".")[0..1].join(".") 19 | exec &"unlink {LocalLibDirPath}/{lib_name}.dylib" 20 | exec &"ln -s {FFmpegDylibDirPath}/{full_lib_name}.dylib {LocalLibDirPath}/{lib_name}.dylib" 21 | 22 | for lib_name in LibNames: 23 | unlinkAndLink(lib_name) 24 | -------------------------------------------------------------------------------- /src/mockuppkg/triangle.nim: -------------------------------------------------------------------------------- 1 | import nagu, glm, muml 2 | import utils 3 | 4 | proc init* (T: type naguTriangle, 5 | header: mumlHeader, 6 | positions: array[3, Vec3[int]], 7 | # colors: array[3, tColor], 8 | colors: array[3, Vec4[float32]], 9 | vertex_shader_path: string, 10 | fragment_shader_path: string): naguTriangle = 11 | result = naguTriangle.make( 12 | positions.naguCoordinate(header), 13 | colors, 14 | vertex_shader_path, 15 | fragment_shader_path, 16 | mockupInitializeMvpMatrix 17 | ) 18 | 19 | type Position2D = tuple[x, y, z: float] 20 | type Scale2D = tuple[width, height: float] 21 | 22 | func triangleCoordByCenterCoord* (pos: Position2D, scale: Scale2D): array[3, Position2D] = 23 | let 24 | (x, y, z) = pos 25 | (width, height) = scale 26 | discard 27 | -------------------------------------------------------------------------------- /mockup_ffmpeg/tests/test_h264.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import mockup_ffmpeg/h264 3 | import mockup_ffmpeg/results 4 | import macros 5 | 6 | {.experimental: "caseStmtMacros".} 7 | 8 | template checkResult [T, E] (res: Result[T, E]) = 9 | case res 10 | of ok(_): 11 | check true 12 | of err(e): 13 | echo e 14 | check false 15 | 16 | test "decode cloud.mp4": 17 | proc main (): Result[(), string] = 18 | var movie = ?openH264("tests/testdata/cloud.mp4") 19 | for frame in movie.decodeH264: 20 | discard 21 | checkResult main() 22 | 23 | test "encode H.264 from cloud.mp4": 24 | proc main (): Result[(), string] = 25 | var 26 | src = ?openH264("tests/testdata/cloud.mp4") 27 | var dest = ?newH264("tests/testdata/out/cloud.mp4", src.width, src.height, src.fps) 28 | for frame in src.decodeH264: 29 | discard ?dest.addFrame(frame) 30 | dest.flush() 31 | checkResult main() 32 | -------------------------------------------------------------------------------- /muml/src/muml/header.nim: -------------------------------------------------------------------------------- 1 | import std/[json, importutils] 2 | import types, utils 3 | 4 | privateAccess(mumlHeader) 5 | 6 | func projectName* (header: mumlHeader): string = header.projectName 7 | func width* (header: mumlHeader): int = header.width 8 | func height* (header: mumlHeader): int = header.height 9 | func frameCount* (header: mumlHeader): int = header.frameCount 10 | func outputPath* (header: mumlHeader): string = header.outputPath 11 | func fps* (header: mumlHeader): float = header.fps 12 | 13 | proc parseHeader* (muml: mumlNode): mumlHeader = 14 | result = mumlHeader() 15 | for key, val in muml.header.pairs: 16 | case key: 17 | of "project_name": 18 | result.projectName = val.getStr.removeDoubleQuotation 19 | of "width": result.width = val.getInt 20 | of "height": result.height = val.getInt 21 | of "frame_count": 22 | result.frameCount = val.getInt 23 | of "output_path": 24 | result.outputPath = val.getStr.removeDoubleQuotation 25 | of "fps": result.fps = val.getFloat 26 | -------------------------------------------------------------------------------- /mockmedia/scaler.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | import types 3 | 4 | type 5 | Scaler* = object 6 | context: ptr SwsContext 7 | 8 | ScalerProp* = object 9 | width, height: int32 10 | format: AVPixelFormat 11 | filter: ptr SwsFilter 12 | 13 | proc init* (_: typedesc[Scaler], srcProp, destProp: ScalerProp): Scaler = 14 | result = Scaler() 15 | result.context = sws_getContext( 16 | srcProp.width, srcProp.height, srcProp.format, 17 | destProp.width, destProp.height, destProp.format, 18 | SWS_BICUBIC, srcProp.filter, destProp.filter, nil 19 | ) 20 | 21 | proc init* (_: typedesc[ScalerProp], width, height: int32, format: VideoFrameFormat, filter: ptr SwsFilter = nil): ScalerProp = 22 | result = ScalerProp() 23 | result.width = width 24 | result.height = height 25 | result.format = cast[AVPixelFormat](format) 26 | result.filter = filter 27 | 28 | proc scale* (scaler: Scaler, src_frame, dest_frame: VideoFrame) = 29 | discard 30 | # discard sws_scale( 31 | # scaler.context, 32 | # src_frame. 33 | # ) -------------------------------------------------------------------------------- /muml/README.md: -------------------------------------------------------------------------------- 1 | # muml 2 | ![](https://github.com/mock-up/muml/workflows/build/badge.svg) 3 | ![](https://github.com/mock-up/muml/workflows/docs/badge.svg) 4 | 5 | mock-up markup languageは[mock-up](https://github.com/mock-up/mock-up)において動画編集を記述するための、JSONで表現されるマークアップ言語です。 6 | 7 | ## mumlBuilder 8 | mumlはmock-upがデフォルトで提供するタグとプロパティを読み出す他に、`mumlBuilder`を用いたユーザー拡張が認められます。 9 | この機能はmock-upのプラグインを処理するために必要です。 10 | 11 | mumlを拡張するためには`mumlRootObj`型を継承した`ref object`型を定義します。 12 | 内部的に構造体が含まれていても構いませんが、現状ではそれらも`mumlRootObj`型を継承した`ref object`型である必要があります。 13 | 14 | ```nim 15 | type 16 | nest1 = ref object of mumlRootObj 17 | nest1_field1: int 18 | 19 | newElement = ref object of mumlRootObj 20 | field1: int 21 | field2: string 22 | field3: nest1 23 | 24 | mumlBuilder(newElement) 25 | ``` 26 | 27 | 上のコードを実行すると、次のJSONをmumlとして解釈できるようになります。 28 | 29 | ```json 30 | { 31 | "field1": 10, 32 | "field2": "hello", 33 | "field3": { 34 | "nest1_field1": 20 35 | } 36 | } 37 | ``` 38 | 39 | ## Document 40 | - [muml - nim docs](https://mock-up.github.io/muml/muml.html) 41 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/opengl:base-ubuntu20.04 2 | RUN apt-get update && \ 3 | DEBIAN_FRONTEND=noninteractive \ 4 | apt-get install -y libx11-dev xorg-dev \ 5 | libglu1-mesa libglu1-mesa-dev \ 6 | libgl1-mesa-glx libgl1-mesa-dev \ 7 | libglfw3 libglfw3-dev \ 8 | libglew-dev \ 9 | ffmpeg 10 | RUN apt-get update; apt-get install -y wget xz-utils g++; \ 11 | wget -qO- https://deb.nodesource.com/setup_13.x | bash -; \ 12 | apt-get install -y nodejs 13 | RUN wget https://nim-lang.org/download/nim-1.6.6.tar.xz; \ 14 | tar xf nim-1.6.6.tar.xz; rm nim-1.6.6.tar.xz; \ 15 | mv nim-1.6.6 nim; \ 16 | cd nim; sh build.sh; \ 17 | rm -r c_code tests; \ 18 | ln -s `pwd`/bin/nim /bin/nim 19 | RUN apt-get update; apt-get install -y git mercurial libssl-dev 20 | RUN cd nim; nim c koch; ./koch tools;\ 21 | ln -s `pwd`/bin/nimble /bin/nimble;\ 22 | ln -s `pwd`/bin/nimsuggest /bin/nimsuggest;\ 23 | ln -s `pwd`/bin/testament /bin/testament 24 | ENV PATH="$HOME/.nimble/bin:$PATH" -------------------------------------------------------------------------------- /nagu/nagu.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "momeemt" 5 | description = "Nim Abstract OpenGL Utility" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 1.6.2" 12 | requires "nimgl == 1.3.2" 13 | requires "glm == 1.1.1" 14 | requires "Palette == 0.2.1" 15 | 16 | # Tasks 17 | 18 | ## https://qiita.com/SFITB/items/dceb1537e4086fa696d2 19 | task test, "run all tests": 20 | exec "testament cat /" 21 | 22 | ## https://github.com/jiro4989/maze/blob/master/maze.nimble 23 | task docs, "Generate documents": 24 | rmDir "docs" 25 | exec "nimble doc --project --index:on -o:docs src/nagu.nim" 26 | 27 | task docsCi, "Run Docs CI": 28 | exec "nim -v" 29 | exec "nimble -v" 30 | exec "nimble check" 31 | exec "nimble install -Y" 32 | exec "nimble docs -Y" 33 | 34 | task mainCi, "Run CI": 35 | exec "nimble docsCi" 36 | exec "nimble test -Y" 37 | 38 | task dev, "dev envoironment": 39 | exec "cd example && nimble -d:debuggingOpenGL run" 40 | 41 | task release, "release environment": 42 | exec "cd example && nimble run --silent" -------------------------------------------------------------------------------- /src/mockuppkg/render_buffer.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl as gl import nil 2 | 3 | type 4 | MockupRenderBuffer* = object 5 | color_render_buffer*: gl.GLuint 6 | depth_render_buffer*: gl.GLuint 7 | frame_buffer*: gl.GLuint 8 | 9 | proc newGLRenderBuffer: gl.GLuint = 10 | gl.glGenRenderbuffers(1, result.addr) 11 | gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, result) 12 | 13 | proc newGLFrameBuffer: gl.GLuint = 14 | gl.glGenFramebuffers(1, result.addr) 15 | gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, result) 16 | 17 | proc newRenderBuffer*: MockupRenderBuffer = 18 | result.color_render_buffer = newGLRenderBuffer() 19 | result.depth_render_buffer = newGLRenderBuffer() 20 | result.frame_buffer = newGLFrameBuffer() 21 | 22 | proc attach* (render_buffer: MockupRenderBuffer) = 23 | gl.glFramebufferRenderbuffer( 24 | gl.GL_FRAMEBUFFER, 25 | gl.GL_COLOR_ATTACHMENT0, 26 | gl.GL_RENDERBUFFER, 27 | render_buffer.color_render_buffer 28 | ) 29 | gl.glFramebufferRenderbuffer( 30 | gl.GL_FRAMEBUFFER, 31 | gl.GL_DEPTH_ATTACHMENT, 32 | gl.GL_RENDERBUFFER, 33 | render_buffer.depth_render_buffer 34 | ) -------------------------------------------------------------------------------- /src/mockuppkg/textures.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl as gl import nil 2 | 3 | type 4 | MockupTexture* = object 5 | ## 画像であれ図形であれ、レイヤ層としてテクスチャに変換してから描画する 6 | id: gl.GLuint 7 | 8 | proc newTexture* (width, height: gl.GLsizei): MockupTexture = 9 | gl.glGenTextures(1, result.id.addr) 10 | gl.glBindTexture(gl.GL_TEXTURE_2D, result.id) 11 | gl.glTexImage2D( 12 | gl.GL_TEXTURE_2D, 0, gl.GLint(gl.GL_RGBA), 13 | width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, nil 14 | ) 15 | gl.glTexParameteri( 16 | gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, 17 | gl.GLint(gl.GL_CLAMP_TO_EDGE) 18 | ) 19 | gl.glTexParameteri( 20 | gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, 21 | gl.GLint(gl.GL_CLAMP_TO_EDGE) 22 | ) 23 | gl.glTexParameteri( 24 | gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, 25 | gl.GLint(gl.GL_LINEAR) 26 | ) 27 | gl.glTexParameteri( 28 | gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, 29 | gl.GLint(gl.GL_LINEAR) 30 | ) 31 | 32 | proc setFrameBuffer* (texture: MockupTexture) = 33 | gl.glFramebufferTexture2D( 34 | gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, 35 | gl.GL_TEXTURE_2D, texture.id, 0 36 | ) 37 | -------------------------------------------------------------------------------- /nagu/src/nagu/color.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/color.nim defines the Color type and procedures. 2 | 3 | from Palette import rgb, tColor 4 | from std/strformat import `&` 5 | 6 | type 7 | ColorObj = object 8 | r, g, b: float32 9 | 10 | Color* = ref ColorObj 11 | ## Represents RGB Color. 12 | 13 | func init* (_: typedesc[Color], r, g, b: range[0.0..1.0]): Color = 14 | ## Initializes Color. 15 | result = Color(r: r, g: g, b: b) 16 | 17 | proc `$`* (color: Color): string = 18 | result = &"(red: {color.r}, green: {color.g}, blue: {color.b})" 19 | 20 | func rgb* (color: Color): tuple[r, g, b: float32] = 21 | ## Gets rgb in `color`. 22 | result = (color.r, color.g, color.b) 23 | 24 | proc toColor* (hex: string): Color = 25 | ## Converts `hex` into Color. 26 | let rgb = hex.rgb 27 | result = Color( 28 | r: rgb.red / 255.0, 29 | g: rgb.green / 255.0, 30 | b: rgb.blue / 255.0 31 | ) 32 | 33 | proc toColor* (color: tColor): Color = 34 | ## Converts `color` into Color. 35 | let rgb = color.hsv.rgb 36 | result = Color( 37 | r: rgb.red / 255.0, 38 | g: rgb.green / 255.0, 39 | b: rgb.blue / 255.0 40 | ) 41 | 42 | proc `+`* (hex: string): Color = 43 | result = ("#" & hex).toColor() 44 | -------------------------------------------------------------------------------- /mockup.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.0.1" 4 | author = "momeemt" 5 | description = "Movie Compilation Kit with Unified and Progressive" 6 | license = "MIT" 7 | srcDir = "src" 8 | installExt = @["nim"] 9 | binDir = "bin" 10 | bin = @["mockup"] 11 | 12 | # Dependencies 13 | 14 | requires "nim >= 1.4.4" 15 | requires "ffmpeg >= 0.5.5" 16 | requires "cligen >= 1.5.2" 17 | requires "nimgl >= 1.3.2" 18 | requires "glm >= 1.1.1" 19 | requires "neo >= 0.3.1" 20 | requires "jester >= 0.5.0" 21 | requires "https://github.com/mock-up/nagu" 22 | requires "https://github.com/mock-up/muml" 23 | requires "uuids == 0.1.11" 24 | 25 | # Tasks 26 | task ci, "Run CI": 27 | exec "nim -v" 28 | exec "nimble -v" 29 | exec "nimble check" 30 | exec "nimble install -Y" 31 | exec "nimble test -Y" 32 | exec "nimble docs -Y" 33 | exec "nimble build -d:release -Y" 34 | exec "./bin/mockup -h" 35 | exec "./bin/mockup -v" 36 | 37 | task docs, "Generate documents": 38 | rmDir "docs" 39 | exec "nimble doc --project --index:on -o:docs -Y src/mockup.nim" 40 | 41 | task docsCi, "Run Docs CI": 42 | exec "nim -v" 43 | exec "nimble -v" 44 | exec "nimble check" 45 | exec "nimble install -Y" 46 | exec "nimble docs -Y" 47 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | rtmp_auto_push on; 3 | events {} 4 | 5 | http { 6 | sendfile off; 7 | tcp_nopush on; 8 | directio 512; 9 | default_type application/octet-stream; 10 | 11 | server { 12 | listen 8080; 13 | 14 | location / { 15 | add_header 'Cache-Control' 'no-cache'; 16 | 17 | add_header 'Access-Control-Allow-Origin' '*' always; 18 | add_header 'Access-Control-Expose-Headers' 'Content-Length'; 19 | 20 | if ($request_method = 'OPTIONS') { 21 | add_header 'Access-Control-Allow-Origin' '*'; 22 | add_header 'Access-Control-Max-Age' 1728000; 23 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 24 | add_header 'Content-Length' 0; 25 | return 204; 26 | } 27 | 28 | types { 29 | application/dash+xml mpd; 30 | application/vnd.apple.mpegurl m3u8; 31 | video/mp2t ts; 32 | } 33 | 34 | root /var/local/www/; 35 | } 36 | } 37 | } 38 | 39 | rtmp { 40 | server { 41 | listen 1935; 42 | application app { 43 | live on; 44 | record off; 45 | 46 | allow publish all; 47 | deny play all; 48 | 49 | hls on; 50 | hls_type event; 51 | hls_path /var/local/www/hls/; 52 | hls_fragment 3; 53 | hls_cleanup on; 54 | hls_playlist_length 3; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mock up: A framework for developing video editing software 2 | ![](/mockup.png) 3 | 4 | **mock up** is a framework for developing video editing software. It is also under development. 5 | 6 | ## Motivation 7 | - It takes time to develop video editing software. No matter how great an idea you have for video editing, or how much you want to apply the HCI thesis to your video editing software, it takes a lot of preparation to create a minimal editing experience, let alone a practical one. 8 | - The FFmpeg API is very low-level, and other image and video processing libraries are good for processing single multimedia, but none of them support the flow of developing video editing software. 9 | - I enjoy creating environments because I want to develop a lot of video editing software that has never been done before. 10 | 11 | ## Introduction 12 | mock up provides a video rendering engine and custom filters and animations, and by writing the video editing process through an intermediate language called muml, video editing functionality can be implemented with just a few simple front-end calls. 13 | 14 | ### Requires 15 | - Nim >= 1.4.4 16 | - FFmpeg 17 | - OpenGL 18 | 19 | ### Installation 20 | 21 | ```zsh 22 | nimble install https://github.com/mock-up/mock-up/ 23 | ``` 24 | 25 | ## Acknowledgments 26 | This framework was supported by: 27 | - Mitou Jr. 2021 (May - November 2021, [Presentation of results](https://jr.mitou.org/projects/2021/mock_up)) 28 | -------------------------------------------------------------------------------- /src/mockuppkg/utils.nim: -------------------------------------------------------------------------------- 1 | import times, macros 2 | import glm, muml 3 | 4 | const mockupInitializeMvpMatrix* = [ 5 | 1f, 0.0f, 0.0f, 0.0f, 6 | 0.0f, 1f, 0.0f, 0.0f, 7 | 0.0f, 0.0f, -1.0f, 0.0f, 8 | -0.5f, -0.5f, 0.0f, 1.0f 9 | ] 10 | 11 | func naguCoordinate* [I: static int] ( 12 | positions: array[I, Vec3[int]], 13 | header: mumlHeader 14 | ): array[I, Vec3[float32]] = 15 | for index in 0 ..< I: 16 | result[index][0] = positions[index][0] / header.width 17 | result[index][1] = positions[index][1] / header.height 18 | result[index][2] = positions[index][2].float32 19 | 20 | template bench* (name: string, body: untyped): untyped = 21 | block: 22 | let now = now() 23 | body 24 | echo "name: ", now() - nows 25 | 26 | proc buffer_offset* (num: int): ptr char = 27 | cast[ptr char](cast[int](cast[ptr char](nil)) + num) 28 | 29 | template `~`*(path: string): string = 30 | when nimvm: 31 | getProjectPath() & "/../" & path 32 | else: 33 | "/" & path 34 | 35 | macro `...`*(args: varargs[typed]): untyped = 36 | result = newStmtList() 37 | result.add newNimNode(nnkBracket) 38 | for node in args[0]: 39 | let sym = node[0] 40 | let impl = sym.getImpl 41 | let idx = node[1][1] 42 | for i in 0 ..< impl[2][0].len - 1: 43 | result[0].add quote do: 44 | `sym`[`idx`].arr[`i`] 45 | 46 | template printOpenGLVersion* = 47 | echo "OpenGL Version: " & $glVersionMajor & "." & $glVersionMinor 48 | -------------------------------------------------------------------------------- /nagu/example/src/example.nim: -------------------------------------------------------------------------------- 1 | when isMainModule: 2 | import nagu 3 | import pnm 4 | import glm 5 | 6 | var 7 | naguContext = setup(1000, 1000, "default") 8 | 9 | sea_tex = Texture.make( 10 | Position.init(0.1, -0.1, 0), 11 | "assets/vertex/id.glsl", 12 | "assets/fragment/id.glsl" 13 | ) 14 | sea = pnm.readPPMFile("assets/sea.ppm") 15 | 16 | cat_tex = Texture.make( 17 | Position.init(-0.4, 0.4, 0), 18 | "assets/vertex/id.glsl", 19 | "assets/fragment/id.glsl" 20 | ) 21 | cat = pnm.readPPMFile("assets/cat.ppm") 22 | 23 | shape = Shape[4, 12, 16].make( 24 | [vec3(0f, 0, 0), vec3(1f, 0, 0), vec3(1f, 1, 0), vec3(0f, 1, 0)], 25 | [vec4(0f, 0, 0, 1), vec4(0f, 0, 0, 1), vec4(0f, 0, 0, 1), vec4(0f, 0, 0, 1)], 26 | "assets/shapes/id/id.vert", 27 | "assets/shapes/id/id.frag" 28 | ) 29 | 30 | sea_tex.use do (texture: var BindedTexture): 31 | texture.format = tfRGB 32 | texture.pixels = (data: sea.data, width: sea.col.uint, height: sea.row.uint) 33 | 34 | cat_tex.use do (texture: var BindedTexture): 35 | texture.format = tfRGB 36 | texture.pixels = (data: cat.data, width: cat.col.uint, height: cat.row.uint) 37 | 38 | var v: float32 = 1.0 39 | naguContext.update: 40 | naguContext.clear(toColor("#ffffff")) 41 | sea_tex.use do (texture: var BindedTexture): 42 | texture.draw() 43 | texture.setModelMatrix(mat4(1f).rotate(v, vec3(1f, 1, 1))) 44 | cat_tex.use do (texture: var BindedTexture): 45 | texture.draw() 46 | texture.setModelMatrix(mat4(1f).rotate(2*v, vec3(1f, 0, 1))) 47 | v += 0.01 48 | -------------------------------------------------------------------------------- /nagu/README-ja.md: -------------------------------------------------------------------------------- 1 | # nagu - **N**im **A**bstract Open**G**L **U**tility 2 | naguはステートレスにOpenGLを扱うためのNimble Packageです。 3 | シェーダのコンパイルやリンク、ProgramやVAO、VBO作成のためのユーティリティプロシージャやオブジェクト型を提供します。また、naguはOpenGL開発における**状態**を裏で機械的に管理するため開発者が考えるべきタスクを十分に減らすことができます。 4 | naguは[mock up](https://www.github.com/mock-up/mock-up)の依存ライブラリとなっており、これが開発され続ける限りnaguも開発されますが、一方でmock upに必要ない機能は自発的に実装されません。気軽なIssue、Pull Requestを歓迎しています。 5 | 6 | ## インストール 7 | Nim 1.6.2以上をサポートしており、Nimbleを使ってインストールできます。 8 | 9 | ```zsh 10 | $ nimble install nagu 11 | ``` 12 | 13 | ## サンプル 14 | ```nim 15 | proc main = 16 | let w = initializeOpenGL(500, 500) 17 | if w == nil: 18 | quit(-1) 19 | discard w.setKeyCallback(keyProc) 20 | w.makeContextCurrent() 21 | 22 | let 23 | vertex_shader = shaderObject.make(soVertex, "assets/basic.vert") 24 | fragment_shader = shaderObject.make(soFragment, "assets/basic.frag") 25 | program = programObject.make(vertex_shader, fragment_shader, @["VertexPosition", "VertexColor"], @["mvpMatrix"]) 26 | vao_handle = VAO.make() 27 | 28 | var t1 = Triangle.make( 29 | Position.init(-1.0, -1.3, 0.0), 30 | "#0000ff".toColor, 31 | Position.init(-0.2, -1.3, 0.0), 32 | "#00ff00".toColor, 33 | Position.init(-0.2, -0.2, 0.0), 34 | "#ff0000".toColor 35 | ) 36 | 37 | program.correspond(t1, "VertexPosition", "VertexColor", size=3) 38 | 39 | let mvp = [ 40 | 1/sqrt(2f), -1/sqrt(2f), 0, 0, 41 | 1/sqrt(2f), 1/sqrt(2f), 0, 0, 42 | 0, 0, 1, 0, 43 | 0, 0, 0, 1 44 | ] 45 | program.applyMatrix("mvpMatrix", mvp) 46 | 47 | w.mainLoop: 48 | clear("#ffffff".toColor) 49 | t1 += 0.001 50 | vao_handle.draw() 51 | ``` -------------------------------------------------------------------------------- /src/mockuppkg/opengl.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl as gl import nil 2 | from glm import nil 3 | import nimgl/glfw 4 | #from nimgl/glfw import nil 5 | 6 | proc initGlfw = 7 | doAssert glfw.glfwInit() 8 | glfw.glfwWindowHint(glfw.GLFWContextVersionMajor, 3) 9 | glfw.glfwWindowHint(glfw.GLFWContextVersionMinor, 3) 10 | glfw.glfwWindowHint(glfw.GLFWOpenglForwardCompat, glfw.GLFW_TRUE) 11 | glfw.glfwWindowHint(glfw.GLFWOpenglProfile, glfw.GLFW_OPENGL_CORE_PROFILE) 12 | glfw.glfwWindowHint(glfw.GLFWResizable, glfw.GLFW_FALSE) 13 | 14 | proc keyProc(window: glfw.GLFWWindow, key: int32, scancode: int32, action: int32, mods: int32): void {.cdecl.} = 15 | if key == GLFWKey(Escape) and action == glfw.GLFWPress: 16 | glfw.setWindowShouldClose(window, true) 17 | 18 | proc initializeOpenGL* (width, height: int32): glfw.GLFWWindow = 19 | initGlfw() 20 | result = glfw.glfwCreateWindow(width, height, "mock up", nil, nil) 21 | doAssert result != nil 22 | discard result.setKeyCallback(keyProc) 23 | result.makeContextCurrent() 24 | doAssert gl.glInit() 25 | gl.glEnable(gl.GL_TEXTURE_2D) 26 | 27 | proc toRGB* (color: tuple[r, g, b: uint]): glm.Vec4f = 28 | result = glm.vec4f( 29 | color.r.float32 / 255, 30 | color.g.float32 / 255, 31 | color.b.float32 / 255, 32 | 1.0 33 | ) 34 | 35 | template clearColorRGB* (rgb: glm.Vec3[float32], alpha: float32) = 36 | glClearColor(rgb.r, rgb.b, rgb.b, alpha) 37 | 38 | template genVertexArrays*(n: gl.GLsizei): var uint32 = 39 | var vao: uint32 40 | glGenVertexArrays(n, vao.addr) 41 | vao 42 | 43 | template genBuffers* (n: gl.GLsizei): var uint32 = 44 | var vbo: uint32 45 | glGenBuffers(n, vbo.addr) 46 | vbo -------------------------------------------------------------------------------- /muml/src/muml/deserializer.nim: -------------------------------------------------------------------------------- 1 | import std/macros, json, builder 2 | import builtin/commonTypes 3 | import uuids 4 | 5 | const BuiltInElements* = [ 6 | "Video", # "image", "audio", 7 | "Triangle", 8 | "Rectangle", "Text" 9 | ] 10 | 11 | proc caseOfApplyParserAST (elements: seq[string]): NimNode = 12 | result = nnkCaseStmt.newTree( 13 | nnkDotExpr.newTree( 14 | nnkBracketExpr.newTree( 15 | newIdentNode("element"), 16 | newLit("type") 17 | ), 18 | newIdentNode("getStr") 19 | ) 20 | ) 21 | for element in elements: 22 | result.add nnkOfBranch.newTree( 23 | newLit(element), 24 | nnkStmtList.newTree( 25 | nnkCall.newTree( 26 | newIdentNode("parse_" & element), 27 | newIdentNode("element") 28 | ) 29 | ) 30 | ) 31 | result.add nnkElse.newTree( 32 | quote do: 33 | raise newException(Exception, "not found tag") 34 | ) 35 | 36 | macro mumlDeserializer* (customElements: varargs[untyped]): untyped = 37 | var 38 | elements = @BuiltInElements 39 | elementsAST = nnkBracket.newTree() 40 | for customElement in customElements: 41 | elements.add $customElement 42 | for element in elements: 43 | elementsAST.add newLit($element) 44 | 45 | let 46 | procName = newIdentNode("deserialize") 47 | elementIdent = newIdentNode("element") 48 | definedElementsIdent = newIdentNode("DefinedElements") 49 | caseOfApplyParser = caseOfApplyParserAST(elements) 50 | 51 | result = quote do: 52 | const `definedElementsIdent`* = `elementsAST` 53 | proc `procName`* (muml: mumlNode): seq[mumlRootElement] = 54 | for `elementIdent` in muml.contents: 55 | var mumlObj = `caseOfApplyParser` 56 | mumlObj.id = genUuid() 57 | result.add mumlObj 58 | -------------------------------------------------------------------------------- /nagu/src/nagu/old_vbo.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/vbo.nim defines the VBO type and procedures related to its for abstracting OpenGL VBO. 2 | 3 | from nimgl/opengl import nil 4 | from program import Program 5 | 6 | type 7 | vboObj [I: static[int]; T] = object 8 | id: opengl.GLuint 9 | data: array[I, T] 10 | 11 | vboRef* [I: static[int]; T] = ref vboObj[I, T] 12 | 13 | VBO* = object 14 | 15 | proc init* [I, T] (_: typedesc[vboRef[I, T]]): vboRef[I, T] = 16 | ## Initializes vbo. 17 | result = vboRef[I, T]() 18 | opengl.glGenBuffers(1, result.id.addr) 19 | 20 | func id* [I, T] (vbo: vboRef[I, T]): opengl.GLuint = 21 | result = vbo.id 22 | 23 | func data* [I, T] (vbo: vboRef[I, T]): array[I, T] = 24 | result = vbo.data 25 | 26 | proc bindArrayBuffer[I, T] (vbo: vboRef[I, T]): vboRef[I, T] = 27 | result = vbo 28 | opengl.glBindBuffer(opengl.GL_ARRAY_BUFFER, vbo.id) 29 | 30 | proc assignArray[I, T] (vbo: var vboRef[I, T], data: array[I, T]) = 31 | vbo.data = data 32 | discard vbo.bindArrayBuffer() 33 | opengl.glBufferData(opengl.GL_ARRAY_BUFFER, vbo.data.len * sizeof(vbo.data[0]), vbo.data[0].addr, opengl.GL_STATIC_DRAW) 34 | 35 | proc `:=`* [I, T] (vbo: var vboRef[I, T], data: array[I, T]) = 36 | ## Assigns `data` to `vbo`. 37 | vbo.assignArray(data) 38 | 39 | proc make* [I: static[int]; T] (_: typedesc[VBO], data: array[I, T]): vboRef[I, T] = 40 | ## Makes vbo and assigns `data` to it. 41 | result = vboRef[I, T].init() 42 | result.data = data 43 | result := result.data 44 | 45 | proc correspond* [I] (program: Program, vbo: vboRef[I, float32], name: string, size: int) = 46 | ## Ties `vbo` to `program`. 47 | let index = opengl.GLuint(program.nameToIndex[name]) 48 | opengl.glEnableVertexAttribArray(index) 49 | opengl.glBindBuffer(opengl.GL_ARRAY_BUFFER, vbo.id) 50 | opengl.glVertexAttribPointer(index, opengl.GLint(size), opengl.EGL_FLOAT, false, 0, nil) 51 | -------------------------------------------------------------------------------- /src/mockuppkg/fonts.nim: -------------------------------------------------------------------------------- 1 | import freetype, fp, math, unicode 2 | 3 | type 4 | mockupFont* = object 5 | face: FT_Face 6 | slot: FT_GlyphSlot 7 | 8 | proc init* : Either[string, FT_Library] = 9 | var library: FT_Library 10 | let err_code = FT_Init_FreeType(library) 11 | if err_code == 0: 12 | result = Right[string, FT_Library](library) 13 | else: 14 | result = Left[string, FT_Library]("FreeFontの初期化に失敗しました") 15 | 16 | proc getFont* (library: FT_Library, path: string): mockupFont = 17 | discard FT_New_Face(library, path, 0, result.face) 18 | discard FT_Set_Pixel_Sizes(result.face, 0, 48) 19 | result.slot = result.face.glyph 20 | 21 | # seq[int]の各要素に10ピクセルくらい開けて継ぎ足していけば実装できそう 22 | proc getText* (font: mockupFont, text: string): seq[seq[int]] = 23 | var index = 0 24 | result = @[] 25 | while index < text.runeLen: 26 | discard FT_Load_Glyph(font.face, FT_Get_Char_Index(font.face, cast[culong](text.toRunes[index])), FT_LOAD_RENDER) 27 | let bitmap = font.face.glyph.bitmap 28 | var charas: seq[seq[int]] = @[] 29 | for row in 0..time_baseに相当するものに修正 38 | frame.ffmpeg_frame[].pts = av_rescale_q(frame_number, frame.timeBase, frame.timeBase) 39 | 40 | proc init* (_: typedesc[VideoFrame], width, height, fps: int32): VideoFrame = 41 | result = VideoFrame() 42 | result.ffmpeg_frame = av_frame_alloc() 43 | if result.ffmpeg_frame.isNil: 44 | raise newException(FFmpegDefect, "could not allocate video frame") 45 | result.format = vff_RGBA 46 | result.width = width 47 | result.height = height 48 | result.fps = fps 49 | 50 | if av_frame_get_buffer(result[].ffmpeg_frame, 32) < 0: 51 | raise newException(FFmpegDefect, "バッファの割り当てに失敗しました") 52 | 53 | proc init* (_: typedesc[VideoFrame], encoder: VideoEncoder): VideoFrame = 54 | result = VideoFrame() 55 | result.type.privateAccess() 56 | result.ffmpeg_frame = av_frame_alloc() 57 | if result.ffmpeg_frame.isNil: 58 | raise newException(FFmpegDefect, "could not allocate video frame") 59 | result.format = encoder.format 60 | result.width = encoder.width 61 | result.height = encoder.height 62 | result.timebase = encoder.timeBase 63 | 64 | if av_frame_get_buffer(result.ffmpeg_frame, 0) < 0: 65 | raise newException(FFmpegDefect, "could not allocate the video frame data") -------------------------------------------------------------------------------- /mockup_ffmpeg/src/mockup_ffmpeg/results.nim: -------------------------------------------------------------------------------- 1 | import std/macros 2 | 3 | # {.experimental: "strictCaseObjects".} 4 | {.experimental: "caseStmtMacros".} 5 | 6 | type 7 | ResultKind* = enum 8 | rOk, rErr 9 | 10 | Result* [T, E] = object 11 | case kind*: ResultKind 12 | of rOk: 13 | ok*: T 14 | else: 15 | err*: E 16 | 17 | proc ok* [T, E] (_: typedesc[Result[T, E]], value: T): Result[T, E] = 18 | result = Result[T, E](kind: rOk, ok: value) 19 | 20 | proc err* [T, E] (_: typedesc[Result[T, E]], err: E): Result[T, E] = 21 | result = Result[T, E](kind: rErr, err: err) 22 | 23 | proc ok2* [T, E] (res: Result[T, E], value: T): Result[T, E] = 24 | result = Result[T, E](kind: ResultKind.rOk, ok: value) 25 | 26 | proc err2* [T, E] (res: Result[T, E], err: E): Result[T, E] = 27 | result = Result[T, E](kind: ResultKind.rErr, err: err) 28 | 29 | template ok* (value: untyped): untyped = 30 | result.ok2(value) 31 | 32 | template err* (err: untyped): untyped = 33 | result.err2(err) 34 | 35 | proc unwrap* [T, E] (res: Result[T, E]): T = 36 | if res.kind == rOk: 37 | result = res.ok 38 | else: 39 | raise newException(Defect, "") 40 | 41 | proc error* [T, E] (res: Result[T, E]): E = 42 | if res.kind == rErr: 43 | result = res.err 44 | else: 45 | raise newException(Defect, "") 46 | 47 | macro `case`* (ast: Result): untyped = 48 | result = ast 49 | result[0] = nnkStmtListExpr.newTree( 50 | nnkLetSection.newTree( 51 | nnkIdentDefs.newTree( 52 | newIdentNode("resInCaseStatement"), 53 | newEmptyNode(), 54 | result[0], 55 | ) 56 | ), 57 | nnkDotExpr.newTree( 58 | newIdentNode("resInCaseStatement"), 59 | newIdentNode("kind") 60 | ) 61 | ) 62 | for i in 1..2: 63 | if $result[i][0][0] == "ok": 64 | result[i][1].insert(0, 65 | nnkLetSection.newTree( 66 | nnkIdentDefs.newTree( 67 | newIdentNode(result[i][0][1].strVal), 68 | newEmptyNode(), 69 | nnkCall.newTree( 70 | nnkDotExpr.newTree( 71 | newIdentNode("resInCaseStatement"), 72 | newIdentNode("unwrap") 73 | ) 74 | ) 75 | ) 76 | ) 77 | ) 78 | result[i][0] = newIdentNode("rOk") 79 | elif $result[i][0][0] == "err": 80 | result[i][1].insert(0, 81 | nnkLetSection.newTree( 82 | nnkIdentDefs.newTree( 83 | newIdentNode(result[i][0][1].strVal), 84 | newEmptyNode(), 85 | nnkCall.newTree( 86 | nnkDotExpr.newTree( 87 | newIdentNode("resInCaseStatement"), 88 | newIdentNode("error") 89 | ) 90 | ) 91 | ) 92 | ) 93 | ) 94 | result[i][0] = newIdentNode("rErr") 95 | 96 | template `?`* [T, E] (res: Result[T, E]): untyped = 97 | let r = res 98 | if r.kind == rOk: 99 | r.ok 100 | else: 101 | return err(r.err) 102 | -------------------------------------------------------------------------------- /nagu/src/nagu/shader.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/shader.nim defines the ShaderObject type and procedures related to its for abstracting OpenGL shader. 2 | 3 | from nimgl/opengl import nil 4 | from opengl as naguOpengl import OpenGLDefect 5 | 6 | type 7 | ShaderObjectObj = object 8 | id: uint 9 | 10 | ShaderObject* = ref ShaderObjectObj 11 | ## The ShaderObject type representations OpenGL shader object. 12 | 13 | ShaderObjectKind* = enum 14 | ## The ShaderObjectKind type representations the kind of OpenGL shader. 15 | soVertex, soFragment, soGeometry, soTessEvaluation, soTessControl 16 | 17 | ShaderDefect* = object of OpenGLDefect 18 | ## Raised by something with OpenGL shaders. 19 | 20 | ShaderCreationDefect* = object of ShaderDefect 21 | ## Raised by creating OpenGL shaders. 22 | 23 | ShaderFailedCompilationDefect* = object of ShaderDefect 24 | ## Raised by failed compilation OpenGL shaders. 25 | 26 | func convertGLExpression* (kind: ShaderObjectKind): opengl.GLenum = 27 | case kind: 28 | of soVertex: opengl.GL_VERTEX_SHADER 29 | of soFragment: opengl.GL_FRAGMENT_SHADER 30 | of soGeometry: opengl.GL_GEOMETRY_SHADER 31 | of soTessEvaluation: opengl.GL_TESS_EVALUATION_SHADER 32 | of soTessControl: opengl.GL_TESS_CONTROL_SHADER 33 | 34 | proc init* (_: typedesc[ShaderObject], kind: ShaderObjectKind): ShaderObject {.raises: [ShaderCreationDefect, Exception].} = 35 | ## Initializes ShaderObject by `kind`. 36 | let shader = opengl.glCreateShader(kind.convertGLExpression) 37 | if shader == opengl.GLuint(0): 38 | # TODO: OpenGLからログを取って原因を出力する(よくバグるので, なぜ?) or 例外を切り替える 39 | raise newException(ShaderCreationDefect, "Failed to create the shader for some reason.") 40 | result = ShaderObject(id: shader) 41 | 42 | func id* (shader: ShaderObject): uint = 43 | ## Gets ShaderObjectObj id. 44 | result = shader.id 45 | 46 | proc log* (shader: ShaderObject): string = 47 | ## Gets logs about OpenGL shaders. 48 | var log_length: opengl.GLint 49 | opengl.glGetShaderiv(opengl.GLuint(shader.id), opengl.GL_INFO_LOG_LENGTH, log_length.addr) 50 | if log_length.int > 0: 51 | var 52 | log: cstring 53 | written_length: opengl.GLsizei 54 | opengl.glGetShaderInfoLog(opengl.GLuint(shader.id), log_length, written_length.addr, log) 55 | result = $log 56 | 57 | proc load* (shader: ShaderObject, path: string): ShaderObject = 58 | ## Loads the shader from `path`. 59 | result = shader 60 | var shader_code: cstring = block: 61 | var shader_file = open(path) 62 | defer: 63 | shader_file.close() 64 | shader_file.readAll().cstring 65 | opengl.glShaderSource(opengl.GLuint(result.id), opengl.GLsizei(1), shader_code.addr, nil) 66 | 67 | proc successCompile (shader: ShaderObject): bool = 68 | var res: opengl.GLint 69 | opengl.glGetShaderiv(opengl.GLuint(shader.id), opengl.GL_COMPILE_STATUS, res.addr) 70 | result = res == opengl.GLint(opengl.GL_TRUE) 71 | 72 | proc compile* (shader: ShaderObject): ShaderObject {.raises: [ShaderFailedCompilationDefect, Exception].} = 73 | ## Compiles the shader in `shader`. 74 | result = shader 75 | opengl.glCompileShader(opengl.GLuint(result.id)) 76 | if not result.successCompile: 77 | raise newException(ShaderFailedCompilationDefect, "Failed to compile the shader: ") # & result.log) 78 | 79 | proc make* (_: typedesc[ShaderObject], kind: ShaderObjectKind, path: string): ShaderObject = 80 | ## Makes a compiled ShaderObject from `path`. 81 | result = ShaderObject 82 | .init(kind) 83 | .load(path) 84 | .compile() 85 | -------------------------------------------------------------------------------- /nagu/src/nagu/glfw.nim: -------------------------------------------------------------------------------- 1 | import nimgl/glfw 2 | from nimgl/opengl import glInit, glEnable, GL_TEXTURE_2D 3 | from opengl as naguOpengl import OpenGLDefect 4 | from color import Color, rgb 5 | from std/exitprocs import addExitProc 6 | 7 | type 8 | NaguContextObj* = object 9 | glfw_initialized: bool 10 | gl_initialized: bool 11 | window: glfw.GLFWWindow 12 | clearColor*: Color 13 | 14 | NaguContext* = ref NaguContextObj 15 | ## Represents GLFW context 16 | 17 | GLFWDefect* = object of Defect 18 | ## Base defect type for exceptions for GLFW 19 | 20 | GLFWInitializeDefect* = object of GLFWDefect 21 | ## Raised by failed initializing GLFW 22 | 23 | WindowCreationDefect* = object of GLFWDefect 24 | ## Raised by failed creation GLFW Window 25 | 26 | OpenGLInitializeDefect* = object of OpenGLDefect 27 | ## Raised by failed initializing OpenGL 28 | 29 | proc init (_: typedesc[NaguContext]): NaguContext = 30 | if not glfw.glfwInit(): 31 | raise newException(GLFWInitializeDefect, "Failed initializing GLFW") 32 | result = NaguContext( 33 | glfw_initialized: true, 34 | gl_initialized: false, 35 | window: nil 36 | ) 37 | glfw.glfwWindowHint(glfw.GLFWContextVersionMajor, 3) 38 | glfw.glfwWindowHint(glfw.GLFWContextVersionMinor, 3) 39 | glfw.glfwWindowHint(glfw.GLFWOpenglForwardCompat, glfw.GLFW_TRUE) 40 | glfw.glfwWindowHint(glfw.GLFWOpenglProfile, glfw.GLFW_OPENGL_CORE_PROFILE) 41 | glfw.glfwWindowHint(glfw.GLFWResizable, glfw.GLFW_FALSE) 42 | 43 | proc initWindow (width, height: int32, title: string, monitor: glfw.GLFWMonitor, share: glfw.GLFWWindow, icon: bool): glfw.GLFWWindow = 44 | result = glfw.glfwCreateWindow(width, height, title, monitor, share, icon) 45 | if result == nil: 46 | raise newException(WindowCreationDefect, "Failed creation Window") 47 | 48 | proc initOpenGL: bool = 49 | result = glInit() 50 | if not result: 51 | raise newException(OpenGLInitializeDefect, "Failed initializing OpenGL Context") 52 | 53 | proc attention (window: glfw.GLFWWindow) = 54 | glfw.makeContextCurrent(window) 55 | 56 | proc keyProc(window: glfw.GLFWWindow, key: int32, scancode: int32, action: int32, mods: int32): void {.cdecl.} = 57 | if key == GLFWKey(Escape) and action == glfw.GLFWPress: 58 | glfw.setWindowShouldClose(window, true) 59 | 60 | proc setup* (width: int32 = 500, height: int32 = 500, title: string = "window"): NaguContext = 61 | ## Initializes OpenGL context and gets GLFW Window. 62 | result = NaguContext.init() 63 | addExitProc(proc () {.closure.} = glfw.glfwTerminate()) 64 | result.window = initWindow(width, height, title, nil, nil, false) 65 | result.window.attention() 66 | discard result.window.setKeyCallback(keyProc) 67 | result.gl_initialized = initOpenGL() 68 | glEnable(GL_TEXTURE_2D) 69 | 70 | proc isWindowOpen (context: NaguContext): bool = 71 | result = not context.window.windowShouldClose 72 | 73 | proc destroyWindow (context: NaguContext) = 74 | context.window.destroyWindow() 75 | 76 | proc pollEventsAndSwapBuffers (context: NaguContext) = 77 | glfw.glfwPollEvents() 78 | context.window.swapBuffers() 79 | 80 | template update* (context: NaguContext, body: untyped) = 81 | ## The main-loop in GLFW Window. 82 | while context.isWindowOpen: 83 | body 84 | context.pollEventsAndSwapBuffers() 85 | context.destroyWindow() 86 | 87 | proc clear* (context: var NaguContext, color: Color, alpha: float32 = 0f) = 88 | ## Fills a window with (`color`, `alpha`) 89 | if not (context.clearColor == color): 90 | let (r, g, b) = color.rgb 91 | opengl.glClearColor(r, g, b, alpha) 92 | opengl.glClear(opengl.GL_COLOR_BUFFER_BIT) 93 | 94 | export isWindowOpen, pollEventsAndSwapBuffers, destroyWindow 95 | -------------------------------------------------------------------------------- /src/mockuppkg/shaders.nim: -------------------------------------------------------------------------------- 1 | import utils 2 | from nimgl/opengl as gl import nil 3 | 4 | type 5 | FilterKind* = enum 6 | ColorInversionFilter = "colorInversionFilter" 7 | IdFilter = "idFilter" 8 | 9 | MockupProgram = object 10 | 11 | MockupFilter* = object 12 | kind*: FilterKind 13 | 14 | proc newFilter* (kind: FilterKind): MockupFilter = 15 | result.kind = kind 16 | 17 | template checkResult (id: uint32, checkType: untyped, checkExeType: gl.GLenum) = 18 | var compileResult: int32 19 | gl.`glGet checkType iv`(id, checkExeType, compileResult.addr) 20 | if compileResult != gl.GL_TRUE.ord: 21 | var infoLogLength: int32 22 | gl.`glGet checkType iv`(id, gl.GL_INFO_LOG_LENGTH, infoLogLength.addr) 23 | if infoLogLength > 0: 24 | var message: cstring = newString(infoLogLength) 25 | gl.`glGet checkType InfoLog`(id, infoLogLength, nil, message[0].addr) 26 | if message[0] != '\0': 27 | echo "<" & astToStr(checkType InfoLog) & ">" 28 | echo message 29 | quit() 30 | 31 | template compileShaderAux (shaderType: gl.GLenum, shaderCode: string): uint32 = 32 | var id = gl.glCreateShader(shaderType) 33 | var code: cstring = shaderCode 34 | gl.glShaderSource(id, 1'i32, code.addr, nil) 35 | gl.glCompileShader(id) 36 | # id.checkResult(Shader, gl.GL_COMPILE_STATUS) 37 | id 38 | 39 | template compileShader (shaderType: gl.GLenum, path: static string): uint32 = 40 | #echo path 41 | #echo static staticRead(path) 42 | compileShaderAux(shaderType, static staticRead(path)) 43 | 44 | # template compileShader (shaderType: gl.GLenum, path: string): uint32 = 45 | # echo path 46 | # echo readFile(path) 47 | # compileShaderAux(shaderType, readFile(path)) 48 | 49 | template linkProgramTemplate* (shaders: varargs[uint32]): uint32 = 50 | var programID: uint32 = gl.glCreateProgram() 51 | for shader in shaders: 52 | gl.glAttachShader(programID, shader) 53 | var a: GLint = 0 54 | gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS, a.addr) 55 | # echo "linkProgramTemplate: ", a 56 | var log: array[100000, char] 57 | var size: GLint = 0 58 | gl.glGetShaderInfoLog(shader, 100000, size.addr, log.addr) 59 | for ch in log: 60 | stdout.write ch 61 | 62 | gl.glLinkProgram(programID) 63 | # programID.checkResult(Program, gl.GL_LINK_STATUS) 64 | for shader in shaders: 65 | gl.glDetachShader(programID, shader) 66 | gl.glDeleteShader(shader) 67 | programID 68 | 69 | template linkProgram* (vertex_shader, fragment_shader: string): uint32 = 70 | linkProgramTemplate( 71 | compileShader( 72 | gl.GL_VERTEX_SHADER, 73 | vertex_shader 74 | ), 75 | compileShader( 76 | gl.GL_FRAGMENT_SHADER, 77 | fragment_shader 78 | ) 79 | ) 80 | 81 | type 82 | ShaderKind = enum 83 | SKTexture = "textures" 84 | 85 | template fragmentPath (shaderKind: ShaderKind, filterKind: FilterKind): string = 86 | ~"shaders/" & $shaderKind & "/fragment/" & $filterKind & ".glsl" 87 | 88 | template vertexPath (shaderKind: ShaderKind, filterKind: FilterKind): string = 89 | ~"shaders/" & $shaderKind & "/vertex/" & $filterKind & ".glsl" 90 | 91 | template linkTextureProgram* (filter: FilterKind): uint32 = 92 | var id: uint32 = linkProgram( 93 | ~("shaders/textures/vertex/" & $filter & ".glsl"), 94 | ~("shaders/textures/fragment/" & $filter & ".glsl") 95 | ) 96 | id 97 | 98 | template linkTriangleProgram* (filter: FilterKind): uint32 = 99 | var id: uint32 = linkProgram( 100 | ~("shaders/triangles/vertex/" & $filter & ".glsl"), 101 | ~("shaders/triangles/fragment/" & $filter & ".glsl") 102 | ) 103 | id -------------------------------------------------------------------------------- /experiment/src/encode_video.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | import std/[strformat, streams] 3 | 4 | proc getError (num: int): string = 5 | var buf: array[200, cchar] 6 | discard av_strerror(num.cint, buf[0].addr, 200.csize_t) 7 | for c in buf: 8 | if c == '\x00': return 9 | result.add c 10 | 11 | proc encode (enc_ctx: ptr AVCodecContext, frame: ptr AVFrame, pkt: ptr AVPacket, outfile: FileStream) = 12 | var ret: int 13 | if not frame.isNil: 14 | echo &"Send frame {frame[].pts}" 15 | ret = avcodec_send_frame(enc_ctx, frame) 16 | if ret < 0: 17 | raise newException(Defect, "Error sending a frame for encoding") 18 | 19 | while ret >= 0: 20 | ret = avcodec_receive_packet(enc_ctx, pkt) 21 | echo getError(ret) 22 | 23 | if ret < 0: 24 | return 25 | # if ret == -11: # or ret == AVERROR_EOF() 26 | # return 27 | # elif ret < 0: 28 | # raise newException(Defect, "Error during encoding") 29 | 30 | echo &"Write packet {pkt[].pts} (size={pkt[].size})" 31 | outfile.writeData(pkt[].data, pkt[].size) 32 | av_packet_unref(pkt) 33 | 34 | proc encodeVideo* = 35 | const 36 | FileName = "dummy.h264" 37 | CodecName = "libx264" 38 | var 39 | codec: ptr AVCodec 40 | c: ptr AVCodecContext = nil 41 | ret: int 42 | frame: ptr AVFrame 43 | pkt: ptr AVPacket 44 | endcode: array[4, uint8] = [0'u8, 0, 1, 0xb7] 45 | 46 | codec = avcodec_find_encoder_by_name(CodecName) 47 | if codec.isNil: 48 | raise newException(Defect, &"Codec {CodecName} not found") 49 | 50 | c = avcodec_alloc_context3(codec) 51 | if c.isNil: 52 | raise newException(Defect, "Could not allocate video codec context") 53 | 54 | pkt = av_packet_alloc(); 55 | if pkt.isNil: 56 | quit(1) 57 | 58 | c[].bit_rate = 400000; 59 | c[].width = 352; 60 | c[].height = 288; 61 | c[].time_base = av_make_q(1, 25) 62 | c[].framerate = av_make_q(25, 1) 63 | c[].gop_size = 10 64 | c[].max_b_frames = 1 65 | c[].pix_fmt = AV_PIX_FMT_YUV420P 66 | 67 | if codec[].id == AV_CODEC_ID_H264: 68 | discard av_opt_set(c[].priv_data, "preset", "slow", 0) 69 | 70 | ret = avcodec_open2(c, codec, nil) 71 | if ret < 0: 72 | raise newException(Defect, "Could not open codec: av_err2str(ret)") 73 | 74 | var f = newFileStream(FileName, FileMode.fmReadWrite) 75 | 76 | frame = av_frame_alloc() 77 | if frame.isNil: 78 | raise newException(Defect, "Could not allocate video frame") 79 | 80 | frame[].format = c[].pix_fmt.int32 81 | frame[].width = c[].width 82 | frame[].height = c[].height 83 | 84 | ret = av_frame_get_buffer(frame, 0) 85 | if ret < 0: 86 | raise newException(Defect, "Could not allocate the video frame data") 87 | 88 | for i in 0 ..< 250: 89 | stdout.flushFile() 90 | ret = av_frame_make_writable(frame) 91 | if ret < 0: 92 | quit(1) 93 | 94 | for y in 0 ..< c[].height: 95 | for x in 0 ..< c[].width: 96 | cast[ptr uint8](cast[int](frame[].data[0]) + y * frame[].linesize[0] + x)[] = (x + y + i).uint8 97 | 98 | for y in 0 ..< c[].height div 2: 99 | for x in 0 ..< c[].width div 2: 100 | cast[ptr uint8](cast[int](frame[].data[1]) + y * frame[].linesize[1] + x)[] = (128 + y + i * 4).uint8 101 | cast[ptr uint8](cast[int](frame[].data[2]) + y * frame[].linesize[2] + x)[] = (64 + x + i * 3).uint8 102 | 103 | frame[].pts = i 104 | encode(c, frame, pkt, f) 105 | 106 | encode(c, nil, pkt, f) 107 | 108 | if codec[].id == AV_CODEC_ID_MPEG1VIDEO or codec[].id == AV_CODEC_ID_MPEG2VIDEO: 109 | f.writeData(endcode[0].addr, sizeof(endcode)) 110 | 111 | f.close() 112 | 113 | avcodec_free_context(c.addr) 114 | av_frame_free(frame.addr) 115 | av_packet_free(pkt.addr) 116 | -------------------------------------------------------------------------------- /muml/src/muml/utils.nim: -------------------------------------------------------------------------------- 1 | proc removeDoubleQuotation* (str: string): string = 2 | result = str[0..str.len-1] 3 | 4 | # proc getFloatValueProperty* (muml: mumlNode, name: string): mumlFloatRange = 5 | # result = (start: muml["value"]["start"][name].getFloat, `end`: muml["value"]["end"][name].getFloat) 6 | 7 | # proc getFrame* (muml: mumlNode): mumlIntRange = 8 | # result = (start: muml["frame"]["start"].getInt, `end`: muml["frame"]["end"].getInt) 9 | 10 | # proc getNumberValue* (muml: mumlNode): seq[mumlValue] = 11 | # result = @[] 12 | # case muml.kind: 13 | # of JInt, JFloat: 14 | # var value = mumlValue() 15 | # value.frame = (-1, -1) 16 | # value.value = (muml.getFloat, NAN) 17 | # result.add value 18 | # of JArray: 19 | # for item in muml.items: 20 | # var value = mumlValue() 21 | # value.frame = item.getFrame 22 | # value.value = item.getFloatValueProperty("value") 23 | # result.add value 24 | # else: raise newException(Exception, "invalid value") 25 | 26 | # proc getPosition* (muml: mumlNode): seq[muml2DPosition] = 27 | # result = @[] 28 | # case muml.kind: 29 | # of JObject: 30 | # var position = muml2DPosition() 31 | # position.frame = (-1, -1) 32 | # position.x = (muml["x"].getFloat, NAN) 33 | # position.y = (muml["y"].getFloat, NAN) 34 | # result.add position 35 | # of JArray: 36 | # for item in muml.items: 37 | # var position = muml2DPosition() 38 | # position.frame = item.getFrame 39 | # position.x = item.getFloatValueProperty("x") 40 | # position.y = item.getFloatValueProperty("y") 41 | # result.add position 42 | # else: raise newException(Exception, "invalid value") 43 | 44 | # proc getScale* (muml: mumlNode): seq[mumlScale] = 45 | # result = @[] 46 | # case muml.kind: 47 | # of JObject: 48 | # var scale = mumlScale() 49 | # scale.frame = (-1, -1) 50 | # scale.width = (muml["width"].getFloat, NAN) 51 | # scale.height = (muml["height"].getFloat, NAN) 52 | # result.add scale 53 | # of JArray: 54 | # for item in muml: 55 | # var scale = mumlScale() 56 | # scale.width = item.getFloatValueProperty("width") 57 | # scale.height = item.getFloatValueProperty("height") 58 | # result.add scale 59 | # else: raise newException(Exception, "invalid value") 60 | 61 | # proc getRGB* (muml: mumlNode): seq[mumlRGB] = 62 | # result = @[] 63 | # case muml.kind: 64 | # of JObject: 65 | # var color = mumlRGB() 66 | # color.frame = (-1, -1) 67 | # var 68 | # red = muml["red"].getFloat 69 | # green = muml["green"].getFloat 70 | # blue = muml["blue"].getFloat 71 | # color.color = newRGB(red.tBinaryRange, green.tBinaryRange, blue.tBinaryRange) 72 | # result.add color 73 | # of JArray: 74 | # for item in muml: 75 | # var color = mumlRGB() 76 | # color.frame = (-1, -1) 77 | # var 78 | # red = muml["red"].getFloat 79 | # green = muml["green"].getFloat 80 | # blue = muml["blue"].getFloat 81 | # color.color = newRGB(red.tBinaryRange, green.tBinaryRange, blue.tBinaryRange) 82 | # result.add color 83 | # else: raise newException(Exception, "invalid value") 84 | 85 | # proc getFilters* (muml: mumlNode): seq[mumlFilter] = 86 | # result = @[] 87 | # case muml.kind: 88 | # of JArray: 89 | # for item in muml: 90 | # for key, value in item: 91 | # case key: 92 | # of "colorInversion": 93 | # result.add mumlFilter( 94 | # kind: colorInversion, 95 | # red: value["red"].getBool, 96 | # green: value["green"].getBool, 97 | # blue: value["blue"].getBool 98 | # ) 99 | # of "grayScale": 100 | # result.add mumlFilter( 101 | # kind: grayScale, 102 | # value: value["value"].getFloat 103 | # ) 104 | # else: raise newException(Exception, "invalid value") -------------------------------------------------------------------------------- /nagu/src/nagu/vbo.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl import nil 2 | import strformat 3 | import utils 4 | 5 | type 6 | VBOObj [binded: static bool, I: static int, T] = object 7 | id: opengl.GLuint 8 | target: VBOTarget 9 | data: array[I, T] 10 | # 安易に公開すると`data=`が死に、OpenGL命令が実行されなくなりSIGSEGVで落ちる 11 | usage: VBOUsage 12 | 13 | VBO* [I: static int, T] = ref VBOObj[false, I, T] 14 | BindedVBO* [I: static int, T] = ref VBOObj[true, I, T] 15 | 16 | VBOTarget* = enum 17 | vtArrayBuffer = opengl.GL_ARRAY_BUFFER 18 | vtElementArrayBuffer = opengl.GL_ELEMENT_ARRAY_BUFFER 19 | vtPixelPackBuffer = opengl.GL_PIXEL_PACK_BUFFER 20 | vtPixelUnpackBuffer = opengl.GL_PIXEL_UNPACK_BUFFER 21 | vtUniformBuffer = opengl.GL_UNIFORM_BUFFER 22 | vtTextureBuffer = opengl.GL_TEXTURE_BUFFER 23 | vtTransformFeedbackBuffer = opengl.GL_TRANSFORM_FEEDBACK_BUFFER 24 | vtCopyReadBuffer = opengl.GL_COPY_READ_BUFFER 25 | vtCopyWriteBuffer = opengl.GL_COPY_WRITE_BUFFER 26 | vtDrawIndirectBuffer = opengl.GL_DRAW_INDIRECT_BUFFER 27 | vtShaderStorageBuffer = opengl.GL_SHADER_STORAGE_BUFFER 28 | vtDispatchIndirectBuffer = opengl.GL_DISPATCH_INDIRECT_BUFFER 29 | vtQueryBuffer = opengl.GL_QUERY_BUFFER 30 | vtAtomicCounterBuffer = opengl.GL_ATOMIC_COUNTER_BUFFER 31 | 32 | VBOUsage* = enum 33 | vuStreamDraw = opengl.GL_STREAM_DRAW 34 | vuStreamRead = opengl.GL_STREAM_READ 35 | vuStreamCopy = opengl.GL_STREAM_COPY 36 | vuStaticDraw = opengl.GL_STATIC_DRAW 37 | vuStaticRead = opengl.GL_STATIC_READ 38 | vuStaticCopy = opengl.GL_STATIC_COPY 39 | vuDynamicDraw = opengl.GL_DYNAMIC_DRAW 40 | vuDynamicRead = opengl.GL_DYNAMIC_READ 41 | vuDynamicCopy = opengl.GL_DYNAMIC_COPY 42 | 43 | func toBindedVBO* [I: static int, T] (vbo: VBO[I, T]): BindedVBO[I, T] = 44 | result = BindedVBO[I, T]( 45 | id: vbo.id, 46 | target: vbo.target, 47 | data: vbo.data, 48 | usage: vbo.usage 49 | ) 50 | 51 | func toVBO* [I: static int, T] (vbo: BindedVBO[I, T]): VBO[I, T] = 52 | result = VBO[I, T]( 53 | id: vbo.id, 54 | target: vbo.target, 55 | data: vbo.data, 56 | usage: vbo.usage 57 | ) 58 | 59 | proc `target=`* [I: static int, T] (vbo: var BindedVBO[I, T], target: VBOTarget) = 60 | vbo.target = target 61 | 62 | proc `data=`* [I: static int, T] (vbo: var BindedVBO[I, T], data: array[I, T]) = 63 | let size = cint(sizeof(vbo.data)) 64 | vbo.data = data 65 | opengl.glBufferData( 66 | opengl.GLenum(vbo.target), 67 | size, 68 | vbo.data[0].addr, 69 | opengl.GLenum(vbo.usage) 70 | ) 71 | debugOpenGLStatement: 72 | echo &"glBufferData({vbo.target}, {size}, data[0].addr, {vbo.usage})" 73 | 74 | proc `usage=`* [I: static int, T] (vbo: var BindedVBO[I, T], usage: VBOUsage) = 75 | vbo.usage = usage 76 | 77 | proc init* [I: static int, T] (_: typedesc[VBO[I, T]]): VBO[I, T] = 78 | var data: array[I, T] 79 | result = VBO[I, T](target: vtArrayBuffer, usage: vuStaticDraw, data: data) 80 | debugOpenGLStatement: 81 | echo &"glGenBuffers(1, result.id.addr)" 82 | opengl.glGenBuffers(1, result.id.addr) 83 | 84 | func id* [B: static bool, I: static int, T] (vbo: VBOObj[B, I, T]): uint = 85 | result = vbo.id.uint 86 | 87 | func target* [B: static bool, I: static int, T] (vbo: VBOObj[B, I, T]): VBOTarget = 88 | result = vbo.target 89 | 90 | func data* [I: static int, T] (vbo: VBO[I, T] | BindedVBO[I, T]): array[I, T] = 91 | result = vbo.data 92 | 93 | func usage* [B: static bool, I: static int, T] (vbo: VBOObj[B, I, T]): VBOUsage = 94 | result = vbo.usage 95 | 96 | proc `bind`* [I: static int, T] (vbo: var VBO[I, T]): BindedVBO[I, T] = 97 | debugOpenGLStatement: 98 | echo &"glBindBuffer({vbo.target}, {vbo.id})" 99 | opengl.glBindBuffer(opengl.GLenum(vbo.target), vbo.id) 100 | result = vbo.toBindedVBO 101 | 102 | proc unbind* [I: static int, T] (vbo: var BindedVBO[I, T]): VBO[I, T] = 103 | debugOpenGLStatement: 104 | echo &"glBindBuffer({vbo.target}, 0)" 105 | opengl.glBindBuffer(opengl.GLenum(vbo.target), 0) 106 | result = vbo.toVBO 107 | 108 | proc use* [I: static int, T] (vbo: var VBO[I, T], procedure: proc (vbo: var BindedVBO[I, T])) = 109 | var bindedVBO = vbo.bind() 110 | bindedVBO.procedure() 111 | vbo = bindedVBO.unbind() 112 | -------------------------------------------------------------------------------- /experiment/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | mock-up デモ 8 | 9 | 10 |
11 | 12 |
13 |
14 | 三角形の色 15 | R: 16 | G: 17 | B: 18 |
19 |
20 | 座標 21 | x: 22 | y: 23 | z: 24 |
25 | 26 |

27 |

28 | 29 | 124 | -------------------------------------------------------------------------------- /nagu/tests/test_position.nim: -------------------------------------------------------------------------------- 1 | import std/unittest 2 | from std/importutils import privateAccess 3 | import nagu/position {.all.} 4 | 5 | privateAccess(PositionObj) 6 | 7 | suite "init": 8 | test "(1, 2, 3)": 9 | check Position.init(1, 2, 3)[] == PositionObj(x: 1.0, y: 2.0, z: 3.0) 10 | 11 | test "(1.1, 2.2, 3.3)": 12 | check Position.init(1.1, 2.2, 3.3)[] == PositionObj(x: 1.1, y: 2.2, z: 3.3) 13 | 14 | suite "coord": 15 | test "(1, -1, 0)": 16 | check Position.init(1, -1, 0).coord == (1f, -1f, 0f) 17 | 18 | test "(1.2, -2.4, 3.6)": 19 | check Position.init(1.2, -2.4, 3.6).coord == (1.2f, -2.4f, 3.6f) 20 | 21 | suite "+": 22 | test "(0, 0, 0) + 2": 23 | check Position.init(0, 0, 0) + 2 == Position.init(2, 2, 2) 24 | 25 | test "(2.5, -5.0, 7.5) + 2.5": 26 | check Position.init(2.5, -5.0, 7.5) + 2.5 =~ Position.init(5.0, -2.5, 10.0) 27 | 28 | test "(1, 2, 3) + (6, 5, 4)": 29 | check Position.init(1, 2, 3) + Position.init(6, 5, 4) == Position.init(7, 7, 7) 30 | 31 | test "(1.1, 1.2, 1.3) + (2.3, 2.2, 2.1)": 32 | check Position.init(1.1, 1.2, 1.3) + Position.init(2.3, 2.2, 2.1) =~ Position.init(3.4, 3.4, 3.4) 33 | 34 | suite "-": 35 | test "-(1, -1, 0)": 36 | check -Position.init(1, -1, 0) == Position.init(-1, 1, 0) 37 | 38 | test "(1, 2, 3) - 1": 39 | check Position.init(1, 2, 3) - 1 == Position.init(0, 1, 2) 40 | 41 | test "(1.2, 3.4, 5.6) - 0.9": 42 | check Position.init(1.2, 3.4, 5.6) - 0.9 =~ Position.init(0.3, 2.5, 4.7) 43 | 44 | test "(4, 5, 6) - (1, 2, 3)": 45 | check Position.init(4, 5, 6) - Position.init(1, 2, 3) == Position.init(3, 3, 3) 46 | 47 | test "(7.8, 9.0, 12.3) - (4.5, 6.7, 7.8)": 48 | check Position.init(7.8, 9.0, 12.3) - Position.init(4.5, 6.7, 7.8) =~ Position.init(3.3, 2.3, 4.5) 49 | 50 | suite "*": 51 | test "(1, 2, 3) * 10": 52 | check Position.init(1, 2, 3) * 10 == Position.init(10, 20, 30) 53 | 54 | test "(1.2, 3.4, 5.6) * 3": 55 | check Position.init(1.2, 3.4, 5.6) * 3 =~ Position.init(3.6, 10.2, 16.8) 56 | 57 | test "(4, 5, 6) * (7, 8, 9)": 58 | check Position.init(4, 5, 6) * Position.init(7, 8, 9) == Position.init(28, 40, 54) 59 | 60 | test "(1.1, 2.2, 3.3) * (4.4, 5.5, 6.6)": 61 | check Position.init(1.1, 2.2, 3.3) * Position.init(4.4, 5.5, 6.6) =~ Position.init(4.84, 12.1, 21.78) 62 | 63 | suite "/": 64 | test "(4, 6, 8) / 2": 65 | check Position.init(4, 6, 8) / 2 == Position.init(2, 3, 4) 66 | 67 | test "(7.2, 2.4, 3.6) / 2.5": 68 | check Position.init(7.2, 2.4, 3.6) / 2.5 =~ Position.init(2.88, 0.96, 1.44) 69 | 70 | test "(15, 16, 17) / (5, 8, 17)": 71 | check Position.init(15, 16, 17) / Position.init(5, 8, 17) == Position.init(3, 2, 1) 72 | 73 | test "(4.8, 3.5, 5.1) / (16, 0.7, 3.4)": 74 | check Position.init(4.8, 3.5, 5.1) / Position.init(16, 7.0, 3.4) =~ Position.init(0.3, 0.5, 1.5) 75 | 76 | suite "$": 77 | test "(1, 2, 3)": 78 | check $Position.init(1, 2, 3) == "(x: 1.0, y: 2.0, z: 3.0)" 79 | 80 | # TODO: Implement an output that rounds to the nearest error. 81 | # test "(1.1, -2.2, 3.3)": 82 | # check $Position.init(1.1, -2.2, 3.3) == "(x: 1.1, y: -2.2, z: 3.3)" 83 | 84 | suite "==": 85 | test "(1, 2, 3) + (4, 5, 6)": 86 | check Position.init(1, 2, 3) + Position.init(4, 5, 6) == Position.init(5, 7, 9) 87 | 88 | test "(1.1, 2.2, 3.3) + (4.4, 5.5, 6.6)": 89 | check not(Position.init(1.1, 2.2, 3.3) + Position.init(1.2, 2.3, 3.4) == Position.init(2.3, 4.5, 6.7)) 90 | 91 | suite "=~": 92 | test "(1, 2, 3) + (4, 5, 6)": 93 | check Position.init(1, 2, 3) + Position.init(4, 5, 6) =~ Position.init(5, 7, 9) 94 | 95 | test "(1.1, 2.2, 3.3) + (4.4, 5.5, 6.6)": 96 | check Position.init(1.1, 2.2, 3.3) + Position.init(1.2, 2.3, 3.4) =~ Position.init(2.3, 4.5, 6.7) 97 | 98 | suite "map": 99 | setup: 100 | proc cube (n: float32): float32 {.used.} = n * n * n 101 | proc min (n, m: float32): float32 {.used.} = 102 | result = if n <= m: n 103 | else: m 104 | test "apply cube to (1, 2, 3)": 105 | check: 106 | Position.init(1, 2, 3).map(cube) == Position.init(1, 8, 27) 107 | Position.init(1.1, 2.2, 3.3).map(cube) =~ Position.init(1.331, 10.648, 35.937) 108 | 109 | test "apply min": 110 | check: 111 | map(Position.init(1, 2, 3), Position.init(3, 2, 1), min) == Position.init(1, 2, 1) 112 | map(Position.init(1.1, 2.2, 3.3), Position.init(3.2, 2.1, 1.0), min) =~ Position.init(1.1, 2.1, 1.0) 113 | -------------------------------------------------------------------------------- /nagu/src/nagu/old_shape.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/shape.nim defines the Shape type and procedures related to its. 2 | 3 | from position import Position, coord, init 4 | from color import Color, rgb, init 5 | from vao import VAO, draw, VAODrawMode 6 | from old_vbo import VBO, vboRef, data, `:=` 7 | from program import mvpMatrix, ProgramObject 8 | import nimgl/opengl 9 | 10 | from std/sugar import `->`, `=>` 11 | 12 | type 13 | ShapeObj[I: static int] = object 14 | vao: VAO 15 | positions_vbo: vboRef[I, float32] 16 | colors_vbo: vboRef[I, float32] 17 | mvp_matrix: mvpMatrix 18 | 19 | Shape* [I: static int] = ref ShapeObj[I] 20 | ## Represents shapes. 21 | 22 | func toArray [I: static int] (positions: array[I, Position]): array[3*I, float32] = 23 | for index, position in positions: 24 | (result[index*3], result[index*3+1], result[index*3+2]) = position.coord 25 | 26 | func toArray [I: static int] (colors: array[I, Color]): array[3*I, float32] = 27 | for index, color in colors: 28 | (result[index*3], result[index*3+1], result[index*3+2]) = color.rgb 29 | 30 | func `@`* [I: static int] (positions: array[I, Position]): array[3*I, float32] = 31 | result = positions.toArray() 32 | 33 | func `@`* [I: static int] (colors: array[I, Color]): array[3*I, float32] = 34 | result = colors.toArray() 35 | 36 | func positions* [I: static int] (shape: Shape[I]): array[I div 3, Position] = 37 | let 38 | positions_len = I div 3 39 | data = shape.positions_vbo.data 40 | for index in 0 ..< positions_len: 41 | result[index] = Position.init(data[index*3], data[index*3+1], data[index*3+2]) 42 | 43 | func colors* [I: static int] (shape: Shape[I]): array[I div 3, Color] = 44 | let 45 | colors_len = I div 3 46 | data = shape.colors_vbo.data 47 | for index in 0 ..< colors_len: 48 | result[index] = Color.init(data[index*3], data[index*3+1], data[index*3+2]) 49 | 50 | proc init* [I: static int] (_: typedesc[Shape[I]], positionsArr: array[I, float32], colorsArr: array[I, float32]): Shape[I] = 51 | result = Shape[I]( 52 | vao: VAO.make(), 53 | positions_vbo: VBO.make(positionsArr), 54 | colors_vbo: VBO.make(colorsArr) 55 | ) 56 | ## vboRef[I*3, float32]は推論が効かずコンパイルが通らない 57 | # echo I 58 | # echo typeof result 59 | # echo typeof result.positions_vbo 60 | 61 | proc draw* [I: static int] (shape: Shape[I], mode: VAODrawMode) = 62 | shape.vao.draw(mode) 63 | 64 | proc pMap* [I: static int] (shape: var Shape[I], fn: Position -> Position) = 65 | var positions: array[I div 3, Position] 66 | for index, position in shape.positions: 67 | positions[index] = fn(position) 68 | shape[].positions_vbo := @positions 69 | 70 | func cMap* [I: static int] (shape: Shape[I], fn: Color -> Color): Shape[I] = 71 | result = shape 72 | for index, color in shape.colors: 73 | result[index] = fn(color) 74 | result.colors_vbo := result.colors 75 | 76 | proc correspondPartially [I: static int] (program: ProgramObject, vbo: vboRef[I, float32], name: string, size: int) = 77 | let index = opengl.GLuint(program.index(name)) 78 | opengl.glEnableVertexAttribArray(index) 79 | opengl.glBindBuffer(opengl.GL_ARRAY_BUFFER, vbo.id) 80 | opengl.glVertexAttribPointer(index, opengl.GLint(size), opengl.EGL_FLOAT, false, 0, nil) 81 | 82 | proc correspond* [I: static int] (program: ProgramObject, shape: Shape[I], position_name, color_name: string) = 83 | ## Ties `shape` to `program`. 84 | program.correspondPartially(shape.positions_vbo, position_name, 3) 85 | program.correspondPartially(shape.colors_vbo, color_name, 3) 86 | 87 | # proc `+`* [I: static int] (shape: Shape[I], position: Position): Shape[I] = 88 | # result = shape.pMap(p => p + position) 89 | 90 | proc `+=`* [I: static int] (shape: var Shape[I], position: Position) = 91 | shape.pMap(p => p + position) 92 | 93 | # proc `-`* [I: static int] (shape: Shape[I], position: Position): Shape[I] = 94 | # result = shape.pMap(p => p - position) 95 | 96 | proc `-=`* [I: static int] (shape: var Shape[I], position: Position) = 97 | shape.pMap(p => p - position) 98 | 99 | # proc `*`* [I: static int] (shape: Shape[I], position: Position): Shape[I] = 100 | # result = shape.pMap(p => p * position) 101 | 102 | proc `*=`* [I: static int] (shape: var Shape[I], position: Position) = 103 | shape.pMap(p => p * position) 104 | 105 | # proc `/`* [I: static int] (shape: Shape[I], position: Position): Shape[I] = 106 | # result = shape.pMap(p => p / position) 107 | 108 | proc `/=`* [I: static int] (shape: var Shape[I], position: Position) = 109 | shape.pMap(p => p * position) 110 | -------------------------------------------------------------------------------- /experiment/src/encode_mp4.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | 3 | proc encode (codec_context: ptr AVCodecContext, frame: ptr AVFrame, stream: ptr AVStream, format_context: ptr AVFormatContext) = 4 | var packet = ffmpeg.AVPacket() 5 | if avcodec_send_frame(codec_context, frame) < 0: 6 | raise newException(Defect, "エンコーダーへのフレームの供給に失敗しました") 7 | while avcodec_receive_packet(codec_context, packet.addr) == 0: 8 | packet.stream_index = 0 9 | av_packet_rescale_ts(packet.addr, codec_context.time_base, stream.time_base) 10 | if av_interleaved_write_frame(format_context, packet.addr) != 0: 11 | raise newException(Defect, "パケットの書き込みに失敗しました") 12 | av_packet_unref(packet.addr) 13 | 14 | proc encodeMp4* (dist_path: string) = 15 | # H264コーデックの取得 16 | var codec = avcodec_find_encoder(AV_CODEC_ID_H264) 17 | if codec.isNil: 18 | raise newException(Defect, "エンコーダが見つかりませんでした") 19 | 20 | # コーデックコンテキストの取得 21 | var codec_context = avcodec_alloc_context3(codec) 22 | if codec_context.isNil: 23 | raise newException(Defect, "コーデックの割り当てに失敗しました") 24 | 25 | codec_context.pix_fmt = AV_PIX_FMT_YUV420P 26 | codec_context.width = 1280 27 | codec_context.height = 720 28 | # codec_context.field_order = AV_FIELD_PROGRESSIVE # これ何 29 | codec_context.time_base = av_make_q(1, 25) 30 | codec_context.framerate = av_make_q(25, 1) 31 | codec_context.gop_size = 10 32 | codec_context.max_b_frames = 1 33 | codec_context.bit_rate = 400000 34 | 35 | var codec_options: ptr ffmpeg.AVDictionary = nil 36 | discard ffmpeg.av_dict_set(codec_options.addr, "profile", "high", 0) 37 | discard ffmpeg.av_dict_set(codec_options.addr, "preset", "medium", 0) 38 | discard ffmpeg.av_dict_set(codec_options.addr, "crf", "22", 0) 39 | discard ffmpeg.av_dict_set(codec_options.addr, "level", "4.0", 0) 40 | 41 | var io_context: ptr AVIOContext 42 | if avio_open(io_context.addr, dist_path, AVIO_FLAG_WRITE) < 0: 43 | raise newException(Defect, "IOコンテキストの初期化に失敗しました") 44 | 45 | var format_context: ptr AVFormatContext 46 | if avformat_alloc_output_context2(format_context.addr, nil, "mp4", nil) < 0: 47 | raise newException(Defect, "出力フォーマットへのコンテキスト割り当てに失敗しました") 48 | format_context.pb = io_context 49 | 50 | if ffmpeg.avcodec_open2(codec_context, codec_context[].codec, codec_options.addr) < 0: 51 | raise newException(Defect, "コーデックコンテキストの初期化に失敗しました") 52 | 53 | var stream = avformat_new_stream(format_context, codec) 54 | if stream.isNil: 55 | raise newException(Defect, "ストリームの取得に失敗しました") 56 | # video.encoder_stream.sample_aspect_ratio = video.encoder_codec_context.sample_aspect_ratio 57 | # video.encoder_stream.time_base = video.encoder_codec_context.time_base 58 | 59 | if avcodec_parameters_from_context(stream.codecpar, codec_context) < 0: 60 | raise newException(Defect, "コーデックコンテキストによる塗りつぶしに失敗しました") 61 | 62 | if avformat_write_header(format_context, nil) < 0: 63 | raise newException(Defect, "ヘッダーの書き込みに失敗しました") 64 | 65 | var frame = av_frame_alloc() 66 | if frame.isNil: 67 | raise newException(Defect, "Could not allocate video frame") 68 | 69 | frame.format = codec_context.pix_fmt.int32 70 | frame.width = codec_context.width 71 | frame.height = codec_context.height 72 | 73 | if av_frame_get_buffer(frame, 0) < 0: 74 | raise newException(Defect, "Could not allocate the video frame data") 75 | 76 | for i in 0 ..< 250: 77 | if av_frame_make_writable(frame) < 0: 78 | quit(1) 79 | 80 | for y in 0 ..< codec_context.height: 81 | for x in 0 ..< codec_context.width: 82 | cast[ptr uint8](cast[int](frame[].data[0]) + y * frame[].linesize[0] + x)[] = (x + y + i).uint8 83 | 84 | for y in 0 ..< codec_context.height div 2: 85 | for x in 0 ..< codec_context.width div 2: 86 | cast[ptr uint8](cast[int](frame[].data[1]) + y * frame[].linesize[1] + x)[] = (128 + y + i * 4).uint8 87 | cast[ptr uint8](cast[int](frame[].data[2]) + y * frame[].linesize[2] + x)[] = (64 + x + i * 3).uint8 88 | 89 | frame[].pts = i 90 | encode(codec_context, frame, stream, format_context) 91 | 92 | if avcodec_send_frame(codec_context, nil) != 0: 93 | return 94 | var packet = AVPacket() 95 | while avcodec_receive_packet(codec_context, packet.addr) == 0: 96 | packet.stream_index = 0 97 | av_packet_rescale_ts(packet.addr, codec_context.time_base, stream.time_base) 98 | if av_interleaved_write_frame(format_context, packet.addr) != 0: 99 | return 100 | if av_write_trailer(format_context) != 0: 101 | return 102 | avcodec_free_context(codec_context.addr) 103 | avformat_free_context(format_context) 104 | discard avio_closep(io_context.addr) 105 | -------------------------------------------------------------------------------- /nagu/src/nagu/position.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/position.nim defines the Position type and procedures related to its. 2 | 3 | from std/sugar import `->`, `=>` 4 | from std/strformat import `&` 5 | from std/math import almostEqual 6 | 7 | type 8 | PositionObj = object 9 | x, y, z: float32 10 | 11 | Position* = ref PositionObj 12 | ## The Position type representations 3D coordinates. 13 | ## It has x, y and z members, but they are not published. 14 | ## You should call the `coord` function, if you get coordinates information of a position. 15 | 16 | func init* (_: typedesc[Position], x, y, z: float32): Position = 17 | ## Initializes a Position object by `x`, `y` and `z`. 18 | result = Position(x: x, y: y, z: z) 19 | 20 | func coord* (pos: Position): tuple[x, y, z: float32] = 21 | ## Gets coordinates information of a Position object. 22 | runnableExamples: 23 | doAssert Position.init(1, 2, 3).coord == (1.0f, 2.0f, 3.0f) 24 | result = (pos.x, pos.y, pos.z) 25 | 26 | func `$`* (pos: Position): string = 27 | ## Converts a Position object into string. 28 | runnableExamples: 29 | doAssert $Position.init(1, 2, 3) == "(x: 1.0, y: 2.0, z: 3.0)" 30 | result = &"(x: {pos.x}, y: {pos.y}, z: {pos.z})" 31 | 32 | func map* (pos: Position, fn: float32 -> float32): Position = 33 | ## Applies `fn` to each element of `pos`. 34 | runnableExamples: 35 | func square (n: float32): float32 = n * n 36 | doAssert Position.init(1, 2, 3).map(square) == Position.init(1, 4, 9) 37 | result = Position( 38 | x: fn(pos.x), 39 | y: fn(pos.y), 40 | z: fn(pos.z) 41 | ) 42 | 43 | func map* (pos1, pos2: Position, fn: (float32, float32) -> float32): Position = 44 | ## Applies `fn` to each element of `pos`. 45 | runnableExamples: 46 | func max (n, m: float32): float32 = 47 | result = if n >= m: n 48 | else: m 49 | let 50 | position1 = Position.init(1, 4, 9) 51 | position2 = Position.init(2, 4, 6) 52 | doAssert map(position1, position2, max) == Position.init(2, 4, 9) 53 | result = Position( 54 | x: fn(pos1.x, pos2.x), 55 | y: fn(pos1.y, pos2.y), 56 | z: fn(pos1.z, pos2.z) 57 | ) 58 | 59 | func `==`* (pos1, pos2: Position): bool = 60 | ## Checks each element of `pos1` against each element of `pos2`. 61 | runnableExamples: 62 | doAssert Position.init(1, 2, 3) == Position.init(5, 4, 3) - Position.init(4, 2, 0) 63 | result = pos1.coord == pos2.coord 64 | 65 | func `=~`* (pos1, pos2: Position): bool = 66 | ## Almost checks each element of `pos1` against each element of `pos2`. 67 | runnableExamples: 68 | doAssert Position.init(0.1, 0.2, 0.3) =~ Position.init(1.5, 1.3, 1.1) - Position.init(1.4, 1.1, 0.8) 69 | result = almostEqual(pos1.x, pos2.x) and 70 | almostEqual(pos1.y, pos2.y) and 71 | almostEqual(pos1.z, pos2.z) 72 | 73 | func `+`* (pos: Position, value: float32): Position = 74 | ## Adds `value` to each element of `pos`. 75 | runnableExamples: 76 | doAssert Position.init(1, 2, 3) + 5.0 == Position.init(6, 7, 8) 77 | pos.map(v => v + value) 78 | 79 | func `+`* (pos1, pos2: Position): Position = 80 | ## Adds each element of `pos2` to each element of `pos1`. 81 | runnableExamples: 82 | doAssert Position.init(1, 2, 3) + Position.init(4, 5, 6) == Position.init(5, 7, 9) 83 | map(pos1, pos2, (v1, v2) => v1 + v2) 84 | 85 | func `-`* (pos: Position): Position = 86 | ## Multiplies each element of `pos` by -1. 87 | runnableExamples: 88 | doAssert -Position.init(1, 2, 3) == Position.init(-1, -2, -3) 89 | pos.map(v => -v) 90 | 91 | func `-`* (pos: Position, value: float32): Position = 92 | ## Subtracts `value` to each element of `pos`. 93 | runnableExamples: 94 | doAssert Position.init(5, 6, 7) - 4.0 == Position.init(1, 2, 3) 95 | pos.map(v => v - value) 96 | 97 | func `-`* (pos1, pos2: Position): Position = 98 | ## Subtracts each element of `pos2` from each element of `pos1`. 99 | runnableExamples: 100 | doAssert Position.init(10, 20, 30) - Position.init(1, 2, 3) == Position.init(9, 18, 27) 101 | map(pos1, pos2, (v1, v2) => v1 - v2) 102 | 103 | func `*`* (pos: Position, value: float32): Position = 104 | ## Multiplies each element of `pos` by `value`. 105 | runnableExamples: 106 | doAssert Position.init(1, 2, 3) * 2.0 == Position.init(2, 4, 6) 107 | pos.map(v => v * value) 108 | 109 | func `*`* (pos1, pos2: Position): Position = 110 | ## Multiplies each element of `pos1` by each element of `pos2`. 111 | runnableExamples: 112 | doAssert Position.init(5, 6, 7) * Position.init(4, 3, 2) == Position.init(20, 18, 14) 113 | map(pos1, pos2, (v1, v2) => v1 * v2) 114 | 115 | func `/`* (pos: Position, value: float32): Position = 116 | ## Divides each element of `pos` by `value` 117 | runnableExamples: 118 | doAssert Position.init(14, 12, 10) / 2.0 == Position.init(7, 6, 5) 119 | pos.map(v => v / value) 120 | 121 | func `/`* (pos1, pos2: Position): Position = 122 | ## Divides each element of `pos1` by each element of `pos2` 123 | runnableExamples: 124 | doAssert Position.init(21, 18, 15) / Position.init(7, 6, 5) == Position.init(3, 3, 3) 125 | map(pos1, pos2, (v1, v2) => v1 / v2) 126 | -------------------------------------------------------------------------------- /src/mockuppkg/encode_mp4.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | 3 | type 4 | MP4Encoder* = object 5 | frame_num: int # フレーム数 6 | codec_context: ptr AVCodecContext 7 | stream: ptr AVStream 8 | format_context: ptr AVFormatContext 9 | io_context: ptr AVIOContext 10 | 11 | proc openMP4* (dist_path: string, width, height, fps: int32): MP4Encoder = 12 | result.frame_num = 0 13 | 14 | # H264コーデックの取得 15 | var codec = avcodec_find_encoder(AV_CODEC_ID_H264) 16 | if codec.isNil: 17 | raise newException(Defect, "エンコーダが見つかりませんでした") 18 | 19 | # コーデックコンテキストの取得 20 | result.codec_context = avcodec_alloc_context3(codec) 21 | if result.codec_context.isNil: 22 | raise newException(Defect, "コーデックの割り当てに失敗しました") 23 | 24 | result.codec_context.pix_fmt = AV_PIX_FMT_YUV420P 25 | result.codec_context.width = width 26 | result.codec_context.height = height 27 | # codec_context.field_order = AV_FIELD_PROGRESSIVE # これ何 28 | result.codec_context.time_base = av_make_q(1, fps) 29 | result.codec_context.framerate = av_make_q(fps, 1) 30 | result.codec_context.gop_size = 10 31 | result.codec_context.max_b_frames = 1 32 | result.codec_context.bit_rate = 400000 33 | 34 | var codec_options: ptr ffmpeg.AVDictionary = nil 35 | discard ffmpeg.av_dict_set(codec_options.addr, "profile", "high", 0) 36 | discard ffmpeg.av_dict_set(codec_options.addr, "preset", "medium", 0) 37 | discard ffmpeg.av_dict_set(codec_options.addr, "crf", "22", 0) 38 | discard ffmpeg.av_dict_set(codec_options.addr, "level", "4.0", 0) 39 | 40 | if avio_open(result.io_context.addr, dist_path, AVIO_FLAG_WRITE) < 0: 41 | raise newException(Defect, "IOコンテキストの初期化に失敗しました") 42 | 43 | if avformat_alloc_output_context2(result.format_context.addr, nil, "mp4", nil) < 0: 44 | raise newException(Defect, "出力フォーマットへのコンテキスト割り当てに失敗しました") 45 | result.format_context.pb = result.io_context 46 | 47 | if ffmpeg.avcodec_open2(result.codec_context, result.codec_context[].codec, codec_options.addr) < 0: 48 | raise newException(Defect, "コーデックコンテキストの初期化に失敗しました") 49 | 50 | result.stream = avformat_new_stream(result.format_context, codec) 51 | if result.stream.isNil: 52 | raise newException(Defect, "ストリームの取得に失敗しました") 53 | # video.encoder_stream.sample_aspect_ratio = video.encoder_codec_context.sample_aspect_ratio 54 | # video.encoder_stream.time_base = video.encoder_codec_context.time_base 55 | 56 | if avcodec_parameters_from_context(result.stream.codecpar, result.codec_context) < 0: 57 | raise newException(Defect, "コーデックコンテキストによる塗りつぶしに失敗しました") 58 | 59 | if avformat_write_header(result.format_context, nil) < 0: 60 | raise newException(Defect, "ヘッダーの書き込みに失敗しました") 61 | 62 | proc prepareCopyFrame (src: ptr AVFrame): ptr AVFrame = 63 | result = av_frame_alloc() 64 | result[].format = src[].format 65 | result[].height = src[].height 66 | result[].width = src[].width 67 | result[].channels = src[].channels # ? 68 | result[].channel_layout = src[].channel_layout 69 | result[].nb_samples = src[].nb_samples 70 | # result[].pts = src[].pts 71 | 72 | proc addFrame* (encoder: var MP4Encoder, src_frame: ptr AVFrame) = 73 | var frame = src_frame 74 | var dest_frame = frame.prepareCopyFrame 75 | dest_frame.format = encoder.codec_context.pix_fmt.cint 76 | if av_frame_get_buffer(dest_frame, 32) < 0: 77 | raise newException(Defect, "バッファの割り当てに失敗しました") 78 | var swsCtxEnc = sws_getContext( 79 | encoder.codec_context.width, 80 | encoder.codec_context.height, 81 | AV_PIX_FMT_RGBA, 82 | encoder.codec_context.width, 83 | encoder.codec_context.height, 84 | encoder.codec_context.pix_fmt, 85 | ffmpeg.SWS_BICUBIC, 86 | nil, nil, nil 87 | ) 88 | discard ffmpeg.sws_scale( 89 | swsCtxEnc, 90 | frame[].data[0].addr, 91 | frame[].linesize[0].addr, 92 | 0, 93 | frame[].height, 94 | dest_frame[].data[0].addr, 95 | dest_frame[].linesize[0].addr 96 | ) 97 | var packet = ffmpeg.AVPacket() 98 | dest_frame.pts = encoder.frame_num 99 | encoder.frame_num += 1 100 | if avcodec_send_frame(encoder.codec_context, dest_frame) < 0: 101 | raise newException(Defect, "エンコーダーへのフレームの供給に失敗しました") 102 | while avcodec_receive_packet(encoder.codec_context, packet.addr) == 0: 103 | packet.stream_index = 0 104 | av_packet_rescale_ts(packet.addr, encoder.codec_context.time_base, encoder.stream.time_base) 105 | if av_interleaved_write_frame(encoder.format_context, packet.addr) != 0: 106 | raise newException(Defect, "パケットの書き込みに失敗しました") 107 | av_packet_unref(packet.addr) 108 | 109 | proc close* (encoder: var MP4Encoder) = 110 | if avcodec_send_frame(encoder.codec_context, nil) != 0: 111 | return 112 | var packet = AVPacket() 113 | while avcodec_receive_packet(encoder.codec_context, packet.addr) == 0: 114 | packet.stream_index = 0 115 | av_packet_rescale_ts(packet.addr, encoder.codec_context.time_base, encoder.stream.time_base) 116 | if av_interleaved_write_frame(encoder.format_context, packet.addr) != 0: 117 | return 118 | if av_write_trailer(encoder.format_context) != 0: 119 | return 120 | avcodec_free_context(encoder.codec_context.addr) 121 | avformat_free_context(encoder.format_context) 122 | discard avio_closep(encoder.io_context.addr) 123 | -------------------------------------------------------------------------------- /nagu/src/nagu/types/texture.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl import nil 2 | import ../vao, ../vbo, ../program, ../utils, ../mvp_matrix 3 | 4 | type 5 | naguTextureQuad* = VBO[12, float32] 6 | naguBindedTextureQuad* = BindedVBO[12, float32] 7 | naguTextureUV* = VBO[8, float32] 8 | naguBindedTextureUV* = BindedVBO[8, float32] 9 | naguTextureElem* = VBO[6, uint8] 10 | naguBindedTextureElem* = BindedVBO[6, uint8] 11 | 12 | naguTextureObj [binded: static bool] = object 13 | id: opengl.GLuint 14 | vao*: VAO 15 | quad*: naguTextureQuad 16 | uv*: naguTextureUV 17 | elem*: naguTextureElem 18 | format: naguTextureFormat 19 | model_matrix*: array[4, ModelMatrixVector[16]] 20 | wrapS, wrapT: naguTextureWrapParameter 21 | magFilter: naguTextureMagFilterParameter 22 | minFilter: naguTextureMinFilterParameter 23 | program*: Program 24 | initializedPixels*: bool 25 | 26 | naguTexture* = ref naguTextureObj[false] 27 | naguBindedTexture* = ref naguTextureObj[true] 28 | naguAllTextures = naguTexture | naguBindedTexture 29 | 30 | naguTextureWrapParameter* {.pure.} = enum 31 | tInitialValue = 0 32 | tRepeat = opengl.GL_REPEAT 33 | tClampToEdge = opengl.GL_CLAMP_TO_EDGE 34 | tMirroredRepeat = opengl.GL_MIRRORED_REPEAT 35 | 36 | naguTextureMagFilterParameter* {.pure.} = enum 37 | tInitialValue = 0 38 | tNearest = opengl.GL_NEAREST 39 | tLinear = opengl.GL_LINEAR 40 | 41 | naguTextureMinFilterParameter* {.pure.} = enum 42 | tInitialValue = 0 43 | tNearest = opengl.GL_NEAREST 44 | tLinear = opengl.GL_LINEAR 45 | tNearestMipmapNearest = opengl.GL_NEAREST_MIPMAP_NEAREST 46 | tLinearMipmapNearest = opengl.GL_LINEAR_MIPMAP_NEAREST 47 | tNearestMipmapLinear = opengl.GL_NEAREST_MIPMAP_LINEAR 48 | tLinearMipmapLinear = opengl.GL_LINEAR_MIPMAP_LINEAR 49 | 50 | naguTextureFormat* {.pure.} = enum 51 | tfRGB = opengl.GL_RGB 52 | tfRGBA = opengl.GL_RGBA 53 | 54 | func toBindedTexture* (texture: naguTexture): naguBindedTexture = 55 | result = naguBindedTexture( 56 | id: texture.id, 57 | vao: texture.vao, 58 | quad: texture.quad, 59 | uv: texture.uv, 60 | elem: texture.elem, 61 | format: texture.format, 62 | model_matrix: texture.model_matrix, 63 | wrapS: texture.wrapS, wrapT: texture.wrapT, 64 | magFilter: texture.magFilter, 65 | minFilter: texture.minFilter, 66 | program: texture.program 67 | ) 68 | 69 | func toTexture* (texture: naguBindedTexture): naguTexture = 70 | result = naguTexture( 71 | id: texture.id, 72 | vao: texture.vao, 73 | quad: texture.quad, 74 | uv: texture.uv, 75 | elem: texture.elem, 76 | format: texture.format, 77 | model_matrix: texture.model_matrix, 78 | wrapS: texture.wrapS, wrapT: texture.wrapT, 79 | magFilter: texture.magFilter, 80 | minFilter: texture.minFilter, 81 | program: texture.program 82 | ) 83 | 84 | func id* (texture: naguAllTextures): opengl.GLuint = texture.id 85 | 86 | func format* (texture: naguAllTextures): naguTextureFormat = texture.format 87 | 88 | func wrapS* (texture: naguAllTextures): naguTextureWrapParameter = texture.wrapS 89 | 90 | func wrapT* (texture: naguAllTextures): naguTextureWrapParameter = texture.wrapT 91 | 92 | func magFilter* (texture: naguAllTextures): naguTextureMagFilterParameter = texture.magFilter 93 | 94 | func minFilter* (texture: naguAllTextures): naguTextureMinFilterParameter = texture.minFilter 95 | 96 | proc `format=`* (texture: var naguBindedTexture, format: naguTextureFormat) = 97 | texture.format = format 98 | 99 | proc assignParameterBoiler (texture: var naguBindedTexture, name: opengl.GLenum, param: opengl.GLint) = 100 | debugOpenGLStatement: 101 | echo &"glTexParameteri(opengl.GL_TEXTURE_2D, {name.repr}, {param.repr})" 102 | opengl.glTexParameteri(opengl.GL_TEXTURE_2D, name, param) 103 | 104 | proc `wrapS=`* (texture: var naguBindedTexture, wrap_param: naguTextureWrapParameter) = 105 | texture.assignParameterBoiler(opengl.GL_TEXTURE_WRAP_S, opengl.GLint(wrap_param)) 106 | 107 | proc `wrapT=`* (texture: var naguBindedTexture, wrap_param: naguTextureWrapParameter) = 108 | texture.assignParameterBoiler(opengl.GL_TEXTURE_WRAP_T, opengl.GLint(wrap_param)) 109 | 110 | proc `magFilter=`* (texture: var naguBindedTexture, mag_filter_param: naguTextureMagFilterParameter) = 111 | texture.assignParameterBoiler(opengl.GL_TEXTURE_MAG_FILTER, opengl.GLint(mag_filter_param)) 112 | 113 | proc `minFilter=`* (texture: var naguBindedTexture, min_filter_param: naguTextureMinFilterParameter) = 114 | texture.assignParameterBoiler(opengl.GL_TEXTURE_MIN_FILTER, opengl.GLint(min_filter_param)) 115 | 116 | proc init* (_: typedesc[naguTexture], 117 | id: opengl.GLuint = 0, 118 | vao: VAO = nil, 119 | quad: naguTextureQuad = nil, 120 | uv: naguTextureUV = nil, 121 | elem: naguTextureElem = nil, 122 | model_matrix: array[4, ModelMatrixVector[16]], 123 | format: naguTextureFormat = tfRGBA, 124 | wrapS: naguTextureWrapParameter = naguTextureWrapParameter.tInitialValue, 125 | wrapT: naguTextureWrapParameter = naguTextureWrapParameter.tInitialValue, 126 | magFilter: naguTextureMagFilterParameter = naguTextureMagFilterParameter.tInitialValue, 127 | minFilter: naguTextureMinFilterParameter = naguTextureMinFilterParameter.tInitialValue, 128 | program: Program = nil): naguTexture = 129 | result = naguTexture( 130 | id: id, 131 | vao: vao, quad: quad, uv: uv, elem: elem, 132 | model_matrix: model_matrix, 133 | format: format, 134 | wrapS: wrapS, wrapT: wrapT, 135 | magFilter: magFilter, minFilter: minFilter, program: program 136 | ) 137 | opengl.glGenTextures(1, result.id.addr) 138 | opengl.glActiveTexture(opengl.GL_TEXTURE0) 139 | -------------------------------------------------------------------------------- /nagu/src/nagu/old_triangle.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/vbo.nim defines the Triangle type and procedures. 2 | 3 | from nimgl/opengl import nil 4 | from program import Program, mvpMatrix, identityMatrix, index 5 | from position import Position, map, coord 6 | from old_vbo import vboRef, VBO, make, init, `:=`, id 7 | from color import Color, rgb 8 | from std/sugar import `->`, `=>` 9 | from math import sin, cos, degToRad 10 | 11 | type 12 | vboRefForTriangle = vboRef[9, float32] 13 | triangleArray = array[9, float32] 14 | 15 | TriangleObj = object 16 | p1, p2, p3: Position 17 | c1, c2, c3: Color 18 | position_vbo: vboRefForTriangle 19 | color_vbo: vboRefForTriangle 20 | mvp_matrix: mvpMatrix 21 | 22 | Triangle* = ref TriangleObj 23 | ## Represents triangles. 24 | 25 | proc xAxisRotationMatrix (rotation: float32): mvpMatrix = [ 26 | 1.0f, 0, 0, 0, 27 | 0, cos(rotation), -sin(rotation), 0, 28 | 0, sin(rotation), cos(rotation), 0, 29 | 0, 0, 0, 1 30 | ] 31 | 32 | proc yAxisRotationMatrix (rotation: float32): mvpMatrix = [ 33 | cos(rotation), 0, sin(rotation), 0, 34 | 0, 1, 0, 0, 35 | -sin(rotation), 0, cos(rotation), 0, 36 | 0, 0, 0, 1 37 | ] 38 | 39 | proc zAxisRotationMatrix (rotation: float32): mvpMatrix = [ 40 | cos(rotation), -sin(rotation), 0, 0, 41 | sin(rotation), cos(rotation), 0, 0, 42 | 0, 0, 1, 0, 43 | 0, 0, 0, 1 44 | ] 45 | 46 | proc init* (_: typedesc[Triangle], 47 | p1: Position, c1: Color, 48 | p2: Position, c2: Color, 49 | p3: Position, c3: Color, 50 | x_axis_rotation: float32 = 0.0, 51 | y_axis_rotation: float32 = 0.0, 52 | z_axis_rotation: float32 = 0.0): Triangle = 53 | ## Initializes triangle by `p1`, `c1`, `p2`, `c2`, `p3`, `c3`, `x_axis_rotation`, `y_axis_rotation` and `z_axis_rotation`. 54 | let 55 | x_axis_rotation_matrix = xAxisRotationMatrix(x_axis_rotation.degToRad) 56 | y_axis_rotation_matrix = yAxisRotationMatrix(y_axis_rotation.degToRad) 57 | z_axis_rotation_matrix = zAxisRotationMatrix(z_axis_rotation.degToRad) 58 | result = Triangle( 59 | p1: p1, p2: p2, p3: p3, 60 | c1: c1, c2: c2, c3: c3, 61 | mvp_matrix: identityMatrix() # * x_axios_rotation_matrix 62 | ) 63 | 64 | proc init* (_: typedesc[Triangle], p1, p2, p3: Position, color: Color): Triangle = 65 | ## Initializes triangle by `p1`, `p2`, `p3` and `color`. 66 | result = Triangle( 67 | p1: p1, p2: p2, p3: p3, 68 | c1: color, c2: color, c3: color, 69 | mvp_matrix: identityMatrix() 70 | ) 71 | 72 | proc positionArray* (t: Triangle): triangleArray = 73 | let 74 | (p1x, p1y, p1z) = t.p1.coord 75 | (p2x, p2y, p2z) = t.p2.coord 76 | (p3x, p3y, p3z) = t.p3.coord 77 | result = [ 78 | p1x, p1y, p1z, 79 | p2x, p2y, p2z, 80 | p3x, p3y, p3z 81 | ] 82 | 83 | proc colorArray (t: Triangle): triangleArray = 84 | let 85 | (c1x, c1y, c1z) = t.c1.rgb 86 | (c2x, c2y, c2z) = t.c2.rgb 87 | (c3x, c3y, c3z) = t.c3.rgb 88 | result = [ 89 | c1x, c1y, c1z, 90 | c2x, c2y, c2z, 91 | c3x, c3y, c3z 92 | ] 93 | 94 | proc handle (t: Triangle): Triangle = 95 | result = t 96 | result.position_vbo = VBO.make(result.positionArray) 97 | result.color_vbo = VBO.make(result.colorArray) 98 | 99 | proc make* (_: typedesc[Triangle], p1, p2, p3: Position, color: Color): Triangle = 100 | ## Initializes and handles triangle by `p1`, `p2`, `p3` and `color`. 101 | result = Triangle.init(p1, p2, p3, color).handle() 102 | 103 | proc make* (_: typedesc[Triangle], 104 | p1: Position, c1: Color, 105 | p2: Position, c2: Color, 106 | p3: Position, c3: Color): Triangle = 107 | ## Initializes and handles triangle by `p1`, `c1`, `p2`, `c2`, `p3` and `c3`. 108 | result = Triangle.init(p1, c1, p2, c2, p3, c3).handle() 109 | 110 | func pMap* (t: Triangle, fn: Position -> Position): Triangle = 111 | ## Applies `fn` to each positions of `t`. 112 | result = t 113 | result.p1 = fn(t.p1) 114 | result.p2 = fn(t.p2) 115 | result.p3 = fn(t.p3) 116 | 117 | func cMap* (t: Triangle, fn: Color -> Color): Triangle = 118 | ## Applies `fn` to each colors of `t`. 119 | result = t 120 | result.c1 = fn(t.c1) 121 | result.c2 = fn(t.c2) 122 | result.c3 = fn(t.c3) 123 | 124 | proc `+=`* (t: var Triangle, value: float32) = 125 | ## Adds `value` to each positions of `t`. 126 | t = t.pMap(p => (p.map(v => v + value))) 127 | t.position_vbo := t.positionArray 128 | 129 | proc `-=`* (t: var Triangle, value: float32) = 130 | ## Subtracts `value` to each positions of `t`. 131 | t = t.pMap(p => (p.map(v => v - value))) 132 | t.position_vbo := t.positionArray 133 | 134 | proc `*=`* (t: var Triangle, value: float32) = 135 | ## Multiplies `value` to each positions of `t`. 136 | t = t.pMap(p => (p.map(v => v * value))) 137 | t.position_vbo := t.positionArray 138 | 139 | proc `/=`* (t: var Triangle, value: float32) = 140 | ## Divides `value` to each positions of `t`. 141 | t = t.pMap(p => (p.map(v => v / value))) 142 | t.position_vbo := t.positionArray 143 | 144 | proc correspondPartially (program: Program, vbo: vboRefForTriangle, name: string, size: int) = 145 | let index = opengl.GLuint(program.index(name)) 146 | opengl.glEnableVertexAttribArray(index) 147 | opengl.glBindBuffer(opengl.GL_ARRAY_BUFFER, vbo.id) 148 | opengl.glVertexAttribPointer(index, opengl.GLint(size), opengl.EGL_FLOAT, false, 0, nil) 149 | 150 | proc correspond* (program: Program, t: Triangle, position_name, color_name: string, size: int) = 151 | ## Ties `t` to `program`. 152 | program.correspondPartially(t.position_vbo, position_name, size) 153 | program.correspondPartially(t.color_vbo, color_name, size) 154 | -------------------------------------------------------------------------------- /src/mockuppkg/streaming.nim: -------------------------------------------------------------------------------- 1 | from ffmpeg import nil 2 | import nimgl/glfw 3 | import frames 4 | import videos 5 | 6 | proc initialize_avformat_context (format_name: string): ptr ffmpeg.AVFormatContext = 7 | result = nil 8 | if ffmpeg.avformat_alloc_output_context2(result.addr, nil, format_name, nil) < 0: 9 | raise newException(FFmpegError, "Could not allocate output format context!") 10 | 11 | proc initialize_io_context (fctx: ptr ffmpeg.AVFormatContext, output: string) = 12 | echo output 13 | if (fctx[].oformat[].flags and ffmpeg.AVFMT_NOFILE) == 0: 14 | let ret: cint = ffmpeg.avio_open2(fctx[].pb.addr, output, ffmpeg.AVIO_FLAG_WRITE, nil, nil) 15 | var error: array[1024, char] 16 | discard ffmpeg.av_strerror(ret, error[0].addr, 1024) 17 | # echo error 18 | if ret < 0: 19 | raise newException(FFmpegError, "Could not open output IO context!") 20 | 21 | proc set_codec_params (fctx: ptr ffmpeg.AVFormatContext, codec_ctx: ptr ffmpeg.AVCodecContext, video: mockupVideo) = 22 | 23 | codec_ctx[].codec_tag = 0 24 | codec_ctx[].codec_id = ffmpeg.AV_CODEC_ID_H264 25 | codec_ctx[].codec_type = ffmpeg.AVMEDIA_TYPE_VIDEO 26 | codec_ctx[].width = video.width 27 | codec_ctx[].height = video.height 28 | codec_ctx[].gop_size = 12 29 | codec_ctx[].pix_fmt = ffmpeg.AV_PIX_FMT_YUV420P 30 | codec_ctx[].framerate = ffmpeg.AVRational(num: 30, den: 1) 31 | codec_ctx[].time_base = ffmpeg.AVRational(num: 1, den: 1) # video.codecContext[].time_base 32 | # codec_ctx[].ticks_per_frame = 28 33 | # codec_ctx[].debug = 1 34 | if (fctx[].oformat[].flags and ffmpeg.AVFMT_GLOBALHEADER) != 0: 35 | codec_ctx[].flags = codec_ctx[].flags or ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER 36 | 37 | proc initialize_codec_stream (stream: ptr ffmpeg.AVStream, codec_ctx: ptr ffmpeg.AVCodecContext, codec: ptr ffmpeg.AVCodec) = 38 | if ffmpeg.avcodec_parameters_from_context(stream[].codecpar, codec_ctx) < 0: 39 | raise newException(FFmpegError, "Could not initialize stream codec parameters!") 40 | var codec_options: ptr ffmpeg.AVDictionary = nil 41 | 42 | discard ffmpeg.av_dict_set(codec_options.addr, "profile", "high", 0) 43 | discard ffmpeg.av_dict_set(codec_options.addr, "preset", "superfast", 0) 44 | discard ffmpeg.av_dict_set(codec_options.addr, "tune", "zerolatency", 0) 45 | 46 | if ffmpeg.avcodec_open2(codec_ctx, codec, codec_options.addr) < 0: 47 | raise newException(FFmpegError, "Could not open video encoder!") 48 | 49 | type mockupStreaming = object 50 | ofmt_ctx: ptr ffmpeg.AVFormatContext 51 | out_stream: ptr ffmpeg.AVStream 52 | out_codec_ctx: ptr ffmpeg.AVCodecContext 53 | 54 | proc initStreaming* (output: string, video: mockupVideo): mockupStreaming = 55 | result.ofmt_ctx = initialize_avformat_context("flv") 56 | result.ofmt_ctx.initialize_io_context(output) 57 | 58 | var out_codec = ffmpeg.avcodec_find_encoder(ffmpeg.AV_CODEC_ID_H264) 59 | 60 | result.out_stream = ffmpeg.avformat_new_stream(result.ofmt_ctx, out_codec) 61 | result.out_codec_ctx = ffmpeg.avcodec_alloc_context3(out_codec) 62 | 63 | set_codec_params(result.ofmt_ctx, result.out_codec_ctx, video) 64 | initialize_codec_stream(result.out_stream, result.out_codec_ctx, out_codec) 65 | 66 | result.out_stream[].codecpar[].extradata = result.out_codec_ctx[].extradata 67 | result.out_stream[].codecpar[].extradata_size = result.out_codec_ctx[].extradata_size 68 | 69 | if ffmpeg.avformat_write_header(result.ofmt_ctx, nil) < 0: 70 | raise newException(FFmpegError, "Could not write header!") 71 | 72 | var framenum = 1 73 | 74 | proc sendFrame* (streaming: mockupStreaming, src_frame: mockupFrame) = 75 | var 76 | frame = src_frame 77 | dest_frame = src_frame 78 | packet = ffmpeg.AVPacket() 79 | dest_frame.frame = src_frame.frame.prepareCopyFrame 80 | dest_frame.frame[].format = streaming.out_codec_ctx[].pix_fmt.cint 81 | if ffmpeg.av_frame_get_buffer(dest_frame.frame, 32) < 0: 82 | raise newException(FFmpegError, "バッファの割り当てに失敗しました") 83 | let context = ffmpeg.sws_getContext( 84 | streaming.out_codec_ctx.width, 85 | streaming.out_codec_ctx.height, 86 | ffmpeg.AV_PIX_FMT_RGBA, 87 | streaming.out_codec_ctx.width, 88 | streaming.out_codec_ctx.height, 89 | streaming.out_codec_ctx.pix_fmt, 90 | ffmpeg.SWS_BICUBIC, 91 | nil, nil, nil 92 | ) 93 | discard ffmpeg.sws_scale( 94 | context, 95 | frame.frame[].data[0].addr, 96 | frame.frame[].linesize[0].addr, 97 | 0, 98 | frame.frame[].height, 99 | dest_frame.frame[].data[0].addr, 100 | dest_frame.frame[].linesize[0].addr 101 | ) 102 | # dest_frame.frame.pts = ffmpeg.av_rescale_q( 103 | # src_frame.frame.pts, streaming.out_codec_ctx.time_base, streaming.out_stream[].time_base 104 | # ) 105 | dest_frame.frame.pts += ffmpeg.av_rescale_q(1, streaming.out_codec_ctx[].time_base, streaming.out_stream[].time_base) 106 | #dest_frame.frame.key_frame = 0 107 | #dest_frame.frame.pict_type = ffmpeg.AV_PICTURE_TYPE_NONE 108 | if ffmpeg.avcodec_send_frame(streaming.out_codec_ctx, dest_frame.frame) != 0: 109 | raise newException(FFmpegError, "エンコーダーへのフレームの供給に失敗しました") 110 | while ffmpeg.avcodec_receive_packet(streaming.out_codec_ctx, packet.addr) == 0: 111 | packet.stream_index = 0 112 | streaming.out_stream.time_base = ffmpeg.AVRational(num: 30, den: 1) 113 | ffmpeg.av_packet_rescale_ts( 114 | packet.addr, streaming.out_codec_ctx.time_base, streaming.out_stream.time_base 115 | ) 116 | framenum += 1 117 | if ffmpeg.av_interleaved_write_frame(streaming.ofmt_ctx, packet.addr) != 0: 118 | raise newException(FFmpegError, "パケットの書き込みに失敗しました") 119 | ffmpeg.av_packet_unref(packet.addr) 120 | 121 | proc finish* (streaming: mockupStreaming) = 122 | discard ffmpeg.av_write_trailer(streaming.ofmt_ctx) 123 | discard ffmpeg.avcodec_close(streaming.out_codec_ctx) 124 | discard ffmpeg.avio_close(streaming.ofmt_ctx[].pb) 125 | # ffmpeg.avformat_free_context(streaming.ofmt_ctx) -------------------------------------------------------------------------------- /src/mockup.nim: -------------------------------------------------------------------------------- 1 | import jester, uuids 2 | import std/[json, db_sqlite, strformat] 3 | import mockuppkg/[frames, opengl, utils, shaders, textures, streaming, triangle, encode_mp4, videos] 4 | import nimgl/opengl as gl 5 | import ffmpeg 6 | import muml 7 | import glm 8 | import nagu 9 | 10 | mumlDeserializer() 11 | 12 | proc encode = 13 | let muml = readMuml("assets/live/livecoding.json") 14 | let header = muml.parseHeader 15 | 16 | let _ = initializeOpenGL(header.width.GLsizei, header.height.GLsizei) 17 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f) 18 | 19 | let mumlElements = muml.deserialize() 20 | for element in mumlElements: 21 | if element of Video: 22 | echo Video(element)[] 23 | elif element of Triangle: 24 | echo Triangle(element)[] 25 | elif element of Rectangle: 26 | echo Rectangle(element)[] 27 | elif element of Text: 28 | echo Text(element)[] 29 | 30 | var video = mockupVideo.init( 31 | vec3(0, 0, 0), 32 | "assets/mockup.mp4", 33 | "shaders/textures/vertex/idFilter.glsl", 34 | "shaders/textures/fragment/idFilter.glsl" 35 | ) 36 | 37 | var output_mp4 = openMP4(header.outputPath, int32(header.width), int32(header.height), int32(header.fps)) 38 | 39 | video.seek(60000) 40 | 41 | for frame in video.decodeVideo(header): 42 | glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) 43 | var frame = frame 44 | frame.draw() 45 | 46 | var triangle = naguTriangle.init( 47 | header, 48 | [vec3(0, 0, 0), vec3(500, 0, 0), vec3(500, 500, 0)], 49 | [vec4(1f, 0, 0, 0), vec4(0f, 1, 0, 1), vec4(0f, 0, 1, 1)], 50 | "shaders/shapes/id/id.vert", 51 | "shaders/shapes/id/id.frag" 52 | ) 53 | triangle.use do (triangle: var naguBindedTriangle): 54 | triangle.draw(vdmTriangles) 55 | 56 | output_mp4.addFrame(readFrame(header.width.int32, header.height.int32).frame) 57 | 58 | output_mp4.close() 59 | 60 | proc preview (muml: mumlNode) = 61 | let _ = initializeOpenGL(1920, 1080) 62 | let header = muml.parseHeader 63 | 64 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f) 65 | 66 | let mumlElements = muml.deserialize() 67 | 68 | var video = mockupVideo.init( 69 | vec3(0, 0, 0), 70 | "assets/mockup.mp4", 71 | "shaders/textures/vertex/idFilter.glsl", 72 | "shaders/textures/fragment/idFilter.glsl" 73 | ) 74 | 75 | var output_mp4 = openMP4(header.outputPath, int32(header.width), int32(header.height), int32(header.fps)) 76 | 77 | for frame in video.decodeVideo(header): 78 | glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) 79 | var frame = frame 80 | frame.draw() 81 | 82 | for element in mumlElements: 83 | if element of Video: 84 | discard 85 | elif element of Triangle: 86 | let 87 | pos = Triangle(element).position[0] 88 | x = pos.x.int 89 | y = pos.y.int 90 | z = pos.z.int 91 | echo x, " ", y, " ", z 92 | var triangle = naguTriangle.init( 93 | header, 94 | [vec3(x, y, z), vec3(x+500, y, z), vec3(x+500, y+500, z)], 95 | [vec4(1f, 0, 0, 0), vec4(0f, 1, 0, 1), vec4(0f, 0, 1, 1)], 96 | "shaders/shapes/id/id.vert", 97 | "shaders/shapes/id/id.frag" 98 | ) 99 | triangle.use do (triangle: var naguBindedTriangle): 100 | triangle.draw(vdmTriangles) 101 | elif element of Rectangle: 102 | discard 103 | elif element of Text: 104 | discard 105 | 106 | output_mp4.addFrame(readFrame(header.width.int32, header.height.int32).frame) 107 | 108 | output_mp4.close() 109 | 110 | proc existsProject (db: DbConn, id: string): bool = 111 | result = db.getValue(sql"select id FROM projects where id = ?", id) == id 112 | 113 | template corsResp (message: untyped): untyped = 114 | mixin resp 115 | resp Http200, [("Access-Control-Allow-Origin", "*"), ("Access-Control-Allow-Headers", "Content-Type")], message 116 | 117 | template corsResp (code, message: untyped): untyped = 118 | mixin resp 119 | resp code, [("Access-Control-Allow-Origin", "*"), ("Access-Control-Allow-Headers", "Content-Type")], message 120 | 121 | template corsResp (code, header, message: untyped): untyped = 122 | mixin resp 123 | resp code, [("Access-Control-Allow-Origin", "*"), ("Access-Control-Allow-Headers", "Content-Type")] & header, message 124 | 125 | router mockup_router: 126 | post "/projects/new": 127 | let 128 | project_id = $genUUID() 129 | response = %*{ "project_id": project_id } 130 | db = open("db/mockup.db", "", "", "") 131 | db.exec sql"create table if not exists projects (id string, muml text)" 132 | db.exec(sql"insert into projects (id, muml) values (?, ?)", project_id, "") 133 | db.close() 134 | corsResp $response 135 | 136 | options "/projects/@id/update": 137 | corsResp Http200, "ok" 138 | 139 | post "/projects/@id/update": 140 | let 141 | db = open("db/mockup.db", "", "", "") 142 | id = @"id" 143 | if db.existsProject(id): 144 | try: 145 | let 146 | params = request.body.parseJson 147 | response = %*{ "message": &"プロジェクト{id}のmumlを更新しました" } 148 | db.exec(sql"update projects set muml=? where id=?", $params, $id) 149 | db.close() 150 | corsResp $response 151 | except Exception: 152 | let response = %*{ "message": getCurrentExceptionMsg() } 153 | corsResp Http500, $response 154 | else: 155 | db.close() 156 | let response = %*{ "message": "存在しないプロジェクトです" } 157 | corsResp Http400, $response 158 | 159 | get "/projects/@id/preview": 160 | let 161 | db = open("db/mockup.db", "", "", "") 162 | id = @"id" 163 | if db.existsProject(id): 164 | try: 165 | let 166 | response = %*{ "message": &"プロジェクト{id}のプレビューを要求しました" } 167 | muml = db.getValue(sql"select muml from projects where id = ?", id).parseJson 168 | echo muml 169 | db.close() 170 | preview(muml.readMuml) 171 | corsResp $response 172 | except Exception: 173 | echo getCurrentException().repr() 174 | let response = %*{ "message": getCurrentExceptionMsg() } 175 | corsResp Http500, $response 176 | else: 177 | db.close() 178 | let 179 | response = %*{ "message": "存在しないプロジェクトです" } 180 | corsResp Http400, $response 181 | 182 | get "/projects/@id/encode": 183 | encode() 184 | corsResp "エンコードの要求(動画パスを返却)" 185 | 186 | 187 | when isMainModule: 188 | import cligen 189 | 190 | proc serveMockup (): int = 191 | let settings = newSettings(port=Port(5001)) 192 | var jester = initJester(mockup_router, settings=settings) 193 | jester.serve() 194 | 195 | dispatchMulti( 196 | [serveMockup, cmdName = "serve"], 197 | [encode] 198 | ) 199 | -------------------------------------------------------------------------------- /src/mockuppkg/videos.nim: -------------------------------------------------------------------------------- 1 | import frames 2 | import ffmpeg 3 | import nagu 4 | import muml 5 | import glm 6 | import utils 7 | 8 | type 9 | mockupVideo* = object 10 | format_context: ptr AVFormatContext 11 | codec: ptr AVCodec 12 | codec_param: ptr AVCodecParameters 13 | codec_context: ptr AVCodecContext 14 | stream: ptr AVStream 15 | vertex_shader_path: string 16 | fragment_shader_path: string 17 | positions: array[4, Vec3[int]] 18 | frame_counter: tuple[start, stop: int] 19 | decoded_frames: seq[ptr AVFrame] 20 | 21 | proc width* (video: mockupVideo): int32 = 22 | result = video.codec_context.width.int32 23 | 24 | proc height* (video: mockupVideo): int32 = 25 | result = video.codec_context.height.int32 26 | 27 | proc formatConvert (src: ptr AVFrame, format_converter: ptr SwsContext): ptr AVFrame = 28 | result = prepareCopyFrame(src) 29 | result[].format = ffmpeg.AV_PIX_FMT_RGBA.cint 30 | if ffmpeg.av_frame_get_buffer(result, 32) < 0: 31 | raise newException(FFmpegError, "バッファの割り当てに失敗しました") 32 | discard ffmpeg.sws_scale( 33 | format_converter, 34 | src[].data[0].addr, 35 | src[].linesize[0].addr, 36 | 0, 37 | src[].height, 38 | result[].data[0].addr, 39 | result[].linesize[0].addr 40 | ) 41 | 42 | proc init* (T: type mockupVideo, 43 | position: Vec3[int], 44 | src_path: string, 45 | vertex_shader_path: string, 46 | fragment_shader_path: string): mockupVideo = 47 | result.vertex_shader_path = vertex_shader_path 48 | result.fragment_shader_path = fragment_shader_path 49 | result.frame_counter = (-1, -1) 50 | if avformat_open_input(result.format_context.addr, src_path, nil, nil) != 0: 51 | raise newException(Defect, "動画ファイルを開けませんでした") 52 | 53 | if avformat_find_stream_info(result.format_context, nil) < 0: 54 | raise newException(Defect, "ストリーム情報取得に失敗しました") 55 | 56 | let streams = cast[ptr UncheckedArray[ptr AVStream]](result.format_context[].streams) 57 | for stream_index in 0 ..< result.format_context[].nb_streams: 58 | let locpar = streams[stream_index][].codecpar 59 | if locpar[].codec_type == AVMEDIA_TYPE_VIDEO: 60 | result.codec = avcodec_find_decoder(locpar[].codec_id) 61 | result.codec_param = locpar 62 | result.stream = streams[stream_index] 63 | break 64 | if result.codec.isNil or result.codec_param.isNil: 65 | raise newException(Defect, "デコーダの取得に失敗しました") 66 | 67 | result.codec_context = avcodec_alloc_context3(result.codec) 68 | 69 | if avcodec_parameters_to_context(result.codec_context, result.codec_param) < 0: 70 | raise newException(Defect, "コーデックパラメータの初期化に失敗しました") 71 | 72 | if avcodec_open2(result.codec_context, result.codec, nil) < 0: 73 | raise newException(Defect, "コーデックの初期化に失敗しました") 74 | 75 | let (x, y, z) = ( 76 | (result.width) div 2 + position[0], 77 | (result.height) div 2 + position[1], 78 | position[2] 79 | ) 80 | result.positions = [ 81 | vec3(-x, y, z), 82 | vec3(-x, -y, z), 83 | vec3(x, -y, z), 84 | vec3(x, y, z), 85 | ] 86 | 87 | func frameNumber (video: mockupVideo): int = 88 | # `video`が持つフレーム数を返します 89 | result = video.codec_context[].frame_number 90 | 91 | proc decode* (video: var mockupVideo, header: mumlHeader, frameCount: int): mockupFrame = 92 | var 93 | packet = AVPacket() 94 | format_converter = sws_getContext( 95 | video.codec_context[].width, 96 | video.codec_context[].height, 97 | video.codec_context[].pix_fmt, 98 | video.codec_context[].width, 99 | video.codec_context[].height, 100 | AV_PIX_FMT_RGBA, 101 | SWS_BICUBIC, 102 | nil, nil, nil 103 | ) 104 | var image = mockupFrame.init( 105 | header, 106 | video.positions, 107 | video.vertex_shader_path, 108 | video.fragment_shader_path 109 | ) 110 | 111 | 112 | if video.frame_counter.start <= frameCount and frameCount <= video.frame_counter.stop: 113 | var frame = video.decoded_frames[frameCount - video.frame_counter.start] 114 | image.naguTexture.use do (texture: var naguBindedTexture): 115 | texture.pixels = (data: frame.data[0], width: frame[].width.uint, height: frame[].height.uint) 116 | return image 117 | 118 | var frame_counter = video.frame_counter.stop 119 | video.frame_counter.start = video.frame_counter.stop + 1 120 | video.decoded_frames = @[] 121 | 122 | while av_read_frame(video.format_context, packet.addr) == 0: 123 | if packet.stream_index != video.stream.index: 124 | av_packet_unref(packet.addr) 125 | continue 126 | if avcodec_send_packet(video.codec_context, packet.addr) != 0: 127 | raise newException(Defect, "パケットの取り出しに失敗しました") 128 | var frame = av_frame_alloc() 129 | while avcodec_receive_frame(video.codec_context, frame) == 0: 130 | var 131 | copy_frame = frame.copy # 元ポインタを直接操作するとバグる 132 | frame_RGBA = copy_frame.formatConvert(format_converter) # RGBAに変換する 133 | frame_counter += 1 134 | video.decoded_frames.add frame_RGBA 135 | av_frame_free(frame.addr) 136 | av_packet_unref(packet.addr) 137 | video.frame_counter.stop = frame_counter 138 | 139 | echo frame_counter 140 | var frame = video.decoded_frames[0] 141 | image.naguTexture.use do (texture: var naguBindedTexture): 142 | texture.pixels = (data: frame.data[0], width: frame[].width.uint, height: frame[].height.uint) 143 | return image 144 | 145 | iterator decodeVideo* (video: var mockupVideo, header: mumlHeader): mockupFrame = 146 | var 147 | packet = AVPacket() 148 | format_converter = sws_getContext( 149 | video.codec_context[].width, 150 | video.codec_context[].height, 151 | video.codec_context[].pix_fmt, 152 | video.codec_context[].width, 153 | video.codec_context[].height, 154 | AV_PIX_FMT_RGBA, 155 | SWS_BICUBIC, 156 | nil, nil, nil 157 | ) 158 | var image = mockupFrame.init( 159 | header, 160 | video.positions, 161 | video.vertex_shader_path, 162 | video.fragment_shader_path 163 | ) 164 | while av_read_frame(video.format_context, packet.addr) == 0: 165 | if packet.stream_index != video.stream.index: 166 | av_packet_unref(packet.addr) 167 | continue 168 | if avcodec_send_packet(video.codec_context, packet.addr) != 0: 169 | raise newException(Defect, "パケットの取り出しに失敗しました") 170 | var frame = av_frame_alloc() 171 | while avcodec_receive_frame(video.codec_context, frame) == 0: 172 | var 173 | copy_frame = frame.copy # 元ポインタを直接操作するとバグる 174 | frame_RGBA = copy_frame.formatConvert(format_converter) # RGBAに変換する 175 | image.naguTexture.use do (texture: var naguBindedTexture): 176 | texture.pixels = (data: frame_RGBA.data[0], width: frame_RGBA[].width.uint, height: frame_RGBA[].height.uint) 177 | 178 | yield image 179 | av_frame_free(frame.addr) 180 | av_packet_unref(packet.addr) 181 | 182 | proc seek* (video: var mockupVideo, frame: int) = 183 | if av_seek_frame(video.format_context, video.stream[].index, frame, AVSEEK_FLAG_BACKWARD) < 0: 184 | echo "av_seek_frame failed" 185 | avcodec_flush_buffers(video.codec_context) 186 | -------------------------------------------------------------------------------- /nagu/src/nagu/shape.nim: -------------------------------------------------------------------------------- 1 | import vao, vbo, mvp_matrix, shader, program 2 | import glm 3 | import std/strformat 4 | 5 | type 6 | ShapeObj* [ 7 | binded: static bool, 8 | vertex_num: static int, 9 | vertex_num_3x: static int, 10 | vertex_num_4x: static int 11 | ] = object 12 | vao: VAO 13 | positions: VBO[vertex_num_3x, float32] # 本来は 3*V 14 | colors: VBO[vertex_num_4x, float32] # 本来は 4*V 15 | model_matrix: array[4, VBO[vertex_num_4x, float32]] # 本来は 4*V 16 | program: Program 17 | 18 | Shape* [ 19 | vertex_num: static int, 20 | vertex_num_3x: static int, 21 | vertex_num_4x: static int 22 | ] = ref ShapeObj[false, vertex_num, vertex_num_3x, vertex_num_4x] 23 | 24 | # Shape* [vertex_num, vertex_num_3x, vertex_num_4x: static int] = concept x 25 | # vertex_num * 3 == vertex_num_3x 26 | # vertex_num * 4 == vertex_num_4x 27 | # x is ShapeBase 28 | 29 | BindedShape* [ 30 | vertex_num: static int, 31 | vertex_num_3x: static int, 32 | vertex_num_4x: static int 33 | ] = ref ShapeObj[true, vertex_num, vertex_num_3x, vertex_num_4x] 34 | 35 | proc `$`* [V, V3x, V4x: static int] (shape: Shape[V, V3x, V4x] | BindedShape[V, V3x, V4x]): string = 36 | result = &"Shape[{V}, {V3x}, {V4x}]" & "\n" 37 | result &= $shape.vao[] & "\n" 38 | result &= $shape.positions[] & "\n" 39 | result &= $shape.colors[] & "\n" 40 | result &= $shape.model_matrix[0][] & "\n" 41 | result &= $shape.model_matrix[1][] & "\n" 42 | result &= $shape.model_matrix[2][] & "\n" 43 | result &= $shape.model_matrix[3][] & "\n" 44 | result &= $shape.program[] & "\n" 45 | 46 | func toBindedShape [V, V3x, V4x: static int] (shape: Shape[V, V3x, V4x]): BindedShape[V, V3x, V4x] = 47 | result = BindedShape[V, V3x, V4x]( 48 | vao: shape.vao, 49 | positions: shape.positions, 50 | colors: shape.colors, 51 | model_matrix: shape.model_matrix, 52 | program: shape.program 53 | ) 54 | 55 | func toShape [V, V3x, V4x: static int] (shape: BindedShape[V, V3x, V4x]): Shape[V, V3x, V4x] = 56 | result = Shape[V, V3x, V4x]( 57 | vao: shape.vao, 58 | positions: shape.positions, 59 | colors: shape.colors, 60 | model_matrix: shape.model_matrix, 61 | program: shape.program 62 | ) 63 | 64 | proc use* [V, V3x, V4x: static int] (shape: var Shape[V, V3x, V4x], procedure: proc (shape: var BindedShape[V, V3x, V4x])) = 65 | discard shape.program.bind() 66 | discard shape.vao.bind() 67 | var bindedShape = shape.toBindedShape 68 | procedure(bindedShape) 69 | shape = bindedShape.toShape 70 | # Programをunbindする? 71 | 72 | proc useVAO* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], procedure: proc (shape: var BindedShape[V, V3x, V4x], vao: var BindedVAO)) = 73 | var bindedVAO = shape.vao.bind() 74 | procedure(shape, bindedVAO) 75 | unbind() # TODO: 本当は変更後のBindedVAOを代入する方が良い(ここでは必要ない) 76 | shape = shape 77 | # shape.vao = bindedVAO.unbind() 78 | 79 | proc usePositions* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], procedure: proc (shape: var BindedShape[V, V3x, V4x], vbo: var BindedVBO[V3x, float32])) = 80 | var bindedVBO = shape.positions.bind() 81 | procedure(shape, bindedVBO) 82 | shape = shape 83 | shape.positions = bindedVBO.unbind() 84 | 85 | proc usePositions* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], procedure: proc (shape: var BindedShape[V, V3x, V4x], vao: var BindedVAO, vbo: var BindedVBO[V3x, float32])) = 86 | var 87 | bindedVAO = shape.vao.bind() 88 | bindedVBO = shape.positions.bind() 89 | procedure(shape, bindedVAO, bindedVBO) 90 | shape.positions = bindedVBO.unbind() 91 | 92 | proc useColors* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], procedure: proc (shape: var BindedShape[V, V3x, V4x], vbo: var BindedVBO[V4x, float32])) = 93 | var bindedVBO = shape.colors.bind() 94 | procedure(shape, bindedVBO) 95 | shape = shape 96 | shape.colors = bindedVBO.unbind() 97 | 98 | func toArray [V, V3x, V4x: static int] (_: BindedShape[V, V3x, V4x], vector: array[V, Vec3[float32]]): array[V3x, float32] = 99 | for vec_index, vec in vector: 100 | for elem_index, elem in vec.arr: 101 | result[vec_index*3 + elem_index] = elem 102 | 103 | func toArray [V, V3x, V4x: static int] (_: BindedShape[V, V3x, V4x], vector: array[V, Vec4[float32]]): array[V4x, float32] = 104 | for vec_index, vec in vector: 105 | for elem_index, elem in vec.arr: 106 | result[vec_index*4 + elem_index] = elem 107 | 108 | proc useModelMatrixVector* [V, V3x, V4x: static int] (bindedShape: var BindedShape[V, V3x, V4x], index: range[0..3], procedure: proc (shape: var BindedShape[V, V3x, V4x], vbo: var BindedModelMatrixVector[V4x])) = 109 | var bindedVBO = bindedShape.model_matrix[index].bind() 110 | procedure(bindedShape, bindedVBO) 111 | bindedShape.model_matrix[index] = bindedVBO.unbind() 112 | 113 | proc setModelMatrix* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], matrix4v: array[16, float32]) = 114 | for index in 0 ..< 4: 115 | var matrix: array[V4x, float32] 116 | for matrix_gen_index in 0 ..< V: 117 | matrix[matrix_gen_index*4] = matrix4v[index*4] 118 | matrix[matrix_gen_index*4+1] = matrix4v[index*4+1] 119 | matrix[matrix_gen_index*4+2] = matrix4v[index*4+2] 120 | matrix[matrix_gen_index*4+3] = matrix4v[index*4+3] 121 | shape.useModelMatrixVector(index) do (shape: var BindedShape[V, V3x, V4x], vbo: var BindedModelMatrixVector[V4x]): 122 | `data=`(vbo, matrix) 123 | shape.program[&"modelMatrixVec{index+1}"] = (vbo, 4) 124 | 125 | proc toArray[T] (matrix4v: Mat4[T]): array[16, T] = 126 | for vec_index, vec in matrix4v.arr: 127 | for elem_index, elem in vec.arr: 128 | result[vec_index * 4 + elem_index] = elem 129 | 130 | proc make* [V, V3x, V4x: static int] ( 131 | _: typedesc[Shape[V, V3x, V4x]], 132 | positions: array[V, Vec3[float32]], 133 | colors: array[V, Vec4[float32]], 134 | vertex_shader_path: string, 135 | fragment_shader_path: string, 136 | mvpMatrix = identityMatrix() 137 | ): Shape[V, V3x, V4x] = 138 | result = Shape[V, V3x, V4x]( 139 | vao: VAO.init(), 140 | positions: VBO[V3x, float32].init(), 141 | colors: VBO[V4x, float32].init(), 142 | model_matrix: ModelMatrix[V4x].init() 143 | ) 144 | ## FIXME: depend on VBO[16, float32] 145 | 146 | let 147 | vertex_shader = ShaderObject.make(soVertex, vertex_shader_path) 148 | fragment_shader = ShaderObject.make(soFragment, fragment_shader_path) 149 | 150 | result.program = Program.make( 151 | vertex_shader, 152 | fragment_shader, 153 | @["vertexPositions", "vertexColors", "modelMatrixVec1", "modelMatrixVec2", "modelMatrixVec3", "modelMatrixVec4"], 154 | @["mvpMatrix"], 155 | ) 156 | 157 | discard result.program.bind() 158 | 159 | result.use do (shape: var BindedShape[V, V3x, V4x]): 160 | shape.usePositions do (shape: var BindedShape[V, V3x, V4x], vbo: var BindedVBO[V3x, float32]): 161 | `data=`(vbo, toArray(shape, positions)) 162 | shape.program["vertexPositions"] = (vbo, 3) 163 | shape.useColors do (shape: var BindedShape[V, V3x, V4x], vbo: var BindedVBO[V4x, float32]): 164 | `data=`(vbo, toArray(shape, colors)) 165 | shape.program["vertexColors"] = (vbo, 4) 166 | 167 | shape.setModelMatrix(identityMatrix()) 168 | shape.program["mvpMatrix"] = mvpMatrix 169 | 170 | proc draw* [V, V3x, V4x: static int] (shape: var BindedShape[V, V3x, V4x], mode: VAODrawMode) = 171 | shape.usePositions do (shape: var BindedShape[V, V3x, V4x], vao: var BindedVAO, vbo: var BindedVBO[V3x, float32]): 172 | vao.draw(V, mode) 173 | -------------------------------------------------------------------------------- /nagu/src/nagu/texture.nim: -------------------------------------------------------------------------------- 1 | from nimgl/opengl import nil 2 | import glm 3 | import vao, vbo, program, shader, utils, position, mvp_matrix 4 | import types/texture 5 | import strformat 6 | 7 | import tables 8 | 9 | proc `bind` (texture: var naguTexture): naguBindedTexture = 10 | opengl.glBindTexture(opengl.GL_TEXTURE_2D, texture.id) 11 | result = texture.toBindedTexture 12 | 13 | debugOpenGLStatement: 14 | echo &"glBindTexture(GL_TEXTURE_2D, {texture.id})" 15 | 16 | proc unbind (bindedTexture: var naguBindedTexture): naguTexture = 17 | opengl.glBindTexture(opengl.GL_TEXTURE_2D, 0) 18 | result = bindedTexture.toTexture 19 | 20 | debugOpenGLStatement: 21 | echo &"glBindTexture(GL_TEXTURE_2D, 0)" 22 | 23 | proc use* (texture: var naguTexture, procedure: proc (texture: var naguBindedTexture)) = 24 | var bindedTexture = texture.bind() 25 | discard bindedTexture.program.bind() 26 | bindedTexture.procedure() 27 | texture = bindedTexture.unbind() 28 | 29 | proc useVAO* (bindedTexture: var naguBindedTexture, procedure: proc (texture: var naguBindedTexture, vao: var BindedVAO)) = 30 | var bindedVAO = bindedTexture.vao.bind() 31 | procedure(bindedTexture, bindedVAO) 32 | vao.unbind() 33 | 34 | proc useElem* (bindedTexture: var naguBindedTexture, procedure: proc (texture: var naguBindedTexture, vbo: var naguBindedTextureElem)) = 35 | var bindedVBO = bindedTexture.elem.bind() 36 | procedure(bindedTexture, bindedVBO) 37 | bindedTexture.elem = bindedVBO.unbind() 38 | 39 | proc useQuad* (bindedTexture: var naguBindedTexture, procedure: proc (texture: var naguBindedTexture, vbo: var naguBindedTextureQuad)) = 40 | var bindedVBO = bindedTexture.quad.bind() 41 | procedure(bindedTexture, bindedVBO) 42 | bindedTexture.quad = bindedVBO.unbind() 43 | 44 | proc useUV* (bindedTexture: var naguBindedTexture, procedure: proc (texture: var naguBindedTexture, vbo: var naguBindedTextureUV)) = 45 | var bindedVBO = bindedTexture.uv.bind() 46 | procedure(bindedTexture, bindedVBO) 47 | bindedTexture.uv = bindedVBO.unbind() 48 | 49 | proc useModelMatrix* (bindedTexture: var naguBindedTexture, procedure: proc (texture: var naguBindedTexture, vbo: var array[4, BindedModelMatrixVector[16]])) = 50 | discard 51 | # var 52 | # bindedVec1VBO = bindedTexture.model_matrix[0].bind() 53 | # procedure(bindedTexture, bindedVBO) 54 | # bindedTexture.model_matrix[index] = bindedVBO.unbind() 55 | # めっちゃ難しい。行列に対する操作をしつつbind管理もしなければいけない 56 | # 仮想的な行列を持って置いて、代入するタイミングで順番にuseModelMatrixVectorを回すのが良いのかも。 57 | 58 | proc useModelMatrixVector* (bindedTexture: var naguBindedTexture, index: range[0..3], procedure: proc (texture: var naguBindedTexture, vbo: var BindedModelMatrixVector[16])) = 59 | var bindedVBO = bindedTexture.model_matrix[index].bind() 60 | procedure(bindedTexture, bindedVBO) 61 | bindedTexture.model_matrix[index] = bindedVBO.unbind() 62 | 63 | proc textureImage2DOrSubImage2D (texture: var naguBindedTexture, format: naguTextureFormat, width, height: uint, pixels_pointer: ptr uint8) = 64 | if texture.initializedPixels: 65 | opengl.glTexSubImage2D( 66 | opengl.GL_TEXTURE_2D, 0, 0, 0, 67 | opengl.GLsizei(width), opengl.GLsizei(height), 68 | opengl.GLenum(format), opengl.GL_UNSIGNED_BYTE, pixels_pointer 69 | ) 70 | debugOpenGLStatement: 71 | echo &"glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, {width}, {height}, GL_RGB, GL_UNSIGNED_BYTE, pixels)" 72 | else: 73 | opengl.glTexImage2D( 74 | opengl.GL_TEXTURE_2D, 0, opengl.GLint(opengl.GLenum(format)), 75 | opengl.GLsizei(width), opengl.GLsizei(height), 0, 76 | opengl.GLenum(format), opengl.GL_UNSIGNED_BYTE, pixels_pointer 77 | ) 78 | texture.initializedPixels = true 79 | debugOpenGLStatement: 80 | echo &"glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, {width}, {height}, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels)" 81 | 82 | proc `pixels=`* [W, H: static uint] (texture: var naguBindedTexture, img: array[H, array[W, array[3, uint8]]]) = 83 | var data = img 84 | texture.textureImage2DOrSubImage2D(W, H, data[0].addr) 85 | 86 | proc `pixels=`* (texture: var naguBindedTexture, img: tuple[data: seq[uint8], width: uint, height: uint]) = 87 | var data = img.data 88 | texture.textureImage2DOrSubImage2D(texture.format, img.width, img.height, data[0].addr) 89 | 90 | proc `pixels=`* (texture: var naguBindedTexture, img: tuple[data: ptr uint8, width: uint, height: uint]) = 91 | texture.textureImage2DOrSubImage2D(texture.format, img.width, img.height, img.data) 92 | 93 | proc draw* (texture: var naguBindedTexture) = 94 | texture.useVAO do (texture: var naguBindedTexture, vao: var BindedVAO): 95 | # texture.useElem do (texture: var naguBindedTexture, vbo: var BindedTextureElem): 96 | opengl.glDrawArrays(opengl.GLenum(vdmTriangleFan), 0, 4) 97 | 98 | debugOpenGLStatement: 99 | echo &"glDrawArrays(vdmTriangleFan, 0, 4)" 100 | 101 | proc pixelStore (texture: var naguBindedTexture, pname: opengl.GLenum, param: opengl.GLint) = 102 | opengl.glPixelStorei(pname, param) 103 | 104 | debugOpenGLStatement: 105 | echo &"glPixelStorei({pname.repr}, {param.repr})" 106 | 107 | func quad (positions: array[4, Vec3[float32]]): array[12, float32] = 108 | var index = 0 109 | for position in positions: 110 | result[index*3] = position[0] 111 | result[index*3+1] = position[1] 112 | result[index*3+2] = position[2] 113 | index += 1 114 | 115 | func uv: array[8, float32] = [ 116 | 0.0'f32, 1.0, 117 | 0.0, 0.0, 118 | 1.0, 0.0, 119 | 1.0, 1.0 120 | ] 121 | 122 | func elem: array[6, uint8] = [ 123 | 0'u8, 1, 2, 0, 2, 3 124 | ] 125 | 126 | # proc `[]=`* (texture: var naguBindedTexture, name: string, matrix4v: array[16, float32]) = 127 | # そのうち一般化する、VBO名に依存したuse関数を作るのをやめる、customなvertex attrib変数に対して 128 | # 適用できるUse関数を定義する 129 | # VBOを専用のフィールドで持つのではなくテーブルに持っておくと良いと思う 130 | proc setModelMatrix* (texture: var naguBindedTexture, matrix4v: array[16, float32]) = 131 | for index in 0 ..< 4: 132 | let matrix = [ 133 | matrix4v[index*4], matrix4v[index*4+1], matrix4v[index*4+2], matrix4v[index*4+3], 134 | matrix4v[index*4], matrix4v[index*4+1], matrix4v[index*4+2], matrix4v[index*4+3], 135 | matrix4v[index*4], matrix4v[index*4+1], matrix4v[index*4+2], matrix4v[index*4+3], 136 | matrix4v[index*4], matrix4v[index*4+1], matrix4v[index*4+2], matrix4v[index*4+3], 137 | ] 138 | texture.useModelMatrixVector(index) do (texture: var naguBindedTexture, vbo: var BindedModelMatrixVector[16]): 139 | vbo.data = matrix 140 | texture.program[&"modelMatrixVec{index+1}"] = (vbo, 4) 141 | 142 | proc toArray[T] (matrix4v: Mat4[T]): array[16, T] = 143 | for vec_index, vec in matrix4v.arr: 144 | for elem_index, elem in vec.arr: 145 | result[vec_index * 4 + elem_index] = elem 146 | 147 | proc setModelMatrix* (texture: var naguBindedTexture, matrix4v: Mat4[float32]) = 148 | setModelMatrix(texture, matrix4v.toArray) 149 | 150 | proc make* (_: typedesc[naguTexture], 151 | positions: array[4, Vec3[float32]], 152 | vertex_shader_path: string, 153 | fragment_shader_path: string, 154 | mvpMatrix = identityMatrix() 155 | ): naguTexture = 156 | 157 | result = naguTexture.init( 158 | vao = VAO.init(), 159 | quad = naguTextureQuad.init(), 160 | uv = naguTextureUV.init(), 161 | elem = naguTextureElem.init(), 162 | model_matrix = ModelMatrix[16].init() 163 | ) 164 | 165 | let 166 | vertex_shader = ShaderObject.make(soVertex, vertex_shader_path) 167 | fragment_shader = ShaderObject.make(soFragment, fragment_shader_path) 168 | 169 | result.program = Program.make( 170 | vertex_shader, 171 | fragment_shader, 172 | @["vertex", "texCoord0", "modelMatrixVec1", "modelMatrixVec2", "modelMatrixVec3", "modelMatrixVec4"], 173 | @["frameTex", "mvpMatrix"], 174 | # @[(soVertex, "mvpMatrix")] 175 | ) 176 | 177 | result.use do (texture: var naguBindedTexture): 178 | texture.useVAO do (texture: var naguBindedTexture, vao: var BindedVAO): 179 | texture.pixelStore(opengl.GL_UNPACK_ALIGNMENT, 1) 180 | 181 | texture.useQuad do (texture: var naguBindedTexture, vbo: var naguBindedTextureQuad): 182 | `data=`(vbo, quad(positions)) 183 | texture.program["vertex"] = (vbo, 3) 184 | 185 | texture.useUV do (texture: var naguBindedTexture, vbo: var naguBindedTextureUV): 186 | `data=`(vbo, uv()) 187 | texture.program["texCoord0"] = (vbo, 2) 188 | 189 | texture.useElem do (texture: var naguBindedTexture, vbo: var naguBindedTextureElem): 190 | var elem = elem() 191 | `target=`(vbo, vtArrayBuffer) 192 | `usage=`(vbo, vuStaticDraw) 193 | `data=`(vbo, elem) 194 | 195 | texture.program["frameTex"] = 0 196 | 197 | texture.setModelMatrix(identityMatrix()) 198 | texture.program["mvpMatrix"] = mvpMatrix 199 | 200 | `wrapS=`(texture, tRepeat) 201 | `wrapT=`(texture, tRepeat) 202 | `magFilter=`(texture, naguTextureMagFilterParameter.tLinear) 203 | `minFilter=`(texture, naguTextureMinFilterParameter.tLinear) 204 | -------------------------------------------------------------------------------- /nagu/src/nagu/program.nim: -------------------------------------------------------------------------------- 1 | ## src/nagu/program.nim defines the ProgramObject type and procedures related to its for abstracting OpenGL program. 2 | 3 | from nimgl/opengl import nil 4 | from shader import ShaderObject, id, ShaderObjectKind, convertGLExpression 5 | from opengl as naguOpengl import OpenGLDefect 6 | from std/tables import Table, initTable, len, `[]`, `[]=` 7 | from std/strformat import `&` 8 | import utils, vbo 9 | 10 | type 11 | ProgramVariableKind* = enum 12 | pvkAttrib, 13 | pvkUniform, 14 | pvkSubroutineUniform 15 | 16 | ProgramObj [binded: static bool] = object 17 | id: opengl.GLuint 18 | linked: bool 19 | nameToIndex*: Table[string, tuple[index: int, kind: ProgramVariableKind]] 20 | 21 | Program* = ref ProgramObj[false] 22 | ## The ProgramObject type representations OpenGL program object. 23 | 24 | BindedProgram* = ref ProgramObj[true] 25 | 26 | ProgramDefect* = object of OpenGLDefect 27 | ## Raised by something with OpenGL programs. 28 | 29 | ProgramCreationDefect* = object of ProgramDefect 30 | ## Raised by creating OpenGL programs. 31 | 32 | ProgramLinkingDefect* = object of ProgramDefect 33 | ## Raised by linking OpenGL programs. 34 | 35 | ProgramNotExistsActiveUniformDefect* = object of ProgramDefect 36 | ## Raised by the condition that program don't have active uniform variables. 37 | 38 | ProgramNotExistsActiveSubroutineUniformDefect* = object of ProgramDefect 39 | 40 | mvpMatrix* = array[16, float32] 41 | ## Represents model view projection matrixes. 42 | 43 | func identityMatrix*: mvpMatrix = [ 44 | 1.0'f, 0.0, 0.0, 0.0, 45 | 0.0, 1.0, 0.0, 0.0, 46 | 0.0, 0.0, 1.0, 0.0, 47 | 0.0, 0.0, 0.0, 1.0 48 | ] 49 | ## Matrix which value does not change when applied. 50 | 51 | proc init* (_: typedesc[Program]): Program = 52 | ## Initializes ProgramObject. 53 | let program = opengl.glCreateProgram() 54 | if program == opengl.GLuint(0): 55 | raise newException(ProgramCreationDefect, "Failed to create the program for some reason.") 56 | result = Program( 57 | id: program, 58 | linked: false, 59 | nameToIndex: initTable[string, (int, ProgramVariableKind)]() 60 | ) 61 | 62 | func id* (program: Program | BindedProgram): opengl.GLuint = 63 | ## Gets id of `program`. 64 | result = program.id 65 | 66 | func linked* (program: Program | BindedProgram): bool = 67 | ## Gets `program` linked or not. 68 | result = program.linked 69 | 70 | func index* (program: Program | BindedProgram, name: string): int = 71 | ## Queries `program` for `name` and corresponding index. 72 | result = program.nameToIndex[name].index 73 | 74 | # FIXME: Programを消す 75 | proc attach* (program: Program | var BindedProgram, shader: ShaderObject) = 76 | ## Attach `shader` to `program`. 77 | debugOpenGLStatement: 78 | echo &"glAttachShader({program.id}, {shader.id})" 79 | opengl.glAttachShader(program.id, opengl.GLuint(shader.id)) 80 | 81 | proc successLink (program: Program | BindedProgram): bool = 82 | var status: opengl.GLint 83 | opengl.glGetProgramiv(program.id, opengl.GL_LINK_STATUS, status.addr) 84 | debugOpenGLStatement: 85 | echo &"glGetProgramiv({program.id}, GL_LINK_STATUS, {status})" 86 | result = status == opengl.GLint(opengl.GL_TRUE) 87 | 88 | # FIXME: PRogramを消す 89 | proc log* (program: var Program | var BindedProgram): string = 90 | ## Gets logs about OpenGL programs. 91 | var log_length: opengl.GLint 92 | opengl.glGetProgramiv(program.id, opengl.GL_INFO_LOG_LENGTH, log_length.addr) 93 | if log_length.int > 0: 94 | var 95 | log: array[1000, cchar] 96 | written_length: opengl.GLsizei 97 | opengl.glGetProgramInfoLog(program.id, log_length, written_length.addr, log[0].addr) 98 | for logchar in log: 99 | if not (logchar == '\x00'): 100 | result.add logchar 101 | 102 | func toBindedProgram (program: Program): BindedProgram = 103 | result = BindedProgram( 104 | id: program.id, 105 | linked: program.linked, 106 | nameToIndex: program.nameToIndex 107 | ) 108 | 109 | func toProgram (program: BindedProgram): Program = 110 | result = Program( 111 | id: program.id, 112 | linked: program.linked, 113 | nameToIndex: program.nameToIndex 114 | ) 115 | 116 | proc `bind`* (program: var Program): BindedProgram = 117 | ## Use `program` if it is linked. 118 | if program.linked: 119 | debugOpenGLStatement: 120 | echo &"glUseProgram({program.id})" 121 | opengl.glUseProgram(program.id) 122 | result = program.toBindedProgram 123 | 124 | proc unbind* (program: var BindedProgram): Program = 125 | opengl.glUseProgram(0) 126 | debugOpenGLStatement: 127 | echo "glUseProgram(0)" 128 | result = program.toProgram 129 | 130 | proc use* (program: var Program, procedure: proc (program: var BindedProgram)) = 131 | ## Use `program` if it is linked. 132 | var bindedProgram = program.bind() 133 | procedure(bindedProgram) 134 | program = bindedProgram.unbind() 135 | 136 | # FIXME: Programを消す 137 | proc link* (program: var Program | var BindedProgram) = 138 | ## Links `program`. 139 | debugOpenGLStatement: 140 | echo &"glLinkProgram({program.id})" 141 | opengl.glLinkProgram(program.id) 142 | if program.successLink: 143 | program.linked = true 144 | else: 145 | raise newException(ProgramLinkingDefect, "Failed to link shader program: " & program.log) 146 | 147 | # FIXME: Programを消す 148 | proc registerAttrib* (program: var Program | var BindedProgram, name: string) = 149 | ## Register an attrib variable named `name` in `program` 150 | let index = program.nameToIndex.len 151 | program.nameToIndex[name] = (index, pvkAttrib) 152 | debugOpenGLStatement: 153 | echo &"glBindAttribLocation({program.id}, {index}, {name})" 154 | opengl.glBindAttribLocation(program.id, opengl.GLuint(index), name) 155 | 156 | # FIXME: Programを消す 157 | proc registerUniform* (program: var Program | var BindedProgram, name: string) = 158 | ## Register a uniform variable named `name` in `program` 159 | let index = opengl.glGetUniformLocation(program.id, name).int 160 | if index == -1: 161 | raise newException(ProgramNotExistsActiveUniformDefect, &"Active Uniform variable {name} does not exist in GLSL.") 162 | program.nameToIndex[name] = (index, pvkUniform) 163 | 164 | # FIXME: Programを消す 165 | proc registerSubroutineUniform* (program: var Program | var BindedProgram, shaderType: ShaderObjectKind, name: string) = 166 | let index = opengl.glGetSubroutineUniformLocation(program.id, shaderType.convertGLExpression, name).int 167 | if index == -1: 168 | raise newException(ProgramNotExistsActiveSubroutineUniformDefect, &"Active Subroutine-Uniform variable {name} does not exist in GLSL.") 169 | program.nameToIndex[name] = (index, pvkSubroutineUniform) 170 | 171 | proc make* (_: typedesc[Program], vertex_shader: ShaderObject, fragment_shader: ShaderObject): Program = 172 | ## Makes a program linking `vertex_shader` and `fragment_shader`. 173 | result = Program 174 | .init() 175 | .attach(vertex_shader) 176 | .attach(fragment_shader) 177 | result.link() 178 | 179 | proc make* (_: typedesc[Program], vertex_shader: ShaderObject, fragment_shader: ShaderObject, attributes: seq[string] = @[], uniforms: seq[string] = @[], subroutine_uniforms: seq[(ShaderObjectKind, string)] = @[]): Program = 180 | ## Makes a program linking `vertex_shader` and `fragment_shader`; registering `attributes` and `uniforms`. 181 | result = Program 182 | .init() 183 | result.attach(vertex_shader) 184 | result.attach(fragment_shader) 185 | for attribute in attributes: 186 | result.registerAttrib(attribute) 187 | result.link() 188 | result.use do (program: var BindedProgram): 189 | for uniform in uniforms: 190 | program.registerUniform(uniform) 191 | for (shader_kind, subroutine_uniform) in subroutine_uniforms: 192 | program.registerSubroutineUniform(shader_kind, subroutine_uniform) 193 | 194 | # FIXME: Programを消す 195 | proc `[]`* (program: Program | BindedProgram, name: string): int = 196 | result = program.nameToIndex[name].index 197 | 198 | # FIXME: Programを消す 199 | proc `[]=`* (program: Program | BindedProgram, name: string, v1: int) = 200 | let index = program[name] 201 | opengl.glUniform1i(opengl.GLint(index), opengl.GLint(v1)) 202 | 203 | debugOpenGLStatement: 204 | echo &"glUniform1i({index}, {v1})" 205 | 206 | # FIXME: Programを消す 207 | proc `[]=`* (program: Program | BindedProgram, name: string, matrix4v: array[16, float32]) = 208 | let index = program[name] 209 | var matrix4v = matrix4v 210 | opengl.glUniformMatrix4fv(opengl.GLint(index), 1, opengl.GLboolean(false), matrix4v[0].addr) 211 | 212 | debugOpenGLStatement: 213 | echo &"glUniformMatrix4fv(index, 1, false, {matrix4v})" 214 | 215 | # FIXME: Programを消す 216 | proc `[]=`* [I: static int, T] (program: Program | BindedProgram, name: string, data: tuple[vbo: BindedVBO[I ,T], size: int]) = 217 | let index = opengl.GLuint(program[name]) 218 | opengl.glEnableVertexAttribArray(index) 219 | opengl.glVertexAttribPointer(index, opengl.GLint(data.size), opengl.EGL_FLOAT, false, opengl.GLSizei(0), cast[pointer](0)) 220 | -------------------------------------------------------------------------------- /muml/src/muml/builder.nim: -------------------------------------------------------------------------------- 1 | import std/[random, macros, json, strutils] 2 | import builtin/commonTypes, utils, parserAST 3 | export json, parseEnum, removeDoubleQuotation 4 | 5 | proc serialize (typedescNimNode: NimNode, rootType: bool): JsonNode {.compileTime.} 6 | 7 | proc parseTypeName (typeNameAST: NimNode): JsonNode {.compileTime.} = 8 | expectKind(typeNameAST, nnkSym) 9 | const SupportTypes = [ 10 | "int", "int8", "int16", "int32", "int64", 11 | "uint", "uint8", "uint16", "uint32", "uint64", 12 | "char", "string", "bool", "float", "float32", "float64" 13 | ] 14 | if $typeNameAST in SupportTypes: 15 | result = %* $typeNameAST 16 | else: 17 | result = serialize(typeNameAST, false) 18 | 19 | proc serializeObject (prevJson: JsonNode, typeImplFields: NimNode): JsonNode = 20 | result = prevJson 21 | for typeImplField in typeImplFields: 22 | let fieldName = case typeImplField[0].kind 23 | of nnkIdent: $typeImplField[0] 24 | of nnkPostfix: $typeImplField[0][1] 25 | else: 26 | error("unsupported type") 27 | "" 28 | expectKind(typeImplField[1], {nnkSym, nnkBracketExpr}) 29 | case typeImplField[1].kind 30 | of nnkSym: 31 | result[fieldName] = parseTypeName(typeImplField[1]) 32 | of nnkBracketExpr: 33 | expectLen(typeImplField[1], 2) 34 | expectIdent(typeImplField[1][0], "Animation") 35 | result["@" & fieldName] = parseTypeName(typeImplField[1][1]) 36 | else: discard 37 | 38 | proc serialize (typedescNimNode: NimNode, rootType: bool): JsonNode {.compileTime.} = 39 | result = %*{} 40 | let typeImpl = typedescNimNode.getImpl 41 | 42 | if rootType: 43 | expectKind(typeImpl[2], nnkRefTy) 44 | expectKind(typeImpl[2][0], nnkObjectTy) 45 | 46 | result["type"] = %* $typedescNimNode 47 | if typeImpl[2].kind == nnkObjectTy: 48 | let typeImplFields = typeImpl[2][2] 49 | result = serializeObject(result, typeImplFields) 50 | elif typeImpl[2][0].kind == nnkObjectTy: 51 | let typeImplFields = typeImpl[2][0][2] 52 | result = serializeObject(result, typeImplFields) 53 | elif typeImpl[2].kind == nnkEnumTy: 54 | result = %* ("enum:" & $typedescNimNode) 55 | 56 | var randObject {.compileTime.} = initRand(20031030) 57 | 58 | proc generateParser (prevAST: NimNode, keyValueID: int, deserializeMap: JsonNode): NimNode {.compileTime.} = 59 | ## of節以降を生成する、ネストしたオブジェクトがある場合はfor文、case節までを生成する 60 | result = prevAST 61 | 62 | for deserializeKey, deserializeVal in deserializeMap.pairs: 63 | if deserializeKey == "type": 64 | continue 65 | 66 | if deserializeKey[0] == '@': 67 | ### アニメーション 68 | if deserializeVal.kind == JString: 69 | let typeName = deserializeVal.getStr 70 | if typeName == "int": 71 | result.add getIntSequenceParserAST(deserializeKey[1..^1], keyValueID) 72 | elif typeName == "int8": 73 | result.add getInt8SequenceParserAST(deserializeKey[1..^1], keyValueID) 74 | elif typeName == "int16": 75 | result.add getInt16SequenceParserAST(deserializeKey[1..^1], keyValueID) 76 | elif typeName == "int32": 77 | result.add getInt32SequenceParserAST(deserializeKey[1..^1], keyValueID) 78 | elif typeName == "int64": 79 | result.add getInt64SequenceParserAST(deserializeKey[1..^1], keyValueID) 80 | elif typeName == "uint": 81 | result.add getUintSequenceParserAST(deserializeKey[1..^1], keyValueID) 82 | elif typeName == "uint8": 83 | result.add getUint8SequenceParserAST(deserializeKey[1..^1], keyValueID) 84 | elif typeName == "uint16": 85 | result.add getUint16SequenceParserAST(deserializeKey[1..^1], keyValueID) 86 | elif typeName == "uint32": 87 | result.add getUint32SequenceParserAST(deserializeKey[1..^1], keyValueID) 88 | elif typeName == "uint64": 89 | result.add getUint64SequenceParserAST(deserializeKey[1..^1], keyValueID) 90 | elif typeName == "float": 91 | result.add getFloatSequenceParserAST(deserializeKey[1..^1], keyValueID) 92 | elif typeName == "float32": 93 | result.add getFloat32SequenceParserAST(deserializeKey[1..^1], keyValueID) 94 | elif typeName == "float64": 95 | result.add getFloat64SequenceParserAST(deserializeKey[1..^1], keyValueID) 96 | elif typeName == "bool": 97 | result.add getBoolSequenceParserAST(deserializeKey[1..^1], keyValueID) 98 | elif typeName == "char": 99 | result.add getCharSequenceParserAST(deserializeKey[1..^1], keyValueID) 100 | elif typeName == "string": 101 | result.add getStringSequenceParserAST(deserializeKey[1..^1], keyValueID) 102 | else: 103 | error("unsupported type") 104 | 105 | elif deserializeVal.kind == JObject: 106 | ### ネストしたオブジェクトのためにforとcaseを生成する 107 | let 108 | nextKeyValueID = randObject.rand(1000000) 109 | currentResultElement = newIdentNode("resultElement_" & $keyValueID) 110 | nextResultElement = newIdentNode("resultElement_" & $nextKeyValueID) 111 | nextKey = newIdentNode("key_" & $nextKeyValueID) 112 | nextVal = newIdentNode("val_" & $nextKeyValueID) 113 | currentVal = newIdentNode("val_" & $keyValueID) 114 | field = newIdentNode(deserializeKey[1..^1]) 115 | nextObjectName = newIdentNode(deserializeVal["type"].getStr.removeDoubleQuotation) 116 | 117 | result.add nnkOfBranch.newTree( 118 | newLit(deserializeKey[1..^1]), 119 | quote do: 120 | for jsonArrayElement in `currentVal`: 121 | var `nextResultElement` = `nextObjectName`() 122 | for `nextKey`, `nextVal` in pairs(jsonArrayElement): 123 | discard 124 | ) 125 | result[^1][^1][^1][^1][^1][0] = nnkCaseStmt.newTree(nextKey) 126 | result[^1][^1][^1][^1][^1][^1] = generateParser(result[^1][^1][^1][^1][^1][0], nextKeyValueID, deserializeVal) 127 | result[^1][^1][^1].add quote do: 128 | `currentResultElement`.`field`.add `nextResultElement` 129 | 130 | elif deserializeVal.kind == JString: 131 | ### of節を生成する 132 | let typeName = deserializeVal.getStr 133 | 134 | if typeName == "int": 135 | result.add getIntParserAST(deserializeKey, keyValueID) 136 | elif typeName == "int8": 137 | result.add getInt8ParserAST(deserializeKey, keyValueID) 138 | elif typeName == "int16": 139 | result.add getInt16ParserAST(deserializeKey, keyValueID) 140 | elif typeName == "int32": 141 | result.add getInt32ParserAST(deserializeKey, keyValueID) 142 | elif typeName == "int64": 143 | result.add getInt64ParserAST(deserializeKey, keyValueID) 144 | elif typeName == "uint": 145 | result.add getUintParserAST(deserializeKey, keyValueID) 146 | elif typeName == "uint8": 147 | result.add getUint8ParserAST(deserializeKey, keyValueID) 148 | elif typeName == "uint16": 149 | result.add getUint16ParserAST(deserializeKey, keyValueID) 150 | elif typeName == "uint32": 151 | result.add getUint32ParserAST(deserializeKey, keyValueID) 152 | elif typeName == "uint64": 153 | result.add getUint64ParserAST(deserializeKey, keyValueID) 154 | elif typeName == "float": 155 | result.add getFloatParserAST(deserializeKey, keyValueID) 156 | elif typeName == "float32": 157 | result.add getFloat32ParserAST(deserializeKey, keyValueID) 158 | elif typeName == "float64": 159 | result.add getFloat64ParserAST(deserializeKey, keyValueID) 160 | elif typeName == "bool": 161 | result.add getBoolParserAST(deserializeKey, keyValueID) 162 | elif typeName == "char": 163 | result.add getCharParserAST(deserializeKey, keyValueID) 164 | elif typeName == "string": 165 | result.add getStringParserAST(deserializeKey, keyValueID) 166 | elif typeName.len >= 4 and typeName[0..4] == "enum:": 167 | result.add getEnumParserAST(typeName[5..^1], deserializeKey, keyValueID) 168 | else: 169 | error("unsupported type") 170 | 171 | elif deserializeVal.kind == JObject: 172 | ### ネストしたオブジェクトのためにforとcaseを生成する 173 | let 174 | nextKeyValueID = randObject.rand(1000000) 175 | currentResultElement = newIdentNode("resultElement_" & $keyValueID) 176 | nextResultElement = newIdentNode("resultElement_" & $nextKeyValueID) 177 | nextKey = newIdentNode("key_" & $nextKeyValueID) 178 | nextVal = newIdentNode("val_" & $nextKeyValueID) 179 | currentVal = newIdentNode("val_" & $keyValueID) 180 | field = newIdentNode(deserializeKey) 181 | nextObjectName = newIdentNode(deserializeVal["type"].getStr.removeDoubleQuotation) 182 | 183 | result.add nnkOfBranch.newTree( 184 | newLit(deserializeKey), 185 | quote do: 186 | var `nextResultElement` = `nextObjectName`() 187 | for `nextKey`, `nextVal` in pairs(`currentVal`): 188 | discard 189 | ) 190 | result[^1][^1][^1][^1][0] = nnkCaseStmt.newTree(nextKey) 191 | result[^1][^1][^1][^1][^1] = generateParser(result[^1][^1][^1][^1][0], nextKeyValueID, deserializeVal) 192 | result[^1][^1].add quote do: 193 | `currentResultElement`.`field` = `nextResultElement` 194 | 195 | proc generateParserProc (procName, typeName: NimNode, json: JsonNode): NimNode {.compileTime.} = 196 | let 197 | keyValueID = randObject.rand(1000000) 198 | key = ident("key_" & $keyValueID) 199 | val = ident("val_" & $keyValueID) 200 | resultElement = ident("resultElement_" & $keyValueID) 201 | 202 | result = quote do: 203 | proc `procName`* (muml: JsonNode): mumlRootElement = 204 | var `resultElement` = `typeName`() 205 | for `key`, `val` in muml.pairs: 206 | discard 207 | 208 | # prevASTの`discard`文を置換 209 | result[^1][^1][^1] = nnkCaseStmt.newTree( 210 | newIdentNode("key_" & $keyValueID) 211 | ) 212 | 213 | result[^1][^1][^1] = generateParser(result[^1][^1][^1], keyValueID, json) 214 | 215 | result[^1].add nnkAsgn.newTree( 216 | newIdentNode("result"), 217 | newIdentNode("resultElement_" & $keyValueID) 218 | ) 219 | 220 | macro mumlBuilder* (mumlElement: typedesc): untyped = 221 | let 222 | typeName = ident($mumlElement) 223 | procName = ident("parse_" & $mumlElement) 224 | serializedMumlElement = serialize(mumlElement, true) 225 | result = generateParserProc(procName, typeName, serializedMumlElement) 226 | -------------------------------------------------------------------------------- /mockup_ffmpeg/src/mockup_ffmpeg/h264.nim: -------------------------------------------------------------------------------- 1 | import ffmpeg 2 | import results 3 | 4 | type 5 | H264* = object 6 | codec: ptr AVCodec # DE 7 | codecCtx: ptr AVCodecContext # DE 8 | codecParams: ptr AVCodecParameters # D 9 | ioCtx: ptr AVIOContext # E 10 | fmtCtx: ptr AVFormatContext # DE 11 | stream: ptr AVStream # DE 12 | 13 | VideoFrame* = object 14 | frame: ptr AVFrame 15 | 16 | func width* (h264: H264): int32 = 17 | result = h264.codecCtx.width 18 | 19 | func height* (h264: H264): int32 = 20 | result = h264.codecCtx.height 21 | 22 | func fps* (h264: H264): int32 = 23 | result = h264.codecCtx.timebase.den 24 | result = av_q2d(h264.stream.r_frame_rate).int32 25 | 26 | # proc `=destroy`* (h264: var H264) = 27 | # echo "=destroy" 28 | # avcodec_free_context(h264.codecCtx.addr) 29 | # avformat_free_context(h264.fmtCtx) 30 | # discard avio_closep(h264.ioCtx.addr) 31 | 32 | # proc `=destroy`* (frame: var VideoFrame) = 33 | # discard 34 | 35 | # proc `=copy`* (dest: var VideoFrame; src: VideoFrame) = 36 | # if dest == src: return 37 | # `=destroy`(dest) 38 | 39 | proc findH264Encoder* (): Result[ptr AVCodec, string] = 40 | let encoder = avcodec_find_encoder(AV_CODEC_ID_H264) 41 | if encoder.isNil: 42 | return err("failed to find H264 encoder") 43 | return ok(encoder) 44 | 45 | proc allocCodecContext* (codec: ptr AVCodec): Result[ptr AVCodecContext, string] = 46 | let ctx = avcodec_alloc_context3(codec) 47 | if ctx.isNil: 48 | return err("failed to alloc codec context") 49 | return ok(ctx) 50 | 51 | proc set* (dict: var ptr AVDictionary, key, value: string): Result[(), string] = 52 | if av_dict_set(dict.addr, key.cstring, value.cstring, 0) < 0: 53 | return err("failed to set to AVDictionary") 54 | return ok(()) 55 | 56 | func toAVIOFlag* (mode: FileMode): cint = 57 | case mode 58 | of fmRead: 59 | result = AVIO_FLAG_READ 60 | of fmWrite: 61 | result = AVIO_FLAG_WRITE 62 | of fmReadWrite: 63 | result = AVIO_FLAG_READ_WRITE 64 | else: 65 | # TODO 66 | discard 67 | 68 | proc allocIOContext* (distPath: string, flag: FileMode): Result[ptr AVIOContext, string] = 69 | var ioCtx: ptr AVIOContext = nil 70 | if avio_open(ioCtx.addr, distPath, flag.toAVIOFlag) < 0: 71 | return err("failed to initialize IO-context") 72 | return ok(ioCtx) 73 | 74 | proc allocFormatContext* (formatName: string): Result[ptr AVFormatContext, string] = 75 | var fmtCtx: ptr AVFormatContext = nil 76 | if avformat_alloc_output_context2(fmtCtx.addr, nil, formatName.cstring, nil) < 0: 77 | return err("failed to alloc context for output format") 78 | return ok(fmtCtx) 79 | 80 | proc initializeCodecContext* (codecCtx: ptr AVCodecContext, codecOpts: var ptr AVDictionary): Result[(), string] = 81 | if avcodec_open2(codecCtx, codecCtx[].codec, codecOpts.addr) < 0: 82 | return err("failed to initialize codec context") 83 | return ok(()) 84 | 85 | proc initializeCodecContext* (codecCtx: ptr AVCodecContext, codec: ptr AVCodec): Result[(), string] = 86 | if avcodec_open2(codecCtx, codec, nil) < 0: 87 | return err("failed to initialize codec context") 88 | return ok(()) 89 | 90 | proc allocStream* (fmtCtx: ptr AVFormatContext, codec: ptr AVCodec): Result[ptr AVStream, string] = 91 | var stream = avformat_new_stream(fmtCtx, codec) 92 | if stream.isNil: 93 | return err("failed to alloc stream") 94 | return ok(stream) 95 | 96 | proc fillCodecParameters* (params: ptr AVCodecParameters, codecCtx: ptr AVCodecContext): Result[(), string] = 97 | if avcodec_parameters_from_context(params, codecCtx) < 0: 98 | return err("failed to fill codec context") 99 | return ok(()) 100 | 101 | proc writeHeader* (fmtCtx: ptr AVFormatContext): Result[(), string] = 102 | if avformat_write_header(fmtCtx, nil) < 0: 103 | return err("failed to write header") 104 | return ok(()) 105 | 106 | func frameCount* (h264: H264): int = 107 | result = h264.codecCtx.frame_number 108 | 109 | proc newH264* (distPath: string, width, height, fps: int32): Result[H264, string] = 110 | var h264 = H264() 111 | h264.codec = ?findH264Encoder() 112 | h264.codecCtx = ?allocCodecContext(h264.codec) 113 | h264.codecCtx.pix_fmt = AV_PIX_FMT_YUV420P 114 | h264.codecCtx.width = width 115 | h264.codecCtx.height = height 116 | h264.codecCtx.time_base = av_make_q(1, fps) 117 | h264.codecCtx.framerate = av_make_q(fps, 1) 118 | h264.codecCtx.gop_size = 10 119 | h264.codecCtx.max_b_frames = 1 120 | h264.codecCtx.bit_rate = 400000 121 | var codecOptions: ptr AVDictionary = nil 122 | discard ?codecOptions.set("profile", "high") 123 | discard ?codecOptions.set("preset", "medium") 124 | discard ?codecOptions.set("crf", "22") 125 | discard ?codecOptions.set("level", "4.0") 126 | h264.ioCtx = ?allocIOContext(distPath, fmWrite) 127 | h264.fmtCtx = ?allocFormatContext("mp4") 128 | h264.fmtCtx.pb = h264.ioCtx 129 | discard ?initializeCodecContext(h264.codecCtx, codecOptions) 130 | h264.stream = ?h264.fmtCtx.allocStream(h264.codec) 131 | discard ?h264.stream.codecpar.fillCodecParameters(h264.codecCtx) 132 | discard ?h264.fmtCtx.writeHeader() 133 | result = ok(h264) 134 | 135 | proc openH264FromSrc (fmtCtx: var ptr AVFormatContext, srcPath: string): Result[(), string] = 136 | if avformat_open_input(fmtCtx.addr, srcPath, nil, nil) != 0: 137 | return err("failed to open video file") 138 | return ok(()) 139 | 140 | proc findStreamInfo (fmtCtx: var ptr AVFormatContext): Result[(), string] = 141 | echo fmtCtx.repr 142 | if avformat_find_stream_info(fmtCtx, nil) < 0: 143 | return err("failed to find stream information") 144 | return ok(()) 145 | 146 | proc getVideoDecoder (fmtCtx: ptr AVFormatContext): Result[(ptr AVCodec, ptr AVCodecParameters, ptr AVStream), string] = 147 | var 148 | codec: ptr AVCodec = nil 149 | codecParams: ptr AVCodecParameters = nil 150 | videoStream: ptr AVStream = nil 151 | var streams = cast[ptr UncheckedArray[ptr AVStream]](fmtCtx[].streams) 152 | for index in 0 ..< fmtCtx.nb_streams: 153 | let locpar = streams[index].codecpar 154 | if locpar.codec_type == AVMEDIA_TYPE_VIDEO: 155 | codec = avcodec_find_decoder(locpar.codec_id) 156 | codecParams = locpar 157 | videoStream = streams[index] 158 | break 159 | if codec.isNil or codecParams.isNil: 160 | return err("failed to get video decoder") 161 | return ok((codec, codecParams, videoStream)) 162 | 163 | proc newPacket* (): Result[ptr AVPacket, string] = 164 | var p = av_packet_alloc() 165 | if p.isNil: 166 | return err("failed to alloc packet") 167 | return ok(p) 168 | 169 | proc initSwsContext* (width, height: int32, srcFmtPix, destFmtPix: AVPixelFormat): ptr SwsContext = 170 | result = sws_getContext( 171 | width, height, srcFmtPix, 172 | width, height, destFmtPix, 173 | SWS_BICUBIC, 174 | nil, nil, nil 175 | ) 176 | 177 | proc scale* (swsCtx: ptr SwsContext, srcFrame, destFrame: VideoFrame): int = 178 | result = sws_scale( 179 | swsCtx, 180 | srcFrame.frame[].data[0].addr, 181 | srcFrame.frame[].linesize[0].addr, 182 | 0, 183 | srcFrame.frame[].height, 184 | destFrame.frame[].data[0].addr, 185 | destFrame.frame[].linesize[0].addr 186 | ) 187 | 188 | proc openH264* (srcPath: string): Result[H264, string] = 189 | var h264 = H264() 190 | discard ?h264.fmtCtx.openH264FromSrc(srcPath) 191 | discard ?h264.fmtCtx.findStreamInfo() 192 | (h264.codec, h264.codecParams, h264.stream) = ?h264.fmtCtx.getVideoDecoder() 193 | h264.codecCtx = ?allocCodecContext(h264.codec) 194 | if avcodec_parameters_to_context(h264.codecCtx, h264.codecParams) < 0: 195 | return err("failed to initialize codec parameters") 196 | discard ?h264.codecCtx.initializeCodecContext(h264.codec) 197 | echo h264.width 198 | return ok(h264) 199 | 200 | proc send* (codecCtx: var ptr AVCodecContext, packet: ptr AVPacket): Result[(), string] = 201 | if avcodec_send_packet(codecCtx, packet) != 0: 202 | return err("failed to get packet") 203 | return ok(()) 204 | 205 | proc newFrame* (): VideoFrame = 206 | result = VideoFrame(frame: av_frame_alloc()) 207 | 208 | proc copy* (src: VideoFrame): Result[VideoFrame, string] = 209 | var frame = newFrame() 210 | frame.frame.format = src.frame.format 211 | frame.frame.height = src.frame.height 212 | frame.frame.width = src.frame.width 213 | if av_frame_get_buffer(frame.frame, 32) < 0: 214 | return err("failed to allocate buffer") 215 | if av_frame_copy(frame.frame, src.frame) < 0: 216 | return err("failed to copy frame data") 217 | if av_frame_copy_props(frame.frame, src.frame) < 0: 218 | return err("failed to copy metadata of frame") 219 | return ok(frame) 220 | 221 | proc allocFrameBuffer* (frame: var VideoFrame, align: int32): Result[(), string] = 222 | if av_frame_get_buffer(frame.frame, align) < 0: 223 | return err("failed to alloc frame buffer") 224 | return ok(()) 225 | 226 | iterator decodeH264* (h264: var H264): VideoFrame = 227 | var 228 | packet = newPacket().unwrap() # TODO 229 | swsCtx = initSwsContext(h264.codecCtx.width, h264.codecCtx.height, AV_PIX_FMT_YUV420P, AV_PIX_FMT_RGBA) 230 | while av_read_frame(h264.fmtCtx, packet) == 0: 231 | if packet.stream_index != h264.stream.index: 232 | av_packet_unref(packet) 233 | continue 234 | discard h264.codecCtx.send(packet).unwrap() 235 | var frame = newFrame() 236 | while avcodec_receive_frame(h264.codecCtx, frame.frame) == 0: 237 | var 238 | copyFrame = frame.copy().unwrap() 239 | frameRGBA = newFrame() 240 | frameRGBA.frame.format = AV_PIX_FMT_RGBA.cint 241 | frameRGBA.frame.height = frame.frame.height 242 | frameRGBA.frame.width = frame.frame.width 243 | discard frameRGBA.allocFrameBuffer(32).unwrap() 244 | discard swsCtx.scale(copyFrame, frameRGBA) 245 | yield frameRGBA 246 | av_frame_free(frame.frame.addr) 247 | av_packet_unref(packet) 248 | 249 | proc send* (codecCtx: var ptr AVCodecContext, frame: VideoFrame): Result[(), string] = 250 | if avcodec_send_frame(codecCtx, frame.frame) < 0: 251 | return err("failed to supply frame for codec context") 252 | return ok(()) 253 | 254 | proc receive* (h264: var H264): Result[(), string] = 255 | var packet = ?newPacket() 256 | while avcodec_receive_packet(h264.codecCtx, packet) == 0: 257 | packet.stream_index = 0 258 | av_packet_rescale_ts(packet, h264.codecCtx.time_base, h264.stream.time_base) 259 | if av_interleaved_write_frame(h264.fmtCtx, packet) != 0: 260 | return err("failed to write to packet") 261 | av_packet_unref(packet) 262 | return ok(()) 263 | 264 | proc addFrame* (h264: var H264, srcFrame: VideoFrame): Result[(), string] = 265 | var frame = newFrame() 266 | frame.frame.format = h264.codecCtx.pix_fmt.cint 267 | frame.frame.width = srcFrame.frame.width 268 | frame.frame.height = srcFrame.frame.height 269 | frame.frame.pts = h264.frameCount() 270 | discard ?frame.allocFrameBuffer(32) 271 | var swsCtx = initSwsContext(h264.codecCtx.width, h264.codecCtx.height, AV_PIX_FMT_RGBA, h264.codecCtx.pix_fmt) 272 | discard swsCtx.scale(srcFrame, frame) 273 | discard ?h264.codecCtx.send(frame) 274 | discard ?h264.receive() 275 | 276 | proc flush* (h264: var H264): Result[(), string] = 277 | if avcodec_send_frame(h264.codecCtx, nil) != 0: 278 | return err("failed to flush") 279 | var packet = ?newPacket() 280 | while avcodec_receive_packet(h264.codecCtx, packet) == 0: 281 | packet.stream_index = 0 282 | av_packet_rescale_ts(packet, h264.codecCtx.time_base, h264.stream.time_base) 283 | if av_interleaved_write_frame(h264.fmtCtx, packet) != 0: 284 | return err("failed to flush") 285 | if av_write_trailer(h264.fmtCtx) != 0: 286 | return err("failed to flush") 287 | avcodec_free_context(h264.codecCtx.addr) 288 | avformat_free_context(h264.fmtCtx) 289 | discard avio_closep(h264.ioCtx.addr) 290 | return ok(()) 291 | -------------------------------------------------------------------------------- /muml/src/muml/parserAST.nim: -------------------------------------------------------------------------------- 1 | import std/[macros] 2 | 3 | func toIdent (deserializeKey: string, keyValueID: int): tuple[val, resultElement, key: NimNode] = 4 | result.val = newIdentNode("val_" & $keyValueID) 5 | result.resultElement = newIdentNode("resultElement_" & $keyValueID) 6 | result.key = newIdentNode(deserializeKey) 7 | 8 | func toIdent (typeName, deserializeKey: string, keyValueID: int): tuple[typeName, val, resultElement, key: NimNode] = 9 | (result.val, result.resultElement, result.key) = toIdent(deserializeKey, keyValueID) 10 | result.typeName = newIdentNode(typeName) 11 | 12 | proc getIntParserAST* (deserializeKey: string, keyValueID: int): NimNode = 13 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 14 | result = nnkOfBranch.newTree( 15 | newLit(deserializeKey), 16 | quote do: 17 | `resultElement`.`key` = getInt(`val`) 18 | ) 19 | 20 | proc getInt8ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 21 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 22 | result = nnkOfBranch.newTree( 23 | newLit(deserializeKey), 24 | quote do: 25 | `resultElement`.`key` = int8(getInt(`val`)) 26 | ) 27 | 28 | proc getInt16ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 29 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 30 | result = nnkOfBranch.newTree( 31 | newLit(deserializeKey), 32 | quote do: 33 | `resultElement`.`key` = int16(getInt(`val`)) 34 | ) 35 | 36 | proc getInt32ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 37 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 38 | result = nnkOfBranch.newTree( 39 | newLit(deserializeKey), 40 | quote do: 41 | `resultElement`.`key` = int32(getInt(`val`)) 42 | ) 43 | 44 | proc getInt64ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 45 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 46 | result = nnkOfBranch.newTree( 47 | newLit(deserializeKey), 48 | quote do: 49 | `resultElement`.`key` = int64(getInt(`val`)) 50 | ) 51 | 52 | proc getUintParserAST* (deserializeKey: string, keyValueID: int): NimNode = 53 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 54 | result = nnkOfBranch.newTree( 55 | newLit(deserializeKey), 56 | quote do: 57 | `resultElement`.`key` = uint(getInt(`val`)) 58 | ) 59 | 60 | proc getUint8ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 61 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 62 | result = nnkOfBranch.newTree( 63 | newLit(deserializeKey), 64 | quote do: 65 | `resultElement`.`key` = uint8(getInt(`val`)) 66 | ) 67 | 68 | proc getUint16ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 69 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 70 | result = nnkOfBranch.newTree( 71 | newLit(deserializeKey), 72 | quote do: 73 | `resultElement`.`key` = uint16(getInt(`val`)) 74 | ) 75 | 76 | proc getUint32ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 77 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 78 | result = nnkOfBranch.newTree( 79 | newLit(deserializeKey), 80 | quote do: 81 | `resultElement`.`key` = uint32(getInt(`val`)) 82 | ) 83 | 84 | proc getUint64ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 85 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 86 | result = nnkOfBranch.newTree( 87 | newLit(deserializeKey), 88 | quote do: 89 | `resultElement`.`key` = uint64(getInt(`val`)) 90 | ) 91 | 92 | proc getFloatParserAST* (deserializeKey: string, keyValueID: int): NimNode = 93 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 94 | result = nnkOfBranch.newTree( 95 | newLit(deserializeKey), 96 | quote do: 97 | `resultElement`.`key` = getFloat(`val`) 98 | ) 99 | 100 | proc getFloat32ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 101 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 102 | result = nnkOfBranch.newTree( 103 | newLit(deserializeKey), 104 | quote do: 105 | `resultElement`.`key` = float32(getFloat(`val`)) 106 | ) 107 | 108 | proc getFloat64ParserAST* (deserializeKey: string, keyValueID: int): NimNode = 109 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 110 | result = nnkOfBranch.newTree( 111 | newLit(deserializeKey), 112 | quote do: 113 | `resultElement`.`key` = float64(getFloat(`val`)) 114 | ) 115 | 116 | proc getStringParserAST* (deserializeKey: string, keyValueID: int): NimNode = 117 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 118 | result = nnkOfBranch.newTree( 119 | newLit(deserializeKey), 120 | quote do: 121 | `resultElement`.`key` = getStr(`val`).removeDoubleQuotation 122 | ) 123 | 124 | proc getCharParserAST* (deserializeKey: string, keyValueID: int): NimNode = 125 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 126 | result = nnkOfBranch.newTree( 127 | newLit(deserializeKey), 128 | quote do: 129 | if getStr(`val`).len == 1: 130 | `resultElement`.`key` = getStr(`val`).removeDoubleQuotation[0] 131 | else: 132 | raise newException(Defect, "Cannot accept type string; can accept only one ASCII character.") 133 | ) 134 | 135 | proc getEnumParserAST* (typeName, deserializeKey: string, keyValueID: int): NimNode = 136 | let (typeName, val, resultElement, key) = toIdent(typeName, deserializeKey, keyValueID) 137 | result = nnkOfBranch.newTree( 138 | newLit(deserializeKey), 139 | quote do: 140 | `resultElement`.`key` = parseEnum[`typeName`](getStr(`val`).removeDoubleQuotation) 141 | ) 142 | 143 | proc getBoolParserAST* (deserializeKey: string, keyValueID: int): NimNode = 144 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 145 | result = nnkOfBranch.newTree( 146 | newLit(deserializeKey), 147 | quote do: 148 | `resultElement`.`key` = getBool(`val`) 149 | ) 150 | 151 | proc getFloatSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 152 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 153 | result = nnkOfBranch.newTree( 154 | newLit(deserializeKey), 155 | quote do: 156 | for jsonArrayElement in `val`: 157 | `resultElement`.`key`.add jsonArrayElement.getFloat 158 | ) 159 | 160 | proc getFloat32SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 161 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 162 | result = nnkOfBranch.newTree( 163 | newLit(deserializeKey), 164 | quote do: 165 | for jsonArrayElement in `val`: 166 | `resultElement`.`key`.add float32(jsonArrayElement.getFloat) 167 | ) 168 | 169 | proc getFloat64SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 170 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 171 | result = nnkOfBranch.newTree( 172 | newLit(deserializeKey), 173 | quote do: 174 | for jsonArrayElement in `val`: 175 | `resultElement`.`key`.add float64(jsonArrayElement.getFloat) 176 | ) 177 | 178 | proc getIntSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 179 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 180 | result = nnkOfBranch.newTree( 181 | newLit(deserializeKey), 182 | quote do: 183 | for jsonArrayElement in `val`: 184 | `resultElement`.`key`.add jsonArrayElement.getInt 185 | ) 186 | 187 | proc getInt8SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 188 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 189 | result = nnkOfBranch.newTree( 190 | newLit(deserializeKey), 191 | quote do: 192 | for jsonArrayElement in `val`: 193 | `resultElement`.`key`.add int8(jsonArrayElement.getInt) 194 | ) 195 | 196 | proc getInt16SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 197 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 198 | result = nnkOfBranch.newTree( 199 | newLit(deserializeKey), 200 | quote do: 201 | for jsonArrayElement in `val`: 202 | `resultElement`.`key`.add int16(jsonArrayElement.getInt) 203 | ) 204 | 205 | proc getInt32SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 206 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 207 | result = nnkOfBranch.newTree( 208 | newLit(deserializeKey), 209 | quote do: 210 | for jsonArrayElement in `val`: 211 | `resultElement`.`key`.add int32(jsonArrayElement.getInt) 212 | ) 213 | 214 | proc getInt64SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 215 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 216 | result = nnkOfBranch.newTree( 217 | newLit(deserializeKey), 218 | quote do: 219 | for jsonArrayElement in `val`: 220 | `resultElement`.`key`.add int64(jsonArrayElement.getInt) 221 | ) 222 | 223 | proc getUintSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 224 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 225 | result = nnkOfBranch.newTree( 226 | newLit(deserializeKey), 227 | quote do: 228 | for jsonArrayElement in `val`: 229 | `resultElement`.`key`.add uint(jsonArrayElement.getInt) 230 | ) 231 | 232 | proc getUint8SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 233 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 234 | result = nnkOfBranch.newTree( 235 | newLit(deserializeKey), 236 | quote do: 237 | for jsonArrayElement in `val`: 238 | `resultElement`.`key`.add uint8(jsonArrayElement.getInt) 239 | ) 240 | 241 | proc getUint16SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 242 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 243 | result = nnkOfBranch.newTree( 244 | newLit(deserializeKey), 245 | quote do: 246 | for jsonArrayElement in `val`: 247 | `resultElement`.`key`.add uint16(jsonArrayElement.getInt) 248 | ) 249 | 250 | proc getUint32SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 251 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 252 | result = nnkOfBranch.newTree( 253 | newLit(deserializeKey), 254 | quote do: 255 | for jsonArrayElement in `val`: 256 | `resultElement`.`key`.add uint32(jsonArrayElement.getInt) 257 | ) 258 | 259 | proc getUint64SequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 260 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 261 | result = nnkOfBranch.newTree( 262 | newLit(deserializeKey), 263 | quote do: 264 | for jsonArrayElement in `val`: 265 | `resultElement`.`key`.add uint64(jsonArrayElement.getInt) 266 | ) 267 | 268 | proc getStringSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 269 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 270 | result = nnkOfBranch.newTree( 271 | newLit(deserializeKey), 272 | quote do: 273 | for jsonArrayElement in `val`: 274 | `resultElement`.`key`.add jsonArrayElement.getStr.removeDoubleQuotation 275 | ) 276 | 277 | proc getCharSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 278 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 279 | result = nnkOfBranch.newTree( 280 | newLit(deserializeKey), 281 | quote do: 282 | for jsonArrayElement in `val`: 283 | if getStr(`val`).len == 1: 284 | `resultElement`.`key`.add = getStr(`val`).removeDoubleQuotation[0] 285 | else: 286 | raise newException(Defect, "Cannot accept type string; can accept only one ASCII character.") 287 | ) 288 | 289 | proc getBoolSequenceParserAST* (deserializeKey: string, keyValueID: int): NimNode = 290 | let (val, resultElement, key) = toIdent(deserializeKey, keyValueID) 291 | result = nnkOfBranch.newTree( 292 | newLit(deserializeKey), 293 | quote do: 294 | for jsonArrayElement in `val`: 295 | `resultElement`.`key`.add jsonArrayElement.getBool 296 | ) 297 | --------------------------------------------------------------------------------