diff --git a/.github/workflows/sync_wiki.yml b/.github/workflows/sync_wiki.yml deleted file mode 100644 index 9e480556c7..0000000000 --- a/.github/workflows/sync_wiki.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Synchronize Wiki - -on: - schedule: - # daily, midnight UTC - - cron: "0 0 * * *" - workflow_dispatch: - -jobs: - synchronize: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - with: - repository: ${{ github.repository }}.wiki - fetch-depth: 0 - - name: Set git credentials - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - name: Fetch - run: | - git remote add upstream ${{ github.server_url }}/${{ github.repository_owner }}/heaps-doc - git fetch upstream master - - name: Merge - run: | - git merge upstream/master --no-edit - - name: Push - run: | - git push diff --git a/.gitignore b/.gitignore index f8055b0719..9d46b97c24 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,9 @@ bin /hxd/net/inspect.min.css /hxd/inspect/inspect.min.css /samples/build +/node_modules /*.xml .vscode /tools/mikktspace/out *.exe +.history \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000..f4bd3c2858 --- /dev/null +++ b/.npmignore @@ -0,0 +1,7 @@ +node_modules +.gitlab-ci.yml +.idea +*.iml +bin +.history +.haxelib \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..a7076e2f0b --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +registry=https://hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com/npm/hacksaw-client-artifacts/ +//hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com/npm/hacksaw-client-artifacts/:always-auth=true +//hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com/npm/hacksaw-client-artifacts/:_authToken=${CODEARTIFACT_AUTH_TOKEN} \ No newline at end of file diff --git a/all.hxml b/all.hxml index af60de6415..9343797f02 100644 --- a/all.hxml +++ b/all.hxml @@ -19,7 +19,6 @@ -lib hlopenal -xml heaps_hl.xml hxsl.CacheFileBuilder --D hl-ver=1.15.0 --next @@ -27,7 +26,6 @@ hxsl.CacheFileBuilder -lib hldx -lib hlopenal -xml heaps_hldx.xml --D hl-ver=1.15.0 --next @@ -36,4 +34,4 @@ hxsl.CacheFileBuilder -lib hlopenal -D dx12 -xml heaps_hldx12.xml --D hl-ver=1.15.0 +-D hl-ver=1.14.0 \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000..cfd09658e5 Binary files /dev/null and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000000..a9ed1e7bc2 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install] +registry = { url = "https://hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com/npm/hacksaw-client-artifacts/:_authToken=$CODEARTIFACT_AUTH_TOKEN" } diff --git a/h2d/Font.hx b/h2d/Font.hx index 1949b2d53b..6479c82892 100644 --- a/h2d/Font.hx +++ b/h2d/Font.hx @@ -185,6 +185,14 @@ class Font { Defaults to `hxd.Charset.getDefault()`. **/ public var charset : hxd.Charset; + /** + Distance range used to generate SDF Font and usefull for some implementations of SDF Font rendering. + **/ + public var distanceRange:Int; + /** + SDF Field type used when generating. + **/ + public var fieldType:String; var glyphs : Map; var nullChar : FontChar; var defaultChar : FontChar; @@ -279,7 +287,7 @@ class Font { @param size The new font size. **/ public function resizeTo( size : Int ) { - var ratio = size / this.size; + var ratio = size / initSize; for( c in glyphs ) { c.width *= ratio; c.t.scaleToSize(c.t.width * ratio, c.t.height * ratio); diff --git a/h2d/Graphics.hx b/h2d/Graphics.hx index 85badbcd53..c0f8d24b8a 100644 --- a/h2d/Graphics.hx +++ b/h2d/Graphics.hx @@ -36,12 +36,16 @@ private class GraphicsContent extends h3d.prim.Primitive { var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes, state : BatchDrawState }>; var bufferDirty : Bool; var indexDirty : Bool; + #if track_alloc var allocPos : hxd.impl.AllocPos; + #end public function new() { buffers = []; state = new BatchDrawState(); - this.allocPos = hxd.impl.AllocPos.make(); + #if track_alloc + this.allocPos = new hxd.impl.AllocPos(); + #end } public inline function addIndex(i) { @@ -84,7 +88,9 @@ private class GraphicsContent extends h3d.prim.Primitive { if (index.length <= 0) return ; var alloc = Allocator.get(); buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D); + #if track_alloc @:privateAccess buffer.allocPos = allocPos; + #end indexes = alloc.ofIndexes(index); for( b in buffers ) { if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, hxd.BufferFormat.H2D); @@ -460,7 +466,7 @@ class Graphics extends Drawable { Position a virtual tile at the given position and scale. Every draw will display a part of this tile relative to these coordinates. - Note that in by default, Tile is not wrapped, and in order to render a tiling texture, `Drawable.tileWrap` has to be set. + Note that in by default, Tile is not wrapped, and in order to render tiling texture, `Drawable.tileWrap` have to be set. Additionally, both `Tile.dx` and `Tile.dy` are ignored (use `dx`/`dy` arguments instead) as well as tile defined size of the tile through `Tile.width` and `Tile.height` (use `scaleX`/`scaleY` relative to texture size). diff --git a/h2d/HtmlText.hx b/h2d/HtmlText.hx index fdafc82892..d0dd248aaf 100644 --- a/h2d/HtmlText.hx +++ b/h2d/HtmlText.hx @@ -298,7 +298,7 @@ class HtmlText extends Text { var str = splitNode.node.nodeValue; var info = metrics[metrics.length - 1]; var w = info.width; - var cc = StringTools.fastCodeAt(str, splitNode.pos); + var cc = str.charCodeAt(splitNode.pos); // Restore line metrics to ones before split. // Potential bug: `Text [Image] texttext` - third line will use metrics as if image is present in the line. info.width = splitNode.width; @@ -421,12 +421,12 @@ class HtmlText extends Text { var x = leftMargin; var breakChars = 0; for ( i in 0...text.length ) { - var cc = StringTools.fastCodeAt(text, i); + var cc = text.charCodeAt(i); var g = font.getChar(cc); var newline = cc == '\n'.code; var esize = g.width + g.getKerningOffset(prevChar); - var isComplement = (i < text.length - 1 && font.charset.isComplementChar(StringTools.fastCodeAt(text, i + 1))); - if ( font.charset.isBreakChar(cc) && !isComplement) { + var nc = text.charCodeAt(i+1); + if ( font.charset.isBreakChar(cc) && (nc == null || !font.charset.isComplementChar(nc) )) { // Case: Very first word in text makes the line too long hence we want to start it off on a new line. if (x > maxWidth && textSplit.length == 0 && splitNode.node != null) { metrics.push(makeLineInfo(x, info.height, info.baseLine)); @@ -437,17 +437,13 @@ class HtmlText extends Text { var k = i + 1, max = text.length; var prevChar = cc; while ( size <= maxWidth && k < max ) { - var cc = StringTools.fastCodeAt(text, k++); + var cc = text.charCodeAt(k++); if ( lineBreak && (font.charset.isSpace(cc) || cc == '\n'.code ) ) break; var e = font.getChar(cc); size += e.width + letterSpacing + e.getKerningOffset(prevChar); prevChar = cc; - if ( font.charset.isBreakChar(cc) ) { - if ( k >= text.length ) - break; - var nc = StringTools.fastCodeAt(text, k); - if ( !font.charset.isComplementChar(nc) ) break; - } + var nc = text.charCodeAt(k); + if ( font.charset.isBreakChar(cc) && (nc == null || !font.charset.isComplementChar(nc)) ) break; } // Avoid empty line when last char causes line-break while being CJK if ( lineBreak && size > maxWidth && i != max - 1 ) { @@ -561,7 +557,7 @@ class HtmlText extends Text { var startI = 0; var index = Lambda.indexOf(e.parent, e); for (i in 0...text.length) { - if (StringTools.fastCodeAt(text, i) == '\n'.code) { + if (text.charCodeAt(i) == '\n'.code) { var pre = text.substring(startI, i); if (pre != "") e.parent.insertChild(Xml.createPCData(pre), index++); e.parent.insertChild(Xml.createElement("br"),index++); @@ -675,7 +671,7 @@ class HtmlText extends Text { switch( a.toLowerCase() ) { case "color": if( prevColor == null ) prevColor = @:privateAccess glyphs.curColor.clone(); - if( v.length == 4 && StringTools.fastCodeAt(v, 0) == '#'.code ) + if( v.charCodeAt(0) == '#'.code && v.length == 4 ) v = "#" + v.charAt(1) + v.charAt(1) + v.charAt(2) + v.charAt(2) + v.charAt(3) + v.charAt(3); glyphs.setDefaultColor(Std.parseInt("0x" + v.substr(1))); case "opacity": @@ -785,7 +781,7 @@ class HtmlText extends Text { var t = e.nodeValue; var dy = metrics[sizePos].baseLine - font.baseLine; for( i in 0...t.length ) { - var cc = StringTools.fastCodeAt(t, i); + var cc = t.charCodeAt(i); if( cc == "\n".code ) { makeLineBreak(); dy = metrics[sizePos].baseLine - font.baseLine; diff --git a/h2d/LoadingScene.hx b/h2d/LoadingScene.hx index 48db9ddaed..e8f197b2eb 100644 --- a/h2d/LoadingScene.hx +++ b/h2d/LoadingScene.hx @@ -1,44 +1,22 @@ package h2d; class LoadingScene extends h2d.Scene { - var renderTarget : h3d.mat.Texture; + var presentCooldown : Float; public function new(presentCooldown : Float) { super(); this.presentCooldown = presentCooldown; - renderTarget = new h3d.mat.Texture(width, height, [Target]); } - var lastPresentTime : Float = 0.0; - public override function render( engine : h3d.Engine ) { - var time = haxe.Timer.stamp(); - if ( time - lastPresentTime < presentCooldown) + override function render( engine : h3d.Engine ) { + hxd.Timer.update(); + var dt = hxd.Timer.dt; + ctx.elapsedTime += dt; + if ( ctx.elapsedTime < presentCooldown ) return; - lastPresentTime = time; - - #if usesys - haxe.System.emitEvents(@:privateAccess hxd.Window.inst.event); - #elseif hldx - dx.Loop.processEvents(@:privateAccess hxd.Window.inst.onEvent); - #elseif hlsdl - sdl.Sdl.processEvents(@:privateAccess hxd.Window.inst.onEvent); - #end - - if ( renderTarget.width != engine.width || renderTarget.height != engine.height) { - renderTarget.dispose(); - renderTarget = new h3d.mat.Texture(engine.width, engine.height, [Target]); - } - - engine.pushTarget(renderTarget); + ctx.elapsedTime = 0.0; + hxd.System.timeoutTick(); super.render(engine); - engine.popTarget(); - h3d.pass.Copy.run(renderTarget, null); engine.driver.present(); } - - override function onRemove() { - super.onRemove(); - if ( renderTarget != null ) - renderTarget.dispose(); - } } diff --git a/h2d/ObjectFollower.hx b/h2d/ObjectFollower.hx index c7c458a7ce..4ab74eebb7 100644 --- a/h2d/ObjectFollower.hx +++ b/h2d/ObjectFollower.hx @@ -55,11 +55,6 @@ class ObjectFollower extends Object { **/ public var cameraRelative : Bool = false; - /** - If enabled, the ObjectFollower will remove itself if the object followed is null or removed. - **/ - public var autoRemove : Bool = false; - var zValue : Float = 0.; var outputScale : Float = 1.; var tmpPos = new h3d.Vector(); @@ -76,17 +71,11 @@ class ObjectFollower extends Object { } function followObject() { - if( follow == null ) { - if ( autoRemove ) - remove(); + if( follow == null ) return; - } var scene = @:privateAccess follow.getScene(); - if( scene == null ) { - if ( autoRemove ) - remove(); + if( scene == null ) return; - } var s2d = getScene(); var width = s2d == null ? h3d.Engine.getCurrent().width : s2d.width; var height = s2d == null ? h3d.Engine.getCurrent().height : s2d.height; diff --git a/h2d/RenderContext.hx b/h2d/RenderContext.hx index fe936781d6..548edb6ca9 100644 --- a/h2d/RenderContext.hx +++ b/h2d/RenderContext.hx @@ -1,12 +1,9 @@ package h2d; -private typedef ViewportStackEntry = { +private typedef CameraStackEntry = { va : Float, vb : Float, vc : Float, vd : Float, vx : Float, vy : Float }; -private typedef CameraStackEntry = ViewportStackEntry & { - camera: h2d.Camera -}; -private typedef TargetStackEntry = ViewportStackEntry & { +private typedef TargetStackEntry = CameraStackEntry & { t : h3d.mat.Texture, hasRZ : Bool, rzX:Float, rzY:Float, rzW:Float, rzH:Float }; @@ -81,12 +78,6 @@ class RenderContext extends h3d.impl.RenderContext { **/ @:dox(hide) public var tmpBounds = new h2d.col.Bounds(); - - /** - The camera instance that is currently being rendered, if present, `null` otherwise. - **/ - public var currentCamera(default, null): Null = null; - var texture : h3d.mat.Texture; var baseShader : h3d.shader.Base2d; var output : h3d.pass.OutputShader; @@ -95,7 +86,6 @@ class RenderContext extends h3d.impl.RenderContext { var pass : h3d.mat.Pass; var currentShaders : hxsl.ShaderList; var baseShaderList : hxsl.ShaderList; - var needInitShaders : Bool; var currentObj : Drawable; var stride : Int; var targetsStack : Array; @@ -218,7 +208,6 @@ class RenderContext extends h3d.impl.RenderContext { } function initShaders( shaders ) { - needInitShaders = false; currentShaders = shaders; compiledShader = output.compileShaders(globals, shaders); var buffers = shaderBuffers; @@ -253,7 +242,7 @@ class RenderContext extends h3d.impl.RenderContext { public function pushCamera( cam : h2d.Camera ) { var entry = cameraStack[cameraStackIndex++]; if ( entry == null ) { - entry = { va: 0, vb: 0, vc: 0, vd: 0, vx: 0, vy: 0, camera: null }; + entry = { va: 0, vb: 0, vc: 0, vd: 0, vx: 0, vy: 0 }; cameraStack.push(entry); } var tmpA = viewA; @@ -268,9 +257,6 @@ class RenderContext extends h3d.impl.RenderContext { entry.vx = viewX; entry.vy = viewY; - entry.camera = currentCamera; - currentCamera = cam; - viewA = cam.matA * tmpA + cam.matB * tmpC; viewB = cam.matA * tmpB + cam.matB * tmpD; viewC = cam.matC * tmpA + cam.matD * tmpC; @@ -296,10 +282,6 @@ class RenderContext extends h3d.impl.RenderContext { viewD = inf.vd; viewX = inf.vx; viewY = inf.vy; - - currentCamera = inf.camera; - inf.camera = null; - var flipY = curTarget != null ? -targetFlipY : -baseFlipY; baseShader.viewportA.set(viewA, viewC, viewX); baseShader.viewportB.set(viewB * flipY, viewD * flipY, viewY * flipY); @@ -779,7 +761,7 @@ class RenderContext extends h3d.impl.RenderContext { var stride = 8; if( hasBuffering() && currentObj != null && (texture != this.texture || stride != this.stride || obj.blendMode != currentObj.blendMode || obj.filter != currentObj.filter) ) flush(); - var shaderChanged = needInitShaders, paramsChanged = false; + var shaderChanged = false, paramsChanged = false; var objShaders = obj.shaders; var curShaders = currentShaders.next; while( objShaders != null && curShaders != null ) { @@ -818,8 +800,4 @@ class RenderContext extends h3d.impl.RenderContext { return true; } - override function setCurrent() { - super.setCurrent(); - needInitShaders = true; - } } diff --git a/h2d/ScaleGrid.hx b/h2d/ScaleGrid.hx index 82bb3a49e4..294ab71a06 100644 --- a/h2d/ScaleGrid.hx +++ b/h2d/ScaleGrid.hx @@ -40,16 +40,13 @@ class ScaleGrid extends h2d.TileGroup { The height of the bitmap. Setting to values less than `borderTop + borderBottom` leads to undefined results. **/ public var height(default,set) : Float; - - /** - When enabled, borders will be repeated along the edges instead of stretching to match the desired dimensions. + When enabled, borders will be tiled along the edges instead of stretching to match the desired dimensions. + + Center tile is always stretched. **/ public var tileBorders(default, set) : Bool; - /** - When enabled, center will be repeated along the edges instead of stretching to match the desired dimensions. - **/ public var tileCenter(default, set) : Bool; /** diff --git a/h2d/Scene.hx b/h2d/Scene.hx index 0d87365e72..abb458b9a0 100644 --- a/h2d/Scene.hx +++ b/h2d/Scene.hx @@ -782,20 +782,15 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I ctx.frame++; ctx.time += ctx.elapsedTime; ctx.globalAlpha = alpha; - mark("s2d"); sync(ctx); - if( children.length != 0 ) { - ctx.begin(); - #if sceneprof h3d.impl.SceneProf.begin("2d", ctx.frame); #end - ctx.drawScene(); - #if sceneprof h3d.impl.SceneProf.end(); #end - ctx.end(); - } - mark("vsync"); + if( children.length == 0 ) return; + ctx.begin(); + #if sceneprof h3d.impl.SceneProf.begin("2d", ctx.frame); #end + ctx.drawScene(); + #if sceneprof h3d.impl.SceneProf.end(); #end + ctx.end(); } - public dynamic function mark(name : String) {} - override function sync( ctx : RenderContext ) { var forceCamSync = posChanged; if( !allocated ) diff --git a/h2d/Text.hx b/h2d/Text.hx index 527d913c6b..67369f226e 100644 --- a/h2d/Text.hx +++ b/h2d/Text.hx @@ -326,14 +326,13 @@ class Text extends Drawable { if ( font == null ) font = this.font; var lines = [], restPos = 0; var x = leftMargin; - var wLastSep = 0.; for( i in 0...text.length ) { - var cc = StringTools.fastCodeAt(text, i); + var cc = text.charCodeAt(i); var e = font.getChar(cc); var newline = cc == '\n'.code; var esize = e.width + e.getKerningOffset(prevChar); - var isComplement = (i < text.length - 1 && font.charset.isComplementChar(StringTools.fastCodeAt(text, i + 1))); - if( font.charset.isBreakChar(cc) && !isComplement ) { + var nc = text.charCodeAt(i+1); + if( font.charset.isBreakChar(cc) && (nc == null || !font.charset.isComplementChar(nc)) ) { if( lines.length == 0 && leftMargin > 0 && x > maxWidth ) { lines.push(""); if ( sizes != null ) sizes.push(leftMargin); @@ -341,10 +340,10 @@ class Text extends Drawable { } var size = x + esize + letterSpacing; /* TODO : no letter spacing */ var k = i + 1, max = text.length; - var prevChar = cc; + var prevChar = prevChar; var breakFound = false; while( size <= maxWidth && k < max ) { - var cc = StringTools.fastCodeAt(text, k++); + var cc = text.charCodeAt(k++); if( lineBreak && (font.charset.isSpace(cc) || cc == '\n'.code ) ) { breakFound = true; break; @@ -352,12 +351,8 @@ class Text extends Drawable { var e = font.getChar(cc); size += e.width + letterSpacing + e.getKerningOffset(prevChar); prevChar = cc; - if ( font.charset.isBreakChar(cc) ) { - if ( k >= text.length ) - break; - var nc = StringTools.fastCodeAt(text, k); - if ( !font.charset.isComplementChar(nc) ) break; - } + var nc = text.charCodeAt(k+1); + if( font.charset.isBreakChar(cc) && (nc == null || !font.charset.isComplementChar(nc)) ) break; } if( lineBreak && (size > maxWidth || (!breakFound && size + afterData > maxWidth)) ) { newline = true; @@ -369,19 +364,12 @@ class Text extends Drawable { } restPos = i + 1; } - else wLastSep = size; - } - else if( (x + esize + letterSpacing) - wLastSep > maxWidth ) { - newline = true; - lines.push(text.substr(restPos, i - restPos)); - restPos = i + 1; } if( e != null && cc != '\n'.code ) x += esize + letterSpacing; if( newline ) { if ( sizes != null ) sizes.push(x); x = 0; - wLastSep = 0.; prevChar = -1; } else prevChar = cc; @@ -433,7 +421,7 @@ class Text extends Drawable { } for( i in 0...t.length ) { - var cc = StringTools.fastCodeAt(t, i); + var cc = t.charCodeAt(i); var e = font.getChar(cc); var offs = e.getKerningOffset(prevChar); var esize = e.width + offs; diff --git a/h2d/TextInput.hx b/h2d/TextInput.hx index 540de7081b..661d227b84 100644 --- a/h2d/TextInput.hx +++ b/h2d/TextInput.hx @@ -177,10 +177,6 @@ class TextInput extends Text { var oldText = text; switch( e.keyCode ) { - case K.UP if( multiline ): - moveCursorVertically(-1); - case K.DOWN if( multiline ): - moveCursorVertically(1); case K.LEFT if (K.isDown(K.CTRL)): cursorIndex = getWordStart(); case K.LEFT: @@ -192,15 +188,9 @@ class TextInput extends Text { if( cursorIndex < text.length ) cursorIndex++; case K.HOME: - if( multiline ) { - var currentLine = getCurrentLine(); - cursorIndex = currentLine.startIndex; - } else cursorIndex = 0; + cursorIndex = 0; case K.END: - if( multiline ) { - var currentLine = getCurrentLine(); - cursorIndex = currentLine.startIndex + currentLine.value.length - 1; - } else cursorIndex = text.length; + cursorIndex = text.length; case K.BACKSPACE, K.DELETE if( selectionRange != null ): if( !canEdit ) return; beforeChange(); @@ -358,68 +348,6 @@ class TextInput extends Text { return ret; } - function moveCursorVertically(yDiff: Int){ - if( !multiline || yDiff == 0) - return; - var lines = []; - var cursorLineIndex = -1, currLineIndex = 0, currIndex = 0; - for( line in getAllLines() ) { - lines.push( { line: line, startIndex: currIndex } ); - var prevIndex = currIndex; - currIndex += line.length; - if( cursorIndex >= prevIndex && cursorIndex < currIndex ) - cursorLineIndex = currLineIndex; - currLineIndex++; - } - if (cursorLineIndex == -1) - return; - var destinationIndex = hxd.Math.iclamp(cursorLineIndex + yDiff, -1, lines.length); - if (destinationIndex == cursorLineIndex) - return; - // We're moving down from the last line, move to the end of the line - if( destinationIndex == lines.length) { - cursorIndex = text.length; - return; - } - // We're moving up from the first line, snap to beginning - if( destinationIndex == -1 ) { - cursorIndex = 0; - return; - } - var current = lines[cursorLineIndex]; - var xOffset = 0.; - var prevCC: Null = null; - var cI = 0; - while( current.startIndex + cI < cursorIndex) { - var cc = current.line.charCodeAt(cI); - var c = font.getChar(cc); - xOffset += c.width + c.getKerningOffset(prevCC) + letterSpacing; - prevCC = cc; - cI++; - } - var destination = lines[destinationIndex]; - var currOffset = 0.; - prevCC = null; - for( cI in 0...destination.line.length ) { - var cc = StringTools.fastCodeAt(destination.line, cI); - var c = font.getChar(cc); - var newCurrOffset = currOffset + c.width + c.getKerningOffset(prevCC) + letterSpacing; - if( newCurrOffset > xOffset ) { - cursorIndex = destination.startIndex + cI + 1; - if( xOffset - currOffset < newCurrOffset - xOffset ) - cursorIndex--; - return; - } - currOffset = newCurrOffset; - prevCC = cc; - } - cursorIndex = destination.startIndex + destination.line.length; - // The last character in this line may be the \n, check for this and move back by one. - // we can't just assume this because the last line typically won't end with a newline. - if( destination.line.charAt(destination.line.length-1) == "\n") - cursorIndex--; - } - function setState(h:TextHistoryElement) { text = h.t; cursorIndex = h.c; @@ -460,22 +388,23 @@ class TextInput extends Text { return finalLines; } - function getCurrentLine() : {value: String, startIndex: Int} { + function getCurrentLine() : String { var lines = getAllLines(); var currIndex = 0; - for( i in 0...lines.length ) { - var newCurrIndex = currIndex + lines[i].length; - if( cursorIndex < newCurrIndex ) - return { value: lines[i], startIndex: currIndex }; - currIndex = newCurrIndex; + + for(i in 0...lines.length) { + currIndex += lines[i].length; + if(cursorIndex < currIndex) { + return lines[i]; + } } - return { value: '', startIndex: -1 }; + return ''; } function getCursorXOffset() { var lines = getAllLines(); var offset = cursorIndex; - var currLine = getCurrentLine().value; + var currLine = getCurrentLine(); var currIndex = 0; for(i in 0...lines.length) { diff --git a/h2d/col/Matrix.hx b/h2d/col/Matrix.hx index 473d89022d..9ae3e755e0 100644 --- a/h2d/col/Matrix.hx +++ b/h2d/col/Matrix.hx @@ -123,7 +123,7 @@ class Matrix { var a = m.a, b = m.b; var c = m.c, d = m.d; var x = m.x, y = m.y; - var invDet = 1 / m.getDeterminant(); + var invDet = 1 / getDeterminant(); this.a = d * invDet; this.b = -b * invDet; this.c = -c * invDet; diff --git a/h2d/col/Polygon.hx b/h2d/col/Polygon.hx index 74f9099338..9cc535c1ae 100644 --- a/h2d/col/Polygon.hx +++ b/h2d/col/Polygon.hx @@ -202,25 +202,14 @@ abstract Polygon(Array) from Array to Array { } /** - Transforms Polygon points by the provided matrix. + Transforms Polygon points by provided matrix. **/ public function transform(mat: h2d.col.Matrix) { for( i in 0...points.length ) { - points[i].transform(mat); + points[i] = mat.transform(points[i]); } } - /** - Returns a new transformed Polygon points by the provided matrix. - **/ - public function transformed(mat: h2d.col.Matrix) { - var ret = points.copy(); - for( i in 0...ret.length ) { - ret[i] = ret[i].transformed(mat); - } - return new Polygon(ret); - } - /** Tests if Point `p` is inside this Polygon. @param p The point to test against. @@ -273,26 +262,21 @@ abstract Polygon(Array) from Array to Array { /** Return the closest point on the edges of the polygon - @param pt The point to test against. - @param out Optional Point instance to which closest point is written. If not provided, returns new Point instance. - @returns A `Point` instance of the closest point on the edges of the polygon. **/ - public function projectPoint(pt: h2d.col.Point, ?out : h2d.col.Point) { + public function projectPoint(pt: h2d.col.Point) { var p1 = points[points.length - 1]; - var closest = new h2d.col.Point(); - if (out == null) out = new Point(); + var closestProj = null; var minDistSq = 1e10; for(p2 in points) { - new Segment(p1, p2).project(pt, out); - var distSq = out.distanceSq(pt); - if (distSq < minDistSq) { - closest.load(out); + var proj = new Segment(p1, p2).project(pt); + var distSq = proj.distanceSq(pt); + if(distSq < minDistSq) { + closestProj = proj; minDistSq = distSq; } p1 = p2; } - out.load(closest); - return out; + return closestProj; } /** diff --git a/h2d/col/Segment.hx b/h2d/col/Segment.hx index 511a82c209..2587e28635 100644 --- a/h2d/col/Segment.hx +++ b/h2d/col/Segment.hx @@ -100,24 +100,19 @@ class Segment { /** Projects Point `p` onto Segment. Returns position of intersection between Segment and line perpendicular to it going through Point `p`. - @param p Point to project onto this Segment. - @param out Optional Point instance to which projection point is written. If not provided, returns new Point instance. - @returns A `Point` with projection position. **/ - public inline function project( p : Point, ?out : Point ) : Point { + public inline function project( p : Point ) : Point { var px = p.x - x; var py = p.y - y; var t = px * dx + py * dy; - if (out == null) out = new Point(); - if( t < 0 ) - out.set(x, y); - else if( t > lenSq ) - out.set(x + dx, y + dy); + return if( t < 0 ) + new Point(x, y); + else if( t > lenSq ) + new Point(x + dx, y + dy); else { var tl2 = t * invLenSq; - out.set(x + tl2 * dx, y + tl2 * dy); + new Point(x + tl2 * dx, y + tl2 * dy); } - return out; } /** diff --git a/h2d/domkit/BaseComponents.hx b/h2d/domkit/BaseComponents.hx index fa70e902ce..304c1a40c1 100644 --- a/h2d/domkit/BaseComponents.hx +++ b/h2d/domkit/BaseComponents.hx @@ -333,18 +333,6 @@ class CustomParser extends domkit.CssValue.ValueParser { #else new h2d.filter.Glow(c, a, r, g, q, b); #end - case VCall("glow",[VIdent("none"), r, g, q]): - var r = parseFloat(r); - var g = parseFloat(g); - var q = parseFloat(q); - #if macro - true; - #else - var glow = new h2d.filter.Glow(0xFFFFFF, 0., r, g, q); - // since 'hasFixedColor' is set to false, alpha will be ignored. - @:privateAccess glow.pass.shader.hasFixedColor = false; - glow; - #end case VCall("blur",[r]): var r = parseFloat(r); #if macro @@ -363,33 +351,6 @@ class CustomParser extends domkit.CssValue.ValueParser { } } - public function transitionColorAdjust(col1: h3d.Matrix.ColorAdjust, col2: h3d.Matrix.ColorAdjust, t: Float) { - inline function defaultValues(col: h3d.Matrix.ColorAdjust) { - var c : h3d.Matrix.ColorAdjust = { saturation: 0, lightness: 0, hue: 0, contrast: 0, gain: { color: 0, alpha: 0 } }; - if (col != null) { - if (col.saturation != null) c.saturation = col.saturation; - if (col.lightness != null) c.lightness = col.lightness; - if (col.hue != null) c.hue = col.hue; - if (col.contrast != null) c.contrast = col.contrast; - if (col.gain != null) c.gain = col.gain; - } - return c; - } - - var col1 = defaultValues(col1); - var col2 = defaultValues(col2); - return { - saturation: hxd.Math.lerp(col1.saturation, col2.saturation, t), - lightness: hxd.Math.lerp(col1.lightness, col2.lightness, t), - hue: hxd.Math.lerp(col1.hue, col2.hue, t), - contrast: hxd.Math.lerp(col1.contrast, col2.contrast, t), - gain: { - color: transitionColor(col1.gain.color, col2.gain.color, t), - alpha: hxd.Math.lerp(col1.gain.alpha, col2.gain.alpha, t) - } - }; - } - public function parseColorAdjust(value:CssValue) : h3d.Matrix.ColorAdjust { if( value.match(VIdent("none")) ) return null; @@ -424,30 +385,6 @@ class CustomParser extends domkit.CssValue.ValueParser { return adj; } - public function parseAngleRad(value:CssValue) : Float { - return switch(value) { - case VUnit(v, "rad"): - v; - case VUnit(v, "deg"): - hxd.Math.degToRad(v); - default: - parseFloat(value); - } - return 0.; - } - - public function parseAngleDeg(value:CssValue) : Float { - return switch(value) { - case VUnit(v, "rad"): - hxd.Math.radToDeg(v); - case VUnit(v, "deg"): - v; - default: - parseFloat(value); - } - return 0.; - } - } #if !macro @@ -457,11 +394,11 @@ class ObjectComp implements h2d.domkit.Object implements domkit.Component.Compon @:p var x : Float; @:p var y : Float; @:p var alpha : Float = 1; - @:p(angleRad) var rotation : Float; + @:p var rotation : Float; @:p var visible : Bool = true; @:p(scale) var scale : { x : Float, y : Float }; - @:p var scaleX : Float = 1; - @:p var scaleY : Float = 1; + @:p var scaleX : Float; + @:p var scaleY : Float; @:p var blend : h2d.BlendMode = Alpha; @:p(filter) var filter : h2d.filter.Filter; @:p var filterSmooth : Bool; @@ -641,7 +578,7 @@ class DrawableComp extends ObjectComp implements domkit.Component.ComponentDecl< @:p(colorF) @:t(colorF) #if domkit_drawable_color var color #else var tint #end : h3d.Vector4; @:p(auto) var smooth : Null; - @:p(colorAdjust) @:t(colorAdjust) var colorAdjust : Null; + @:p(colorAdjust) var colorAdjust : Null; @:p var tileWrap : Bool; static function #if domkit_drawable_color set_color #else set_tint #end( o : h2d.Drawable, v ) { @@ -689,8 +626,6 @@ class BitmapComp extends DrawableComp implements domkit.Component.ComponentDecl< @:p(tile) var src : h2d.Tile; @:p(tilePos) var srcPos : { p : Int, ?y : Int }; - @:p var srcFlipX : Null; - @:p var srcFlipY : Null; @:p var srcPosX : Null; @:p var srcPosY : Null; @:p(auto) var width : Null; @@ -716,14 +651,6 @@ class BitmapComp extends DrawableComp implements domkit.Component.ComponentDecl< o.tile = setTilePosY(o.tile, y); } - static function set_srcFlipX( o : h2d.Bitmap, b: Bool ) { - o.tile = setTileFlipX(o.tile, b); - } - - static function set_srcFlipY( o : h2d.Bitmap, b: Bool ) { - o.tile = setTileFlipY(o.tile, b); - } - static function setTilePos( t : h2d.Tile, pos : Null<{ p : Int, ?y : Int }> ) { if( t == null ) return null; if( pos == null ) pos = {p:0}; @@ -750,30 +677,6 @@ class BitmapComp extends DrawableComp implements domkit.Component.ComponentDecl< return t; } - static function setTileFlipX(t : h2d.Tile, b : Bool) { - if (t == null) return null; - var xFlip = t.u2 < t.u; - if (xFlip != b) { - t = t.clone(); - var tmp = t.u; - t.u = t.u2; - t.u2 = tmp; - } - return t; - } - - static function setTileFlipY(t : h2d.Tile, b : Bool) { - if (t == null) return null; - var yFlip = t.v2 < t.v; - if (yFlip != b) { - t = t.clone(); - var tmp = t.v; - t.v = t.v2; - t.v2 = tmp; - } - return t; - } - static function set_width( o : h2d.Bitmap, v : Null ) { o.width = v; } @@ -844,7 +747,6 @@ class ScaleGridComp extends DrawableComp implements domkit.Component.ComponentDe @:p var ignoreScale : Bool; @:p var borderScale : Float; @:p var tileBorders : Bool; - @:p var tileCenter : Bool; @:p var width : Float; @:p var height : Float; @@ -864,10 +766,6 @@ class ScaleGridComp extends DrawableComp implements domkit.Component.ComponentDe o.tileBorders = v; } - static function set_tileCenter(o : h2d.ScaleGrid, v) { - o.tileCenter = v; - } - static function set_width( o : h2d.ScaleGrid, v : Float ) { o.width = v; } @@ -893,7 +791,6 @@ class FlowComp extends ObjectComp implements domkit.Component.ComponentDecl; @:p var backgroundAlpha : Float = 1; @:p(auto) var backgroundSmooth : Null; - @:p var backgroundRepeat : Bool; @:p var debug : Bool; @:p var layout : h2d.Flow.FlowLayout; @:p var vertical : Bool; @@ -971,11 +868,6 @@ class FlowComp extends ObjectComp implements domkit.Component.ComponentDecl; - @:p var multiline : Bool; static function create( parent : h2d.Object ) { return new h2d.TextInput(hxd.res.DefaultFont.get(),parent); @@ -1138,10 +1029,6 @@ class InputComp extends TextComp implements domkit.Component.ComponentDecl= version("3.8.0")) sourceMap: format.map.Data, #end -} - class Style extends domkit.CssStyle { var currentObjects : Array = []; @@ -34,7 +28,7 @@ class Style extends domkit.CssStyle { }); resources.push(r); var variables = cssParser.variables.copy(); - add(cssParser.parseSheet(r.entry.getText(), r.name)); + add(cssParser.parseSheet(r.entry.getText())); cssParser.variables = variables; for( o in currentObjects ) o.dom.applyStyle(this); @@ -83,110 +77,31 @@ class Style extends domkit.CssStyle { ee = ee.parent; } if( msg == null ) msg = "Invalid property value '"+(domkit.CssParser.valueStr(s.value))+"'"; - var posStr = ""; - var f = find(sourceFiles, f -> f.name == s.pos.file); - if (s.pos != null && f != null) { - var pos = getPos(f, s.pos.pmin); - posStr = pos.file+":"+pos.line+": "; - } - errors.push(posStr+msg+" for " + path); + errors.push(msg+" for " + path); } } - inline function find( it : Array, f : T -> Bool ) : Null { - var ret = null; - for( v in it ) { - if(f(v)) { - ret = v; - break; - } - } - return ret; - } - inline function countLines(str: String, until = -1, code = "\n".code) { - var ret = { - line: 1, - col: 0, - } - if (until < 0) - until = str.length; - var lastFound = 0; - for( i in 0...until ) { - if( StringTools.fastCodeAt(str, i) == code ) { - ret.line++; - lastFound = i; - } - } - ret.col = until - lastFound; - return ret; - } - inline function getPos(f: SourceFile, pmin) { - var count = countLines(f.txt, pmin); - var line = count.line; - var col = count.col; - var file = f.name; - #if (format >= version("3.8.0")) - if (f.sourceMap != null) { - var pos = f.sourceMap.originalPositionFor(count.line, count.col); - if (pos != null) { - file = pos.source; - line = pos.originalLine; - col = pos.originalColumn; - } - } - #end - return { - line: line, - count: count, - file: file, - }; - } - - #if (format >= version("3.8.0")) - function getSourceMapFor(r: hxd.res.Resource) { - var mapFile = r.entry.path + ".map"; - if( hxd.res.Loader.currentInstance.exists(mapFile) ) { - var mapContent = hxd.res.Loader.currentInstance.load(mapFile).toText(); - try { - return new format.map.Reader().parse(mapContent); - } catch(e) {} - } - return null; - } - #end - var sourceFiles: Array = []; - function onChange( ntry : Int = 0 ) { if( ntry >= 10 ) return; ntry++; var oldRules = data.rules; errors = []; data.rules = []; - sourceFiles = []; for( r in resources ) { var txt = try r.entry.getText() catch( e : Dynamic ) { haxe.Timer.delay(onChange.bind(ntry),100); data.rules = oldRules; return; } - var curFile = { - name: r.entry.name, - txt: txt, - #if (format >= version("3.8.0")) - sourceMap: getSourceMapFor(r), - #end - }; - sourceFiles.push(curFile); try { - data.add(cssParser.parseSheet(txt, r.name)); + data.add(cssParser.parseSheet(txt)); } catch( e : domkit.Error ) { cssParser.warnings.push({ msg : e.message, pmin : e.pmin, pmax : e.pmax }); } for( w in cssParser.warnings ) { - var pos = getPos(curFile, w.pmin); - errors.push(pos.file+":"+pos.line+": " + w.msg); + var line = txt.substr(0,w.pmin).split("\n").length; + errors.push(r.entry.path+":"+line+": " + w.msg); } } for( o in currentObjects ) o.dom.applyStyle(this); refreshErrors(); - sourceFiles = []; } function refreshErrors( ?scene ) { @@ -245,10 +160,7 @@ class Style extends domkit.CssStyle { if( s == null || scenes.indexOf(s) >= 0 ) continue; scenes.push(s); function scanRec(o:h2d.Object) { - if( o.dom != null ) { - @:privateAccess o.dom.currentValues = null; - @:privateAccess o.dom.currentRuleStyles = null; - } + if( o.dom != null ) @:privateAccess o.dom.currentValues = null; for( o in o ) scanRec(o); } scanRec(s); @@ -261,11 +173,9 @@ class Style extends domkit.CssStyle { return allowInspect = b; } - var lastFrame = -1; function onWindowEvent( e : hxd.Event ) { switch( e.kind ) { case EPush if( inspectKeyCode == 0 || hxd.Key.isDown(inspectKeyCode) ): - lastFrame = -1; if( e.button == hxd.Key.MOUSE_MIDDLE ) { if(hxd.Key.isDown(inspectDetailsKeyCode)) { inspectModeActive = true; @@ -289,20 +199,8 @@ class Style extends domkit.CssStyle { } } case EMove: - if( inspectModeActive && !inspectModeDetails ) { - var anyScene = null; - for( o in currentObjects ) { - anyScene = o.getScene(); - if( anyScene != null ) break; - } - if( anyScene == null || lastFrame < anyScene.renderer.frame ) { - if( anyScene != null ) - lastFrame = anyScene.renderer.frame; - updatePreview(e); - } - } + if( inspectModeActive && !inspectModeDetails) updatePreview(e); case EWheel if( inspectKeyCode == 0 || hxd.Key.isDown(inspectKeyCode) ): - lastFrame = -1; if( inspectPreviewObjects != null ) { if( e.wheelDelta > 0 ) { var p = inspectPreviewObjects[0].parent; @@ -491,74 +389,25 @@ class Style extends domkit.CssStyle { prevFlow.backgroundTile = h2d.Tile.fromColor(0,0.8); prevFlow.padding = 7; prevFlow.paddingTop = 4; - prevFlow.layout = Vertical; - prevFlow.verticalSpacing = 16; - - var previewTitle = new h2d.HtmlText(hxd.res.DefaultFont.get(), prevFlow); - var propsFlow = new h2d.Flow(prevFlow); - var propsLineText = new h2d.HtmlText(hxd.res.DefaultFont.get(), propsFlow); - var propsValueText = new h2d.HtmlText(hxd.res.DefaultFont.get(), propsFlow); - - previewTitle.text = getDisplayInfo(obj); + var previewText = new h2d.HtmlText(hxd.res.DefaultFont.get(), prevFlow); + var lines = []; + lines.push(getDisplayInfo(obj)); + lines.push(""); var dom = obj.dom; - if(dom != null) { - var posLines = []; - var valueLines = []; - var files: Array = []; - var lineDigits = 0; - for( i in 0...dom.currentSet.length ) { - if( dom.currentRuleStyles == null || dom.currentRuleStyles[i] == null ) - continue; - var vs = dom.currentRuleStyles[i]; - if (find(files, f -> f.name == vs.pos.file) != null) - continue; - var r = find(resources, r -> r.name == vs.pos.file); - if (r != null) { - var txt = r.entry.getText(); - files.push({ - name: vs.pos.file, - txt: txt, - #if (format >= version("3.8.0")) - sourceMap: getSourceMapFor(r), - #end - }); - lineDigits = hxd.Math.imax(lineDigits, Std.int(Math.log(countLines(txt).line) / Math.log(10))); - } - } - for( s in dom.style ) { if( s.p.name == "text" || Std.isOfType(s.value,h2d.Tile) ) continue; - valueLines.push(' ${s.p.name} ${s.value} (style)'); + lines.push(' ${s.p.name} ${s.value} (style)'); } - var emptyDigits = ""; - for (i in 0...lineDigits) - emptyDigits += " "; for( i in 0...dom.currentSet.length ) { var p = dom.currentSet[i]; if( p.name == "text" ) continue; var v = dom.currentValues == null ? null : dom.currentValues[i]; - var vs = dom.currentRuleStyles == null ? null : dom.currentRuleStyles[i]; - var lStr = emptyDigits; - if (vs != null) { - v = vs.value; - var f = find(files, f -> f.name == vs.pos.file); - if (f != null) { - var pos = getPos(f, vs.pos.pmin); - var s = "" + pos.line; - if (pos.file == null) - lStr = '$s'; - else - lStr = '${pos.file}:$s'; - } - } var vstr = v == null ? "???" : StringTools.htmlEscape(domkit.CssParser.valueStr(v)); - posLines.push(lStr); - valueLines.push(' ${p.name} $vstr'); + lines.push(' ${p.name} $vstr'); } - propsLineText.text = posLines.join("
"); - propsValueText.text = valueLines.join("
"); + previewText.text = lines.join("
"); } var size = prevFlow.getBounds(); diff --git a/h3d/Buffer.hx b/h3d/Buffer.hx index 1ff4b73bca..f5ee03a1c5 100644 --- a/h3d/Buffer.hx +++ b/h3d/Buffer.hx @@ -27,9 +27,11 @@ enum BufferFlag { class Buffer { public static var GUID = 0; public var id : Int; + #if track_alloc var allocPos : hxd.impl.AllocPos; + var allocNext : Buffer; + #end var engine : h3d.Engine; - var lastFrame : Int; @:allow(h3d.impl.Driver) var vbuf : h3d.impl.Driver.GPUBuffer; public var vertices(default,null) : Int; @@ -41,7 +43,9 @@ class Buffer { this.vertices = vertices; this.format = format; this.flags = new haxe.EnumFlags(); - this.allocPos = hxd.impl.AllocPos.make(); + #if track_alloc + this.allocPos = new hxd.impl.AllocPos(); + #end if( flags != null ) for( f in flags ) this.flags.set(f); @@ -130,7 +134,7 @@ class Buffer { } public static function ofFloats( v : hxd.FloatBuffer, format : hxd.BufferFormat, ?flags ) { - var nvert = Math.ceil(v.length / format.stride); + var nvert = Std.int(v.length / format.stride); var b = new Buffer(nvert, format, flags); b.uploadFloats(v, 0, nvert); return b; diff --git a/h3d/Camera.hx b/h3d/Camera.hx index 84ab3028ea..6ce2bb2a41 100644 --- a/h3d/Camera.hx +++ b/h3d/Camera.hx @@ -33,11 +33,6 @@ class Camera { public var m : Matrix; public var pos : Vector; - /** - up is used for the lookAt matrix. - it is not the actual up axis of the camera. - use getUp instead. - **/ public var up : Vector; public var target : Vector; @@ -48,14 +43,10 @@ class Camera { public var frustum(default, null) : h3d.col.Frustum; - public var jitterOffsetX : Float = 0.; - public var jitterOffsetY : Float = 0.; - var minv : Matrix; var mcamInv : Matrix; var mprojInv : Matrix; var needInv : Bool; - var directions : Matrix; public function new( fovY = 25., zoom = 1., screenRatio = 1.333333, zNear = 0.02, zFar = 4000., rightHanded = false ) { this.fovY = fovY; @@ -139,86 +130,6 @@ class Camera { return mcamInv; } - function calcDirections() { - var cameraForward = ( target - pos ).normalized(); - var cameraRight = up.cross(cameraForward).normalized(); - var cameraUp = cameraForward.cross(cameraRight); - - directions._11 = cameraForward.x; - directions._12 = cameraForward.y; - directions._13 = cameraForward.z; - - directions._21 = cameraRight.x; - directions._22 = cameraRight.y; - directions._23 = cameraRight.z; - - directions._31 = cameraUp.x; - directions._32 = cameraUp.y; - directions._33 = cameraUp.z; - - directions._44 = 1; - } - - /** - Returns the forward of the camera. Cache the result until the next update(). - **/ - - inline public function getForward() : h3d.Vector { - var forward = new h3d.Vector(); - if ( directions == null ) { - directions = new h3d.Matrix(); - directions._44 = 0; - } - if ( directions._44 == 0 ) - calcDirections(); - - forward.x = directions._11; - forward.y = directions._12; - forward.z = directions._13; - - return forward; - } - - /** - Returns the right of the camera. Cache the result until the next update(). - **/ - - inline public function getRight() : h3d.Vector { - var right = new h3d.Vector(); - if ( directions == null ) { - directions = new h3d.Matrix(); - directions._44 = 0; - } - if ( directions._44 == 0 ) - calcDirections(); - - right.x = directions._21; - right.y = directions._22; - right.z = directions._23; - - return right; - } - - /** - Returns the up of the camera. Cache the result until the next update(). - **/ - - inline public function getUp() : h3d.Vector { - var up = new h3d.Vector(); - if ( directions == null ) { - directions = new h3d.Matrix(); - directions._44 = 0; - } - if ( directions._44 == 0 ) - calcDirections(); - - up.x = directions._31; - up.y = directions._32; - up.z = directions._33; - - return up; - } - /** Setup camera for cubemap rendering on the given face. **/ @@ -288,7 +199,6 @@ class Camera { needInv = true; if( mcamInv != null ) mcamInv._44 = 0; if( mprojInv != null ) mprojInv._44 = 0; - if( directions != null ) directions._44 = 0; frustum.loadMatrix(m); } @@ -421,9 +331,6 @@ class Camera { m._33 = zFar / (zFar - zNear); m._34 = 1; m._43 = -(zNear * zFar) / (zFar - zNear); - - m._31 = jitterOffsetX; - m._32 = jitterOffsetY; } m._11 += viewX * m._14; @@ -446,20 +353,6 @@ class Camera { /** Project a 3D point into the 2D screen. Make sure to update() the camera if it's been moved before using that. **/ - - inline public function projectInline( x : Float, y : Float, z : Float, screenWidth : Float, screenHeight : Float, snapToPixel = true ) { - var p = new h3d.Vector(); - p.set(x, y, z); - p.project(m); - p.x = (p.x + 1) * 0.5 * screenWidth; - p.y = (-p.y + 1) * 0.5 * screenHeight; - if( snapToPixel ) { - p.x = Math.round(p.x); - p.y = Math.round(p.y); - } - return p; - } - public function project( x : Float, y : Float, z : Float, screenWidth : Float, screenHeight : Float, snapToPixel = true, ?p: h3d.Vector) { if(p == null) p = new h3d.Vector(); diff --git a/h3d/Engine.hx b/h3d/Engine.hx index 66e8069f50..a73d8d08a9 100644 --- a/h3d/Engine.hx +++ b/h3d/Engine.hx @@ -25,10 +25,6 @@ enum DepthBinding { } class Engine { - #if multidriver - static var ID = 0; - public var id(default, null) : Int; - #end public var driver(default,null) : h3d.impl.Driver; @@ -41,7 +37,6 @@ class Engine { public var drawTriangles(default, null) : Int; public var drawCalls(default, null) : Int; - public var dispatches(default, null) : Int; public var shaderSwitches(default, null) : Int; public var backgroundColor : Null = 0xFF000000; @@ -74,10 +69,6 @@ class Engine { @:access(hxd.Window) function new() { - #if multidriver - this.id = ID; - ID++; - #end this.hardware = !SOFTWARE_DRIVER; this.antiAlias = ANTIALIASING; this.autoResize = true; @@ -86,7 +77,6 @@ class Engine { realFps = hxd.System.getDefaultFrameRate(); lastTime = haxe.Timer.stamp(); window.addResizeEvent(onWindowResize); - setCurrent(); #if macro driver = new h3d.impl.NullDriver(); #elseif (js || hlsdl || usegl) @@ -109,6 +99,7 @@ class Engine { #else #if sys Sys.println #else trace #end("No output driver available." #if hl + " Compile with -lib hlsdl or -lib hldx" #end); #end + setCurrent(); } static var CURRENT : Engine = null; @@ -124,7 +115,6 @@ class Engine { public inline function setCurrent() { CURRENT = this; - window.setCurrent(); } public function init() { @@ -288,7 +278,7 @@ class Engine { if( !driver.isDisposed() ) driver.resize(width, height); } - public function begin() { + public function begin(preventClear = false) { if( driver.isDisposed() ) return false; // init @@ -296,14 +286,14 @@ class Engine { drawTriangles = 0; shaderSwitches = 0; drawCalls = 0; - dispatches = 0; targetStack = null; needFlushTarget = currentTargetTex != null; #if (usesys && !macro) haxe.System.beginFrame(); #end driver.begin(hxd.Timer.frameCount); - if( backgroundColor != null ) clear(backgroundColor, 1, 0); + if( backgroundColor != null && !preventClear ) + clear(backgroundColor, 1, 0); return true; } @@ -429,25 +419,9 @@ class Engine { return true; } - public function onTextureBiasChanged(t : h3d.mat.Texture) { - if ( !t.isDepth() ) - throw "Can change texture bias on depth buffer only"; - driver.onTextureBiasChanged(t); - } - public function dispose() { driver.dispose(); window.removeResizeEvent(onWindowResize); - if ( mem != null ) - mem.dispose(); - #if multidriver - for ( r in resCache ) { - var resource = Std.downcast(r, hxd.res.Resource); - if ( resource != null ) { - resource.entry.unwatch(id); - } - } - #end } function get_fps() { diff --git a/h3d/Matrix.hx b/h3d/Matrix.hx index d241620e60..02263fb7ba 100644 --- a/h3d/Matrix.hx +++ b/h3d/Matrix.hx @@ -831,8 +831,9 @@ class MatrixImpl { /** Build a rotation Matrix so the X axis will look at the given direction, and the Z axis will be the Up vector ([0,0,1] by default) **/ - - public static inline function lookAtXInline( dir : Vector, up : Vector, m : Matrix ) { + public static function lookAtX( dir : Vector, ?up : Vector, ?m : Matrix ) { + if( up == null ) up = new Vector(0, 0, 1); + if( m == null ) m = new Matrix(); var ax = dir.normalized(); var ay = up.cross(ax).normalized(); if( ay.lengthSq() < Math.EPSILON2 ) { @@ -860,10 +861,4 @@ class MatrixImpl { return m; } - public static function lookAtX( dir : Vector, ?up : Vector, ?m : Matrix ) { - if( up == null ) up = new Vector(0, 0, 1); - if( m == null ) m = new Matrix(); - return lookAtXInline(dir, up, m); - } - } \ No newline at end of file diff --git a/h3d/Quat.hx b/h3d/Quat.hx index 2eb472f0c2..c006f7c8e3 100644 --- a/h3d/Quat.hx +++ b/h3d/Quat.hx @@ -63,23 +63,14 @@ class Quat { normalize(); } - public function initNormal( dir : h3d.col.Point, rotate : Float = 0.0 ) { + public function initNormal( dir : h3d.col.Point ) { var dir = dir.normalized(); if( dir.x*dir.x+dir.y*dir.y < Math.EPSILON2 ) initDirection(new h3d.Vector(1,0,0)); else { var ay = new h3d.col.Point(dir.x, dir.y, 0).normalized(); var az = dir.cross(ay); - var ax = dir.cross(az).toVector(); - if (dir.z < 0.0) - initDirection(ax, new Vector(0.0, 0.0, -1.0)); - else - initDirection(ax); - } - if ( rotate != 0.0) { - var quat = new Quat(); - quat.initRotateAxis(dir.x, dir.y, dir.z, rotate); - multiply(quat, this); + initDirection(dir.cross(az).toVector()); } } diff --git a/h3d/Vector.hx b/h3d/Vector.hx index 0c8b3d4586..f7dfecf38b 100644 --- a/h3d/Vector.hx +++ b/h3d/Vector.hx @@ -265,28 +265,6 @@ class VectorImpl #if apicheck implements h2d.impl.PointApi #end { return new h3d.Vector(h, s, l); } - public function toColorHSV() { - var max = hxd.Math.max(hxd.Math.max(r, g), b); - var min = hxd.Math.min(hxd.Math.min(r, g), b); - var h, s, v = max; - - if(max == min) - h = s = 0.0; // achromatic - else { - var d = max - min; - s = (max + min) > 1.0 ? d / (2 - max - min) : d / (max + min); - if(max == r) - h = (g - b) / d + (g < b ? 6.0 : 0.0); - else if(max == g) - h = (b - r) / d + 2.0; - else - h = (r - g) / d + 4.0; - h *= Math.PI / 3.0; - } - - return new h3d.Vector(h, s, v); - } - } diff --git a/h3d/Vector4.hx b/h3d/Vector4.hx index 99559b6687..6f5f31cb95 100644 --- a/h3d/Vector4.hx +++ b/h3d/Vector4.hx @@ -275,28 +275,6 @@ class Vector4Impl /*#if apicheck implements h2d.impl.PointApi #e return new h3d.Vector4(h, s, l, a); } - public function toColorHSV() { - var max = hxd.Math.max(hxd.Math.max(r, g), b); - var min = hxd.Math.min(hxd.Math.min(r, g), b); - var h, s, v = max; - - if(max == min) - h = s = 0.0; // achromatic - else { - var d = max - min; - s = (max + min) > 1.0 ? d / (2 - max - min) : d / (max + min); - if(max == r) - h = (g - b) / d + (g < b ? 6.0 : 0.0); - else if(max == g) - h = (b - r) / d + 2.0; - else - h = (r - g) / d + 4.0; - h *= Math.PI / 3.0; - } - - return new h3d.Vector4(h, s, v, a); - } - } diff --git a/h3d/anim/Animation.hx b/h3d/anim/Animation.hx index ff668373f9..a0799f4742 100644 --- a/h3d/anim/Animation.hx +++ b/h3d/anim/Animation.hx @@ -102,15 +102,6 @@ class Animation { events[frame].push(data); } - public function removeEvent(frame : Int, data : String) { - if (events == null || events[frame] == null || !events[frame].contains(data)) - throw 'Can\'t delete event $data because it doesn\'t exist at frame $frame'; - - events[frame].remove(data); - if (events[frame].length == 0) - events[frame] = null; - } - public function getEvents() return events; public function getObjects() return objects; diff --git a/h3d/anim/SmoothTarget.hx b/h3d/anim/SmoothTarget.hx index ea8a56eb9f..8a57256185 100644 --- a/h3d/anim/SmoothTarget.hx +++ b/h3d/anim/SmoothTarget.hx @@ -36,7 +36,6 @@ class SmoothTarget extends Animation { initObjects(); } - static var tmpMat : Matrix = new h3d.Matrix(); function initObjects() { objects = []; for( o in target.getObjects() ) { @@ -46,19 +45,8 @@ class SmoothTarget extends Animation { s.targetSkin = o.targetSkin; s.targetJoint = o.targetJoint; objects.push(s); - if( o.targetSkin != null ) { + if( o.targetSkin != null ) mat = @:privateAccess o.targetSkin.currentRelPose[o.targetJoint]; - if ( mat != null && o.targetSkin.prevEnableRetargeting ) { - var j = @:privateAccess o.targetSkin.skinData.allJoints[o.targetJoint]; - if ( j != null && j.retargetAnim ) { - tmpMat.load(mat); - mat = tmpMat; - mat._41 = j.defMat._41; - mat._42 = j.defMat._42; - mat._43 = j.defMat._43; - } - } - } else if( o.targetObject != null ) mat = o.targetObject.defaultTransform; if( mat == null ) diff --git a/h3d/col/Bounds.hx b/h3d/col/Bounds.hx index f16139b5c6..f947744307 100644 --- a/h3d/col/Bounds.hx +++ b/h3d/col/Bounds.hx @@ -389,10 +389,6 @@ class Bounds extends Collider { return new Sphere((xMin + xMax) * 0.5, (yMin + yMax) * 0.5, (zMin + zMax) * 0.5, Math.sqrt(dx * dx + dy * dy + dz * dz) * 0.5); } - public inline function dimension() { - return Math.max(xSize, Math.max(ySize, zSize)); - } - public static inline function fromPoints( min : Point, max : Point ) { var b = new Bounds(); b.setMin(min); diff --git a/h3d/col/Capsule.hx b/h3d/col/Capsule.hx index 6e1be69dce..4a3736b80b 100644 --- a/h3d/col/Capsule.hx +++ b/h3d/col/Capsule.hx @@ -105,10 +105,6 @@ class Capsule extends Collider { return "Capsule{" + a + "," + b + "," + hxd.Math.fmt(r) + "}"; } - public function dimension() { - return a.distance(b) + 2 * r; - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var obj = new h3d.scene.Object(); diff --git a/h3d/col/Collider.hx b/h3d/col/Collider.hx index 0cb85a4507..3668bbedf7 100644 --- a/h3d/col/Collider.hx +++ b/h3d/col/Collider.hx @@ -10,7 +10,6 @@ abstract class Collider { public abstract function contains( p : Point ) : Bool; public abstract function inFrustum( f : Frustum, ?localMatrix : h3d.Matrix ) : Bool; public abstract function inSphere( s : Sphere ) : Bool; - public abstract function dimension() : Float; #if !macro public abstract function makeDebugObj() : h3d.scene.Object; @@ -30,7 +29,7 @@ class OptimizedCollider extends Collider { } public function rayIntersection( r : Ray, bestMatch : Bool ) : Float { - if( a.rayIntersection(r, false) < 0 ) { + if( a.rayIntersection(r, bestMatch) < 0 ) { if( !checkInside ) return -1; if( !a.contains(r.getPoint(0)) ) @@ -51,10 +50,6 @@ class OptimizedCollider extends Collider { return a.inSphere(s) && b.inSphere(s); } - public function dimension() { - return Math.max(a.dimension(), b.dimension()); - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var bobj = b.makeDebugObj(); @@ -113,13 +108,6 @@ class GroupCollider extends Collider { return false; } - public function dimension() { - var d = Math.NEGATIVE_INFINITY; - for ( c in colliders ) { - d = Math.max(d, c.dimension()); - } - return d; - } #if !macro public function makeDebugObj() : h3d.scene.Object { var ret : h3d.scene.Object = null; diff --git a/h3d/col/Frustum.hx b/h3d/col/Frustum.hx index 43730a68f8..c6bf25610d 100644 --- a/h3d/col/Frustum.hx +++ b/h3d/col/Frustum.hx @@ -122,12 +122,10 @@ class Frustum { return false; if( b.testPlane(pbottom) < 0 ) return false; - if ( checkNearFar ) { - if( b.testPlane(pnear) < 0 ) - return false; - if( b.testPlane(pfar) < 0 ) - return false; - } + if( b.testPlane(pnear) < 0 ) + return false; + if( b.testPlane(pfar) < 0 ) + return false; return true; } diff --git a/h3d/col/HeightMap.hx b/h3d/col/HeightMap.hx index 80366ebc5f..595bc9a7b9 100644 --- a/h3d/col/HeightMap.hx +++ b/h3d/col/HeightMap.hx @@ -5,7 +5,7 @@ package h3d.col; In order to use, you need to extends this class and override the getZ method in order to return appropriate Z value based on X and Y coordinates. **/ -abstract class HeightMap extends Collider { +class HeightMap extends Collider { /** When performing raycast check, tells by how much step we advance. @@ -24,7 +24,9 @@ abstract class HeightMap extends Collider { /** Returns the height value at given coordinates. **/ - abstract function getZ( x : Float, y : Float ) : Float; + public function getZ( x : Float, y : Float ) : Float { + throw "Not implemented: requires override"; + } public inline function contains( pt : Point ) : Bool { return getZ(pt.x, pt.y) > pt.z; diff --git a/h3d/col/ObjectCollider.hx b/h3d/col/ObjectCollider.hx index f297efa951..ad8eda726f 100644 --- a/h3d/col/ObjectCollider.hx +++ b/h3d/col/ObjectCollider.hx @@ -64,11 +64,6 @@ class ObjectCollider extends Collider { return res; } - inline public function dimension() { - var scale = obj.getAbsPos().getScale(); - return collider.dimension() * Math.max(Math.max(scale.x, scale.y), scale.z); - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var ret = collider.makeDebugObj(); diff --git a/h3d/col/Polygon.hx b/h3d/col/Polygon.hx index 3279da1b72..8de2075566 100644 --- a/h3d/col/Polygon.hx +++ b/h3d/col/Polygon.hx @@ -26,10 +26,7 @@ class TriPlane extends Collider { var nz : Float; var d : Float; - var oriented : Bool; - - public function new(o = false) { - oriented = o; + public function new() { } public inline function init( p0 : Point, p1 : Point, p2 : Point ) { @@ -58,7 +55,7 @@ class TriPlane extends Collider { } public inline function clone() { - var clone = new TriPlane(oriented); + var clone = new TriPlane(); clone.load(this); if(next != null) clone.next = next.clone(); @@ -115,11 +112,11 @@ class TriPlane extends Collider { inline public function rayIntersection( r : Ray, bestMatch : Bool ) @:privateAccess { var dr = r.lx * nx + r.ly * ny + r.lz * nz; - if( dr >= 0 && oriented ) // backface culling + if( dr >= 0 ) // backface culling return -1.; var nd = d - (r.px * nx + r.py * ny + r.pz * nz); var k = nd / dr; - if( k < 0 && oriented ) + if( k < 0 ) return -1; var px = r.px + r.lx * k; var py = r.py + r.ly * k; @@ -159,27 +156,21 @@ class TriPlane extends Collider { } #end - public function dimension() { - throw "Not implemented"; - return 0.0; - } } class Polygon extends Collider { var triPlanes : TriPlane; - var oriented : Bool; - public function new(o = false) { - oriented = o; + public function new() { } public function addBuffers( vertexes : haxe.ds.Vector, indexes : haxe.ds.Vector, stride = 3 ) { for(i in 0...Std.int(indexes.length / 3)) { var k = i * 3; - var t = new TriPlane(oriented); + var t = new TriPlane(); var i0 = indexes[k] * stride; var i1 = indexes[k + 1] * stride; @@ -202,7 +193,7 @@ class Polygon extends Collider { } public function clone() : h3d.col.Polygon { - var clone = new h3d.col.Polygon(oriented); + var clone = new h3d.col.Polygon(); clone.triPlanes = triPlanes.clone(); return clone; } @@ -274,10 +265,6 @@ class Polygon extends Collider { return false; } - inline public function dimension() { - return getBounds().dimension(); - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var points : Array = []; @@ -295,21 +282,18 @@ class Polygon extends Collider { } var prim = new h3d.prim.Polygon(points); prim.addNormals(); - var mesh = new h3d.scene.Mesh(prim); - if ( !oriented ) - mesh.material.mainPass.culling = None; - return mesh; + return new h3d.scene.Mesh(prim); } #end - public static function fromPolygon2D( p : h2d.col.Polygon, z = 0., oriented = true ) { + public static function fromPolygon2D( p : h2d.col.Polygon, z = 0. ) { var pout = new Polygon(); if( p.isConvex() ) { var p0 = p[0]; for( i in 0...p.length-2 ) { var p1 = p[i+1]; var p2 = p[i+2]; - var t = new TriPlane(oriented); + var t = new TriPlane(); t.init( new h3d.col.Point(p0.x, p0.y, z), new h3d.col.Point(p1.x, p1.y, z), @@ -324,7 +308,7 @@ class Polygon extends Collider { var p0 = p[idx[i*3]]; var p1 = p[idx[i*3+1]]; var p2 = p[idx[i*3+2]]; - var t = new TriPlane(oriented); + var t = new TriPlane(); t.init( new h3d.col.Point(p0.x, p0.y, z), new h3d.col.Point(p1.x, p1.y, z), diff --git a/h3d/col/PolygonBuffer.hx b/h3d/col/PolygonBuffer.hx index cea0e42096..75b9127b1a 100644 --- a/h3d/col/PolygonBuffer.hx +++ b/h3d/col/PolygonBuffer.hx @@ -84,10 +84,6 @@ class PolygonBuffer extends Collider { return false; } - public function dimension() { - return getBounds().dimension(); - } - // Möller–Trumbore intersection public function rayIntersection( r : Ray, bestMatch : Bool ) : Float { var i = startIndex; diff --git a/h3d/col/Seg.hx b/h3d/col/Seg.hx index c461d94cad..7a2110d9d4 100644 --- a/h3d/col/Seg.hx +++ b/h3d/col/Seg.hx @@ -20,7 +20,7 @@ class Seg { else if( t > 1 ) { p.distanceSq(p2); } else - p.distanceSq(new Point(p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y), p1.z + t * (p2.z - p1.z))); + p.distanceSq(new Point(p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y))); } public inline function distance( p : Point ) { diff --git a/h3d/col/SkinCollider.hx b/h3d/col/SkinCollider.hx index 93401102b0..01d11b73c8 100644 --- a/h3d/col/SkinCollider.hx +++ b/h3d/col/SkinCollider.hx @@ -54,10 +54,6 @@ class SkinCollider extends Collider { return transform.rayIntersection(r, bestMatch); } - public function dimension() { - return currentBounds.dimension(); - } - function checkBounds() { if( !obj.jointsUpdated && lastBoundsFrame == obj.lastFrame ) return; lastBoundsFrame = obj.lastFrame; diff --git a/h3d/col/Sphere.hx b/h3d/col/Sphere.hx index add67a7bce..5d1138411f 100644 --- a/h3d/col/Sphere.hx +++ b/h3d/col/Sphere.hx @@ -69,7 +69,7 @@ class Sphere extends Collider { y = v.y; z = v.z; var scale = m.getScale(); - r *= Math.abs(Math.max(Math.max(scale.x, scale.y), scale.z)); + r *= Math.max(Math.max(scale.x, scale.y), scale.z); var res = f.hasSphere(this); x = oldX; y = oldY; @@ -97,19 +97,6 @@ class Sphere extends Collider { return "Sphere{" + getCenter()+","+ hxd.Math.fmt(r) + "}"; } - public inline function dimension() { - return r; - } - - public inline function clone() { - var s = new Sphere(); - s.x = x; - s.y = y; - s.z = z; - s.r = r; - return s; - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var prim = new h3d.prim.Sphere(r, 20, 15); diff --git a/h3d/col/TransformCollider.hx b/h3d/col/TransformCollider.hx index 35649242ae..8a34290b42 100644 --- a/h3d/col/TransformCollider.hx +++ b/h3d/col/TransformCollider.hx @@ -78,12 +78,6 @@ class TransformCollider extends Collider { return res; } - public function dimension() { - var scale = mat.getScale(); - var scaleMax = Math.max(scale.x, Math.max(scale.y, scale.z)); - return collider.dimension() * scaleMax; - } - #if !macro public function makeDebugObj() : h3d.scene.Object { var obj = collider.makeDebugObj(); diff --git a/h3d/impl/Benchmark.hx b/h3d/impl/Benchmark.hx index d898af8bad..4421154847 100644 --- a/h3d/impl/Benchmark.hx +++ b/h3d/impl/Benchmark.hx @@ -8,7 +8,6 @@ private class QueryObject { public var value : Float; public var name : String; public var drawCalls : Int; - public var dispatches : Int; public var next : QueryObject; public function new() { @@ -35,7 +34,6 @@ private class StatsObject { public var name : String; public var time : Float; public var drawCalls : Int; - public var dispatches : Int; public var next : StatsObject; public var xPos : Int; public var xSize : Int; @@ -68,9 +66,6 @@ class Benchmark extends h2d.Graphics { public var smoothTime = 0.95; public var measureCpu = false; - public var displayTriangleCount = true; - - #if target.threaded public var measureCpuThread: sys.thread.Thread = null; #end var tip : h2d.Text; var tipCurrent : StatsObject; @@ -155,24 +150,10 @@ class Benchmark extends h2d.Graphics { } function syncTip(s:StatsObject) { - inline function fmt(i : Int, n : String) { - return i != 0 ? (i + ' ${n} ') : ""; - } - if( s == null ) { - tip.text = "total "+fmt(engine.drawCalls,"draws")+ fmt(engine.dispatches, "dispatches"); - if ( displayTriangleCount ) - tip.text += hxd.Math.fmt(engine.drawTriangles/1000000)+" Mtri"; - } - else { - var t = s.name + "( " + Std.int(s.time / 1e6) + "." + StringTools.lpad(""+(Std.int(s.time/1e4)%100),"0",2) + " ms"; - #if target.threaded - if (measureCpuThread == null) - #end - t += " " + fmt(s.drawCalls, "draws")+ fmt(s.dispatches, "dispatches"); - t += ")"; - - tip.text = t; - } + if( s == null ) + tip.text = "total "+engine.drawCalls+" draws "+hxd.Math.fmt(engine.drawTriangles/1000000)+" Mtri"; + else + tip.text = s.name+"( " + Std.int(s.time / 1e6) + "." + StringTools.lpad(""+(Std.int(s.time/1e4)%100),"0",2) + " ms " + s.drawCalls + " draws )"; var tw = tip.textWidth + 10; var tx = s == null ? curWidth : s.xPos + ((s.xSize - tw) * .5); if( tx + tw > curWidth ) tx = curWidth - tw; @@ -180,7 +161,7 @@ class Benchmark extends h2d.Graphics { if( hxd.Math.abs(tip.parent.x - tx) > 5 ) tip.parent.x = Std.int(tx); } - public function begin(withVisual=true) { + public function begin() { if( !enable ) return; @@ -200,7 +181,7 @@ class Benchmark extends h2d.Graphics { var changed = false; while( waitFrames.length > 0 ) { var q = waitFrames[0]; - if( #if target.threaded measureCpuThread == null && #end !q.isAvailable() ) + if( !q.isAvailable() ) break; waitFrames.shift(); @@ -224,8 +205,6 @@ class Benchmark extends h2d.Graphics { totalTime += dt; s.drawCalls = prev.drawCalls - q.drawCalls; if( s.drawCalls < 0 ) s.drawCalls = 0; - s.dispatches = prev.dispatches - q.dispatches; - if ( s.dispatches < 0 ) s.dispatches = 0; } // recycle var n = q.next; @@ -244,14 +223,12 @@ class Benchmark extends h2d.Graphics { if( vst > 0 ) { var s = allocStat("vsync", vst); s.drawCalls = 0; - s.dispatches = 0; waitT -= vst; } } if( waitT > 0.5e6 /* 0.5 ms */ ) { var s = allocStat(measureCpu ? "gpuwait" : "cpuwait", waitT); s.drawCalls = 0; - s.dispatches = 0; } } } @@ -260,15 +237,13 @@ class Benchmark extends h2d.Graphics { changed = true; } - if (withVisual) { - if( allocated && visible ) - syncVisual(); - } + if( allocated && visible ) + syncVisual(); measure("begin"); } - public function syncVisual() { + function syncVisual() { var s2d = getScene(); var old = labels; labels = null; @@ -290,7 +265,7 @@ class Benchmark extends h2d.Graphics { s = s.next; } - var space = 57; + var space = 52; width -= space; var count = 0; @@ -344,15 +319,7 @@ class Benchmark extends h2d.Graphics { time.visible = true; time.textColor = 0xFFFFFF; var timeMs = totalTime / 1e6; - var totalName = measureCpu ? "cpu" : "gpu"; - #if target.threaded - if (measureCpuThread != null) { - var n = measureCpuThread.getName(); - if (n != null) - totalName = n; - } - #end - time.text = Std.int(timeMs) + "." + Std.int((timeMs * 10) % 10) + " " + totalName; + time.text = Std.int(timeMs) + "." + Std.int((timeMs * 10) % 10) + (measureCpu?" cpu" : " gpu"); while( labels.length > count ) labels.pop().remove(); @@ -410,14 +377,9 @@ class Benchmark extends h2d.Graphics { if( !enable ) return; if( currentFrame != null && currentFrame.name == name ) return; - #if target.threaded - if( measureCpuThread != null && sys.thread.Thread.current() != measureCpuThread ) - return; - #end var q = allocQuery(); q.name = name; q.drawCalls = engine.drawCalls; - q.dispatches = engine.dispatches; q.next = currentFrame; currentFrame = q; engine.driver.endQuery(q.q); diff --git a/h3d/impl/DX12Driver.hx b/h3d/impl/DX12Driver.hx index fa3ebff80c..624c40f636 100644 --- a/h3d/impl/DX12Driver.hx +++ b/h3d/impl/DX12Driver.hx @@ -57,87 +57,6 @@ class ManagedHeapArray { } -@:struct class BumpAllocation { - public var resource : GpuResource = null; - public var cpuAdress : hl.Bytes = null; - public var offset : Int = 0; - public var byteSize : Int = 0; - public function new() { - } -} - -class BumpAllocator { - var resource : GpuResource; - var capacity : Int; - var cpuAdress : hl.Bytes; - var heap : HeapProperties; - var offset : Int = 0; - var next : BumpAllocator; - - public function new( size : Int ) { - this.capacity = size; - heap = new HeapProperties(); - var desc = new ResourceDesc(); - var flags = new haxe.EnumFlags(); - desc.dimension = BUFFER; - desc.width = capacity; - desc.height = 1; - desc.depthOrArraySize = 1; - desc.mipLevels = 1; - desc.sampleDesc.count = 1; - desc.layout = ROW_MAJOR; - heap.type = UPLOAD; - resource = Driver.createCommittedResource(heap, flags, desc, GENERIC_READ, null); - cpuAdress = resource.map(0, null); - } - - public function reset() { - offset = 0; - if ( next != null) { - next.release(); - next = null; - } - } - - public function release() { - resource.release(); - resource = null; - offset = 0; - capacity = 0; - heap = null; - cpuAdress = null; - if ( next != null) { - next.release(); - next = null; - } - } - - public inline function alloc( size : Int, alignment = 256, ?allocation : BumpAllocation ) { - var sz = size & ~(alignment - 1); - if( sz != size ) sz += alignment; - if ( allocation == null ) - allocation = new BumpAllocation(); - return tryAlloc(sz, alignment, allocation); - } - - function tryAlloc( size, alignment = 256, allocation : BumpAllocation ) { - var offsetAligned = offset & ~(alignment - 1); - if( offsetAligned != offset ) offsetAligned += alignment; - var newOffset = size + offsetAligned; - if ( newOffset > capacity ) { - if ( next == null ) - next = new BumpAllocator(hxd.Math.imax(h3d.impl.DX12Driver.INITIAL_BUMP_ALLOCATOR_SIZE, size)); - return next.tryAlloc(size, alignment, allocation); - } - allocation.byteSize = size; - allocation.offset = offsetAligned; - allocation.cpuAdress = cpuAdress.offset(offsetAligned); - allocation.resource = resource; - offset = newOffset; - return allocation; - } -} - class DxFrame { public var backBuffer : ResourceData; public var backBufferView : Address; @@ -159,7 +78,14 @@ class DxFrame { public var queryCurrentHeap : Int; public var queryHeapOffset : Int; public var queryBuffer : GpuResource; - public var bumpAllocator : BumpAllocator; + public function new() { + } +} + +class CachedPipeline { + public var bytes : hl.Bytes; + public var size : Int; + public var pipeline : GraphicsPipelineState; public function new() { } } @@ -171,12 +97,12 @@ class ShaderRegisters { public var textures : Int; public var samplers : Int; public var texturesCount : Int; - public var texturesTypes : Array; + public var textures2DCount : Int; public var bufferTypes : Array; public var srv : Address; public var samplersView : Address; public var lastHeapCount : Int; - public var lastTextures : Array = []; + public var lastTextures : Array = []; public var lastTexturesBits : Array= []; public function new() { } @@ -187,7 +113,7 @@ class CompiledShader { public var fragmentRegisters : ShaderRegisters; public var format : hxd.BufferFormat; public var pipeline : GraphicsPipelineStateDesc; - public var pipelines : PipelineCache = new PipelineCache(); + public var pipelines : Map> = new Map(); public var rootSignature : RootSignature; public var inputLayout : hl.CArray; public var inputCount : Int; @@ -198,38 +124,26 @@ class CompiledShader { } } -@:struct class SrvArgs { - public var res : GpuResource; - @:packed public var resourceDesc : Tex2DSRV; - @:packed public var samplerDesc : SamplerDesc; - public var srvAddr : Address; - public var samplerAddr : Address; -} - @:struct class TempObjects { public var renderTargets : hl.BytesAccess
; public var depthStencils : hl.BytesAccess
; public var vertexViews : hl.CArray; public var descriptors2 : hl.NativeArray; - public var barriers : hl.CArray; - public var resourcesToTransition : Array; - public var maxBarriers : Int; - public var barrierCount : Int; @:packed public var heap(default,null) : HeapProperties; @:packed public var barrier(default,null) : ResourceBarrier; @:packed public var clearColor(default,null) : ClearColor; @:packed public var clearValue(default,null) : ClearValue; @:packed public var viewport(default,null) : Viewport; @:packed public var rect(default,null) : Rect; + @:packed public var tex2DSRV(default,null) : Tex2DSRV; + @:packed public var texCubeSRV(default,null) : TexCubeSRV; + @:packed public var tex2DArraySRV(default,null) : Tex2DArraySRV; @:packed public var bufferSRV(default,null) : BufferSRV; @:packed public var samplerDesc(default,null) : SamplerDesc; @:packed public var cbvDesc(default,null) : ConstantBufferViewDesc; - @:packed public var rtvDesc(default,null) : RenderTargetViewDesc; @:packed public var uavDesc(default,null) : UAVBufferViewDesc; - @:packed public var wtexDesc(default,null) : UAVTextureViewDesc; - @:packed public var subResourceData(default, null) : SubResourceData; - @:packed public var bumpAllocation(default,null) : BumpAllocation; + @:packed public var rtvDesc(default,null) : RenderTargetViewDesc; public var pass : h3d.mat.Pass; @@ -237,13 +151,15 @@ class CompiledShader { renderTargets = new hl.Bytes(8 * 8); depthStencils = new hl.Bytes(8); vertexViews = hl.CArray.alloc(VertexBufferView, 16); - maxBarriers = 100; - barriers = hl.CArray.alloc( ResourceBarrier, maxBarriers ); - resourcesToTransition = new Array(); - resourcesToTransition.resize(maxBarriers); - barrierCount = 0; pass = new h3d.mat.Pass("default"); pass.stencil = new h3d.mat.Stencil(); + tex2DSRV.dimension = TEXTURE2D; + texCubeSRV.dimension = TEXTURECUBE; + tex2DArraySRV.dimension = TEXTURE2DARRAY; + tex2DSRV.mipLevels = texCubeSRV.mipLevels = tex2DArraySRV.mipLevels = -1; + tex2DSRV.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; + texCubeSRV.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; + tex2DArraySRV.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; bufferSRV.dimension = BUFFER; bufferSRV.flags = RAW; bufferSRV.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; @@ -260,7 +176,9 @@ class ManagedHeap { public var stride(default,null) : Int; var size : Int; + var start : Int; var cursor : Int; + var limit : Int; var type : DescriptorHeapType; var heap : DescriptorHeap; var address : Address; @@ -281,7 +199,7 @@ class ManagedHeap { if( type == CBV_SRV_UAV || type == SAMPLER ) desc.flags = SHADER_VISIBLE; heap = new DescriptorHeap(desc); - cursor = 0; + limit = cursor = start = 0; this.size = size; address = heap.getHandle(false); cpuToGpu = desc.flags == SHADER_VISIBLE ? ( heap.getHandle(true).value - address.value ) : 0; @@ -292,8 +210,15 @@ class ManagedHeap { } public function alloc( count : Int ) { - if( cursor + count > size ) { + if( cursor >= limit && cursor + count > size ) { cursor = 0; + if( limit == 0 ) { + var prev = heap; + allocHeap((size * 3) >> 1); + onFree(prev); + } + } + if( cursor < limit && cursor + count >= limit ) { var prev = heap; allocHeap((size * 3) >> 1); onFree(prev); @@ -304,11 +229,24 @@ class ManagedHeap { } inline function get_available() { - return size - cursor; + var d = limit - cursor; + return d <= 0 ? size + d : d; + } + + public inline function grow( onFree ) { + var prev = heap; + allocHeap((size*3)>>1); + onFree(prev); + return heap; } public function clear() { - cursor = 0; + limit = cursor = start = 0; + } + + public function next() { + limit = start; + start = cursor; } public inline function toGPU( address : Address ) : Address { @@ -320,7 +258,6 @@ class ManagedHeap { class ResourceData { public var res : GpuResource; public var state : ResourceState; - public var targetState : ResourceState; public function new() { } } @@ -335,17 +272,10 @@ class VertexBufferData extends BufferData { public var size : Int; } -class TextureUploadBuffer { - public var tmpBuf : dx.Dx12.GpuResource; - public var lastMipMapUploadPerSide : hl.Bytes; - public function new() { - } -} - class TextureData extends ResourceData { public var format : DxgiFormat; public var color : h3d.Vector4; - public var uploadBuffer : TextureUploadBuffer; + public var tmpBuf : dx.Dx12.GpuResource; var clearColorChanges : Int; public function setClearColor( c : h3d.Vector4 ) { var color = color; @@ -367,7 +297,16 @@ class QueryData { class DX12Driver extends h3d.impl.Driver { - var pipelineBuilder = new PipelineCache.PipelineBuilder(); + static inline var PSIGN_MATID = 0; + static inline var PSIGN_COLOR_MASK = PSIGN_MATID + 4; + static inline var PSIGN_UNUSED = PSIGN_COLOR_MASK + 1; + static inline var PSIGN_STENCIL_MASK = PSIGN_UNUSED + 1; + static inline var PSIGN_STENCIL_OPS = PSIGN_STENCIL_MASK + 2; + static inline var PSIGN_RENDER_TARGETS = PSIGN_STENCIL_OPS + 4; + static inline var PSIGN_LAYOUT = PSIGN_RENDER_TARGETS + 1; + + var pipelineSignature = new hl.Bytes(64); + var adlerOut = new hl.Bytes(4); var driver : DriverInstance; var hasDeviceError = false; @@ -384,6 +323,7 @@ class DX12Driver extends h3d.impl.Driver { var currentFrame : Int; var fenceValue : Int64 = 0; + var needPipelineFlush = false; var currentPass : h3d.mat.Pass; var currentWidth : Int; @@ -399,7 +339,6 @@ class DX12Driver extends h3d.impl.Driver { var defaultDepth : h3d.mat.Texture; var depthEnabled = true; var curStencilRef : Int = -1; - var lastRtvDesc : RenderTargetViewDesc; var rtWidth : Int; var rtHeight : Int; var frameCount : Int; @@ -407,8 +346,7 @@ class DX12Driver extends h3d.impl.Driver { var heapCount : Int; public static var INITIAL_RT_COUNT = 1024; - public static var INITIAL_BUMP_ALLOCATOR_SIZE = 2 * 1024 * 1024; - public static var BUFFER_COUNT = #if console 3 #else 2 #end; + public static var BUFFER_COUNT = 2; public static var DEVICE_NAME = null; public static var DEBUG = false; // requires dxil.dll when set to true @@ -443,9 +381,6 @@ class DX12Driver extends h3d.impl.Driver { f.commandList.close(); f.shaderResourceCache = new ManagedHeapArray(CBV_SRV_UAV, 1024); f.samplerCache = new ManagedHeapArray(SAMPLER, 1024); - if ( f.bumpAllocator != null ) - f.bumpAllocator.release(); - f.bumpAllocator = new BumpAllocator(INITIAL_BUMP_ALLOCATOR_SIZE); frames.push(f); } fence = new Fence(0, NONE); @@ -456,15 +391,10 @@ class DX12Driver extends h3d.impl.Driver { depthStenciViews = new ManagedHeap(DSV, INITIAL_RT_COUNT); renderTargetViews.onFree = function(prev) frame.toRelease.push(prev); depthStenciViews.onFree = function(prev) frame.toRelease.push(prev); - if ( h3d.Engine.getCurrent() != null ) { - defaultDepth = new h3d.mat.Texture(0,0, Depth24Stencil8); - defaultDepth.t = new TextureData(); - - defaultDepth.t.state = defaultDepth.t.targetState = DEPTH_WRITE; - defaultDepth.name = "defaultDepth"; - } - - + defaultDepth = new h3d.mat.Texture(0,0, Depth24Stencil8); + defaultDepth.t = new TextureData(); + defaultDepth.t.state = DEPTH_WRITE; + defaultDepth.name = "defaultDepth"; var desc = new CommandSignatureDesc(); var adesc = hl.CArray.alloc(IndirectArgumentDesc, 1); @@ -489,7 +419,6 @@ class DX12Driver extends h3d.impl.Driver { defaultDepth.t.res = frame.depthBuffer; frame.allocator.reset(); frame.commandList.reset(frame.allocator, null); - frame.bumpAllocator.reset(); while( frame.toRelease.length > 0 ) frame.toRelease.pop().release(); while( frame.tmpBufToRelease.length > 0 ) { @@ -500,7 +429,8 @@ class DX12Driver extends h3d.impl.Driver { if ( prevFrame != null ) { while ( prevFrame.tmpBufToNullify.length > 0 ) { var t = prevFrame.tmpBufToNullify.pop(); - t.uploadBuffer = null; + frame.tmpBufToRelease.push(t.tmpBuf); + t.tmpBuf = null; } } beginQueries(); @@ -525,8 +455,8 @@ class DX12Driver extends h3d.impl.Driver { transition(frame.backBuffer, RENDER_TARGET); frame.commandList.iaSetPrimitiveTopology(TRIANGLELIST); - renderTargetViews.clear(); - depthStenciViews.clear(); + renderTargetViews.next(); + depthStenciViews.next(); curStencilRef = -1; currentIndex = null; @@ -553,23 +483,18 @@ class DX12Driver extends h3d.impl.Driver { clear.b = color.b; clear.a = color.a; var count = currentRenderTargets.length; - var needRebind = false; for( i in 0...count ) { var tex = currentRenderTargets[i]; if( tex != null && tex.t.setClearColor(color) ) { - needRebind = true; // update texture to use another clear value var prev = tex.t; tex.t = allocTexture(tex); @:privateAccess tex.t.clearColorChanges = prev.clearColorChanges; frame.toRelease.push(prev.res); - Driver.createRenderTargetView(tex.t.res, lastRtvDesc, tmp.renderTargets[i]); + Driver.createRenderTargetView(tex.t.res, null, tmp.renderTargets[i]); } - tex.flags.set(WasCleared); frame.commandList.clearRenderTargetView(tmp.renderTargets[i], clear); } - if ( needRebind ) - frame.commandList.omSetRenderTargets(count, tmp.renderTargets, true, depthEnabled ? getDepthViewFromTexture(currentRenderTargets[0], currentRenderTargets[0].depthBuffer.t.state & DEPTH_WRITE == COMMON ) : null); // clear backbuffer if( count == 0 ) frame.commandList.clearRenderTargetView(frame.backBufferView, clear); @@ -587,7 +512,7 @@ class DX12Driver extends h3d.impl.Driver { override function resize(width:Int, height:Int) { - if( defaultDepth == null || (currentWidth == width && currentHeight == height) ) + if( currentWidth == width && currentHeight == height ) return; currentWidth = rtWidth = width; @@ -615,7 +540,7 @@ class DX12Driver extends h3d.impl.Driver { for( i => f in frames ) { f.backBuffer.res = Driver.getBackBuffer(i); f.backBuffer.res.setName("Backbuffer#"+i); - f.backBuffer.state = f.backBuffer.targetState = PRESENT; + f.backBuffer.state = PRESENT; var desc = new ResourceDesc(); var flags = new haxe.EnumFlags(); @@ -662,67 +587,38 @@ class DX12Driver extends h3d.impl.Driver { } function transition( res : ResourceData, to : ResourceState ) { - if( res.targetState == to ) + if( res.state == to ) return; - - // Cancel transition - if ( res.state == to ) { - var found = false; - for (i in 0...tmp.barrierCount) { - if (tmp.resourcesToTransition[i] == res) { - tmp.barrierCount -= 1; - for (j in i...tmp.barrierCount) { - tmp.resourcesToTransition[j] = tmp.resourcesToTransition[j + 1]; - } - found = true; - break; - } - } - if (!found) - throw "Resource not found"; - res.targetState = to; - return; - } - - if( tmp.maxBarriers == tmp.barrierCount) { - flushTransitions(); - tmp.maxBarriers += 100; - tmp.barriers = hl.CArray.alloc(ResourceBarrier, tmp.maxBarriers); - tmp.resourcesToTransition = new Array(); - tmp.resourcesToTransition.resize(tmp.maxBarriers); - } - - // If state is different from targetState, a barrier has already been requested so we just have to update the targetState - if (res.state == res.targetState) - tmp.resourcesToTransition[tmp.barrierCount++] = res; - res.targetState = to; - } - - function flushTransitions() { - if (tmp.barrierCount > 0) { - var totalBarrier = 0; - for (i in 0...tmp.barrierCount) { - var res = tmp.resourcesToTransition[i]; - - // Resource has been disposed - if (res.res == null) - continue; - - var b = tmp.barriers[totalBarrier]; - b.resource = res.res; - b.stateBefore = res.state; - b.stateAfter = res.targetState; - res.state = res.targetState; - totalBarrier++; - } - if (totalBarrier > 0) - #if (hldx >= version("1.15.0")) - frame.commandList.resourceBarriers(tmp.barriers, totalBarrier); - #else - for (i in 0...totalBarrier) - frame.commandList.resourceBarrier(tmp.barriers[i]); - #end - tmp.barrierCount = 0; + var b = tmp.barrier; + b.resource = res.res; + b.stateBefore = res.state; + b.stateAfter = to; + frame.commandList.resourceBarrier(b); + res.state = to; + } + + + function getRTBits( tex : h3d.mat.Texture ) { + // 1 bit depth (set later), 5 bits format, 2 bits channels + inline function mk(channels,format) { + return (format << 2) | (channels - 1); + } + return switch( tex.format ) { + case R8: mk(1, 0); + case RG8: mk(2, 0); + case RGB8: mk(3, 0); + case RGBA: mk(4, 0); + case R16F: mk(1, 1); + case RG16F: mk(2, 1); + case RGB16F: mk(3, 1); + case RGBA16F: mk(4, 1); + case R32F: mk(1, 2); + case RG32F: mk(2, 2); + case RGB32F: mk(3, 2); + case RGBA32F: mk(4, 2); + case RG11B10UF: mk(2, 3); + case RGB10A2: mk(3, 4); + default: throw "Unsupported RT format "+tex.format; } } @@ -747,7 +643,7 @@ class DX12Driver extends h3d.impl.Driver { viewDesc.arraySize = 1; viewDesc.mipSlice = 0; viewDesc.firstArraySlice = 0; - viewDesc.format = (depthBuffer == null) ? D24_UNORM_S8_UINT : toDxgiDepthFormat(depthBuffer.format); + viewDesc.format = D24_UNORM_S8_UINT; viewDesc.viewDimension = TEXTURE2D; if ( readOnly ) { viewDesc.flags.set(READ_ONLY_DEPTH); @@ -806,7 +702,6 @@ class DX12Driver extends h3d.impl.Driver { desc.planeSlice = 0; } } - lastRtvDesc = desc; if (tex != null) { var texView = renderTargetViews.alloc(1); Driver.createRenderTargetView(tex.t.res, desc, texView); @@ -816,7 +711,6 @@ class DX12Driver extends h3d.impl.Driver { tmp.renderTargets[0] = frame.backBufferView; } - flushTransitions(); if ( tex != null && !tex.flags.has(WasCleared) ) { tex.flags.set(WasCleared); var clear = tmp.clearColor; @@ -836,22 +730,8 @@ class DX12Driver extends h3d.impl.Driver { if( w == 0 ) w = 1; if( h == 0 ) h = 1; initViewport(w, h); - pipelineBuilder.setRenderTarget(tex, depthEnabled); - } - - function toDxgiDepthFormat( format : hxd.PixelFormat ) { - switch( format ) { - case null: - return cast 0; - case Depth16: - return D16_UNORM; - case Depth24Stencil8, Depth24: - return D24_UNORM_S8_UINT; - case Depth32: - return D32_FLOAT; - default: - throw "Unsupported depth format "+ format; - } + pipelineSignature.setUI8(PSIGN_RENDER_TARGETS, tex == null ? 0 : getRTBits(tex) | (depthEnabled ? 0x80 : 0)); + needPipelineFlush = true; } override function setRenderTargets(textures:Array, depthBinding : h3d.Engine.DepthBinding = ReadWrite) { @@ -860,10 +740,9 @@ class DX12Driver extends h3d.impl.Driver { depthEnabled = depthBinding != NotBound; - lastRtvDesc = null; - var t0 = textures[0]; var texViews = renderTargetViews.alloc(textures.length); + var bits = 0; for( i => t in textures ) { if ( t.t == null ) { t.alloc(); @@ -873,7 +752,7 @@ class DX12Driver extends h3d.impl.Driver { Driver.createRenderTargetView(t.t.res, null, view); tmp.renderTargets[i] = view; currentRenderTargets[i] = t; - transition( t.t, RENDER_TARGET); + bits |= getRTBits(t) << (i << 2); if ( !t.flags.has(WasCleared) ) { t.flags.set(WasCleared); var clear = tmp.clearColor; @@ -881,28 +760,29 @@ class DX12Driver extends h3d.impl.Driver { clear.g = 0; clear.b = 0; clear.a = 0; - flushTransitions(); frame.commandList.clearRenderTargetView(tmp.renderTargets[i], clear); } + transition(t.t, RENDER_TARGET); } - flushTransitions(); frame.commandList.omSetRenderTargets(textures.length, tmp.renderTargets, true, depthEnabled ? getDepthViewFromTexture(t0, depthBinding == ReadOnly) : null); initViewport(t0.width, t0.height); - pipelineBuilder.setRenderTargets(textures, depthEnabled); + pipelineSignature.setUI8(PSIGN_RENDER_TARGETS, bits | (depthEnabled ? 0x80 : 0)); + needPipelineFlush = true; } override function setDepth(depthBuffer : h3d.mat.Texture) { var view = getDepthView(depthBuffer, false); depthEnabled = true; - flushTransitions(); frame.commandList.omSetRenderTargets(0, null, true, view); while( currentRenderTargets.length > 0 ) currentRenderTargets.pop(); initViewport(depthBuffer.width, depthBuffer.height); - pipelineBuilder.setDepth(depthBuffer); + + pipelineSignature.setUI8(PSIGN_RENDER_TARGETS, 0x80); + needPipelineFlush = true; } override function setRenderZone(x:Int, y:Int, width:Int, height:Int) { @@ -987,7 +867,6 @@ class DX12Driver extends h3d.impl.Driver { box.back = 1; transition(tex.t, COPY_SOURCE); - flushTransitions(); dst.res = tmpBuf; frame.commandList.copyTextureRegion(dst, 0, 0, 0, src, box); @@ -1030,12 +909,12 @@ class DX12Driver extends h3d.impl.Driver { var args = []; var out = new hxsl.HlslOut(); out.baseRegister = baseRegister; - if( sh.code == null ) { + if ( sh.code == null ) { sh.code = out.run(sh.data); sh.code = rootStr + sh.code; } var bytes = getBinaryPayload(sh.code); - if( bytes == null ) { + if ( bytes == null ) { return compiler.compile(sh.code, profile, args); } return bytes; @@ -1051,7 +930,7 @@ class DX12Driver extends h3d.impl.Driver { return vsSource+"\n\n\n\n"+psSource; } - function stringifyRootSignature( sign : RootSignatureDesc, name : String, params : hl.CArray, paramsCount : Int ) : String { + function stringifyRootSignature( sign : RootSignatureDesc, name : String, params : hl.CArray, paramsCount : Int ) : String { var s = '#define ${name} "RootFlags('; if ( sign.flags.toInt() == 0 ) s += '0'; // no flags @@ -1068,15 +947,14 @@ class DX12Driver extends h3d.impl.Driver { for ( i in 0...paramsCount ) { var param = params[i]; - var vis = "SHADER_VISIBILITY_"+switch( param.shaderVisibility ) { case VERTEX: "VERTEX"; case PIXEL: "PIXEL"; default: "ALL"; }; + var vis = 'SHADER_VISIBILITY_${param.shaderVisibility == VERTEX ? "VERTEX" : "PIXEL"}'; if ( param.parameterType == CONSTANTS ) { - var p = unsafeCastTo(param, RootParameterConstants); - var shaderRegister = p.shaderRegister; - s += 'RootConstants(num32BitConstants=${p.num32BitValues},b${shaderRegister}, visibility=${vis}),'; + var shaderRegister = param.shaderRegister; + s += 'RootConstants(num32BitConstants=${param.num32BitValues},b${shaderRegister}, visibility=${vis}),'; } else { try { - var p = param; - if( p.descriptorRanges == null ) continue; + var p = unsafeCastTo(param, RootParameterDescriptorTable); + if( p == null || p.descriptorRanges == null ) continue; var descRange = p.descriptorRanges[0]; var baseShaderRegister = descRange.baseShaderRegister; switch ( descRange.rangeType) { @@ -1088,8 +966,7 @@ class DX12Driver extends h3d.impl.Driver { var baseShaderRegister = descRange.baseShaderRegister; s += 'DescriptorTable(Sampler(s${baseShaderRegister}, space=${descRange.registerSpace}, numDescriptors = ${descRange.numDescriptors}), visibility = ${vis}),'; case UAV: - var reg = descRange.baseShaderRegister; - s += 'UAV(u${reg}, visibility = ${vis}),'; + throw "Not supported"; } } catch ( e : Dynamic ) { continue; @@ -1102,26 +979,22 @@ class DX12Driver extends h3d.impl.Driver { } inline function unsafeCastTo( v : T, c : Class ) : R { - #if (hl_ver < version("1.14.0")) var arr = new hl.NativeArray(1); arr[0] = v; return (cast arr : hl.NativeArray)[0]; - #else - return hl.Api.unsafeCast(v); - #end } + function computeRootSignature( shader : hxsl.RuntimeShader ) { var allocatedParams = 16; - var params = hl.CArray.alloc(RootParameterDescriptorTable,allocatedParams); + var params = hl.CArray.alloc(RootParameterConstants,allocatedParams); var paramsCount = 0, regCount = 0; var texDescs = []; - var globalsParamsCBV = false; var vertexParamsCBV = false; var fragmentParamsCBV = false; function allocDescTable(vis) { - var p = params[paramsCount++]; + var p = unsafeCastTo(params[paramsCount++], RootParameterDescriptorTable); p.parameterType = DESCRIPTOR_TABLE; p.numDescriptorRanges = 1; var rangeArr = hl.CArray.alloc(DescriptorRange,1); @@ -1147,7 +1020,7 @@ class DX12Driver extends h3d.impl.Driver { } var pid = paramsCount++; - var p = unsafeCastTo(params[pid], RootParameterConstants); + var p = params[pid]; p.parameterType = CONSTANTS; p.shaderRegister = reg; p.shaderVisibility = vis; @@ -1163,10 +1036,10 @@ class DX12Driver extends h3d.impl.Driver { default: ALL; } var regs = new ShaderRegisters(); - regs.globals = allocConsts(sh.globalsSize, vis, globalsParamsCBV ? CBV : null); + regs.globals = allocConsts(sh.globalsSize, vis, null); regs.params = allocConsts(sh.paramsSize, vis, (sh.kind == Fragment ? fragmentParamsCBV : vertexParamsCBV) ? CBV : null); - regs.buffers = paramsCount; if( sh.bufferCount > 0 ) { + regs.buffers = paramsCount; regs.bufferTypes = []; var p = sh.buffers; while( p != null ) { @@ -1178,47 +1051,35 @@ class DX12Driver extends h3d.impl.Driver { allocConsts(1, vis, switch( kind ) { case Uniform: CBV; case RW: UAV; - default: throw "assert"; }); p = p.next; } } if( sh.texturesCount > 0 ) { - regs.texturesCount = 0; - regs.texturesTypes = []; - - var p = sh.data.vars; - for( v in sh.data.vars ) { - switch( v.type ) { - case TArray(t = TSampler(_) | TRWTexture(_), SConst(n)): - for( i in 0...n ) - regs.texturesTypes.push(t); - if( t.match(TSampler(_)) ) - regs.texturesCount += n; - else { - for( i in 0...n ) - allocConsts(1, vis, UAV); - } + regs.texturesCount = sh.texturesCount; + regs.textures = paramsCount; + + var p = sh.textures; + while( p != null ) { + switch( p.type ) { + case TArray( TSampler2D , SConst(n) ): regs.textures2DCount = n; default: } + p = p.next; } - if( regs.texturesCount > 0 ) { - regs.textures = paramsCount; - - var r = allocDescTable(vis); - r.rangeType = SRV; - r.baseShaderRegister = 0; - r.registerSpace = 0; - r.numDescriptors = regs.texturesCount; - - regs.samplers = paramsCount; - var r = allocDescTable(vis); - r.rangeType = SAMPLER; - r.baseShaderRegister = 0; - r.registerSpace = 0; - r.numDescriptors = regs.texturesCount; - } + var r = allocDescTable(vis); + r.rangeType = SRV; + r.baseShaderRegister = 0; + r.registerSpace = 0; + r.numDescriptors = sh.texturesCount; + + regs.samplers = paramsCount; + var r = allocDescTable(vis); + r.rangeType = SAMPLER; + r.baseShaderRegister = 0; + r.registerSpace = 0; + r.numDescriptors = sh.texturesCount; } return regs; } @@ -1231,10 +1092,7 @@ class DX12Driver extends h3d.impl.Driver { // Root Constants cost 1 per 32-bit value function calcSize( sh : hxsl.RuntimeShader.RuntimeShaderData ) { var s = (sh.globalsSize + sh.paramsSize) << 2; - // 1 descriptor table for all textures and 1 descriptor table for all samplers - s += ( sh.texturesCount > 0 ) ? 2 : 0; - // 1 descriptor table for all buffers - s += ( sh.bufferCount > 0 ) ? 1 : 0; + s += sh.texturesCount; return s; } @@ -1249,7 +1107,7 @@ class DX12Driver extends h3d.impl.Driver { // Remove the size cost of the root constant and add one descriptor table. var withoutVP = total - vertexParamSizeCost + 1; var withoutFP = total - fragmentParamSizeCost + 1; - if( withoutVP <= 64 || ( withoutFP > 64 && withoutVP > 64 ) ) { + if( withoutVP < 64 || withoutFP > 64 ) { vertexParamsCBV = true; total = withoutVP; } @@ -1257,12 +1115,8 @@ class DX12Driver extends h3d.impl.Driver { fragmentParamsCBV = true; total = total - fragmentParamSizeCost + 1; } - if( total > 64 ) { - globalsParamsCBV = true; - var withoutGlobal = total - (shader.vertex.globalsSize << 2) - (shader.fragment.globalsSize << 2) + 2; - if ( withoutGlobal > 64 ) - throw "Too many params. Should not be possible if every params fall into descriptor table."; - } + if( total > 64 ) + throw "Too many globals"; } var regs = []; @@ -1280,10 +1134,6 @@ class DX12Driver extends h3d.impl.Driver { sign.flags.set(DENY_HULL_SHADER_ROOT_ACCESS); sign.flags.set(DENY_DOMAIN_SHADER_ROOT_ACCESS); sign.flags.set(DENY_GEOMETRY_SHADER_ROOT_ACCESS); - #if !xbogdk - sign.flags.set(DENY_AMPLIFICATION_SHADER_ROOT_ACCESS); - sign.flags.set(DENY_MESH_SHADER_ROOT_ACCESS); - #end sign.numParameters = paramsCount; sign.parameters = cast params; @@ -1381,7 +1231,7 @@ class DX12Driver extends h3d.impl.Driver { function disposeResource( r : ResourceData ) { frame.toRelease.push(r.res); r.res = null; - r.state = r.targetState = PRESENT; + r.state = PRESENT; } // ----- BUFFERS @@ -1405,7 +1255,7 @@ class DX12Driver extends h3d.impl.Driver { var buf = new VertexBufferData(); var size = m.getMemSize(); var bufSize = m.flags.has(UniformBuffer) || m.flags.has(ReadWriteBuffer) ? calcCBVSize(size) : size; - buf.state = buf.targetState = COPY_DEST; + buf.state = COPY_DEST; buf.res = allocGPU(bufSize, DEFAULT, COMMON, m.flags.has(ReadWriteBuffer)); if( m.flags.has(UniformBuffer) ) { // no view @@ -1429,12 +1279,16 @@ class DX12Driver extends h3d.impl.Driver { override function allocInstanceBuffer(b:InstanceBuffer, bytes:haxe.io.Bytes) { var dataSize = b.commandCount * 5 * 4; - var buf = new VertexBufferData(); - buf.state = buf.targetState = COPY_DEST; - buf.res = allocGPU(dataSize, DEFAULT, COMMON); - var alloc = allocDynamicBuffer(bytes, dataSize); - frame.commandList.copyBufferRegion(buf.res, 0, alloc.resource, alloc.offset, dataSize); + var buf = allocGPU(dataSize, DEFAULT, COMMON); + var tmpBuf = allocDynamicBuffer(bytes, dataSize); + frame.commandList.copyBufferRegion(buf, 0, tmpBuf, 0, dataSize); b.data = buf; + + var b = tmp.barrier; + b.resource = buf; + b.stateBefore = COPY_DEST; + b.stateAfter = NON_PIXEL_SHADER_RESOURCE; + frame.commandList.resourceBarrier(b); } override function disposeBuffer(v:Buffer) { @@ -1442,20 +1296,32 @@ class DX12Driver extends h3d.impl.Driver { } override function disposeInstanceBuffer(b:InstanceBuffer) { - frame.toRelease.push((b.data.res:GpuResource)); + frame.toRelease.push((b.data:GpuResource)); // disposeResource(b.data); b.data = null; } function updateBuffer( b : BufferData, bytes : hl.Bytes, startByte : Int, bytesCount : Int ) { - var alloc = allocDynamicBuffer(bytes, bytesCount); - frame.commandList.copyBufferRegion(b.res, startByte, alloc.resource, alloc.offset, bytesCount); + var tmpBuf; + if( b.uploaded ) + tmpBuf = allocDynamicBuffer(bytes.offset(startByte), bytesCount); + else { + var size = calcCBVSize(bytesCount); + tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ); + var ptr = tmpBuf.map(0, null); + ptr.blit(0, bytes, 0, bytesCount); + tmpBuf.unmap(0,null); + } + frame.commandList.copyBufferRegion(b.res, startByte, tmpBuf, 0, bytesCount); + if( !b.uploaded ) { + frame.toRelease.push(tmpBuf); + b.uploaded = true; + } } override function uploadIndexData(i:Buffer, startIndice:Int, indiceCount:Int, buf:hxd.IndexBuffer, bufPos:Int) { var bits = i.format.strideBytes >> 1; transition(i.vbuf, COPY_DEST); - flushTransitions(); updateBuffer(i.vbuf, hl.Bytes.getArray(buf.getNative()).offset(bufPos << bits), startIndice << bits, indiceCount << bits); transition(i.vbuf, INDEX_BUFFER); } @@ -1463,16 +1329,14 @@ class DX12Driver extends h3d.impl.Driver { override function uploadBufferData(b:Buffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) { var data = hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2); transition(b.vbuf, COPY_DEST); - flushTransitions(); updateBuffer(b.vbuf, data, startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes); - transition(b.vbuf, b.flags.has(IndexBuffer) ? INDEX_BUFFER : ((b.flags.has(ReadWriteBuffer)) ? UNORDERED_ACCESS : VERTEX_AND_CONSTANT_BUFFER)); + transition(b.vbuf, b.flags.has(IndexBuffer) ? INDEX_BUFFER : VERTEX_AND_CONSTANT_BUFFER); } override function uploadBufferBytes(b:Buffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) { transition(b.vbuf, COPY_DEST); - flushTransitions(); updateBuffer(b.vbuf, @:privateAccess buf.b.offset(bufPos), startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes); - transition(b.vbuf, b.flags.has(IndexBuffer) ? INDEX_BUFFER : ((b.flags.has(ReadWriteBuffer)) ? UNORDERED_ACCESS : VERTEX_AND_CONSTANT_BUFFER)); + transition(b.vbuf, b.flags.has(IndexBuffer) ? INDEX_BUFFER : VERTEX_AND_CONSTANT_BUFFER); } // ------------ TEXTURES ------- @@ -1547,10 +1411,8 @@ class DX12Driver extends h3d.impl.Driver { clear.color.a = color.a; td.color = color; } - if( t.flags.has(Writable) ) - desc.flags.set(ALLOW_UNORDERED_ACCESS); - td.state = td.targetState = isRT ? RENDER_TARGET : COPY_DEST; + td.state = isRT ? RENDER_TARGET : COPY_DEST; td.res = Driver.createCommittedResource(tmp.heap, flags, desc, isRT ? RENDER_TARGET : COMMON, clear); td.res.setName(t.name == null ? "Texture#"+t.id : t.name); t.lastFrame = frameCount; @@ -1569,17 +1431,14 @@ class DX12Driver extends h3d.impl.Driver { desc.depthOrArraySize = 1; desc.mipLevels = 1; desc.sampleDesc.count = 1; - desc.format = toDxgiDepthFormat(b.format); + desc.format = R24G8_TYPELESS; desc.flags.set(ALLOW_DEPTH_STENCIL); - #if console - desc.flags = new haxe.EnumFlags( desc.flags.toInt() | 0x00800000 ); // FORCE_TEXTURE_COMPATIBILITY - #end tmp.heap.type = DEFAULT; - tmp.clearValue.format = desc.format; + tmp.clearValue.format = D24_UNORM_S8_UINT; tmp.clearValue.depth = 1; tmp.clearValue.stencil= 0; - td.state = td.targetState = DEPTH_WRITE; + td.state = DEPTH_WRITE; td.res = Driver.createCommittedResource(tmp.heap, flags, desc, DEPTH_WRITE, tmp.clearValue); return td; } @@ -1604,33 +1463,34 @@ class DX12Driver extends h3d.impl.Driver { pixels.convert(t.format); if( mipLevel >= t.mipLevels ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.mipLevels - 1) + ")"; - var offset : Int64 = 0; - if ( mipLevel != 0 ) - offset += t.t.res.getRequiredIntermediateSize( 0, mipLevel ); - if ( side != 0 ) - offset += t.t.res.getRequiredIntermediateSize( 0, t.mipLevels ) * side; + tmp.heap.type = UPLOAD; + var subRes = mipLevel + side * t.mipLevels; + var nbRes = t.mipLevels * t.layerCount; + // Todo : optimize for video, currently allocating a new tmpBuf every frame. + if ( t.t.tmpBuf == null ) { + var tmpSize = t.t.res.getRequiredIntermediateSize(0, nbRes).low; + t.t.tmpBuf = allocGPU(tmpSize, UPLOAD, GENERIC_READ); + } + var previousSize : hl.BytesAccess = new hl.Bytes(8); + Driver.getCopyableFootprints(makeTextureDesc(t), 0, subRes, 0, null, null, null, previousSize); + var offsetAligned = ((previousSize[0] + 512 - 1) / 512) * 512; + var upd = new SubResourceData(); var stride = @:privateAccess pixels.stride; switch( t.format ) { case S3TC(n): stride = pixels.width * ((n == 1 || n == 4) ? 2 : 4); // "uncompressed" stride ? default: } - - var upd = tmp.subResourceData; upd.data = (pixels.bytes:hl.Bytes).offset(pixels.offset); upd.rowPitch = stride; upd.slicePitch = pixels.dataSize; - var subRes = mipLevel + side * t.mipLevels; - var tmpSize = t.t.res.getRequiredIntermediateSize(subRes, 1).low; - var allocation = frame.bumpAllocator.alloc(tmpSize, 512, tmp.bumpAllocation); - transition(t.t, COPY_DEST); - flushTransitions(); - if( !Driver.updateSubResource(frame.commandList, t.t.res, allocation.resource, allocation.offset, subRes, 1, upd) ) + if( !Driver.updateSubResource(frame.commandList, t.t.res, t.t.tmpBuf, offsetAligned, subRes, 1, upd) ) throw "Failed to update sub resource"; transition(t.t, PIXEL_SHADER_RESOURCE); + frame.tmpBufToNullify.push(t.t); t.flags.set(WasCleared); } @@ -1645,9 +1505,8 @@ class DX12Driver extends h3d.impl.Driver { if( from.t == null ) throw "assert"; if( to.t == null ) return false; } - transition( from.t, COPY_SOURCE); - transition( to.t, COPY_DEST); - flushTransitions(); + transition(from.t, COPY_SOURCE); + transition(to.t, COPY_DEST); var dst = new TextureCopyLocation(); var src = new TextureCopyLocation(); dst.res = to.t.res; @@ -1656,7 +1515,7 @@ class DX12Driver extends h3d.impl.Driver { to.flags.set(WasCleared); for( t in currentRenderTargets ) if( t == to || t == from ) { - transition( t.t, RENDER_TARGET ); + transition(t.t, RENDER_TARGET); break; } return true; @@ -1677,17 +1536,46 @@ class DX12Driver extends h3d.impl.Driver { return sz; } - function allocDynamicBuffer( data : hl.Bytes, dataSize : Int ) : BumpAllocation { - var allocation = frame.bumpAllocator.alloc(dataSize, tmp.bumpAllocation); - allocation.cpuAdress.blit(0, data, 0, dataSize); - return allocation; + function allocDynamicBuffer( data : hl.Bytes, dataSize : Int ) : GpuResource { + var b = frame.availableBuffers, prev = null; + var tmpBuf = null; + var size = calcCBVSize(dataSize); + if ( size == 0 ) size = 1; + while( b != null ) { + if( b.size >= size && b.size < size << 1 ) { + tmpBuf = b.buffer; + if( prev == null ) + frame.availableBuffers = b.next; + else + prev.next = b.next; + b.lastUse = frameCount; + b.next = frame.usedBuffers; + frame.usedBuffers = b; + break; + } + prev = b; + b = b.next; + } + if( tmpBuf == null ) { + tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ); + var b = new TempBuffer(); + b.buffer = tmpBuf; + b.size = size; + b.lastUse = frameCount; + b.next = frame.usedBuffers; + frame.usedBuffers = b; + } + var ptr = tmpBuf.map(0, null); + ptr.blit(0, data, 0, dataSize); + tmpBuf.unmap(0,null); + return tmpBuf; } function hasBuffersTexturesChanged ( buf : h3d.shader.Buffers.ShaderBuffers, regs : ShaderRegisters ) : Bool { var changed = regs.lastHeapCount != heapCount; if( !changed ) { for( i in 0...regs.texturesCount ) - if( regs.lastTextures[i] != ( buf.tex[i] != null ? buf.tex[i].t : null ) || regs.lastTexturesBits[i] != ( buf.tex[i] != null ? buf.tex[i].bits : -1 ) ) { + if( regs.lastTextures[i] != buf.tex[i] || regs.lastTexturesBits[i] != (buf.tex[i] != null ? buf.tex[i].bits : -1 ) ) { changed = true; break; } @@ -1695,120 +1583,6 @@ class DX12Driver extends h3d.impl.Driver { return changed; } - var srvRingBuf : hl.CArray; - var srvHead : Int = 1; - var srvTail : Int = 0; - var srvThreadLaunched : Bool = false; - - inline function computeSRVBufferDistance() : Int { - return (srvHead + (~(srvTail - 1 ) & 0xFF)) & 0xFF; - } - - inline function processSRV() { - var index = (srvTail + 1) & 0xFF; - var args = srvRingBuf[index]; - Driver.createShaderResourceView(args.res, args.resourceDesc, args.srvAddr); - Driver.createSampler(args.samplerDesc, args.samplerAddr); - srvTail = index; - } - - function runThread() { - while(true) { - // Check if ring buffer is empty - if ( computeSRVBufferDistance() != 1 ) - processSRV(); - else - Sys.sleep(0); - } - } - - function createSRV( t : h3d.mat.Texture, srvAddr : Address, samplerAddr : Address ) { - if (!srvThreadLaunched) { - srvThreadLaunched = true; - srvRingBuf = hl.CArray.alloc(SrvArgs, 256); - #if !console - sys.thread.Thread.create(runThread); - #end - } - - // Check if ring buffer is full - while ( computeSRVBufferDistance() == 0 ) {}; - - var srvArgs = srvRingBuf[srvHead]; - - if( t.flags.has(Cube) ) { - var desc = unsafeCastTo(srvArgs.resourceDesc, TexCubeSRV); - desc.format = t.t.format; - desc.dimension = TEXTURECUBE; - desc.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; - desc.mostDetailedMip = t.startingMip; - desc.mipLevels = -1; - desc.resourceMinLODClamp = 0; - } else if( t.flags.has(IsArray) ) { - var desc = unsafeCastTo(srvArgs.resourceDesc, Tex2DArraySRV); - desc.format = t.t.format; - desc.dimension = TEXTURE2DARRAY; - desc.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; - desc.mostDetailedMip = t.startingMip; - desc.mipLevels = -1; - desc.firstArraySlice = 0; - desc.arraySize = t.layerCount; - desc.planeSlice = 0; - desc.resourceMinLODClamp = 0; - } else if ( t.isDepth() ) { - var desc = srvArgs.resourceDesc; - switch (t.format) { - case Depth16: - desc.format = R16_UNORM; - case Depth24, Depth24Stencil8: - desc.format = R24_UNORM_X8_TYPELESS; - case Depth32: - desc.format = R32_FLOAT; - default: - throw "Unsupported depth format "+ t.format; - } - desc.dimension = TEXTURE2D; - desc.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; - desc.mostDetailedMip = t.startingMip; - desc.mipLevels = -1; - desc.planeSlice = 0; - desc.resourceMinLODClamp = 0; - } else { - var desc = srvArgs.resourceDesc; - desc.format = t.t.format; - desc.dimension = TEXTURE2D; - desc.shader4ComponentMapping = ShaderComponentMapping.DEFAULT; - desc.mostDetailedMip = t.startingMip; - desc.mipLevels = -1; - desc.planeSlice = 0; - desc.resourceMinLODClamp = 0; - } - - var desc = srvArgs.samplerDesc; - desc.comparisonFunc = NEVER; - desc.maxLod = 1e30; - desc.filter = switch( [t.filter, t.mipMap] ) { - case [Nearest, None|Nearest]: MIN_MAG_MIP_POINT; - case [Nearest, Linear]: MIN_MAG_POINT_MIP_LINEAR; - case [Linear, None|Nearest]: MIN_MAG_LINEAR_MIP_POINT; - case [Linear, Linear]: MIN_MAG_MIP_LINEAR; - } - desc.addressU = desc.addressV = desc.addressW = switch( t.wrap ) { - case Clamp: CLAMP; - case Repeat: WRAP; - } - desc.mipLODBias = t.lodBias; - - srvArgs.res = t.t.res; - srvArgs.srvAddr = srvAddr; - srvArgs.samplerAddr = samplerAddr; - srvHead = (srvHead + 1) & 0xFF; - - #if console - processSRV(); - #end - } - function uploadBuffers( buffers : h3d.shader.Buffers, buf : h3d.shader.Buffers.ShaderBuffers, which:h3d.shader.Buffers.BufferKind, shader : hxsl.RuntimeShader.RuntimeShaderData, regs : ShaderRegisters ) { switch( which ) { case Params: @@ -1818,10 +1592,10 @@ class DX12Driver extends h3d.impl.Driver { if( regs.params & 0x100 != 0 ) { // update CBV var srv = frame.shaderResourceViews.alloc(1); - var alloc = allocDynamicBuffer(data,dataSize); + var cbv = allocDynamicBuffer(data,dataSize); var desc = tmp.cbvDesc; - desc.bufferLocation = alloc.resource.getGpuVirtualAddress() + alloc.offset; - desc.sizeInBytes = alloc.byteSize; + desc.bufferLocation = cbv.getGpuVirtualAddress(); + desc.sizeInBytes = calcCBVSize(dataSize); Driver.createConstantBufferView(desc, srv); if( currentShader.isCompute ) frame.commandList.setComputeRootDescriptorTable(regs.params & 0xFF, frame.shaderResourceViews.toGPU(srv)); @@ -1834,49 +1608,31 @@ class DX12Driver extends h3d.impl.Driver { } case Globals: if( shader.globalsSize > 0 ) { - var data = hl.Bytes.getArray(buf.globals.toData()); - var dataSize = shader.globalsSize << 4; - if( regs.globals & 0x100 != 0 ) { - // update CBV - var srv = frame.shaderResourceViews.alloc(1); - var alloc = allocDynamicBuffer(data,dataSize); - var desc = tmp.cbvDesc; - desc.bufferLocation = alloc.resource.getGpuVirtualAddress() + alloc.offset; - desc.sizeInBytes = alloc.byteSize; - Driver.createConstantBufferView(desc, srv); - if( currentShader.isCompute ) - frame.commandList.setComputeRootDescriptorTable(regs.globals & 0xFF, frame.shaderResourceViews.toGPU(srv)); - else - frame.commandList.setGraphicsRootDescriptorTable(regs.globals & 0xFF, frame.shaderResourceViews.toGPU(srv)); - } else if( currentShader.isCompute ) - frame.commandList.setComputeRoot32BitConstants(regs.globals, dataSize >> 2, data, 0); + if( currentShader.isCompute ) + frame.commandList.setComputeRoot32BitConstants(regs.globals, shader.globalsSize << 2, hl.Bytes.getArray(buf.globals.toData()), 0); else - frame.commandList.setGraphicsRoot32BitConstants(regs.globals, dataSize >> 2, data, 0); + frame.commandList.setGraphicsRoot32BitConstants(regs.globals, shader.globalsSize << 2, hl.Bytes.getArray(buf.globals.toData()), 0); } case Textures: - if( shader.texturesCount > 0 ) { + if( regs.texturesCount > 0 ) { if ( hasBuffersTexturesChanged(buf, regs) ) { regs.lastHeapCount = heapCount; - regs.srv = frame.shaderResourceViews.alloc(shader.texturesCount); - regs.samplersView = frame.samplerViews.alloc(shader.texturesCount); - if ( regs.lastTextures.length < shader.texturesCount ) { - regs.lastTextures.resize(shader.texturesCount); - regs.lastTexturesBits.resize(shader.texturesCount); + regs.srv = frame.shaderResourceViews.alloc(regs.texturesCount); + regs.samplersView = frame.samplerViews.alloc(regs.texturesCount); + if ( regs.lastTextures.length < regs.texturesCount ) { + regs.lastTextures.resize(regs.texturesCount); + regs.lastTexturesBits.resize(regs.texturesCount); } - var regIndex = regs.buffers + shader.bufferCount; - var outIndex = 0; - for( i in 0...shader.texturesCount ) { + + for( i in 0...regs.texturesCount ) { var t = buf.tex[i]; - var pt = regs.texturesTypes[i]; + if( t == null || t.isDisposed() ) { - switch ( pt ) { - case TSampler(TCube, false): - t = h3d.mat.Texture.defaultCubeTexture(); - case TSampler(_, false): + if( i < regs.textures2DCount ) { var color = h3d.mat.Defaults.loadingTextureColor; t = h3d.mat.Texture.fromColor(color, (color >>> 24) / 255); - default: - throw "Missing texture"; + } else { + t = h3d.mat.Texture.defaultCubeTexture(); } } if( t != null && t.t == null && t.realloc != null ) { @@ -1896,76 +1652,67 @@ class DX12Driver extends h3d.impl.Driver { } } - regs.lastTextures[i] = buf.tex[i] != null ? buf.tex[i].t : null; + regs.lastTextures[i] = buf.tex[i]; regs.lastTexturesBits[i] = buf.tex[i] != null ? buf.tex[i].bits : -1; - switch( pt ) { - case TRWTexture(dim,arr,chans): - var tdim : hxsl.Ast.TexDimension = t.flags.has(Cube) ? TCube : T2D; - var fmt; - if( (arr != t.flags.has(IsArray)) || dim != tdim ) - throw "Texture format does not match: "+t+"["+t.format+"] should be "+hxsl.Ast.Tools.toString(pt); - var srv = frame.shaderResourceViews.alloc(1); - if( !t.flags.has(Writable) ) - throw "Texture was allocated without Writable flag"; - transition(t.t, UNORDERED_ACCESS); - var desc = tmp.wtexDesc; - desc.format = cast getTextureFormat(t); - desc.viewDimension = switch( [dim,arr] ) { - case [T1D, false]: TEXTURE1D; - case [T2D, false]: TEXTURE2D; - case [T3D, false]: TEXTURE3D; - case [T1D, true]: TEXTURE1DARRAY; - case [T2D, true]: TEXTURE2DARRAY; - default: throw "Unsupported RWTexture "+t; - } - desc.mipSlice = 0; - desc.planeSlice = 0; - if( arr ) { - desc.firstArraySlice = 0; - desc.arraySize = 1; - } - Driver.createUnorderedAccessView(t.t.res, null, desc, srv); - if( currentShader.isCompute ) - frame.commandList.setComputeRootDescriptorTable(regIndex++, frame.shaderResourceViews.toGPU(srv)); - else - frame.commandList.setGraphicsRootDescriptorTable(regIndex++, frame.shaderResourceViews.toGPU(srv)); - continue; - default: + var tdesc : ShaderResourceViewDesc; + if( t.flags.has(Cube) ) { + var desc = tmp.texCubeSRV; + desc.format = t.t.format; + desc.mostDetailedMip = t.startingMip; + tdesc = desc; + } else if( t.flags.has(IsArray) ) { + var desc = tmp.tex2DArraySRV; + desc.format = t.t.format; + desc.arraySize = t.layerCount; + desc.mostDetailedMip = t.startingMip; + tdesc = desc; + } else if ( t.isDepth() ) { + var desc = tmp.tex2DSRV; + desc.format = R24_UNORM_X8_TYPELESS; + desc.mostDetailedMip = t.startingMip; + tdesc = desc; + } else { + var desc = tmp.tex2DSRV; + desc.format = t.t.format; + desc.mostDetailedMip = t.startingMip; + tdesc = desc; } - t.lastFrame = frameCount; - var state = if ( shader.kind == Fragment ) - PIXEL_SHADER_RESOURCE; - else - NON_PIXEL_SHADER_RESOURCE; - transition(t.t, state); - createSRV(t, regs.srv.offset(outIndex * frame.shaderResourceViews.stride), regs.samplersView.offset(outIndex * frame.samplerViews.stride)); - outIndex++; - } - } - else { - for( i in 0...regs.texturesCount ) { - var t = buf.tex[i]; - if (t == null || t.t == null) - continue; - - var state = if ( shader.kind == Fragment ) + var state = if ( t.isDepth() ) + if ( t.t.state & ( DEPTH_READ | DEPTH_WRITE ) == COMMON ) + DEPTH_READ; + else + t.t.state; + else if ( shader.kind == Fragment ) PIXEL_SHADER_RESOURCE; else NON_PIXEL_SHADER_RESOURCE; transition(t.t, state); + Driver.createShaderResourceView(t.t.res, tdesc, regs.srv.offset(i * frame.shaderResourceViews.stride)); + + var desc = tmp.samplerDesc; + desc.filter = switch( [t.filter, t.mipMap] ) { + case [Nearest, None|Nearest]: MIN_MAG_MIP_POINT; + case [Nearest, Linear]: MIN_MAG_POINT_MIP_LINEAR; + case [Linear, None|Nearest]: MIN_MAG_LINEAR_MIP_POINT; + case [Linear, Linear]: MIN_MAG_MIP_LINEAR; + } + desc.addressU = desc.addressV = desc.addressW = switch( t.wrap ) { + case Clamp: CLAMP; + case Repeat: WRAP; + } + desc.mipLODBias = t.lodBias; + Driver.createSampler(desc, regs.samplersView.offset(i * frame.samplerViews.stride)); } } - if( regs.texturesCount > 0 ) { - if( currentShader.isCompute ) { - frame.commandList.setComputeRootDescriptorTable(regs.textures, frame.shaderResourceViews.toGPU(regs.srv)); - frame.commandList.setComputeRootDescriptorTable(regs.samplers, frame.samplerViews.toGPU(regs.samplersView)); - } else { - frame.commandList.setGraphicsRootDescriptorTable(regs.textures, frame.shaderResourceViews.toGPU(regs.srv)); - frame.commandList.setGraphicsRootDescriptorTable(regs.samplers, frame.samplerViews.toGPU(regs.samplersView)); - } + if( currentShader.isCompute ) { + frame.commandList.setComputeRootDescriptorTable(regs.textures, frame.shaderResourceViews.toGPU(regs.srv)); + frame.commandList.setComputeRootDescriptorTable(regs.samplers, frame.samplerViews.toGPU(regs.samplersView)); + } else { + frame.commandList.setGraphicsRootDescriptorTable(regs.textures, frame.shaderResourceViews.toGPU(regs.srv)); + frame.commandList.setGraphicsRootDescriptorTable(regs.samplers, frame.samplerViews.toGPU(regs.samplersView)); } } case Buffers: @@ -1991,8 +1738,6 @@ class DX12Driver extends h3d.impl.Driver { desc.numElements = b.vertices; desc.structureSizeInBytes = b.format.strideBytes; Driver.createUnorderedAccessView(cbv.res, null, desc, srv); - default: - throw "assert"; } if( currentShader.isCompute ) frame.commandList.setComputeRootDescriptorTable(regs.buffers + i, frame.shaderResourceViews.toGPU(srv)); @@ -2012,22 +1757,32 @@ class DX12Driver extends h3d.impl.Driver { if( currentShader == sh ) return false; currentShader = sh; - pipelineBuilder.setShader(shader); if( sh.isCompute ) { + needPipelineFlush = false; frame.commandList.setComputeRootSignature(currentShader.rootSignature); frame.commandList.setPipelineState(currentShader.computePipeline); } else { + needPipelineFlush = true; frame.commandList.setGraphicsRootSignature(currentShader.rootSignature); } return true; } override function selectMaterial( pass : h3d.mat.Pass ) @:privateAccess { - pipelineBuilder.selectMaterial(pass); + needPipelineFlush = true; + pipelineSignature.setI32(PSIGN_MATID, pass.bits); + pipelineSignature.setUI8(PSIGN_COLOR_MASK, pass.colorMask); var st = pass.stencil; - if( st != null && curStencilRef != st.reference ) { - curStencilRef = st.reference; - frame.commandList.omSetStencilRef(st.reference); + if( st != null ) { + pipelineSignature.setUI16(PSIGN_STENCIL_MASK, st.maskBits & 0xFFFF); + pipelineSignature.setI32(PSIGN_STENCIL_OPS, st.opBits); + if( curStencilRef != st.reference ) { + curStencilRef = st.reference; + frame.commandList.omSetStencilRef(st.reference); + } + } else { + pipelineSignature.setUI16(PSIGN_STENCIL_MASK, 0); + pipelineSignature.setI32(PSIGN_STENCIL_OPS, 0); } } @@ -2043,9 +1798,9 @@ class DX12Driver extends h3d.impl.Driver { v.sizeInBytes = bview.sizeInBytes; v.strideInBytes = bview.strideInBytes; if( inf.offset >= 256 ) throw "assert"; - pipelineBuilder.setBuffer(i, inf, v.strideInBytes); + pipelineSignature.setUI8(PSIGN_LAYOUT + i, inf.offset | inf.precision.toInt()); } - flushTransitions(); + needPipelineFlush = true; frame.commandList.iaSetVertexBuffers(0, currentShader.inputCount, views[0]); } @@ -2059,8 +1814,10 @@ class DX12Driver extends h3d.impl.Driver { v.bufferLocation = bview.bufferLocation; v.sizeInBytes = bview.sizeInBytes; v.strideInBytes = bview.strideInBytes; - pipelineBuilder.setBuffer(i, inf, v.strideInBytes); + if( inf.offset >= 256 ) throw "assert"; + pipelineSignature.setUI8(PSIGN_LAYOUT + i, inf.offset | inf.precision.toInt()); } + needPipelineFlush = true; frame.commandList.iaSetVertexBuffers(0, map.length, views[0]); } @@ -2079,50 +1836,55 @@ class DX12Driver extends h3d.impl.Driver { function makePipeline( shader : CompiledShader ) { var p = shader.pipeline; - var pass = pipelineBuilder.getCurrentPass(); - var depth = pipelineBuilder.getDepthProps(); - if( pass.wireframe ) pass.culling = None; + var passBits = pipelineSignature.getI32(PSIGN_MATID); + var colorMask = pipelineSignature.getUI8(PSIGN_COLOR_MASK); + var stencilMask = pipelineSignature.getUI16(PSIGN_STENCIL_MASK); + var stencilOp = pipelineSignature.getI32(PSIGN_STENCIL_OPS); + + var csrc = Pass.getBlendSrc(passBits); + var cdst = Pass.getBlendDst(passBits); + var asrc = Pass.getBlendAlphaSrc(passBits); + var adst = Pass.getBlendAlphaDst(passBits); + var cop = Pass.getBlendOp(passBits); + var aop = Pass.getBlendAlphaOp(passBits); + var dw = Pass.getDepthWrite(passBits); + var cmp = Pass.getDepthTest(passBits); + var cull = Pass.getCulling(passBits); + var wire = Pass.getWireframe(passBits); + if( wire != 0 ) cull = 0; var rtCount = currentRenderTargets.length; if( rtCount == 0 ) rtCount = 1; p.numRenderTargets = rtCount; - - p.rasterizerState.cullMode = CULL[pass.culling.getIndex()]; - p.rasterizerState.fillMode = pass.wireframe ? WIREFRAME : SOLID; - p.depthStencilDesc.depthEnable = pass.depthTest != Always; - p.depthStencilDesc.depthWriteMask = !pass.depthWrite || !depthEnabled ? ZERO : ALL; - p.depthStencilDesc.depthFunc = COMP[pass.depthTest.getIndex()]; - p.rasterizerState.depthBias = Std.int(depth.bias); - p.rasterizerState.slopeScaledDepthBias = depth.slopeScaledBias; - p.rasterizerState.depthClipEnable = !depth.clamp; + p.rasterizerState.cullMode = CULL[cull]; + p.rasterizerState.fillMode = wire == 0 ? SOLID : WIREFRAME; + p.depthStencilDesc.depthEnable = cmp != 0; + p.depthStencilDesc.depthWriteMask = dw == 0 || !depthEnabled ? ZERO : ALL; + p.depthStencilDesc.depthFunc = COMP[cmp]; var bl = p.blendState; for( i in 0...rtCount ) { var t = bl.renderTargets[i]; - - t.blendEnable = pass.blendSrc != One || pass.blendDst != Zero; - t.srcBlend = BLEND[pass.blendSrc.getIndex()]; - t.dstBlend = BLEND[pass.blendDst.getIndex()]; - t.srcBlendAlpha = BLEND_ALPHA[pass.blendAlphaSrc.getIndex()]; - t.dstBlendAlpha = BLEND_ALPHA[pass.blendAlphaDst.getIndex()]; - t.blendOp = BLEND_OP[pass.blendOp.getIndex()]; - t.blendOpAlpha = BLEND_OP[pass.blendAlphaOp.getIndex()]; - t.renderTargetWriteMask = pass.colorMask; + t.blendEnable = csrc != 0 || cdst != 1; + t.srcBlend = BLEND[csrc]; + t.dstBlend = BLEND[cdst]; + t.srcBlendAlpha = BLEND_ALPHA[asrc]; + t.dstBlendAlpha = BLEND_ALPHA[adst]; + t.blendOp = BLEND_OP[cop]; + t.blendOpAlpha = BLEND_OP[aop]; + t.renderTargetWriteMask = colorMask; var t = currentRenderTargets[i]; p.rtvFormats[i] = t == null ? R8G8B8A8_UNORM : t.t.format; } - p.dsvFormat = toDxgiDepthFormat(depth.format); - for ( i in rtCount...8 ) - p.rtvFormats[i] = DxgiFormat.UNKNOWN; + p.dsvFormat = depthEnabled ? D24_UNORM_S8_UINT : UNKNOWN; for( i in 0...shader.inputCount ) { var d = shader.inputLayout[i]; - - var inf = pipelineBuilder.getBufferInput(i); - d.alignedByteOffset = inf.offset; - d.format = @:privateAccess switch( [shader.format.inputs[i].type, inf.precision] ) { + var offset = pipelineSignature.getUI8(PSIGN_LAYOUT + i); + d.alignedByteOffset = offset & ~3; + d.format = @:privateAccess switch( [shader.format.inputs[i].type, new hxd.BufferFormat.Precision(offset&3)] ) { case [DFloat, F32]: R32_FLOAT; case [DFloat, F16]: R16_FLOAT; case [DFloat, S8]: R8_SNORM; @@ -2144,34 +1906,65 @@ class DX12Driver extends h3d.impl.Driver { }; } - - var stencil = pass.stencil; + var stencil = stencilMask != 0 || stencilOp != 0; var st = p.depthStencilDesc; - st.stencilEnable = stencil != null; - if( stencil != null ) { + st.stencilEnable = stencil; + if( stencil ) { var front = st.frontFace; var back = st.backFace; - st.stencilReadMask = stencil.readMask; - st.stencilWriteMask = stencil.writeMask; - front.stencilFunc = COMP[stencil.frontTest.getIndex()]; - front.stencilPassOp = STENCIL_OP[stencil.frontPass.getIndex()]; - front.stencilFailOp = STENCIL_OP[stencil.frontSTfail.getIndex()]; - front.stencilDepthFailOp = STENCIL_OP[stencil.frontDPfail.getIndex()]; - back.stencilFunc = COMP[stencil.backTest.getIndex()]; - back.stencilPassOp = STENCIL_OP[stencil.backPass.getIndex()]; - back.stencilFailOp = STENCIL_OP[stencil.backSTfail.getIndex()]; - back.stencilDepthFailOp = STENCIL_OP[stencil.backDPfail.getIndex()]; + st.stencilReadMask = stencilMask & 0xFF; + st.stencilWriteMask = stencilMask >> 8; + front.stencilFunc = COMP[Stencil.getFrontTest(stencilOp)]; + front.stencilPassOp = STENCIL_OP[Stencil.getFrontPass(stencilOp)]; + front.stencilFailOp = STENCIL_OP[Stencil.getFrontSTfail(stencilOp)]; + front.stencilDepthFailOp = STENCIL_OP[Stencil.getFrontDPfail(stencilOp)]; + back.stencilFunc = COMP[Stencil.getBackTest(stencilOp)]; + back.stencilPassOp = STENCIL_OP[Stencil.getBackPass(stencilOp)]; + back.stencilFailOp = STENCIL_OP[Stencil.getBackSTfail(stencilOp)]; + back.stencilDepthFailOp = STENCIL_OP[Stencil.getBackDPfail(stencilOp)]; } return Driver.createGraphicsPipelineState(p); } function flushPipeline() { - if( !pipelineBuilder.needFlush ) return; - var cache = pipelineBuilder.lookup(currentShader.pipelines, currentShader.inputCount); - if( cache.pipeline == null ) - cache.pipeline = makePipeline(currentShader); - frame.commandList.setPipelineState(cache.pipeline); + if( !needPipelineFlush ) return; + needPipelineFlush = false; + var signature = pipelineSignature; + var signatureSize = PSIGN_LAYOUT + currentShader.inputCount; + adlerOut.setI32(0, 0); + hl.Format.digest(adlerOut, signature, signatureSize, 3); + var hash = adlerOut.getI32(0); + var pipes = currentShader.pipelines.get(hash); + if( pipes == null ) { + pipes = new hl.NativeArray(1); + currentShader.pipelines.set(hash, pipes); + } + var insert = -1; + for( i in 0...pipes.length ) { + var p = pipes[i]; + if( p == null ) { + insert = i; + break; + } + if( p.size == signatureSize && p.bytes.compare(0, signature, 0, signatureSize) == 0 ) { + frame.commandList.setPipelineState(p.pipeline); + return; + } + } + + if( insert < 0 ) { + var pipes2 = new hl.NativeArray(pipes.length + 1); + pipes2.blit(0, pipes, 0, insert); + currentShader.pipelines.set(hash, pipes2); + pipes = pipes2; + } + var cp = new CachedPipeline(); + cp.bytes = signature.sub(0, signatureSize); + cp.size = signatureSize; + cp.pipeline = makePipeline(currentShader); + pipes[insert] = cp; + frame.commandList.setPipelineState(cp.pipeline); } // QUERIES @@ -2276,11 +2069,7 @@ class DX12Driver extends h3d.impl.Driver { frame.commandList.iaSetIndexBuffer(ibuf.vbuf.iview); } if( commands.data != null ) { - transition(commands.data, INDIRECT_ARGUMENT); - if ( commands.countBuffer != null ) - transition(commands.countBuffer, INDIRECT_ARGUMENT); - flushTransitions(); - frame.commandList.executeIndirect(indirectCommand, commands.commandCount, commands.data.res, 0, commands.countBuffer != null ? commands.countBuffer.res : null, 0); + frame.commandList.executeIndirect(indirectCommand, commands.commandCount, commands.data, 0, null, 0); } else { frame.commandList.drawIndexedInstanced(commands.indexCount, commands.commandCount, commands.startIndex, 0, 0); } @@ -2299,14 +2088,9 @@ class DX12Driver extends h3d.impl.Driver { } } - function flushSRV() { - while ( computeSRVBufferDistance() != 1 ) {}; - } - function flushFrame( onResize : Bool = false ) { flushQueries(); frame.commandList.close(); - flushSRV(); frame.commandList.execute(); currentShader = null; Driver.flushMessages(); @@ -2317,7 +2101,6 @@ class DX12Driver extends h3d.impl.Driver { override function present() { transition(frame.backBuffer, PRESENT); - flushTransitions(); flushFrame(); Driver.present(window.vsync); @@ -2343,9 +2126,7 @@ class DX12Driver extends h3d.impl.Driver { } override function computeDispatch( x : Int = 1, y : Int = 1, z : Int = 1 ) { - flushTransitions(); frame.commandList.dispatch(x,y,z); - flushResources(); } } diff --git a/h3d/impl/DirectXDriver.hx b/h3d/impl/DirectXDriver.hx index 5186bbaab2..2422a5faa8 100644 --- a/h3d/impl/DirectXDriver.hx +++ b/h3d/impl/DirectXDriver.hx @@ -11,12 +11,12 @@ private class ShaderContext { public var globalsSize : Int; public var paramsSize : Int; public var texturesCount : Int; + public var textures2DCount : Int; public var bufferCount : Int; public var paramsContent : hl.Bytes; public var globals : dx.Resource; public var params : dx.Resource; public var samplersMap : Array; - public var texturesTypes : Array; #if debug public var debugSource : String; #end @@ -869,16 +869,11 @@ class DirectXDriver extends h3d.impl.Driver { ctx.paramsContent = new hl.Bytes(shader.paramsSize * 16); ctx.paramsContent.fill(0, shader.paramsSize * 16, 0xDD); ctx.texturesCount = shader.texturesCount; - ctx.texturesTypes = []; var p = shader.textures; while( p != null ) { switch( p.type ) { - case TArray( t = TSampler(_) | TRWTexture(_) | TChannel(_), SConst(n) ): - for( i in 0...n ) - ctx.texturesTypes.push(t); - case TSampler(_), TRWTexture(_), TChannel(_): - ctx.texturesTypes.push(p.type); + case TArray( TSampler2D , SConst(n) ): ctx.textures2DCount = n; default: } p = p.next; @@ -1294,16 +1289,12 @@ class DirectXDriver extends h3d.impl.Driver { var sstart = -1, smax = -1; for( i in 0...shader.texturesCount ) { var t = buffers.tex[i]; - var tt = shader.texturesTypes[i]; if( t == null || t.isDisposed() ) { - switch( tt ) { - case TSampler(T2D,_): + if( i < shader.textures2DCount ) { var color = h3d.mat.Defaults.loadingTextureColor; t = h3d.mat.Texture.fromColor(color, (color >>> 24) / 255); - case TSampler(TCube,_): + } else { t = h3d.mat.Texture.defaultCubeTexture(); - default: - throw "Missing texture"; } } if( t != null && t.t == null && t.realloc != null ) { diff --git a/h3d/impl/Driver.hx b/h3d/impl/Driver.hx index 5b366dfe1e..c0d65f1d39 100644 --- a/h3d/impl/Driver.hx +++ b/h3d/impl/Driver.hx @@ -10,7 +10,7 @@ typedef Texture = { t : js.html.webgl.Texture, width : Int, height : Int, intern typedef Query = {}; #elseif hlsdl typedef GPUBuffer = sdl.GL.Buffer; -typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float, startMip : Int #if multidriver, driver : Driver #end }; +typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float, startMip : Int }; typedef Query = { q : sdl.GL.Query, kind : QueryKind }; #elseif usegl typedef GPUBuffer = haxe.GLTypes.Buffer; @@ -265,9 +265,6 @@ class Driver { throw "Driver does not allow to read vertex bytes"; } - public function onTextureBiasChanged( t : h3d.mat.Texture ) { - } - /** Returns true if we could copy the texture, false otherwise (not supported by driver or mismatch in size/format) **/ diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 4c0935e717..9e20ffcf67 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -1,13 +1,14 @@ package h3d.impl; import h3d.impl.Driver; +import h3d.mat.Data; import h3d.mat.Pass; import h3d.mat.Stencil; -import h3d.mat.Data; #if (js||hlsdl||usegl) #if js import hxd.impl.TypedArray; + private typedef GL = js.html.webgl.GL2; private typedef Uniform = js.html.webgl.UniformLocation; private typedef Program = js.html.webgl.Program; @@ -87,6 +88,7 @@ class GlDriver extends Driver { static var UID = 0; public var gl : GL; public static var ALLOW_WEBGL2 = true; + public var textureSupport:{astc:Bool, astcHDR:Bool, etc1:Bool, etc2:Bool, dxt:Bool, bptc:Bool}; #end #if (hlsdl||usegl) @@ -128,8 +130,6 @@ class GlDriver extends Driver { var hasMultiIndirect = false; var maxCompressedTexturesSupport = 0; - public static var hasMultiIndirectCount = false; - var drawMode : Int; var isIntelGpu : Bool; @@ -142,11 +142,6 @@ class GlDriver extends Driver { public static var outOfMemoryCheck = #if js false #else true #end; public function new(antiAlias=0) { - #if (hlsdl >= version("1.15.0")) - if ( computeEnabled ) - sdl.Sdl.setGLVersion(4, 3); - #end - #if js canvas = @:privateAccess hxd.Window.getInstance().canvas; var options = {alpha:false,stencil:true,antialias:antiAlias>0}; @@ -176,10 +171,6 @@ class GlDriver extends Driver { isIntelGpu = ~/intel.*graphics/.match(driver); #end - #if (hlsdl >= version("1.15.0")) - hasMultiIndirectCount = gl.hasExtension("GL_ARB_indirect_parameters"); - #end - #if hlmesa hasMultiIndirect = true; maxCompressedTexturesSupport = 7; @@ -206,11 +197,6 @@ class GlDriver extends Driver { shaderVersion = Math.round( Std.parseFloat(reg.matched(0)) * 100 ); } - #if (hlsdl >= version("1.15.0")) - if ( computeEnabled ) - shaderVersion = 430; - #end - drawMode = GL.TRIANGLES; #if js @@ -235,13 +221,8 @@ class GlDriver extends Driver { } #if hlsdl - static var computeEnabled : Bool = false; public static function enableComputeShaders() { - #if (hlsdl >= version("1.15.0")) - computeEnabled = true; - #else - throw "enableComputeShaders() requires hlsdl 1.15+"; - #end + sdl.Sdl.setGLVersion(4, 3); } #end @@ -288,7 +269,7 @@ class GlDriver extends Driver { return makeCompiler().run(sh); } if( shader.mode == Compute ) - return compile(shader.compute.data); + return "// compute:\n" + compile(shader.compute.data); return "// vertex:\n" + compile(shader.vertex.data) + "// fragment:\n" + compile(shader.fragment.data); } @@ -324,6 +305,7 @@ class GlDriver extends Driver { } gl.shaderSource(s, shader.code); gl.compileShader(s); + var log = gl.getShaderInfoLog(s); if ( gl.getShaderParameter(s, GL.COMPILE_STATUS) != cast 1 ) { var log = gl.getShaderInfoLog(s); var lid = Std.parseInt(log.substr(9)); @@ -355,43 +337,16 @@ class GlDriver extends Driver { var tt = t.type; var count = 1; switch( tt ) { - case TChannel(_): tt = TSampler(T2D,false); + case TChannel(_): tt = TSampler2D; case TArray(t,SConst(n)): tt = t; count = n; default: } - if( curT == null || !tt.equals(curT) ) { + if( tt != curT ) { curT = tt; name = switch( tt ) { - case TSampler(dim,arr): - mode = switch( [dim, arr] ) { - case [T2D, false]: GL.TEXTURE_2D; - case [T3D, false]: GL.TEXTURE_3D; - case [TCube, false]: GL.TEXTURE_CUBE_MAP; - case [T2D, true]: GL.TEXTURE_2D_ARRAY; - #if (hlsdl > version("1.15.0")) - case [T1D, false]: GL.TEXTURE_1D; - case [T1D, true]: GL.TEXTURE_1D_ARRAY; - case [TCube, true]: GL.TEXTURE_CUBE_MAP_ARRAY; - #end - default: throw "Texture not supported "+tt; - } - "Textures" + (dim == T2D ? "" : dim.getName().substr(1))+(arr ? "Array" : ""); - case TRWTexture(dim, arr, chans): - #if (js || hlsdl < version("1.15.0")) - throw "Texture not supported "+tt; - #else - mode = switch( [dim, arr] ) { - case [T1D, false]: GL.IMAGE_1D; - case [T2D, false]: GL.IMAGE_2D; - case [T3D, false]: GL.IMAGE_3D; - case [TCube, false]: GL.IMAGE_CUBE; - case [T1D, true]: GL.IMAGE_1D_ARRAY; - case [T2D, true]: GL.IMAGE_2D_ARRAY; - case [TCube, true]: GL.IMAGE_CUBE_MAP_ARRAY; - default: throw "Texture not supported "+tt; - }; - "TexturesRW" + (dim == T2D ? "" : dim.getName().substr(1))+chans+(arr ? "Array" : ""); - #end + case TSampler2D: mode = GL.TEXTURE_2D; "Textures"; + case TSamplerCube: mode = GL.TEXTURE_CUBE_MAP; "TexturesCube"; + case TSampler2DArray: mode = GL.TEXTURE_2D_ARRAY; "TexturesArray"; default: throw "Unsupported texture type "+tt; } index = 0; @@ -437,8 +392,6 @@ class GlDriver extends Driver { #end case Uniform: gl.getUniformBlockIndex(p.p,(shader.kind==Vertex?"vertex_":"")+"uniform_buffer"+i); - default: - throw "assert"; } }]; var start = 0; @@ -451,8 +404,6 @@ class GlDriver extends Driver { #if (hl_ver >= version("1.15.0")) gl.shaderStorageBlockBinding(p.p,s.buffers[i], i + start); #end - default: - throw "assert"; } } } @@ -620,11 +571,7 @@ class GlDriver extends Driver { case Uniform: gl.bindBufferBase(GL.UNIFORM_BUFFER, i + start, buf.buffers[i].vbuf); case RW: - if ( !buf.buffers[i].flags.has(ReadWriteBuffer) ) - throw "Buffer was allocated without ReadWriteBuffer flag"; gl.bindBufferBase(0x90D2 /*GL.SHADER STORAGE BUFFER*/, i + start, buf.buffers[i].vbuf); - default: - throw "assert"; } } case Textures: @@ -634,11 +581,11 @@ class GlDriver extends Driver { var pt = s.textures[i]; if( t == null || t.isDisposed() ) { switch( pt.t ) { - case TSampler(TCube, false): - t = h3d.mat.Texture.defaultCubeTexture(); - case TSampler(_, false): + case TSampler2D: var color = h3d.mat.Defaults.loadingTextureColor; t = h3d.mat.Texture.fromColor(color, (color >>> 24) / 255); + case TSamplerCube: + t = h3d.mat.Texture.defaultCubeTexture(); default: throw "Missing texture"; } @@ -661,41 +608,6 @@ class GlDriver extends Driver { if( pt.u == null ) continue; - #if !js - switch( pt.t ) { - case TRWTexture(dim,arr,chans): - var tdim : hxsl.Ast.TexDimension = t.flags.has(Cube) ? TCube : T2D; - var fmt; - if( (arr != t.flags.has(IsArray)) || dim != tdim ) - fmt = 0; - else { - // we suppose it's possible to map from one pixel format to shader declared 32f - fmt = switch( [chans,t.format] ) { - case [1, R8]: GL.R8; - case [2, RG8]: GL.RG8; - case [4, RGBA]: GL.RGBA8; - case [1, R16F]: GL.R16F; - case [2, RG16F]: GL.RG16F; - case [4, RGBA16F]: GL.RGBA16F; - case [1, R32F]: GL.R32F; - case [2, RG32F]: GL.RG32F; - case [4, RGBA32F]: GL.RGBA32F; - default: 0; - } - } - if( fmt == 0 ) - throw "Texture format does not match: "+t+"["+t.format+"] should be "+hxsl.Ast.Tools.toString(pt.t); - #if (hlsdl < version("1.15.0")) - throw "RWTextures support requires hlsdl 1.15+"; - #else - gl.bindImageTexture(i, cast t.t.t, 0, false, 0, GL.READ_WRITE, fmt); - #end - boundTextures[i] = null; - continue; - default: - } - #end - var idx = s.kind == Fragment ? curShader.vertex.textures.length + i : i; if( boundTextures[idx] != t.t ) { boundTextures[idx] = t.t; @@ -983,7 +895,9 @@ class GlDriver extends Driver { case GL.RED, GL.R8, GL.R16F, GL.R32F, 0x822A: GL.RED; case GL.RG, GL.RG8, GL.RG16F, GL.RG32F, 0x822C: GL.RG; case GL.RGB16F, GL.RGB32F, 0x8054, 0x8E8F: GL.RGB; - case 0x83F1, 0x83F2, 0x83F3, 0x805B, 0x8E8C: GL.RGBA; + case hxd.PixelFormat.DXT_FORMAT.RGBA_DXT1,hxd.PixelFormat.DXT_FORMAT.RGBA_DXT3, + hxd.PixelFormat.DXT_FORMAT.RGBA_DXT5,hxd.PixelFormat.ASTC_FORMAT.RGBA_4x4, 0x805B, 0x8E8C : GL.RGBA; + case hxd.PixelFormat.ETC_FORMAT.RGB_ETC1: GL.RGB; default: throw "Invalid format " + t.internalFmt; } } @@ -995,17 +909,15 @@ class GlDriver extends Driver { case SRGB, SRGB_ALPHA: hasFeature(SRGBTextures); case R8, RG8, RGB8, R16F, RG16F, RGB16F, R32F, RG32F, RGB32F, RG11B10UF, RGB10A2: #if js glES >= 3 #else true #end; case S3TC(n): n <= maxCompressedTexturesSupport; + case ASTC(_), ETC(_): #if js true #else false #end; default: false; } } function getBindType( t : h3d.mat.Texture ) { - if ( t.flags.has(Is3D) ) - return GL.TEXTURE_3D; + var isCube = t.flags.has(Cube); var isArray = t.flags.has(IsArray); - if( t.flags.has(Cube) ) - return #if (hlsdl > version("1.15.0")) isArray ? GL.TEXTURE_CUBE_MAP_ARRAY : #end GL.TEXTURE_CUBE_MAP; - return isArray ? GL.TEXTURE_2D_ARRAY : GL.TEXTURE_2D; + return isCube ? GL.TEXTURE_CUBE_MAP : isArray ? GL.TEXTURE_2D_ARRAY : GL.TEXTURE_2D; } override function allocTexture( t : h3d.mat.Texture ) : Texture { @@ -1073,16 +985,28 @@ class GlDriver extends Driver { tt.internalFmt = GL.R11F_G11F_B10F; tt.pixelFmt = GL.UNSIGNED_INT_10F_11F_11F_REV; case S3TC(n) if( n <= maxCompressedTexturesSupport ): - if( t.width&3 != 0 || t.height&3 != 0 ) - throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + checkMult4(t); switch( n ) { - case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT - case 2: tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT - case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_EXT + case 1: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT1; + case 2: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT3; + case 3: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT5; case 6: tt.internalFmt = 0x8E8F; // COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT case 7: tt.internalFmt = 0x8E8C; // COMPRESSED_RGBA_BPTC_UNORM default: throw "Unsupported texture format "+t.format; } + case ASTC(n): + checkMult4(t); + switch( n ) { + case 10: tt.internalFmt = hxd.PixelFormat.ASTC_FORMAT.RGBA_4x4; + default: throw "Unsupported texture format "+t.format; + } + case ETC(n): + checkMult4(t); + switch( n ) { + case 0: tt.internalFmt = hxd.PixelFormat.ETC_FORMAT.RGB_ETC1; + case 1: tt.internalFmt = hxd.PixelFormat.ETC_FORMAT.RGBA_ETC2; + default: throw "Unsupported texture format "+t.format; + } default: throw "Unsupported texture format "+t.format; } @@ -1119,7 +1043,7 @@ class GlDriver extends Driver { // Patch RGBA to be RGBA8 because texStorage expect a "Sized Internal Format" var sizedFormat = tt.internalFmt == GL.RGBA ? GL.RGBA8 : tt.internalFmt; - if( ( t.flags.has(IsArray) || t.flags.has(Is3D) ) && !t.flags.has(Cube) ) { + if( t.flags.has(IsArray) && !t.flags.has(Cube) ) { gl.texStorage3D(bind, t.mipLevels, sizedFormat, tt.width, tt.height, t.layerCount); checkError(); } else { @@ -1130,7 +1054,6 @@ class GlDriver extends Driver { for(mip in 0...t.mipLevels) { var w = hxd.Math.imax(1, tt.width >> mip); var h = hxd.Math.imax(1, tt.height >> mip); - var d = hxd.Math.imax(1, t.layerCount >> mip); if( t.flags.has(Cube) ) { for( i in 0...6 ) { gl.texImage2D(CUBE_FACES[i], mip, tt.internalFmt, w, h, 0, getChannels(tt), tt.pixelFmt, null); @@ -1139,10 +1062,10 @@ class GlDriver extends Driver { } else if( t.flags.has(IsArray) ) { gl.texImage3D(bind, mip, tt.internalFmt, w, h, t.layerCount, 0, getChannels(tt), tt.pixelFmt, null); checkError(); - } else if ( t.flags.has(Is3D) ) { - gl.texImage3D(bind, mip, tt.internalFmt, w, h, d, 0, getChannels(tt), tt.pixelFmt, null); - checkError(); } else { + #if js + if( !t.format.match(S3TC(_)) && !t.format.match(ETC(_)) && !t.format.match(ASTC(_)))) + #end gl.texImage2D(bind, mip, tt.internalFmt, w, h, 0, getChannels(tt), tt.pixelFmt, null); checkError(); } @@ -1159,6 +1082,11 @@ class GlDriver extends Driver { return tt; } + inline function checkMult4(t:h3d.mat.Texture) { + if( t.width&3 != 0 || t.height&3 != 0 ) + throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + } + function restoreBind() { var t = boundTextures[lastActiveIndex]; if( t == null ) @@ -1179,8 +1107,6 @@ class GlDriver extends Driver { tt.internalFmt = GL.DEPTH24_STENCIL8; tt.pixelFmt = GL.UNSIGNED_INT_24_8; fmt = GL.DEPTH_STENCIL; - case Depth32: - tt.internalFmt = GL.DEPTH_COMPONENT32F; default: throw "Unsupported depth format "+ t.format; } @@ -1278,11 +1204,11 @@ class GlDriver extends Driver { } override function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) { - #if hl + #if hl var pixels = bmp.getPixels(); uploadTexturePixels(t, pixels, mipLevel, side); pixels.dispose(); - #else + #else if( t.format != RGBA || t.layerCount != 1 ) { var pixels = bmp.getPixels(); uploadTexturePixels(t, pixels, mipLevel, side); @@ -1293,7 +1219,7 @@ class GlDriver extends Driver { gl.texSubImage2D(GL.TEXTURE_2D, mipLevel, 0, 0, getChannels(t.t), t.t.pixelFmt, img.getImageData(0, 0, bmp.width, bmp.height)); restoreBind(); } - #end + #end } /* @@ -1356,28 +1282,23 @@ class GlDriver extends Driver { override function uploadTexturePixels( t : h3d.mat.Texture, pixels : hxd.Pixels, mipLevel : Int, side : Int ) { var cubic = t.flags.has(Cube); - var face = GL.TEXTURE_2D; - if ( cubic ) - face = CUBE_FACES[side]; - if ( t.flags.has(IsArray) ) - face = GL.TEXTURE_2D_ARRAY - else if ( t.flags.has(Is3D) ) - face = GL.TEXTURE_3D; + var face = cubic ? CUBE_FACES[side] : t.flags.has(IsArray) ? GL.TEXTURE_2D_ARRAY : GL.TEXTURE_2D; var bind = getBindType(t); gl.bindTexture(bind, t.t.t); pixels.convert(t.format); var dataLen = pixels.dataSize; #if hl var stream = streamData(pixels.bytes.getData(),pixels.offset,dataLen); - if( t.format.match(S3TC(_)) ) { - if( t.flags.has(IsArray) || t.flags.has(Is3D) ) + if( t.format.match(S3TC(_)) || t.format.match(ASTC(_)) || t.format.match(ETC(_))) { + if( t.flags.has(IsArray) ) { #if (hlsdl >= version("1.12.0")) gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, dataLen, stream); #else throw "TextureArray support requires hlsdl 1.12+"; #end - else - gl.compressedTexImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, dataLen, stream); + } else { + gl.compressedTexImage2D(face, 0, t.t.internalFmt, pixels.width, pixels.height, 0, dataLen, stream); + } } else { - if( t.flags.has(IsArray) || t.flags.has(Is3D) ) + if( t.flags.has(IsArray) ) #if (hlsdl >= version("1.12.0")) gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, stream); #else throw "TextureArray support requires hlsdl 1.12+"; #end @@ -1399,16 +1320,18 @@ class GlDriver extends Driver { case RGB10A2, RG11B10UF: new Uint32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>2); default: new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen); } - if( t.format.match(S3TC(_)) ) { - if( t.flags.has(IsArray) || t.flags.has(Is3D) ) - gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, buffer); - else - gl.compressedTexSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, t.t.internalFmt, buffer); - } else { - if( t.flags.has(IsArray) || t.flags.has(Is3D) ) - gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, buffer); - else - gl.texSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, getChannels(t.t), t.t.pixelFmt, buffer); + switch (t.format) { + case S3TC(_), ASTC(_), ETC(_): + if( t.flags.has(IsArray) ) { + gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, buffer); + } else { + gl.compressedTexSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, t.t.internalFmt, buffer); + } + default: + if( t.flags.has(IsArray) ) + gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, buffer); + else + gl.texSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, getChannels(t.t), t.t.pixelFmt, buffer); } #else throw "Not implemented"; @@ -1574,12 +1497,6 @@ class GlDriver extends Driver { #if !js if( hasMultiIndirect && commands.data != null ) { gl.bindBuffer(GL.DRAW_INDIRECT_BUFFER, commands.data); - #if (hlsdl >= version("1.15.0")) - if ( commands.countBuffer != null && hasMultiIndirectCount ) { - gl.bindBuffer(GL.PARAMETER_BUFFER, commands.countBuffer); - gl.multiDrawElementsIndirectCount(drawMode, kind, null, null, commands.commandCount, 0); - } else - #end gl.multiDrawElementsIndirect(drawMode, kind, null, commands.commandCount, 0); gl.bindBuffer(GL.DRAW_INDIRECT_BUFFER, null); return; @@ -1729,9 +1646,6 @@ class GlDriver extends Driver { else gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cube) ? CUBE_FACES[layer] : GL.TEXTURE_2D, tex.t.t, mipLevel); - setPolygonOffset( tex.depthBuffer ); - setDepthClamp( tex.depthBuffer ); - if( tex.depthBuffer != null && depthBinding != NotBound ) { // Depthbuffer and stencilbuffer are combined in one buffer, created with GL.DEPTH_STENCIL if(tex.depthBuffer.hasStencil() && tex.depthBuffer.format == Depth24Stencil8) { @@ -1809,9 +1723,6 @@ class GlDriver extends Driver { gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, null, 0); - setPolygonOffset( depthBuffer ); - setDepthClamp( depthBuffer ); - if(depthBuffer.hasStencil() && depthBuffer.format == Depth24Stencil8) { gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,@:privateAccess depthBuffer.t.t, 0); } else { @@ -1826,6 +1737,11 @@ class GlDriver extends Driver { for( i in 0...boundTextures.length ) boundTextures[i] = null; + // if( !tex.flags.has(WasCleared) ) { + // tex.flags.set(WasCleared); // once we draw to, do not clear again + // clear(BLACK); + // } + #if js if( glDebug ) { var code = gl.checkFramebufferStatus(GL.FRAMEBUFFER); @@ -1835,28 +1751,6 @@ class GlDriver extends Driver { #end } - override function onTextureBiasChanged( t : h3d.mat.Texture ) { - setPolygonOffset(t); - } - - function setPolygonOffset( depthBuffer : h3d.mat.Texture ) { - if ( depthBuffer != null && ( depthBuffer.depthBias != 0 || depthBuffer.slopeScaledBias != 0 ) ) { - gl.enable(GL.POLYGON_OFFSET_FILL); - gl.polygonOffset(depthBuffer.slopeScaledBias, depthBuffer.depthBias); - } - else - gl.disable(GL.POLYGON_OFFSET_FILL); - } - - function setDepthClamp( dephTexture : h3d.mat.Texture ) { - #if !js - if ( dephTexture != null && dephTexture.depthClamp ) - gl.enable(GL.DEPTH_CLAMP); - else - gl.disable(GL.DEPTH_CLAMP); - #end - } - override function init( onCreate : Bool -> Void, forceSoftware = false ) { #if js // wait until all assets have properly load @@ -1883,16 +1777,37 @@ class GlDriver extends Driver { } #if js + + public function checkTextureSupport() { + final checkExtension = ext -> { + gl.getExtension(ext) != null; + } + return { + astc: checkExtension('WEBGL_compressed_texture_astc'), + astcHDR: checkExtension( 'WEBGL_compressed_texture_astc' ) + && gl.getExtension( 'WEBGL_compressed_texture_astc' ).getSupportedProfiles().includes( 'hdr' ), + etc1: false, // Not supported on WebGL2 (https://registry.khronos.org/OpenGL-Refpages/es3/html/glCompressedTexSubImage2D.xhtml); checkExtension('WEBGL_compressed_texture_etc1'), + etc2: checkExtension('WEBGL_compressed_texture_etc'), + dxt: checkExtension('WEBGL_compressed_texture_s3tc'), + bptc: checkExtension('EXT_texture_compression_bptc'), + } + } var features : Map = new Map(); var has16Bits : Bool; function makeFeatures() { for( f in Type.allEnums(Feature) ) features.set(f,checkFeature(f)); - if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null ) { - maxCompressedTexturesSupport = 3; - if( gl.getExtension("EXT_texture_compression_bptc") != null ) - maxCompressedTexturesSupport = 7; + textureSupport = checkTextureSupport(); + maxCompressedTexturesSupport = if (textureSupport.dxt || textureSupport.etc1 || textureSupport.etc2 || textureSupport.astc) { + if( gl.getExtension("EXT_texture_compression_bptc") != null ) { + 7; + } else { + 3; + } + } else { + 3; } + if( glES < 3 ) gl.getExtension("WEBGL_depth_texture"); has16Bits = gl.getExtension("EXT_texture_norm16") != null; // 16 bit textures diff --git a/h3d/impl/InstanceBuffer.hx b/h3d/impl/InstanceBuffer.hx index 0458d48518..ee3cab979e 100644 --- a/h3d/impl/InstanceBuffer.hx +++ b/h3d/impl/InstanceBuffer.hx @@ -3,7 +3,6 @@ package h3d.impl; @:allow(h3d.impl.Driver) class InstanceBuffer { - var countBuffer : Dynamic; var data : Dynamic; var driver : h3d.impl.Driver; var indexCount : Int; diff --git a/h3d/impl/MemoryManager.hx b/h3d/impl/MemoryManager.hx index d25a6e0a0b..2228cd64ef 100644 --- a/h3d/impl/MemoryManager.hx +++ b/h3d/impl/MemoryManager.hx @@ -10,6 +10,7 @@ class MemoryManager { var driver : Driver; var buffers : Array; var textures : Array; + var depths : Array; var triIndexes16 : Indexes; var quadIndexes16 : Indexes; @@ -27,13 +28,10 @@ class MemoryManager { public function init() { textures = new Array(); buffers = new Array(); + depths = new Array(); initIndexes(); } - public static function enableTrackAlloc(?b : Bool) { - @:privateAccess hxd.impl.AllocPos.ENABLED = b != null ? b : true; - } - function initIndexes() { var indices = new hxd.IndexBuffer(); for( i in 0...SIZE ) indices.push(i); @@ -182,7 +180,7 @@ class MemoryManager { free = cleanTextures(false); lastAutoDispose = hxd.Timer.frameCount; } - t.t = t.isDepth() ? driver.allocDepthBuffer(t) : driver.allocTexture(t); + t.t = driver.allocTexture(t); if( t.t != null ) break; if( driver.isDisposed() ) return; @@ -194,6 +192,29 @@ class MemoryManager { texMemory += memSize(t); } + @:allow(h3d.mat.Texture.alloc) + function allocDepth( b : h3d.mat.Texture ) { + while( true ) { + var free = cleanTextures(false); + b.t = driver.allocDepthBuffer(b); + if( b.t != null ) break; + + if( driver.isDisposed() ) return; + while( cleanTextures(false) ) {} // clean all old textures + if( !free && !cleanTextures(true) ) + throw "Maximum texture memory reached"; + } + depths.push(b); + texMemory += b.width * b.height * 4; + } + + @:allow(h3d.mat.Texture.dispose) + function deleteDepth( b : h3d.mat.Texture ) { + if( !depths.remove(b) ) return; + driver.disposeDepthBuffer(b); + texMemory -= b.width * b.height * 4; + } + // ------------------------------------- DISPOSE ------------------------------------------ public function onContextLost() { @@ -212,6 +233,8 @@ class MemoryManager { quadIndexes32 = null; for( t in textures.copy() ) t.dispose(); + for( b in depths.copy() ) + b.dispose(); for( b in buffers.copy() ) b.dispose(); buffers = []; @@ -240,6 +263,9 @@ class MemoryManager { */ @:access(h3d.Buffer) public function allocStats() : Array<{ position : String, count : Int, tex : Bool, size : Int, stacks : Array<{ stack : String, count : Int, size : Int }> }> { + #if !track_alloc + return []; + #else var h = new Map(); var all = []; inline function addStack( a : hxd.impl.AllocPos, stacks : Array<{ stack : String, count : Int, size : Int }>, size : Int ) { @@ -254,8 +280,6 @@ class MemoryManager { stacks.push({ stack : stackStr, count : 1, size : size }); } for( t in textures ) { - if ( t.allocPos == null ) - continue; var key = "$"+t.allocPos.position; var inf = h.get(key); if( inf == null ) { @@ -269,9 +293,7 @@ class MemoryManager { addStack(t.allocPos, inf.stacks, size); } for( b in buffers ) { - if ( b.allocPos == null ) - continue; - var key = b.allocPos.position; + var key = b.allocPos == null ? "null" : b.allocPos.position; var inf = h.get(key); if( inf == null ) { inf = { position : key, count : 0, size : 0, tex : false, stacks : [] }; @@ -285,5 +307,8 @@ class MemoryManager { } all.sort(function(a, b) return b.size - a.size); return all; + #end } + + } \ No newline at end of file diff --git a/h3d/impl/RenderContext.hx b/h3d/impl/RenderContext.hx index a8751e0670..c7dc385f6a 100644 --- a/h3d/impl/RenderContext.hx +++ b/h3d/impl/RenderContext.hx @@ -217,7 +217,7 @@ class RenderContext { if( s.fragment != null ) fill(buf.fragment, s.fragment); } - public function fillParams( buf : h3d.shader.Buffers, s : hxsl.RuntimeShader, shaders : hxsl.ShaderList, compute : Bool = false ) { + public function fillParams( buf : h3d.shader.Buffers, s : hxsl.RuntimeShader, shaders : hxsl.ShaderList ) { var curInstance = -1; var curInstanceValue = null; inline function getInstance( index : Int ) { @@ -225,8 +225,6 @@ class RenderContext { return curInstanceValue; var si = shaders; curInstance = index; - // Compute list has no linker shader. - if ( compute ) index++; while( --index > 0 ) si = si.next; curInstanceValue = si.s; return curInstanceValue; @@ -249,13 +247,11 @@ class RenderContext { while( p != null ) { var v : Dynamic; if( p.perObjectGlobal == null ) { - switch( p.type ) { - case TFloat, TInt: + if( p.type == TFloat ) { var i = getInstance(p.instance); ptr[p.pos] = i.getParamFloatValue(p.index); p = p.next; continue; - default: } v = getInstance(p.instance).getParamValue(p.index); if( v == null ) throw "Missing param value " + curInstanceValue + "." + p.name; diff --git a/h3d/impl/RendererFX.hx b/h3d/impl/RendererFX.hx index 736f1e1647..8952ff674e 100644 --- a/h3d/impl/RendererFX.hx +++ b/h3d/impl/RendererFX.hx @@ -14,7 +14,6 @@ enum Step { interface RendererFX { public var enabled : Bool; - public function start( r : h3d.scene.Renderer ) : Void; public function begin( r : h3d.scene.Renderer, step : Step ) : Void; public function end( r : h3d.scene.Renderer, step : Step ) : Void; public function dispose() : Void; diff --git a/h3d/impl/SceneProf.hx b/h3d/impl/SceneProf.hx index b31aa5f08e..baebd36b53 100644 --- a/h3d/impl/SceneProf.hx +++ b/h3d/impl/SceneProf.hx @@ -145,25 +145,6 @@ class SceneProf { cat : "__metadata", name : "thread_name", args : { name : "CrBrowserMain" } - }, - { - args: { - data: { - frameTreeNodeId: 0, - frames: [ - { - processId: 0, - url: "http://x" - } - ], - persistentIds: true - } - }, - cat: "disabled-by-default-devtools.timeline", - name: "TracingStartedInBrowser", - pid: 0, - tid: 0, - ts: 0 } ]; @@ -194,16 +175,21 @@ class SceneProf { for( f in frames ) { if( f.samples.length == 0 ) continue; - var ts = timeStamp(f.startTime); - var tend = timeStamp(f.samples[f.samples.length-1].time); json.push({ pid : 0, tid : tid, - ts : ts, - dur : tend - ts, - ph : "X", - cat : "disabled-by-default-devtools.timeline", - name : "RunTask", + ts : timeStamp(f.startTime), + ph : "B", + cat : "devtools.timeline", + name : "FunctionCall", + }); + json.push({ + pid : 0, + tid : tid, + ts : timeStamp(f.samples[f.samples.length-1].time), + ph : "E", + cat : "devtools.timeline", + name : "FunctionCall" }); } for( f in frames ) { diff --git a/h3d/impl/TextureCache.hx b/h3d/impl/TextureCache.hx index a7596a16fc..71d921a093 100644 --- a/h3d/impl/TextureCache.hx +++ b/h3d/impl/TextureCache.hx @@ -1,7 +1,7 @@ package h3d.impl; class TextureCache { - static var checkFlags : Int = -1; + var cache : Array; var position : Int = 0; var defaultDepthBuffer : h3d.mat.Texture; @@ -12,13 +12,6 @@ class TextureCache { var engine = h3d.Engine.getCurrent(); defaultFormat = h3d.mat.Texture.nativeFormat; defaultDepthBuffer = h3d.mat.Texture.getDefaultDepth(); - if ( checkFlags < 0 ) { - var flags = new haxe.EnumFlags(); - var flagsArray : Array = [Cube, MipMapped, ManualMipMapGen, Dynamic, IsArray]; - for ( f in flagsArray ) - flags.set(f); - checkFlags = flags.toInt(); - } } public inline function get( index = 0 ) { @@ -45,23 +38,23 @@ class TextureCache { position = 0; } - inline function matchTargetFlags(t : h3d.mat.Texture, flags : Array) { - var enumFlags = new haxe.EnumFlags(); - if ( flags != null ) { - for ( f in flags ) - enumFlags.set(f); - } - return (t.flags.toInt() & checkFlags) == (enumFlags.toInt() & checkFlags); - } - function lookupTarget( name, width, height, format, flags : Array ) { var t = cache[position]; // look for a suitable candidate for( i in position+1...cache.length ) { var t2 = cache[i]; if( t2 != null && !t2.isDisposed() && t2.width == width && t2.height == height && t2.format == format ) { - if ( !matchTargetFlags(t2, flags) ) - continue; + if ( flags != null ) { + var fitFlags = true; + for ( f in flags ) { + if ( !t2.flags.has(f) ) { + fitFlags = false; + break; + } + } + if ( !fitFlags ) + continue; + } // swap cache[position] = t2; cache[i] = t; @@ -73,8 +66,7 @@ class TextureCache { t.dispose(); t = null; } - if ( flags == null ) - flags = []; + if ( flags == null ) flags = []; if ( !flags.contains(Target) ) flags.push(Target); var newt = new h3d.mat.Texture(width, height, flags, format); @@ -93,8 +85,14 @@ class TextureCache { var alloc = false; if( t == null || t.isDisposed() || t.width != width || t.height != height || t.format != format ) alloc = true; - else - alloc = !matchTargetFlags(t, flags); + if ( !alloc && flags != null ) { + for ( f in flags ) { + if ( !t.flags.has(f) ) { + alloc = true; + break; + } + } + } if ( alloc ) t = lookupTarget(name,width,height,format,flags); t.depthBuffer = defaultDepth ? defaultDepthBuffer : null; diff --git a/h3d/mat/BigTexture.hx b/h3d/mat/BigTexture.hx index 496ad007e5..6b8f756aaf 100644 --- a/h3d/mat/BigTexture.hx +++ b/h3d/mat/BigTexture.hx @@ -230,14 +230,19 @@ class BigTexture { } } else q.loadingColor = true; - t.entry.loadBitmap(function(bmp) { + t.entry.loadBitmap(function(bmp, ?texture:h3d.mat.Texture) { if( o.skip ) return; if( !alphaChannel ) q.loadingColor = false; lastEvent = haxe.Timer.stamp(); pending.remove(o); - var bmp = bmp.toBitmap(); - var pixels = bmp.getPixels(); - bmp.dispose(); + var pixels = if( texture != null) { + texture.capturePixels(); + } else { + var bmp = bmp.toBitmap(); + final p = bmp.getPixels(); + bmp.dispose(); + p; + } uploadPixels(pixels, q.x, q.y, alphaChannel); loadCount--; flush(); diff --git a/h3d/mat/Data.hx b/h3d/mat/Data.hx index 0b1ba18146..b816dfe5b1 100644 --- a/h3d/mat/Data.hx +++ b/h3d/mat/Data.hx @@ -130,14 +130,6 @@ enum TextureFlags { By default, the texture are loaded from images when created. If this flag is enabled, the texture will be loaded from disk when first used. **/ LazyLoading; - /** - Texture can be written in shaders using RWTexture - **/ - Writable; - /** - Tells if it's a 3D texture - **/ - Is3D; } typedef TextureFormat = hxd.PixelFormat; diff --git a/h3d/mat/Material.hx b/h3d/mat/Material.hx index f20fea0527..064b678b0e 100644 --- a/h3d/mat/Material.hx +++ b/h3d/mat/Material.hx @@ -124,7 +124,7 @@ class Material extends BaseMaterial { m.textureShader.killAlpha = textureShader.killAlpha; m.textureShader.killAlphaThreshold = textureShader.killAlphaThreshold; } - m.color = color.clone(); + m.color = color; m.blendMode = blendMode; return m; } @@ -283,7 +283,7 @@ class Material extends BaseMaterial { getPass("shadow").culling = mainPass.culling; } - #if (editor && js) + #if editor override function editProps() { return new js.jquery.JQuery('
diff --git a/h3d/mat/Pass.hx b/h3d/mat/Pass.hx index c9c5473303..bcc8b423f4 100644 --- a/h3d/mat/Pass.hx +++ b/h3d/mat/Pass.hx @@ -18,6 +18,7 @@ class Pass { var selfShadersCache : hxsl.ShaderList; var shaders : hxsl.ShaderList; var nextPass : Pass; + var culled : Bool = false; var rendererFlags : Int = 0; @:bits(flags) public var enableLights : Bool; @@ -45,7 +46,6 @@ class Pass { @:bits(bits) public var blendOp : Operation; @:bits(bits) public var blendAlphaOp : Operation; @:bits(bits) public var wireframe : Bool; - @:bits(bits) public var culled : Bool; public var colorMask : Int; public var layer : Int = 0; @@ -219,7 +219,6 @@ class Pass { public function removeShader(s) { var sl = shaders, prev = null; - var shaderFound = false; while( sl != null ) { if( sl.s == s ) { resetRendererFlags(); @@ -229,8 +228,7 @@ class Pass { shaders = sl.next; else prev.next = sl.next; - shaderFound = true; - break; + return true; } prev = sl; sl = sl.next; @@ -251,7 +249,7 @@ class Pass { prev = sl; sl = sl.next; } - return shaderFound; + return false; } public function removeShaders< T:hxsl.Shader >(t:Class) { @@ -362,8 +360,6 @@ class Pass { prev = s; s = s.next; } - if ( s != parentShaders ) - prev = null; parentShaders = parentPass.shaders; if( prev == null ) shaders = parentShaders; diff --git a/h3d/mat/PbrMaterial.hx b/h3d/mat/PbrMaterial.hx index 30e6ac1ad2..ed5471696f 100644 --- a/h3d/mat/PbrMaterial.hx +++ b/h3d/mat/PbrMaterial.hx @@ -6,9 +6,7 @@ enum abstract PbrMode(String) { var Overlay = "Overlay"; var Decal = "Decal"; var BeforeTonemapping = "BeforeTonemapping"; - var BeforeTonemappingDecal = "BeforeTonemappingDecal"; var AfterTonemapping = "AfterTonemapping"; - var AfterTonemappingDecal = "AfterTonemappingDecal"; var Distortion = "Distortion"; var DecalPass = "DecalPass"; var TerrainPass = "TerrainPass"; @@ -93,8 +91,7 @@ typedef PbrProps = { @:optional var stencilReadMask : Int; @:optional var drawOrder : String; - @:optional var depthPrepass : Bool; - @:optional var flipBackFaceNormal : Bool; + @:optional var useChecker : Bool; } class PbrMaterial extends Material { @@ -239,10 +236,8 @@ class PbrMaterial extends Material { Reflect.deleteField(props,"drawOrder"); if( props.depthWrite == Default ) Reflect.deleteField(props, "depthWrite"); - if ( !props.depthPrepass ) - Reflect.deleteField(props, "depthPrepass"); - if ( !props.flipBackFaceNormal ) - Reflect.deleteField(props, "flipBackFaceNormal"); + if ( !props.useChecker ) + Reflect.deleteField(props, "useChecker"); if ( props.parallaxSteps == h3d.shader.Parallax.MAX_LAYERS || props.parallaxSteps == 0 ) Reflect.deleteField(props, "parallaxSteps"); #end @@ -258,11 +253,8 @@ class PbrMaterial extends Material { // pass name set below (in set_blendMode) case Forward: mainPass.setPassName("forward"); - case BeforeTonemapping, BeforeTonemappingDecal: - if ( props.mode == BeforeTonemappingDecal ) - mainPass.setPassName("beforeTonemappingDecal"); - else - mainPass.setPassName("beforeTonemapping"); + case BeforeTonemapping: + mainPass.setPassName("beforeTonemapping"); var gc = mainPass.getShader(h3d.shader.pbr.GammaCorrect); if( gc == null ) { gc = new h3d.shader.pbr.GammaCorrect(); @@ -272,8 +264,6 @@ class PbrMaterial extends Material { } case AfterTonemapping: mainPass.setPassName("afterTonemapping"); - case AfterTonemappingDecal: - mainPass.setPassName("afterTonemappingDecal"); case Distortion: mainPass.setPassName("distortion"); mainPass.depthWrite = false; @@ -403,41 +393,13 @@ class PbrMaterial extends Material { p = p.nextPass; } - if ( props.depthPrepass ) { - var passName = switch (props.mode) { - case PBR: - "depthPrepass"; - case BeforeTonemapping: - "beforeTonemappingDepthPrepass"; - default: - null; - } - if ( passName != null ) { - mainPass.depthTest = switch ( mainPass.depthTest ) { - case Less: - LessEqual; - case Greater: - GreaterEqual; - default: - mainPass.depthTest; - } - - var p = allocPass(passName); - var killAlpha = new h3d.shader.KillAlpha(); - killAlpha.threshold = 0.5; - p.addShader(killAlpha); - p.depthWrite = true; - p.depthTest = Less; - p.culling = mainPass.culling; - p.setBlendMode(None); - } + if ( texture != null && props.useChecker ) { + mainPass.addShader(new h3d.shader.Checker()); + } else { + var s = mainPass.getShader(h3d.shader.Checker); + if ( s != null ) + mainPass.removeShader(s); } - - var sh = mainPass.getShader(h3d.shader.FlipBackFaceNormal); - if ( props.flipBackFaceNormal && sh == null ) - mainPass.addShader(new h3d.shader.FlipBackFaceNormal()); - else if ( !props.flipBackFaceNormal && sh != null ) - mainPass.removeShader(sh); } function setColorMask() { @@ -546,7 +508,7 @@ class PbrMaterial extends Material { return m; } - #if (editor && js) + #if editor override function editProps() { var props : PbrProps = props; var layers : Array< { name : String, value : Int }> = hide.Ide.inst.currentConfig.get("material.drawOrder", []); @@ -558,9 +520,7 @@ class PbrMaterial extends Material { - - @@ -623,8 +583,7 @@ class PbrMaterial extends Material { ${[for( i in 0...layers.length ) ''].join("")} -
Depth prepass
-
Flip back face normal
+
Checker
'); } diff --git a/h3d/mat/PbrMaterialSetup.hx b/h3d/mat/PbrMaterialSetup.hx index ec810cd545..0a8e3c1ac7 100644 --- a/h3d/mat/PbrMaterialSetup.hx +++ b/h3d/mat/PbrMaterialSetup.hx @@ -25,12 +25,6 @@ class PbrMaterialSetup extends MaterialSetup { return @:privateAccess new PbrMaterial(); } - #if hide - public function createModelLibShader() { - return new hrt.prefab.l3d.ModelLibrary.ModelLibShader(); - } - #end - public static function set() { MaterialSetup.current = new PbrMaterialSetup(); } diff --git a/h3d/mat/Texture.hx b/h3d/mat/Texture.hx index fb7eec8589..eb24492aea 100644 --- a/h3d/mat/Texture.hx +++ b/h3d/mat/Texture.hx @@ -22,7 +22,9 @@ class Texture { var t : h3d.impl.Driver.Texture; var mem : h3d.impl.MemoryManager; + #if track_alloc var allocPos : hxd.impl.AllocPos; + #end public var id(default, null) : Int; public var name(default, null) : String; public var width(default, null) : Int; @@ -40,9 +42,6 @@ class Texture { public var startingMip : Int = 0; public var lodBias : Float = 0.; public var mipLevels(get, never) : Int; - public var depthBias(default, set) : Float = 0.; - public var slopeScaledBias(default, set) : Float = 0.; - public var depthClamp : Bool = false; var customMipLevels : Int; /** @@ -93,10 +92,12 @@ class Texture { for( f in flags ) this.flags.set(f); - #if !noEngine - var engine = h3d.Engine.getCurrent(); - this.mem = engine.mem; - #end + if ( !isDepth() ) { + #if !noEngine + var engine = h3d.Engine.getCurrent(); + this.mem = engine.mem; + #end + } var tw = 1, th = 1; while( tw < w ) tw <<= 1; @@ -113,8 +114,10 @@ class Texture { this.filter = Linear; this.wrap = DEFAULT_WRAP; bits &= 0x7FFF; - this.allocPos = hxd.impl.AllocPos.make(); - if( !this.flags.has(NoAlloc) && width > 0 ) alloc(); + #if track_alloc + this.allocPos = new hxd.impl.AllocPos(); + #end + if( !this.flags.has(NoAlloc) && (!isDepth() || width > 0) ) alloc(); } function get_layerCount() { @@ -122,7 +125,9 @@ class Texture { } public function alloc() { - if ( t == null ) + if ( isDepth() ) + h3d.Engine.getCurrent().mem.allocDepth(this); + else if( t == null ) mem.allocTexture(this); } @@ -188,35 +193,17 @@ class Texture { var str = name; if( name == null ) { str = "Texture_" + id; + #if track_alloc if( allocPos != null ) str += "(" + allocPos.position + ")"; + #end } - if ( flags.has(Is3D) ) - str += "("+width+"x"+height+"x"+layerCount+")"; - else - str += "("+width+"x"+height+")"; - return str; + return str+"("+width+"x"+height+")"; } public function setName(n) { name = n; } - public function set_depthBias(v : Float) { - if ( v != depthBias ) { - depthBias = v; - h3d.Engine.getCurrent().onTextureBiasChanged(this); - } - return depthBias; - } - - public function set_slopeScaledBias(v : Float) { - if ( v != slopeScaledBias ) { - slopeScaledBias = v; - h3d.Engine.getCurrent().onTextureBiasChanged(this); - } - return slopeScaledBias; - } - function set_mipMap(m:MipMap) { bits = (bits & ~(3 << 0)) | (Type.enumIndex(m) << 0); return mipMap = m; @@ -233,8 +220,7 @@ class Texture { } public inline function isDisposed() { - // realloc unsupported on depth buffers. - return t == null && (isDepth() || realloc == null); + return isDepth() ? t == null : t == null && realloc == null; } public function resize(width, height) { @@ -255,7 +241,7 @@ class Texture { alloc(); } - public function clearF( r : Float = 0., g : Float = 0., b : Float = 0., a : Float = 0., layer = -1 ) { + public function clearF( r : Float = 0., g : Float = 0., b : Float = 0., a : Float = 0., layer = -1 ) { alloc(); if( !flags.has(Target) ) throw "Texture should be target"; var engine = h3d.Engine.getCurrent(); @@ -349,31 +335,14 @@ class Texture { } public function dispose() { - if( t != null ) - mem.deleteTexture(this); - } - - public function hasStencil() { - return switch( format ) { - case Depth24Stencil8: true; - default: false; - } - } - - public function isDepth() { - return switch( format ) { - case Depth16, Depth24, Depth24Stencil8, Depth32: true; - default: false; + if( t != null ) { + if ( isDepth() ) + h3d.Engine.getCurrent().mem.deleteDepth(this); + else + mem.deleteTexture(this); } } - /** - This will return the default depth buffer, which is automatically resized to the screen size. - **/ - public static function getDefaultDepth() { - return h3d.Engine.getCurrent().driver.getDefaultDepthBuffer(); - } - /** Downloads the current texture data from the GPU. Beware, this is a very slow operation that shouldn't be done during rendering. @@ -401,7 +370,7 @@ class Texture { /** Creates a 1x1 texture using the RGB color passed as parameter. **/ - public static function fromColor( color : Int, alpha = 1. ) { + public static function fromColor( color : Int, ?alpha = 1. ) { var engine = h3d.Engine.getCurrent(); var aval = Std.int(alpha * 255); if( aval < 0 ) aval = 0 else if( aval > 255 ) aval = 255; @@ -531,4 +500,25 @@ class Texture { t.uploadBitmap(b); b.dispose(); } + + public function hasStencil() { + return switch( format ) { + case Depth24Stencil8: true; + default: false; + } + } + + public function isDepth() { + return switch( format ) { + case Depth16, Depth24, Depth24Stencil8: true; + default: false; + } + } + + /** + This will return the default depth buffer, which is automatically resized to the screen size. + **/ + public static function getDefaultDepth() { + return h3d.Engine.getCurrent().driver.getDefaultDepthBuffer(); + } } \ No newline at end of file diff --git a/h3d/pass/Border.hx b/h3d/pass/Border.hx index 31be610969..81ff8381d4 100644 --- a/h3d/pass/Border.hx +++ b/h3d/pass/Border.hx @@ -14,19 +14,9 @@ private class BorderShader extends h3d.shader.ScreenShader { class Border extends ScreenFx { - var width(default, null) : Int; - var height(default, null) : Int; - var size(default, null) : Int; - public function new( width : Int, height : Int, size : Int = 1 ) { super(new BorderShader()); - this.width = width; - this.height = height; - this.size = size; - shader.color.set(1,1,1,1); - } - function createPrimitive() { var bbuf = new hxd.FloatBuffer(); inline function add(x, y) { bbuf.push((x / width) * 2 - 1); @@ -53,18 +43,12 @@ class Border extends ScreenFx { add(width, height); this.primitive = new h3d.prim.RawPrimitive({ vbuf : bbuf, format : hxd.BufferFormat.make([{ name : "position", type : DVec2 }]) }, true); - } - - override function render() { - if (primitive == null) - createPrimitive(); - super.render(); + shader.color.set(1,1,1,1); } override function dispose() { - if (primitive != null) - this.primitive.dispose(); super.dispose(); + this.primitive.dispose(); } } \ No newline at end of file diff --git a/h3d/pass/CascadeShadowMap.hx b/h3d/pass/CascadeShadowMap.hx index 56a4d5c526..ec0aea5f4d 100644 --- a/h3d/pass/CascadeShadowMap.hx +++ b/h3d/pass/CascadeShadowMap.hx @@ -1,42 +1,27 @@ package h3d.pass; typedef CascadeParams = { - var depthBias : Float; - var slopeBias : Float; -} - -typedef CascadeCamera = { - var viewProj : h3d.Matrix; - var scale : h3d.Vector4; - var offset : h3d.Vector4; - var orthoBounds : h3d.col.Bounds; + var bias : Float; } class CascadeShadowMap extends DirShadowMap { var cshader : h3d.shader.CascadeShadow; - var lightCameras : Array = []; + var lightCameras : Array = []; var currentCascadeIndex = 0; - var tmpCorners : Array = [for (i in 0...8) new h3d.Vector()]; - var tmpView = new h3d.Matrix(); - var tmpProj = new h3d.Matrix(); - var tmpFrustum = new h3d.col.Frustum(); - public var cascadeViewProj = new h3d.Matrix(); public var params : Array = []; public var pow : Float = 1.0; - // minimum count of pixels for an object to be drawn in cascade - public var minPixelSize : Int = 1; public var firstCascadeSize : Float = 10.0; public var castingMaxDist : Float = 0.0; - public var transitionFraction : Float = 0.15; public var cascade(default, set) = 1; - public var highPrecision : Bool = false; public function set_cascade(v) { cascade = v; lightCameras = []; - for ( i in 0...cascade ) - lightCameras.push({ viewProj : new h3d.Matrix(), scale : new h3d.Vector4(), offset : new h3d.Vector4(), orthoBounds : new h3d.col.Bounds() }); + for ( i in 0...cascade ) { + lightCameras.push(new h3d.Camera()); + lightCameras[i].orthoBounds = new h3d.col.Bounds(); + } return cascade; } public var debugShader : Bool = false; @@ -57,180 +42,69 @@ class CascadeShadowMap extends DirShadowMap { return cshader.cascadeShadowMaps; } - function computeNearFar( i : Int, previousFar : Float ) { + function computeNearFar( i : Int ) { + var min = minDist < 0.0 ? ctx.camera.zNear : minDist; var max = maxDist < 0.0 ? ctx.camera.zFar : maxDist; - var step = max - firstCascadeSize; - var near = ( i == 0 ) ? 0.0 : previousFar - previousFar * transitionFraction; - var far = ( i == 0 ) ? firstCascadeSize : firstCascadeSize + hxd.Math.pow(i / (cascade - 1), pow) * step; - - // Not related to scale but let's pack it here to save memory - lightCameras[i].scale.w = far; - + if ( i == 0 ) { + return {near : min, far : min + firstCascadeSize}; + } + var step = (max - min - firstCascadeSize) / (cascade - 1); + var near = min + firstCascadeSize + hxd.Math.pow((i - 1) / (cascade - 1), pow) * step; + var far = min + firstCascadeSize + hxd.Math.pow(i / (cascade - 1), pow) * step; return {near : near, far : far}; } - public function calcCascadeMatrices() { - var invCamera = ctx.camera.getInverseView(); - var invG = hxd.Math.tan(hxd.Math.degToRad( ctx.camera.fovY ) / 2.0); - var sInvG = ctx.camera.screenRatio * invG; - var invLight = lightCamera.getInverseView(); - var camToLight = invCamera.multiplied( lightCamera.mcam ); + public function updateCascadeBounds( camera : h3d.Camera ) { + var bounds = camera.orthoBounds; - inline function computeLightPos( bounds : h3d.col.Bounds, d : Float ) { - var t = d / size; - var t2 = t * 2.0; - return new h3d.Vector( - hxd.Math.floor((bounds.xMax + bounds.xMin) / t2) * t, - hxd.Math.floor((bounds.yMax + bounds.yMin) / t2) * t, - bounds.zMin - ); + var shadowNear = hxd.Math.POSITIVE_INFINITY; + var shadowFar = hxd.Math.NEGATIVE_INFINITY; + var corners = lightCamera.getFrustumCorners(); + for ( corner in corners ) { + corner.transform(ctx.camera.mcam); + shadowNear = hxd.Math.min(shadowNear, corner.z); + shadowFar = hxd.Math.max(shadowFar, corner.z); } - - inline function computeCorner(x : Float, y : Float, d : Float, pt : h3d.Vector) { - pt.set( x * (d * sInvG), y * ( d * invG ), d ); - } - - inline function computeCorners(d, i) { - computeCorner(-1.0, -1.0, d, tmpCorners[i]); - computeCorner(-1.0, 1.0, d, tmpCorners[i + 1]); - computeCorner( 1.0, -1.0, d, tmpCorners[i + 2]); - computeCorner( 1.0, 1.0, d, tmpCorners[i + 3]); - } - - inline function computeBounds( bounds : h3d.col.Bounds ) { - bounds.empty(); - for ( pt in tmpCorners ) { - pt.transform( camToLight ); - bounds.addPos(pt.x, pt.y, pt.z); + for ( i in 0...cascade - 1 ) { + var cascadeBounds = new h3d.col.Bounds(); + function addCorner(x,y,d) { + var pt = ctx.camera.unproject(x,y,ctx.camera.distanceToDepth(d)).toPoint(); + pt.transform(camera.mcam); + cascadeBounds.addPos(pt.x, pt.y, pt.z); + } + function addCorners(d) { + addCorner(-1,-1,d); + addCorner(-1,1,d); + addCorner(1,-1,d); + addCorner(1,1,d); } - } - - var nearFar = computeNearFar(0, 0); - computeCorners(nearFar.near, 0); - computeCorners(nearFar.far, 4); - - var d0 = hxd.Math.ceil( hxd.Math.max( tmpCorners[0].distance(tmpCorners[7]), tmpCorners[4].distance(tmpCorners[7]) ) ); - var cascadeBounds0 = lightCameras[0].orthoBounds; - computeBounds( cascadeBounds0 ); - var lightPos0 = computeLightPos(cascadeBounds0, d0); - - var view = tmpView; - view._11 = invLight._11; - view._12 = invLight._21; - view._13 = invLight._31; - view._14 = 0; - view._21 = invLight._12; - view._22 = invLight._22; - view._23 = invLight._32; - view._24 = 0; - view._31 = invLight._13; - view._32 = invLight._23; - view._33 = invLight._33; - view._34 = 0; - view._41 = -lightPos0.x; - view._42 = -lightPos0.y; - view._43 = -lightPos0.z; - view._44 = 1; - - var invD0 = 1 / d0; - var zDist0 = cascadeBounds0.zMax - cascadeBounds0.zMin; - - inline function getBias( i : Int ) { - var depthBiasFactor = (params[i] != null ) ? params[i].depthBias : 1.0; - return 0.00000190734 * depthBiasFactor; // 2^-19 depth offset; - } - - var proj = tmpProj; - proj.zero(); - proj._11 = invD0; - proj._22 = invD0; - proj._33 = 1.0 / (zDist0); - proj._41 = 0.5; - proj._42 = 0.5; - proj._44 = 1.0; - - cascadeViewProj.multiply(view, proj); - - var invD02 = 2.0 * invD0; - proj._11 = invD02; - proj._22 = invD02; - proj._41 = 0.0; - proj._42 = 0.0; - proj._43 = getBias(0); - - lightCameras[0].viewProj.multiply(view, proj); - - for ( i in 1...cascade ) { - nearFar = computeNearFar(i, nearFar.far); - computeCorners(nearFar.near, 0); - computeCorners(nearFar.far, 4); - - var d = hxd.Math.ceil( hxd.Math.max( tmpCorners[0].distance(tmpCorners[7]), tmpCorners[4].distance(tmpCorners[7]) ) ); - var cascadeBounds = lightCameras[i].orthoBounds; - computeBounds( cascadeBounds ); - var lightPos = computeLightPos(cascadeBounds, d); - - var invD = 1.0 / d; - var d0InvD = d0 * invD; - var zDist = ( cascadeBounds.zMax - cascadeBounds.zMin ); - var invZDist = 1.0 / zDist; - var halfMinusD0Inv2D = 0.5 - ( d0 / ( 2.0 * d ) ); - - lightCameras[i].scale.x = d0InvD; - lightCameras[i].scale.y = d0InvD; - lightCameras[i].scale.z = zDist0 * invZDist; - - lightCameras[i].offset.x = ( lightPos0.x - lightPos.x ) * invD + halfMinusD0Inv2D; - lightCameras[i].offset.y = ( lightPos0.y - lightPos.y ) * invD + halfMinusD0Inv2D; - lightCameras[i].offset.z = ( lightPos0.z - lightPos.z ) * invZDist; - - var view = tmpView; - view._41 = -lightPos.x; - view._42 = -lightPos.y; - view._43 = -lightPos.z; - - var proj = tmpProj; - proj.zero(); - var invD2 = 2.0 * invD; - - proj._11 = invD2; - proj._22 = invD2; - proj._33 = invZDist; - proj._43 = getBias(i); - proj._44 = 1.0; - lightCameras[i].viewProj.multiply(view, proj); + var nearFar = computeNearFar(i); + addCorners(nearFar.near); + addCorners(nearFar.far); + // Increasing z range has no effect on resolution, only on depth precision. + cascadeBounds.zMax = lightCamera.orthoBounds.zMax; + cascadeBounds.zMin = lightCamera.orthoBounds.zMin; + lightCameras[i].orthoBounds = cascadeBounds; } + lightCameras[cascade - 1].orthoBounds = lightCamera.orthoBounds.clone(); } - public function getCascadeProj(i:Int) { - var i = hxd.Math.imin(i, lightCameras.length - 1); - return lightCameras[i].viewProj; - } - - public function getCascadeOffset(i:Int) { - var i = hxd.Math.imin(i, lightCameras.length - 1); - return lightCameras[i].offset; - } - - public function getCascadeScale(i:Int) { - var i = hxd.Math.imin(i, lightCameras.length - 1); - return lightCameras[i].scale; + function getCascadeProj(i:Int) { + return lightCameras[i].m; } function syncCascadeShader(textures : Array) { cshader.DEBUG = debugShader; - cshader.cascadeViewProj = cascadeViewProj; - cshader.cascadeTransitionFraction = transitionFraction; for ( i in 0...cascade ) { - cshader.cascadeShadowMaps[i] = textures[i]; - cshader.cascadeOffsets[i] = lightCameras[i].offset; - cshader.cascadeScales[i] = lightCameras[i].scale; + var c = cascade - 1 - i; + cshader.cascadeShadowMaps[c] = textures[i]; + cshader.cascadeProjs[c] = lightCameras[i].m; if ( debugShader ) - cshader.cascadeDebugs[i] = h3d.Vector4.fromColor(debugColors[i]); + cshader.cascadeDebugs[c] = h3d.Vector4.fromColor(debugColors[i]); + cshader.cascadeBias[c] = params[c] != null ? params[c].bias : 0.001; } cshader.CASCADE_COUNT = cascade; - cshader.BLEND = transitionFraction > 0.0; cshader.shadowBias = bias; cshader.shadowPower = power; cshader.shadowProj = getShadowProj(); @@ -250,17 +124,6 @@ class CascadeShadowMap extends DirShadowMap { return getCascadeProj(currentCascadeIndex); } - inline function cullPassesSize( passes : h3d.pass.PassList, frustum : h3d.col.Frustum, minSize : Float ) { - passes.filter(function(p) { - var mb = Std.downcast(p.obj, h3d.scene.MeshBatch); - var col = p.obj.cullingCollider; - return if( mb != null && @:privateAccess mb.instanced.primitive.getBounds().dimension() < minSize ) false; - else if( col == null ) true; - else if ( col.dimension() < minSize ) false; - else col.inFrustum(frustum); - }); - } - override function draw( passes, ?sort ) { if( !enabled ) return; @@ -268,62 +131,73 @@ class CascadeShadowMap extends DirShadowMap { if( !filterPasses(passes) ) return; - var slight = light == null ? ctx.lightSystem.shadowLight : light; - var ldir = slight == null ? null : @:privateAccess slight.getShadowDirection(); - if( ldir == null ) - lightCamera.target.set(0, 0, -1); - else { - lightCamera.target.set(ldir.x, ldir.y, ldir.z); - lightCamera.target.normalize(); - } - lightCamera.pos.set(); - lightCamera.orthoBounds.empty(); - lightCamera.update(); + if( mode != Mixed || ctx.computingStatic ) { + var ct = ctx.camera.target; + var slight = light == null ? ctx.lightSystem.shadowLight : light; + var ldir = slight == null ? null : @:privateAccess slight.getShadowDirection(); + if( ldir == null ) + lightCamera.target.set(0, 0, -1); + else { + lightCamera.target.set(ldir.x, ldir.y, ldir.z); + lightCamera.target.normalize(); + } + lightCamera.target.x += ct.x; + lightCamera.target.y += ct.y; + lightCamera.target.z += ct.z; + lightCamera.pos.load(ct); + lightCamera.update(); + for ( i in 0...lightCameras.length) { + if( ldir == null ) + lightCameras[i].target.set(0, 0, -1); + else { + lightCameras[i].target.set(ldir.x, ldir.y, ldir.z); + lightCameras[i].target.normalize(); + } + lightCameras[i].target.x += ct.x; + lightCameras[i].target.y += ct.y; + lightCameras[i].target.z += ct.z; + lightCameras[i].pos.load(ct); + lightCameras[i].update(); + } - calcCascadeMatrices(); + lightCamera.orthoBounds.empty(); + for ( lC in lightCameras ) lC.orthoBounds.empty(); + if( !passes.isEmpty() ) calcShadowBounds(lightCamera); + var pt = ctx.camera.pos.clone(); + pt.transform(lightCamera.mcam); + lightCamera.orthoBounds.zMax = pt.z + (castingMaxDist > 0.0 ? castingMaxDist : maxDist < 0.0 ? ctx.camera.zFar : maxDist); + lightCamera.orthoBounds.zMin = pt.z - (castingMaxDist > 0.0 ? castingMaxDist : maxDist < 0.0 ? ctx.camera.zFar : maxDist); + lightCamera.update(); + } - for (i in 0...cascade) - lightCamera.orthoBounds.add(lightCameras[i].orthoBounds); - var zDist = (castingMaxDist > 0.0 ? castingMaxDist : maxDist < 0.0 ? ctx.camera.zFar : maxDist); - lightCamera.orthoBounds.zMax += zDist; - lightCamera.orthoBounds.zMin -= zDist; - lightCamera.update(); + cullPasses(passes,function(col) return col.inFrustum(lightCamera.frustum)); - cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum)); - var p = passes.save(); + updateCascadeBounds(lightCamera); + for ( lC in lightCameras ) lC.update(); - var prevCheckNearFar = lightCamera.frustum.checkNearFar; - lightCamera.frustum.checkNearFar = false; var textures = []; for (i in 0...cascade) { - currentCascadeIndex = i; - - var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, #if js Depth24Stencil8 #else highPrecision ? Depth32 : Depth16 #end ); - - // Bilinear depth only make sense if we use sample compare to get weighted shadow occlusion which we doesn't support yet. - texture.filter = Nearest; - - var param = params[i]; - texture.slopeScaledBias = (param != null) ? param.slopeBias : 0; - texture.depthClamp = true; + #if js + var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, format); + if( depth == null || depth.width != size || depth.height != size || depth.isDisposed() ) { + if( depth != null ) depth.dispose(); + depth = new h3d.mat.Texture(size, size, Depth24Stencil8); + depth.name = "dirShadowMapDepth"; + } + texture.depthBuffer = depth; + #else + var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, Depth24Stencil8); + #end - var lc = lightCameras[i]; - var dimension = Math.max(lc.orthoBounds.xMax - lc.orthoBounds.xMin, lc.orthoBounds.yMax - lc.orthoBounds.yMin); - dimension = ( dimension * hxd.Math.clamp(minPixelSize, 0, size) ) / size; - // first cascade draw all objects - if ( i == 0 ) - dimension = 0.0; - lightCamera.orthoBounds = lc.orthoBounds; - lightCamera.update(); - if ( dimension > 0.0 ) - cullPassesSize(passes, lightCamera.frustum, dimension); - else - cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum)); - textures[i] = processShadowMap( passes, texture, sort); + currentCascadeIndex = i; + var p = passes.save(); + cullPasses(passes,function(col) return col.inFrustum(lightCameras[i].frustum)); + texture = processShadowMap( passes, texture, sort); + textures.push(texture); passes.load(p); + } syncCascadeShader(textures); - lightCamera.frustum.checkNearFar = prevCheckNearFar; #if editor drawDebug(); @@ -337,7 +211,7 @@ class CascadeShadowMap extends DirShadowMap { return; for ( i in 0...cascade ) { - drawBounds(lightCameras[i].viewProj.getInverse(), debugColors[i]); + drawBounds(lightCameras[i], debugColors[i]); } } } \ No newline at end of file diff --git a/h3d/pass/CubeShadowMap.hx b/h3d/pass/CubeShadowMap.hx index 2ddab90283..1c45708b8e 100644 --- a/h3d/pass/CubeShadowMap.hx +++ b/h3d/pass/CubeShadowMap.hx @@ -112,9 +112,11 @@ class CubeShadowMap extends Shadows { var tmpTex : h3d.mat.Texture; override function createDefaultShadowMap() { - if( tmpTex != null) - return tmpTex; - tmpTex = new h3d.mat.Texture(1,1, [Target,Cube], format); + if( tmpTex != null) return tmpTex; + if ( mode == Mixed ) + tmpTex = new h3d.mat.Texture(size,size, [Target,Cube], format); + else + tmpTex = new h3d.mat.Texture(1,1, [Target,Cube], format); tmpTex.name = "defaultCubeShadowMap"; tmpTex.realloc = function() clear(tmpTex); clear(tmpTex); @@ -148,7 +150,7 @@ class CubeShadowMap extends Shadows { return; if( passes.isEmpty() ) { - syncEarlyExit(); + syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture); return; } @@ -156,7 +158,7 @@ class CubeShadowMap extends Shadows { cullPasses(passes,function(col) return cull(lightCollider, col)); if( passes.isEmpty() ) { - syncEarlyExit(); + syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture); return; } @@ -221,19 +223,21 @@ class CubeShadowMap extends Shadows { } function merge( dynamicTex : h3d.mat.Texture ) : h3d.mat.Texture{ - if ( staticTexture == null || staticTexture.isDisposed() ) - return dynamicTex; - if ( staticTexture.width != dynamicTex.width ) - throw "Static shadow map doesnt match dynamic shadow map"; - var merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, [Cube]); - for( i in 0 ... 6 ) { - if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) continue; - mergePass.shader.texA = dynamicTex; - mergePass.shader.texB = staticTexture; - mergePass.shader.mat = cubeDir[i]; - ctx.engine.pushTarget(merge, i); - mergePass.render(); - ctx.engine.popTarget(); + var validBakedTexture = (staticTexture != null && staticTexture.width == dynamicTex.width); + var merge : h3d.mat.Texture = null; + if( mode == Mixed && !ctx.computingStatic && validBakedTexture) + merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, [Cube]); + + if( mode == Mixed && !ctx.computingStatic && merge != null ) { + for( i in 0 ... 6 ) { + if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) continue; + mergePass.shader.texA = dynamicTex; + mergePass.shader.texB = staticTexture; + mergePass.shader.mat = cubeDir[i]; + ctx.engine.pushTarget(merge, i); + mergePass.render(); + ctx.engine.popTarget(); + } } return merge; } diff --git a/h3d/pass/DirShadowMap.hx b/h3d/pass/DirShadowMap.hx index 211863231d..afeaee2500 100644 --- a/h3d/pass/DirShadowMap.hx +++ b/h3d/pass/DirShadowMap.hx @@ -280,9 +280,7 @@ class DirShadowMap extends Shadows { ctx.engine.popTarget(); - if( mode == Mixed && !ctx.computingStatic && staticTexture != null && !staticTexture.isDisposed() ) { - if ( staticTexture.width != tex.width ) - throw "Static shadow map doesnt match dynamic shadow map"; + if( mode == Mixed && !ctx.computingStatic ) { var merge = ctx.textures.allocTarget("mergedDirShadowMap", size, size, false, format); mergePass.shader.texA = tex; mergePass.shader.texB = staticTexture; @@ -390,48 +388,36 @@ class DirShadowMap extends Shadows { return; g.clear(); - drawBounds(lightCamera.getInverseViewProj(), 0xffffff); + drawBounds(lightCamera, 0xffffff); } - function drawBounds(invViewModel : h3d.Matrix, color : Int) { + function drawBounds(camera : h3d.Camera, color : Int) { - inline function unproject(screenX, screenY, camZ) { - var p = new h3d.Vector(screenX, screenY, camZ); - p.project(invViewModel); - return p; - } - - var nearPlaneCorner = [unproject(-1, 1, 0), unproject(1, 1, 0), unproject(1, -1, 0), unproject(-1, -1, 0)]; - var farPlaneCorner = [unproject(-1, 1, 1), unproject(1, 1, 1), unproject(1, -1, 1), unproject(-1, -1, 1)]; + var nearPlaneCorner = [camera.unproject(-1, 1, 0), camera.unproject(1, 1, 0), camera.unproject(1, -1, 0), camera.unproject(-1, -1, 0)]; + var farPlaneCorner = [camera.unproject(-1, 1, 1), camera.unproject(1, 1, 1), camera.unproject(1, -1, 1), camera.unproject(-1, -1, 1)]; g.lineStyle(1, color); // Near Plane var last = nearPlaneCorner[nearPlaneCorner.length - 1]; - inline function moveTo(x : Float, y : Float, z : Float) { - g.moveTo(x - ctx.scene.x, y - ctx.scene.y, z - ctx.scene.z); - } - inline function lineTo(x : Float, y : Float, z : Float) { - g.lineTo(x - ctx.scene.x, y - ctx.scene.y, z - ctx.scene.z); - } - moveTo(last.x,last.y,last.z); + g.moveTo(last.x,last.y,last.z); for( fc in nearPlaneCorner ) { - lineTo(fc.x, fc.y, fc.z); + g.lineTo(fc.x, fc.y, fc.z); } // Far Plane var last = farPlaneCorner[farPlaneCorner.length - 1]; - moveTo(last.x,last.y,last.z); + g.moveTo(last.x,last.y,last.z); for( fc in farPlaneCorner ) { - lineTo(fc.x, fc.y, fc.z); + g.lineTo(fc.x, fc.y, fc.z); } // Connections for( i in 0 ... 4 ) { var np = nearPlaneCorner[i]; var fp = farPlaneCorner[i]; - moveTo(np.x, np.y, np.z); - lineTo(fp.x, fp.y, fp.z); + g.moveTo(np.x, np.y, np.z); + g.lineTo(fp.x, fp.y, fp.z); } } } \ No newline at end of file diff --git a/h3d/pass/Output.hx b/h3d/pass/Output.hx index 9960571284..ff89539413 100644 --- a/h3d/pass/Output.hx +++ b/h3d/pass/Output.hx @@ -64,7 +64,6 @@ class Output { function drawObject( p : h3d.pass.PassObject ) { ctx.drawPass = p; ctx.engine.selectMaterial(p.pass); - p.obj.drawn = true; @:privateAccess p.obj.draw(ctx); } @@ -84,7 +83,6 @@ class Output { var buf = ctx.shaderBuffers, prevShader = null; for( p in passes ) { #if sceneprof h3d.impl.SceneProf.mark(p.obj); #end - ctx.globalPreviousModelView = p.obj.prevAbsPos ?? p.obj.absPos; ctx.globalModelView = p.obj.absPos; if( p.shader.hasGlobal(ctx.globalModelViewInverse_id.toInt()) ) ctx.globalModelViewInverse = p.obj.getInvPos(); diff --git a/h3d/pass/Shadows.hx b/h3d/pass/Shadows.hx index 5b5d2009c9..b1e4fa66bb 100644 --- a/h3d/pass/Shadows.hx +++ b/h3d/pass/Shadows.hx @@ -104,10 +104,6 @@ class Shadows extends Output { return tex; } - function syncEarlyExit() { - syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture); - } - function syncShader( texture : h3d.mat.Texture ) { } @@ -119,10 +115,14 @@ class Shadows extends Output { case Dynamic: return true; case Mixed: + if( staticTexture == null || staticTexture.isDisposed() ) + staticTexture = createDefaultShadowMap(); passes.filter(function(p) return p.pass.isStatic == false); return true; case Static: - syncEarlyExit(); + if( staticTexture == null || staticTexture.isDisposed() ) + staticTexture = createDefaultShadowMap(); + syncShader(staticTexture); return false; } } diff --git a/h3d/pass/SpotShadowMap.hx b/h3d/pass/SpotShadowMap.hx index 2a8d0f5b3c..bfe1ade396 100644 --- a/h3d/pass/SpotShadowMap.hx +++ b/h3d/pass/SpotShadowMap.hx @@ -4,6 +4,7 @@ class SpotShadowMap extends Shadows { var depth : h3d.mat.Texture; var sshader : h3d.shader.SpotShadow; + var border : Border; var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader()); public function new( light : h3d.scene.Light ) { @@ -13,6 +14,7 @@ class SpotShadowMap extends Shadows { lightCamera.screenRatio = 1.0; lightCamera.zNear = 0.01; shader = sshader = new h3d.shader.SpotShadow(); + border = new Border(size, size); } override function set_mode(m:Shadows.RenderMode) { @@ -25,6 +27,14 @@ class SpotShadowMap extends Shadows { return enabled = b; } + override function set_size(s) { + if( border != null && size != s ) { + border.dispose(); + border = new Border(s, s); + } + return super.set_size(s); + } + override function isUsingWorldDist(){ return false; } @@ -32,6 +42,7 @@ class SpotShadowMap extends Shadows { override function dispose() { super.dispose(); if( depth != null ) depth.dispose(); + border.dispose(); } public override function getShadowTex() { @@ -113,13 +124,6 @@ class SpotShadowMap extends Shadows { updateCamera(); cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum)); - var prevFar = @:privateAccess ctx.cameraFar; - var prevPos = @:privateAccess ctx.cameraPos; - var prevViewProj = @:privateAccess ctx.cameraViewProj; - @:privateAccess ctx.cameraViewProj = getShadowProj(); - @:privateAccess ctx.cameraFar = lightCamera.zFar; - @:privateAccess ctx.cameraPos = lightCamera.pos; - var texture = ctx.computingStatic ? createStaticTexture() : ctx.textures.allocTarget("spotShadowMap", size, size, false, format); if( depth == null || depth.width != texture.width || depth.height != texture.height || depth.isDisposed() ) { if( depth != null ) depth.dispose(); @@ -130,25 +134,25 @@ class SpotShadowMap extends Shadows { ctx.engine.pushTarget(texture); ctx.engine.clear(0xFFFFFF, 1); super.draw(passes, sort); + if( border != null ) border.render(); ctx.engine.popTarget(); if( blur.radius > 0 ) blur.apply(ctx, texture); - if( mode == Mixed && !ctx.computingStatic && staticTexture != null && !staticTexture.isDisposed() ) { - if ( staticTexture.width != texture.width ) - throw "Static shadow map doesnt match dynamic shadow map"; + var validBakedTexture = (staticTexture != null && staticTexture.width == texture.width); + if( mode == Mixed && !ctx.computingStatic && validBakedTexture ) { var merge = ctx.textures.allocTarget("mergedSpotShadowMap", size, size, false, format); + var prev = @:privateAccess ctx.cameraViewProj; + @:privateAccess ctx.cameraViewProj = getShadowProj(); mergePass.shader.texA = texture; mergePass.shader.texB = staticTexture; ctx.engine.pushTarget(merge); mergePass.render(); ctx.engine.popTarget(); texture = merge; + @:privateAccess ctx.cameraViewProj = prev; } - @:privateAccess ctx.cameraFar = prevFar; - @:privateAccess ctx.cameraPos = prevPos; - @:privateAccess ctx.cameraViewProj = prevViewProj; syncShader(texture); } diff --git a/h3d/prim/BigPrimitive.hx b/h3d/prim/BigPrimitive.hx index 4b537c68a7..1f21a50f84 100644 --- a/h3d/prim/BigPrimitive.hx +++ b/h3d/prim/BigPrimitive.hx @@ -16,7 +16,9 @@ class BigPrimitive extends Primitive { var idxPos : Int = 0; var startIndex : Int = 0; var flushing : Bool; + #if track_alloc var allocPos : hxd.impl.AllocPos; + #end var allocator : hxd.impl.Allocator; @@ -33,7 +35,9 @@ class BigPrimitive extends Primitive { bounds = new h3d.col.Bounds(); this.allocator = alloc; if( format.stride < 3 ) throw "Minimum stride = 3"; - allocPos = hxd.impl.AllocPos.make(); + #if track_alloc + allocPos = new hxd.impl.AllocPos(); + #end } /** @@ -132,9 +136,10 @@ class BigPrimitive extends Primitive { allIndexes.push(idx); flushing = false; + #if track_alloc @:privateAccess b.allocPos = allocPos; - var idx : h3d.Buffer = idx; @:privateAccess idx.allocPos = allocPos; + #end } if( PREV_BUFFER == null || PREV_BUFFER.length < tmpBuf.length ) PREV_BUFFER = tmpBuf; diff --git a/h3d/prim/HMDModel.hx b/h3d/prim/HMDModel.hx index 1ac02ea0e2..81eb6f599a 100644 --- a/h3d/prim/HMDModel.hx +++ b/h3d/prim/HMDModel.hx @@ -2,9 +2,7 @@ package h3d.prim; class HMDModel extends MeshPrimitive { - var data (get, never) : hxd.fmt.hmd.Data.Geometry; - function get_data() { return lods[0]; } - var lods : Array; + var data : hxd.fmt.hmd.Data.Geometry; var dataPosition : Int; var indexCount : Int; var indexesTriPos : Array; @@ -12,22 +10,11 @@ class HMDModel extends MeshPrimitive { var curMaterial : Int; var collider : h3d.col.Collider; var normalsRecomputed : String; - var blendshape : Blendshape; - var lodConfig : Array = null; - public static var lodExportKeyword : String = "LOD"; - - public function new( data : hxd.fmt.hmd.Data.Geometry, dataPos, lib, lods : Array = null ) { - this.lods = [data]; - if (lods != null) { - for (lod in lods) - this.lods.push(lib.header.geometries[lod.geometry]); - } + public function new(data, dataPos, lib) { + this.data = data; this.dataPosition = dataPos; this.lib = lib; - - if (lib.header.shapes != null && lib.header.shapes.length > 0) - this.blendshape = new Blendshape(this); } override function hasInput( name : String ) { @@ -46,8 +33,8 @@ class HMDModel extends MeshPrimitive { return data.bounds; } - override function selectMaterial( material : Int, lod : Int ) { - curMaterial = material + lod * data.indexCounts.length; + override function selectMaterial( i : Int ) { + curMaterial = i; } override function getMaterialIndexes(material:Int):{count:Int, start:Int} { @@ -64,67 +51,26 @@ class HMDModel extends MeshPrimitive { override function alloc(engine:h3d.Engine) { dispose(); + buffer = new h3d.Buffer(data.vertexCount, data.vertexFormat); + + var entry = lib.resource.entry; + + var size = data.vertexCount * data.vertexFormat.strideBytes; + var bytes = entry.fetchBytes(dataPosition + data.vertexPosition, size); + buffer.uploadBytes(bytes, 0, data.vertexCount); - var vertexCount : Int = 0; - var vertexFormat : hxd.BufferFormat = data.vertexFormat; indexCount = 0; indexesTriPos = []; - for ( lod in lods ) { - vertexCount += lod.vertexCount; - for( n in lod.indexCounts ) { - indexesTriPos.push(Std.int(indexCount/3)); - indexCount += n; - } + for( n in data.indexCounts ) { + indexesTriPos.push(Std.int(indexCount/3)); + indexCount += n; } - - buffer = new h3d.Buffer(vertexCount, vertexFormat); - - var is32 : Bool = vertexCount > 0x10000; + var is32 = data.vertexCount > 0x10000; indexes = new h3d.Indexes(indexCount, is32); - var indexStride : Int = (is32 ? 4 : 2); - - var entry = lib.resource.entry; - var curVertexCount : Int = 0; - var curIndexCount : Int = 0; - - for ( i => lod in lods ) { - if (lod.vertexFormat != vertexFormat) { - var error = 'LOD${i} has a different vertex format, has '; - for ( input in lod.vertexFormat.getInputs() ) - error += input.name + " "; - error += ", wants "; - for ( input in vertexFormat.getInputs() ) - error += input.name + " "; - throw error; - } - var size = lod.vertexCount * vertexFormat.strideBytes; - var bytes = entry.fetchBytes(dataPosition + lod.vertexPosition, size); - engine.driver.uploadBufferBytes(buffer, curVertexCount, lod.vertexCount, bytes, 0); - - var indexCount = lod.indexCount; - var lodIs32 = ( lod.vertexCount > 0x10000 ); - size = ( lodIs32 ? 4 : 2 ) * indexCount; - - var inBytes = entry.fetchBytes(dataPosition + lod.indexPosition, size); - var outBytes = ( is32 != lodIs32 ) ? haxe.io.Bytes.alloc( indexCount * indexStride ) : inBytes; - if (is32) - for ( i in 0...indexCount ) - if ( lodIs32 ) - outBytes.setInt32(i << 2, inBytes.getInt32(i << 2) + curVertexCount); - else - outBytes.setInt32(i << 2, inBytes.getUInt16(i << 1) + curVertexCount); - else - for ( i in 0...indexCount ) - if ( lodIs32 ) - outBytes.setUInt16(i << 1, inBytes.getInt32(i << 2) + curVertexCount); - else - outBytes.setUInt16(i << 1, inBytes.getUInt16(i << 1) + curVertexCount); - engine.driver.uploadBufferBytes(indexes, curIndexCount, indexCount, outBytes, 0); - - curVertexCount += lod.vertexCount; - curIndexCount += indexCount; - } + var size = (is32 ? 4 : 2) * indexCount; + var bytes = entry.fetchBytes(dataPosition + data.indexPosition, size); + indexes.uploadBytes(bytes, 0, indexCount); if( normalsRecomputed != null ) { var name = normalsRecomputed; @@ -142,56 +88,54 @@ class HMDModel extends MeshPrimitive { if( name == null ) name = "normal"; - var v = new hxd.FloatBuffer(); - for ( lod in lods ) { - var pos = lib.getBuffers(lod, hxd.BufferFormat.POS3D); - var ids = new Array(); - var pts : Array = []; - var mpts = new Map(); - for( i in 0...lod.vertexCount ) { - var added = false; - var px = pos.vertexes[i * 3]; - var py = pos.vertexes[i * 3 + 1]; - var pz = pos.vertexes[i * 3 + 2]; - var pid = Std.int((px + py + pz) * 10.01); - var arr = mpts.get(pid); - if( arr == null ) { - arr = []; - mpts.set(pid, arr); - } else { - for( idx in arr ) { - var p = pts[idx]; - if( p.x == px && p.y == py && p.z == pz ) { - ids.push(idx); - added = true; - break; - } + var pos = lib.getBuffers(data, hxd.BufferFormat.POS3D); + var ids = new Array(); + var pts : Array = []; + var mpts = new Map(); + + for( i in 0...data.vertexCount ) { + var added = false; + var px = pos.vertexes[i * 3]; + var py = pos.vertexes[i * 3 + 1]; + var pz = pos.vertexes[i * 3 + 2]; + var pid = Std.int((px + py + pz) * 10.01); + var arr = mpts.get(pid); + if( arr == null ) { + arr = []; + mpts.set(pid, arr); + } else { + for( idx in arr ) { + var p = pts[idx]; + if( p.x == px && p.y == py && p.z == pz ) { + ids.push(idx); + added = true; + break; } } - if( !added ) { - ids.push(pts.length); - arr.push(pts.length); - pts.push(new h3d.col.Point(px,py,pz)); - } } + if( !added ) { + ids.push(pts.length); + arr.push(pts.length); + pts.push(new h3d.col.Point(px,py,pz)); + } + } - var idx = new hxd.IndexBuffer(); - for( i in pos.indexes ) - idx.push(ids[i]); + var idx = new hxd.IndexBuffer(); + for( i in pos.indexes ) + idx.push(ids[i]); - var pol = new Polygon(pts, idx); - pol.addNormals(); + var pol = new Polygon(pts, idx); + pol.addNormals(); - var startOffset : Int = v.length; - v.grow(lod.vertexCount*3); - var k = 0; - for( i in 0...lod.vertexCount ) { - var n = pol.normals[ids[i]]; - v[startOffset + k++] = n.x; - v[startOffset + k++] = n.y; - v[startOffset + k++] = n.z; - } + var v = new hxd.FloatBuffer(); + v.grow(data.vertexCount*3); + var k = 0; + for( i in 0...data.vertexCount ) { + var n = pol.normals[ids[i]]; + v[k++] = n.x; + v[k++] = n.y; + v[k++] = n.z; } var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : name, type : DVec3 }])); addBuffer(buf); @@ -201,47 +145,42 @@ class HMDModel extends MeshPrimitive { public function addTangents() { if( hasInput("tangent") ) return; - var v = new hxd.FloatBuffer(); - for ( lod in lods ) { - var pos = lib.getBuffers(lod, hxd.BufferFormat.POS3D); - var ids = new Array(); - var pts : Array = []; - for( i in 0...lod.vertexCount ) { - var added = false; - var px = pos.vertexes[i * 3]; - var py = pos.vertexes[i * 3 + 1]; - var pz = pos.vertexes[i * 3 + 2]; - for(i in 0...pts.length) { - var p = pts[i]; - if(p.x == px && p.y == py && p.z == pz) { - ids.push(i); - added = true; - break; - } - } - if( !added ) { - ids.push(pts.length); - pts.push(new h3d.col.Point(px,py,pz)); + var pos = lib.getBuffers(data, hxd.BufferFormat.POS3D); + var ids = new Array(); + var pts : Array = []; + for( i in 0...data.vertexCount ) { + var added = false; + var px = pos.vertexes[i * 3]; + var py = pos.vertexes[i * 3 + 1]; + var pz = pos.vertexes[i * 3 + 2]; + for(i in 0...pts.length) { + var p = pts[i]; + if(p.x == px && p.y == py && p.z == pz) { + ids.push(i); + added = true; + break; } } - var idx = new hxd.IndexBuffer(); - for( i in pos.indexes ) - idx.push(ids[i]); - var pol = new Polygon(pts, idx); - pol.addNormals(); - pol.addTangents(); - - var startOffset : Int = v.length; - v.grow(lod.vertexCount*3); - var k = 0; - for( i in 0...lod.vertexCount ) { - var t = pol.tangents[ids[i]]; - v[startOffset + k++] = t.x; - v[startOffset + k++] = t.y; - v[startOffset + k++] = t.z; + if( !added ) { + ids.push(pts.length); + pts.push(new h3d.col.Point(px,py,pz)); } } - + var idx = new hxd.IndexBuffer(); + for( i in pos.indexes ) + idx.push(ids[i]); + var pol = new Polygon(pts, idx); + pol.addNormals(); + pol.addTangents(); + var v = new hxd.FloatBuffer(); + v.grow(data.vertexCount*3); + var k = 0; + for( i in 0...data.vertexCount ) { + var t = pol.tangents[ids[i]]; + v[k++] = t.x; + v[k++] = t.y; + v[k++] = t.z; + } var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : "tangent", type : DVec3 }])); addBuffer(buf); } @@ -251,16 +190,12 @@ class HMDModel extends MeshPrimitive { super.render(engine); return; } - - var materialCount = data.indexCounts.length; - var lodLevel = Std.int(curMaterial / data.indexCounts.length); - if( indexes == null || indexes.isDisposed() ) alloc(engine); if( buffers == null ) - engine.renderIndexed(buffer, indexes, indexesTriPos[curMaterial], Std.int(lods[lodLevel].indexCounts[curMaterial % materialCount]/3)); + engine.renderIndexed(buffer, indexes, indexesTriPos[curMaterial], Std.int(data.indexCounts[curMaterial]/3)); else - engine.renderMultiBuffers(formats, buffers, indexes, indexesTriPos[curMaterial], Std.int(lods[lodLevel].indexCounts[curMaterial % materialCount]/3)); + engine.renderMultiBuffers(formats, buffers, indexes, indexesTriPos[curMaterial], Std.int(data.indexCounts[curMaterial]/3)); curMaterial = -1; } @@ -290,40 +225,4 @@ class HMDModel extends MeshPrimitive { return collider; } - override public function lodCount() : Int { - return lods.length; - } - - override public function screenRatioToLod( screenRatio : Float ) : Int { - var lodCount = lodCount(); - - if (forcedLod >= 0) - return hxd.Math.imin(forcedLod, lodCount); - - if ( lodCount == 1 ) - return 0; - - lodConfig = getLodConfig(); - if ( lodConfig != null && lodConfig.length >= lodCount - 1) { - var lodLevel : Int = 0; - var maxIter = ( ( lodConfig.length > lodCount - 1 ) ? lodCount - 1: lodConfig.length ); - for ( i in 0...maxIter ) { - if ( lodConfig[i] > screenRatio ) - lodLevel++; - else - break; - } - return lodLevel; - } - - return 0; - } - - public function getLodConfig() { - if (lodConfig != null) - return lodConfig; - - var d = lib.resource.entry.directory; - return @:privateAccess ModelDatabase.current.getDefaultLodConfig(d); - } } \ No newline at end of file diff --git a/h3d/prim/Instanced.hx b/h3d/prim/Instanced.hx index 59a896a7bc..182624facc 100644 --- a/h3d/prim/Instanced.hx +++ b/h3d/prim/Instanced.hx @@ -57,7 +57,7 @@ class Instanced extends Primitive { } override function render( engine : h3d.Engine ) { - if( primitive.buffer == null || primitive.buffer.isDisposed() ) + if( primitive.indexes == null || primitive.buffer.isDisposed() ) primitive.alloc(engine); @:privateAccess engine.flushTarget(); @:privateAccess if( primitive.buffers == null ) diff --git a/h3d/prim/ModelCache.hx b/h3d/prim/ModelCache.hx index b594158fdd..d249118cc4 100644 --- a/h3d/prim/ModelCache.hx +++ b/h3d/prim/ModelCache.hx @@ -50,7 +50,7 @@ class ModelCache { public function loadModel( res : hxd.res.Model ) : h3d.scene.Object { var m = loadLibraryData(res); - return m.lib.makeObject(texturePath -> loadTexture(res, texturePath)); + return m.lib.makeObject(loadTexture.bind(res)); } public function loadCollider( res : hxd.res.Model ) { @@ -60,9 +60,6 @@ class ModelCache { var colliders = []; for( m in lib.header.models ) { if( m.geometry < 0 ) continue; - var prim = @:privateAccess lib.makePrimitive(m); - if (prim == null) - continue; var pos = m.position.toMatrix(); var parent = lib.header.models[m.parent]; while( parent != null ) { @@ -70,6 +67,7 @@ class ModelCache { pos.multiply3x4(pos, pp); parent = lib.header.models[parent.parent]; } + var prim = @:privateAccess lib.makePrimitive(m.geometry); var col = cast(prim.getCollider(), h3d.col.Collider.OptimizedCollider); colliders.push(new h3d.col.TransformCollider(pos,col)); } @@ -172,6 +170,7 @@ class ModelCache { #if hide public function loadPrefab( res : hxd.res.Prefab, ?p : hrt.prefab.Prefab, ?parent : h3d.scene.Object ) { + #if prefab2 if( p == null ) p = res.load(); var prevChild = 0; @@ -182,8 +181,7 @@ class ModelCache { } else { local3d = new h3d.scene.Object(); } - var sh = new hrt.prefab.ContextShared(res.entry.path, p?.findFirstLocal2d(), local3d); - var ctx2 = p.make(sh); + var ctx2 = p.make(local3d); if( parent != null ) { // only return object if a single child was added // if not - multiple children were added and cannot be returned as a single object @@ -196,6 +194,31 @@ class ModelCache { return obj; } return local3d; + #else + if( p == null ) + p = res.load(); + var ctx = new hrt.prefab.Context(); + ctx.init(res); + @:privateAccess ctx.shared.cache = this; + var prevChild = 0; + if( parent != null ) { + ctx.local3d = ctx.shared.root3d = parent; + prevChild = parent.numChildren; + } + var ctx2 = p.make(ctx); + if( parent != null ) { + // only return object if a single child was added + // if not - multiple children were added and cannot be returned as a single object + return parent.numChildren == prevChild + 1 ? parent.getChildAt(prevChild) : null; + } + if( ctx.local3d.numChildren == 1 ) { + // if we have a single root with no scale/rotate/offset we can return it + var obj = ctx.local3d.getChildAt(0); + if( obj.getTransform().isIdentity() ) + return obj; + } + return ctx.local3d; + #end } #end diff --git a/h3d/prim/Primitive.hx b/h3d/prim/Primitive.hx index c5802bb518..1c42c934b5 100644 --- a/h3d/prim/Primitive.hx +++ b/h3d/prim/Primitive.hx @@ -16,11 +16,6 @@ class Primitive { **/ public var indexes : Indexes; - /** - Allow user to force a specific lod index. If set to -1, forced lod will be ignored. - **/ - public var forcedLod : Int = -1; - /** Current amount of references to this Primitive. Use `incref` and `decref` methods to affect this value. If it reaches 0, it will be atuomatically disposed. @@ -88,7 +83,7 @@ class Primitive { /** Select the specified sub material before drawin. Used for internal usage. **/ - public function selectMaterial( material : Int, lod : Int ) { + public function selectMaterial( material : Int ) { } /** @@ -135,15 +130,4 @@ class Primitive { return Type.getClassName(Type.getClass(this)).split(".").pop(); } - /** - Return the LOD count. - **/ - public function lodCount() { - return 1; - } - - public function screenRatioToLod ( screenRatio : Float ) : Int { - return 0; - } - } \ No newline at end of file diff --git a/h3d/scene/CameraController.hx b/h3d/scene/CameraController.hx index 1a94334320..5afb92bbd1 100644 --- a/h3d/scene/CameraController.hx +++ b/h3d/scene/CameraController.hx @@ -19,7 +19,6 @@ class CameraController extends h3d.scene.Object { public var maxDistance : Float = 1e20; public var lockZPlanes = false; - public var enableZoom = true; var scene : h3d.scene.Scene; var pushing = -1; @@ -209,21 +208,18 @@ class CameraController extends h3d.scene.Object { } function zoom(delta : Float) { - if ( enableZoom ) { - var dist = targetDistance; - if( (dist > minDistance && delta < 0) || (dist < maxDistance && delta > 0) ) { - targetPos.x *= Math.pow(zoomAmount, delta); - var expectedDist = targetDistance; - if( expectedDist < minDistance ) { - targetPos.x = minDistance * targetOffset.w; - } - if( expectedDist > maxDistance ) { - targetPos.x = maxDistance * targetOffset.w; - } + var dist = targetDistance; + if( (dist > minDistance && delta < 0) || (dist < maxDistance && delta > 0) ) { + targetPos.x *= Math.pow(zoomAmount, delta); + var expectedDist = targetDistance; + if( expectedDist < minDistance ) { + targetPos.x = minDistance * targetOffset.w; } - } else { - pan(0.0, 0.0, -panSpeed * delta); - } + if( expectedDist > maxDistance ) { + targetPos.x = maxDistance * targetOffset.w; + } + } else + pan( 0, 0, dist * (1 - Math.pow(zoomAmount, delta)) ); } function rot(dx, dy) { @@ -249,8 +245,8 @@ class CameraController extends h3d.scene.Object { distance * Math.cos(phi) + cam.target.z ); if( !lockZPlanes ) { - cam.zNear = Math.max(distance * 0.01, 0.1); - cam.zFar = Math.max(distance * 100, 1000); + cam.zNear = distance * 0.01; + cam.zFar = distance * 100; } cam.fovY = curOffset.w; cam.update(); diff --git a/h3d/scene/HierarchicalWorld.hx b/h3d/scene/HierarchicalWorld.hx index 0918ee9635..d43be266bd 100644 --- a/h3d/scene/HierarchicalWorld.hx +++ b/h3d/scene/HierarchicalWorld.hx @@ -8,7 +8,6 @@ typedef WorldData = { var depth : Int; var maxDepth : Int; var onCreate : HierarchicalWorld -> Void; - var root : HierarchicalWorld; } class HierarchicalWorld extends Object { @@ -19,12 +18,8 @@ class HierarchicalWorld extends Object { static inline final UNLOCK_COLOR = 0xFFFFFF; static inline final LOCK_COLOR = 0xFF0000; - var loadingQueue : Array Bool>; - var loading : Bool = false; - public var data : WorldData; - var logicBounds : h3d.col.Bounds; - var objectBounds : h3d.col.Bounds; + var bounds : h3d.col.Bounds; var subdivided(default, set) = false; function set_subdivided(v : Bool) { subdivided = v; @@ -44,6 +39,9 @@ class HierarchicalWorld extends Object { return data.maxDepth - data.depth; } + var stateAccu = 0.0; + var stateCooldown = 0.1; + function updateGraphics() { if ( debugGraphics == null ) return; @@ -57,7 +55,7 @@ class HierarchicalWorld extends Object { function createGraphics() { if ( debugGraphics != null ) throw "??"; - var b = logicBounds.clone(); + var b = bounds.clone(); b.transform(getAbsPos().getInverse()); b.zMin = 0.0; b.zMax = 0.1; @@ -74,24 +72,17 @@ class HierarchicalWorld extends Object { this.x = data.x; this.y = data.y; calcAbsPos(); - logicBounds = new h3d.col.Bounds(); - // TBD : z bounds? Negative & positive infinity causes debug bounds bugs. - var pseudoInfinity = 1e4; + bounds = new h3d.col.Bounds(); var halfSize = data.size >> 1; - logicBounds.addPoint(new h3d.col.Point(-halfSize, -halfSize, -pseudoInfinity)); - logicBounds.addPoint(new h3d.col.Point(halfSize,halfSize, pseudoInfinity)); - logicBounds.transform(absPos); - // bounds is twice larger than needed so object levels can be predicted using position and bounds only - objectBounds = logicBounds.clone(); - objectBounds.scaleCenter(2.0); + // TBD : z bounds? Negative & positive infinity causes bounds to break. + var pseudoInfinity = 1e10; + bounds.addPoint(new h3d.col.Point(-halfSize, -halfSize, -pseudoInfinity)); + bounds.addPoint(new h3d.col.Point(halfSize,halfSize, pseudoInfinity)); + bounds.transform(absPos); - if ( data.depth == 0 ) { - data.root = this; - loadingQueue = []; - } - if ( data.depth != 0 && data.onCreate != null ) + if ( data.depth != 0 && data.onCreate != null ) { data.onCreate(this); - inheritCulled = true; + } } function init() { @@ -104,24 +95,16 @@ class HierarchicalWorld extends Object { } function canSubdivide() { - return !subdivided && !isLeaf(); + return true; } function createNode(parent, data) { return new HierarchicalWorld(parent, data); } - function subdivide(ctx : h3d.scene.RenderContext) { - if ( subdivided || getScene() == null ) // parent has been removed during dequeuing. - return false; - if ( !loading && data.depth > 0 ) { - loading = true; - getRoot().loadingQueue.insert(0, subdivide); - return false; - } - loading = false; - if ( !locked && !isClose(ctx) ) - return false; + function subdivide() { + if ( subdivided || isLeaf() ) + return; subdivided = true; var childSize = data.size >> 1; for ( i in 0...2 ) { @@ -134,13 +117,11 @@ class HierarchicalWorld extends Object { y : j * childSize - halfChildSize, depth : data.depth + 1, maxDepth : data.maxDepth, - onCreate : data.onCreate, - root : data.root, + onCreate : data.onCreate }; var node = createNode(this, childData); } } - return true; } function removeSubdivisions() { @@ -160,11 +141,8 @@ class HierarchicalWorld extends Object { return camPos.distance(new h2d.col.Point(chunkPos.x, chunkPos.y)); } - function isClose(ctx : h3d.scene.RenderContext) { - return calcDist(ctx) < data.size * data.subdivPow; - } - override function syncRec(ctx : h3d.scene.RenderContext) { + if ( debugGraphics == null && DEBUG ) { createGraphics(); } else if ( debugGraphics != null && !DEBUG ) { @@ -172,25 +150,22 @@ class HierarchicalWorld extends Object { debugGraphics = null; } - culled = !objectBounds.inFrustum(ctx.camera.frustum); + culled = !bounds.inFrustum(ctx.camera.frustum); if ( !isLeaf() ) { - var close = isClose(ctx); - if ( FULL || close ) { - if ( canSubdivide() && !loading ) - subdivide(ctx); - } else if ( !locked && !close ) { + var isClose = calcDist(ctx) < data.size * data.subdivPow; + if ( (isClose && stateAccu < 0.0) || (!isClose && stateAccu > 0.0) ) + stateAccu = 0.0; + stateAccu += isClose ? ctx.elapsedTime : -ctx.elapsedTime; + if ( FULL || stateAccu > stateCooldown ) { + stateAccu = 0.0; + if ( canSubdivide() ) + subdivide(); + } else if ( !locked && -stateAccu > stateCooldown) { + stateAccu = 0.0; removeSubdivisions(); } } super.syncRec(ctx); - - if ( loadingQueue != null ) { - while ( loadingQueue.length > 0 ) { - var load = loadingQueue.pop(); - if ( load(ctx) ) - break; - } - } } override function emitRec(ctx : h3d.scene.RenderContext) { @@ -210,7 +185,7 @@ class HierarchicalWorld extends Object { } public function containsAt(x : Float, y : Float) { - return logicBounds.contains(new h3d.col.Point(x, y, 0.0)); + return bounds.contains(new h3d.col.Point(x, y, 0.0)); } public function requestCreateAt(x : Float, y : Float, lock : Bool) { @@ -218,10 +193,7 @@ class HierarchicalWorld extends Object { return; if ( lock ) locked = true; - if ( canSubdivide() ) { - loading = true; - subdivide(null); - } + subdivide(); for ( c in children ) { var node = Std.downcast(c, HierarchicalWorld); if ( node == null ) @@ -230,29 +202,6 @@ class HierarchicalWorld extends Object { } } - // Get the chunk at the given position, creating it if it doesn't exist - public function getChunkAtLock(x: Float, y: Float) : HierarchicalWorld { - requestCreateAt(x,y, true); - - function rec(chunk: HierarchicalWorld, x:Float,y:Float) : HierarchicalWorld { - if (!chunk.containsAt(x,y)) - return null; - if (chunk.isLeaf()) - return chunk; - for ( c in chunk.children ) { - var node = Std.downcast(c, HierarchicalWorld); - if ( node == null ) - continue; - var r = rec(node,x,y); - if (r != null) - return r; - } - return null; - } - - return rec(this,x,y); - } - public function lockAt(x : Float, y : Float) { if ( !containsAt(x, y) ) return; @@ -288,7 +237,10 @@ class HierarchicalWorld extends Object { } public function getRoot() : h3d.scene.HierarchicalWorld { - return data.root; + var root : h3d.scene.Object = this; + while ( Std.isOfType(root.parent, HierarchicalWorld) ) + root = root.parent; + return cast root; } public function refresh() { @@ -300,10 +252,4 @@ class HierarchicalWorld extends Object { node.remove(); } } - - override function onRemove() { - if ( data.depth == 0 ) - loadingQueue = []; - super.onRemove(); - } } \ No newline at end of file diff --git a/h3d/scene/Interactive.hx b/h3d/scene/Interactive.hx index f4bdd7327f..e31a53779f 100644 --- a/h3d/scene/Interactive.hx +++ b/h3d/scene/Interactive.hx @@ -48,12 +48,10 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive { public var showDebug(get, set) : Bool; /** - * Tells if our shapes are in absolute space (for example ObjectCollider) or relative to the interactive transform. + * Tells if our shape is in absolute space (for example ObjectCollider) or relative to the interactive transform. */ public var isAbsoluteShape : Bool = false; - public var emittedLastFrame : Bool = false; - var scene : Scene; var mouseDownButton : Int = -1; var lastClickFrame : Int = -1; @@ -129,16 +127,6 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive { super.onRemove(); } - override function sync(ctx){ - super.sync(ctx); - emittedLastFrame = false; - } - - override function emit(ctx){ - super.emit(ctx); - emittedLastFrame = true; - } - /** This can be called during or after a push event in order to prevent the release from triggering a click. **/ diff --git a/h3d/scene/Mesh.hx b/h3d/scene/Mesh.hx index 1086a5fd26..a0102ddb4d 100644 --- a/h3d/scene/Mesh.hx +++ b/h3d/scene/Mesh.hx @@ -16,11 +16,6 @@ class Mesh extends Object { **/ public var material : h3d.mat.Material; - /** - When enabled, the lod level is inherited by children objects. - **/ - public var inheritLod : Bool = false; - /** Creates a new mesh with given primitive, material and parent object. If material is not specified, a new default material is created for the current renderer. @@ -42,6 +37,7 @@ class Mesh extends Object { return [material]; } + static var tmpMat = new h3d.Matrix(); override function addBoundsRec( b : h3d.col.Bounds, relativeTo : h3d.Matrix ) { super.addBoundsRec(b, relativeTo); @@ -68,51 +64,11 @@ class Mesh extends Object { return primitive.getCollider(); } - var curScreenRatio : Float = 1.0; override function draw( ctx : RenderContext ) { - primitive.selectMaterial(0, primitive.screenRatioToLod(curScreenRatio)); primitive.render(ctx.engine); } - function calcScreenRatio( ctx : RenderContext ) { - if ( primitive == null || primitive.lodCount() == 1 ) - return; - - if ( ctx.forcedScreenRatio >= 0.0 ) { - curScreenRatio = ctx.forcedScreenRatio; - return; - } - - var bounds = primitive.getBounds(); - if ( bounds == null ) { - curScreenRatio = 1.0; - return; - } - - var absPos = getAbsPos(); - var worldCenter = absPos.getPosition(); - var worldScale = absPos.getScale(); - var worldRadius = bounds.dimension() * hxd.Math.max( worldScale.x, hxd.Math.max(worldScale.y, worldScale.z) ) / 2.0; - - var cameraRight = ctx.camera.getRight(); - var cameraUp = ctx.camera.getUp(); - var cameraTopLeft = (cameraUp - cameraRight).normalized(); - var worldTopLeft = worldCenter + cameraTopLeft * worldRadius; - var worldBottomRight = worldCenter - cameraTopLeft * worldRadius; - - var screenTopLeft = ctx.camera.projectInline( worldTopLeft.x, worldTopLeft.y, worldTopLeft.z, 1.0, 1.0, false ); - var screenBottomRight = ctx.camera.projectInline( worldBottomRight.x, worldBottomRight.y, worldBottomRight.z, 1.0, 1.0, false ); - - var screenArea = hxd.Math.max( screenBottomRight.x - screenTopLeft.x, screenBottomRight.y - screenTopLeft.y ); - - curScreenRatio = screenArea * screenArea; - - if ( inheritLod ) - ctx.forcedScreenRatio = curScreenRatio; - } - override function emit( ctx : RenderContext ) { - calcScreenRatio(ctx); ctx.emit(material, this); } diff --git a/h3d/scene/MeshBatch.hx b/h3d/scene/MeshBatch.hx index 9c5a3a2464..8fef0eccd9 100644 --- a/h3d/scene/MeshBatch.hx +++ b/h3d/scene/MeshBatch.hx @@ -11,16 +11,11 @@ private class BatchData { public var indexStart : Int; public var instanceBuffers : Array; public var buffers : Array = []; - public var bufferFormat : hxd.BufferFormat; public var data : hxd.FloatBuffer; public var params : hxsl.RuntimeShader.AllocParam; public var shader : hxsl.BatchShader; public var shaders : Array; - public var modelViewPos : Int; public var pass : h3d.mat.Pass; - public var computePass : h3d.mat.Pass; - public var commandBuffers : Array; - public var countBuffers : Array; public var next : BatchData; public function new() { @@ -31,165 +26,10 @@ private class BatchData { class MeshBatchPart { public var indexStart : Int; public var indexCount : Int; - public var lodIndexStart : Array; - public var lodIndexCount : Array; - public var lodConfig : Array; public var baseVertex : Int; public var bounds : h3d.col.Bounds; public function new() { } - - public function clone() { - var cl = new MeshBatchPart(); - cl.indexStart = indexStart; - cl.indexCount = indexCount; - cl.lodIndexStart = lodIndexStart; - cl.lodIndexCount = lodIndexCount; - cl.lodConfig = lodConfig; - cl.baseVertex = baseVertex; - cl.bounds = bounds; - return cl; - } -} - -enum MeshBatchFlag { - EnableGpuCulling; - EnableLod; - EnableResizeDown; - EnableGpuUpdate; - EnableStorageBuffer; -} - -class ComputeIndirect extends hxsl.Shader { - static var SRC = { - - @global var camera : { - var position : Vec3; - } - - // n : material offset, n + 1 : subPart ID - @const var ENABLE_COUNT_BUFFER : Bool; - @param var countBuffer : RWBuffer; - @param var instanceOffsets: RWBuffer; - @param var commandBuffer : RWBuffer; - @param var instanceData : RWPartialBuffer<{ modelView : Mat4 }>; - @param var radius : Float; - - @const var USING_SUB_PART : Bool = false; - @const var MAX_SUB_PART_BUFFER_ELEMENT_COUNT : Int = 16; - @param var subPartCount : Int; - @param var startInstanceOffset : Int; - // x : lodCount, y : radius, - @param var subPartInfos : Buffer; - - // 16 by default because 16 * 4 floats = 256 bytes and cbuffer are aligned to 256 bytes - @const var MAX_MATERIAL_COUNT : Int = 16; - @param var materialCount : Int; - @param var matIndex : Int; - // x : indexCount, y : startIndex, z : minScreenRatio, w : unused - @param var matInfos : Buffer; - - @const var ENABLE_CULLING : Bool; - @param var frustum : Buffer; - - @const var ENABLE_LOD : Bool; - @param var lodCount : Int = 1; - - @const var ENABLE_DISTANCE_CLIPPING : Bool; - @param var maxDistance : Float = -1; - - var modelView : Mat4; - function __init__() { - modelView = instanceData[computeVar.globalInvocation.x].modelView; - } - - function main() { - var invocID = computeVar.globalInvocation.x; - var lod : Int = 0; - var pos = vec3(0) * modelView.mat3x4(); - var vScale = abs(vec3(1) * modelView.mat3x4() - pos); - var scaledRadius = max(max(vScale.x, vScale.y), vScale.z); - var toCam = camera.position - pos.xyz; - var distToCam = length(toCam); - - var radius = radius; - var matOffset = matIndex * lodCount; - var lodCount = lodCount; - - if ( USING_SUB_PART ) { - var id = (invocID + startInstanceOffset) * 2; - matOffset = instanceOffsets[id]; - var subPartID = instanceOffsets[id + 1]; - var subPartInfo = subPartInfos[subPartID / 2]; - - var packedID = (subPartID & 1) << 1; - lodCount = int(subPartInfo[packedID]); - radius = subPartInfo[packedID + 1]; - } - - scaledRadius *= radius; - var culled = false; - - if ( dot(scaledRadius, scaledRadius) < 1e-6 ) - culled = true; - - if ( ENABLE_CULLING ) { - for ( i in 0...6 ) { - var plane = frustum[i]; - if ( plane.x * pos.x + plane.y * pos.y + plane.z * pos.z - plane.w < -scaledRadius ) { - culled = true; - break; - } - } - } - - if ( ENABLE_DISTANCE_CLIPPING ) { - culled = culled || distToCam > maxDistance + scaledRadius; - } - - if ( ENABLE_LOD ) { - var screenRatio = scaledRadius / distToCam; - screenRatio = screenRatio * screenRatio; - for ( i in 0...lodCount ) { - var minScreenRatio = matInfos[i + matOffset].z; - if ( screenRatio > minScreenRatio ) - break; - lod++; - } - lod = clamp(lod, 0, int(lodCount) - 1); - } - - var matInfo = ivec4(0.0); - if ( !culled ) { - matInfo = ivec4(matInfos[lod + matOffset]); - culled = culled || matInfo.x <= 0; - } - if ( ENABLE_COUNT_BUFFER ) { - if ( !culled ) { - var id = atomicAdd( countBuffer, 0, 1); - commandBuffer[ id * 5 ] = matInfo.x; - commandBuffer[ id * 5 + 1] = 1; - commandBuffer[ id * 5 + 2] = matInfo.y; - commandBuffer[ id * 5 + 3] = 0; - commandBuffer[ id * 5 + 4] = invocID; - } - } else { - if ( !culled ) { - commandBuffer[ invocID * 5 ] = matInfo.x; - commandBuffer[ invocID * 5 + 1] = 1; - commandBuffer[ invocID * 5 + 2] = matInfo.y; - commandBuffer[ invocID * 5 + 3] = 0; - commandBuffer[ invocID * 5 + 4] = invocID; - } else { - commandBuffer[ invocID * 5 ] = 0; - commandBuffer[ invocID * 5 + 1] = 0; - commandBuffer[ invocID * 5 + 2] = 0; - commandBuffer[ invocID * 5 + 3] = 0; - commandBuffer[ invocID * 5 + 4] = 0; - } - } - } - } } /** @@ -200,30 +40,12 @@ class MeshBatch extends MultiMaterial { static var modelViewID = hxsl.Globals.allocID("global.modelView"); static var modelViewInverseID = hxsl.Globals.allocID("global.modelViewInverse"); - static var previousModelViewID = hxsl.Globals.allocID("global.previousModelView"); static var MAX_BUFFER_ELEMENTS = 4096; - static var MAX_STORAGE_BUFFER_ELEMENTS = 128 * 1024 * 1024 >> 2; var instanced : h3d.prim.Instanced; var dataPasses : BatchData; var needUpload = false; - public var meshBatchFlags(default, null) : haxe.EnumFlags; - var enableLOD(get, never) : Bool; - function get_enableLOD() return meshBatchFlags.has( EnableLod ); - - public var maxDistance : Float = -1; - var enableGPUCulling(get, never) : Bool; - function get_enableGPUCulling() return meshBatchFlags.has( EnableGpuCulling ); - - var mustCalcBufferFormat(get, never) : Bool; - function get_mustCalcBufferFormat() return meshBatchFlags.has(EnableGpuUpdate) || enableGPUCulling || enableLOD; - - var useStorageBuffer(get, never) : Bool; - function get_useStorageBuffer() return meshBatchFlags.has(EnableStorageBuffer); - - var matInfos : h3d.Buffer; - /** Set if shader list or shader constants has changed, before calling begin() **/ @@ -252,13 +74,6 @@ class MeshBatch extends MultiMaterial { public var calcBounds = true; var instancedParams : hxsl.Cache.BatchInstanceParams; - var emittedSubParts : Array; - var currentSubParts : Int; - var currentMaterialOffset : Int; - var instanceOffsetsCpu : haxe.io.Bytes; - var instanceOffsetsGpu : h3d.Buffer; - var subPartsInfos : h3d.Buffer; - var countBytes : haxe.io.Bytes; public function new( primitive, ?material, ?parent ) { instanced = new h3d.prim.Instanced(); @@ -274,32 +89,12 @@ class MeshBatch extends MultiMaterial { cleanPasses(); } - inline function isCountBufferAllowed() { - #if hlsdl - return h3d.impl.GlDriver.hasMultiIndirectCount; - #else - return true; - #end - } - function cleanPasses() { var alloc = hxd.impl.Allocator.get(); while( dataPasses != null ) { dataPasses.pass.removeShader(dataPasses.shader); for( b in dataPasses.buffers ) alloc.disposeBuffer(b); - - if ( dataPasses.commandBuffers != null && dataPasses.commandBuffers.length > 0 ) { - @:privateAccess instanced.commands.data = null; - for ( buf in dataPasses.commandBuffers ) - alloc.disposeBuffer(buf); - dataPasses.commandBuffers.resize(0); - for ( buf in dataPasses.countBuffers ) - alloc.disposeBuffer(buf); - dataPasses.countBuffers.resize(0); - dataPasses.computePass = null; - } - if( dataPasses.instanceBuffers != null ) { for( b in dataPasses.instanceBuffers ) b.dispose(); @@ -307,23 +102,8 @@ class MeshBatch extends MultiMaterial { alloc.disposeFloats(dataPasses.data); dataPasses = dataPasses.next; } - if ( matInfos != null ) { - alloc.disposeBuffer(matInfos); - matInfos = null; - } if( instanced.commands != null ) instanced.commands.dispose(); - - if ( subPartsInfos != null ) - alloc.disposeBuffer(subPartsInfos); - - if ( instanceOffsetsGpu != null ) - alloc.disposeBuffer(instanceOffsetsGpu); - instanceOffsetsCpu = null; - - primitiveSubBytes = null; - emittedSubParts = null; - countBytes = null; shadersChanged = true; } @@ -348,9 +128,8 @@ class MeshBatch extends MultiMaterial { b.indexCount = matInfo.count; b.indexStart = matInfo.start; b.paramsCount = shader.paramsSize; - b.maxInstance = Std.int( ( useStorageBuffer ? MAX_STORAGE_BUFFER_ELEMENTS : MAX_BUFFER_ELEMENTS ) / b.paramsCount); - b.bufferFormat = hxd.BufferFormat.VEC4_DATA; - if( b.maxInstance <= 0 ) + b.maxInstance = Std.int(MAX_BUFFER_ELEMENTS / b.paramsCount); + if ( b.maxInstance <= 0 ) throw "Mesh batch shaders needs at least one perInstance parameter"; b.params = shader.params; b.shader = shader; @@ -360,57 +139,6 @@ class MeshBatch extends MultiMaterial { p.dynamicParameters = true; p.batchMode = true; - if( mustCalcBufferFormat ) { - var pl = []; - var p = b.params; - while( p != null ) { - pl.push(p); - p = p.next; - } - pl.sort(function(p1,p2) return p1.pos - p2.pos); - var fmt : Array = []; - var curPos = 0; - var paddingIndex = 0; - for( p in pl ) { - var paddingSize = p.pos - curPos; - if ( paddingSize > 0 ) { - var paddingType : hxsl.Ast.Type = switch ( paddingSize ) { - case 0: - TFloat; - case 1,2,3: - TVec(paddingSize, VFloat); - default: - throw "Buffer has padding"; - } - var t = hxd.BufferFormat.InputFormat.fromHXSL(paddingType); - fmt.push(new hxd.BufferFormat.BufferInput("padding_"+paddingIndex,t)); - paddingIndex++; - curPos = p.pos; - } - var name = p.name; - var prev = fmt.length; - switch( p.type ) { - case TMat3: - for( i in 0...3 ) - fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec3)); - case TMat3x4: - for( i in 0...3 ) - fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec4)); - case TMat4: - for( i in 0...4 ) - fmt.push(new hxd.BufferFormat.BufferInput(name+"__m"+i,DVec4)); - default: - var t = hxd.BufferFormat.InputFormat.fromHXSL(p.type); - fmt.push(new hxd.BufferFormat.BufferInput(p.name,t)); - } - for( i in prev...fmt.length ) - curPos += fmt[i].getBytesSize() >> 2; - } - if ( curPos & 3 != 0) - throw "Buffer has padding"; - b.bufferFormat = hxd.BufferFormat.make(fmt); - } - b.next = dataPasses; dataPasses = b; @@ -419,10 +147,9 @@ class MeshBatch extends MultiMaterial { b.shaders.push(sl.s); sl = sl.next; } - shader.Batch_UseStorage = useStorageBuffer; - shader.Batch_Count = useStorageBuffer ? 0 : b.maxInstance * b.paramsCount; - shader.Batch_HasOffset = primitiveSubPart != null || enableLOD || enableGPUCulling; - shader.constBits = (shader.Batch_Count << 2) | (shader.Batch_UseStorage ? ( 1 << 1 ) : 0) | (shader.Batch_HasOffset ? 1 : 0); + shader.Batch_Count = b.maxInstance * b.paramsCount; + shader.Batch_HasOffset = primitiveSubPart != null; + shader.constBits = (shader.Batch_Count << 1) | (shader.Batch_HasOffset ? 1 : 0); shader.updateConstants(null); } } @@ -435,23 +162,7 @@ class MeshBatch extends MultiMaterial { } } - public function begin( emitCountTip = -1, ?flags : haxe.EnumFlags ) { - if ( flags != null ) { - #if (!js && !(hldx && !dx12)) - var allowedLOD = flags.has(EnableLod) && ( primitiveSubPart != null || @:privateAccess instanced.primitive.lodCount() > 1 ); - flags.setTo(EnableLod, allowedLOD); - #else - flags.setTo(EnableLod, false); - flags.setTo(EnableGpuCulling, false); - #end - // Set flags non-related to shaders - meshBatchFlags.setTo( EnableResizeDown, flags.has(EnableResizeDown) ); - if ( meshBatchFlags != flags ) - shadersChanged = true; - meshBatchFlags = flags; - meshBatchFlags.setTo( EnableStorageBuffer, mustCalcBufferFormat || useStorageBuffer ); - } - + public function begin( emitCountTip = -1, resizeDown = false ) { instanceCount = 0; instanced.initBounds(); if( shadersChanged ) { @@ -465,16 +176,12 @@ class MeshBatch extends MultiMaterial { var alloc = hxd.impl.Allocator.get(); while( p != null ) { var size = emitCountTip * p.paramsCount * 4; - if( p.data == null || p.data.length < size || ( meshBatchFlags.has(EnableResizeDown) && p.data.length > size << 1) ) { + if( p.data == null || p.data.length < size || (resizeDown && p.data.length > size << 1) ) { if( p.data != null ) alloc.disposeFloats(p.data); p.data = alloc.allocFloats(size); } p = p.next; } - if ( primitiveSubPart != null && ( enableGPUCulling || enableLOD ) && instanceOffsetsCpu == null ) { - var size = emitCountTip * 2 * 4; - instanceOffsetsCpu = haxe.io.Bytes.alloc(size); - } } function syncData( batch : BatchData ) { @@ -510,10 +217,9 @@ class MeshBatch extends MultiMaterial { buf[pos++] = m._44; } if( p.perObjectGlobal != null ) { - if ( p.perObjectGlobal.gid == modelViewID ) { - batch.modelViewPos = pos - startPos; + if( p.perObjectGlobal.gid == modelViewID ) { addMatrix(worldPosition != null ? worldPosition : absPos); - } else if ( p.perObjectGlobal.gid == modelViewInverseID ) { + } else if( p.perObjectGlobal.gid == modelViewInverseID ) { if( worldPosition == null ) addMatrix(getInvPos()); else { @@ -524,9 +230,7 @@ class MeshBatch extends MultiMaterial { } addMatrix(invWorldPosition); } - } else if ( p.perObjectGlobal.gid == previousModelViewID ) - addMatrix( worldPosition != null ? worldPosition : absPos ); - else + } else throw "Unsupported global param "+p.perObjectGlobal.path; p = p.next; continue; @@ -587,56 +291,23 @@ class MeshBatch extends MultiMaterial { instanced.tmpBounds.transform(worldPosition == null ? absPos : worldPosition); instanced.bounds.add(instanced.tmpBounds); } - if ( enableLOD || enableGPUCulling ) { - if (emittedSubParts == null) { - currentSubParts = 0; - currentMaterialOffset = 0; - emittedSubParts = [ primitiveSubPart.clone() ]; - } else { - var currentIndexStart = emittedSubParts[currentSubParts].indexStart; - if ( currentIndexStart != primitiveSubPart.indexStart ) { - currentSubParts = -1; - currentIndexStart = primitiveSubPart.indexStart; - currentMaterialOffset = 0; - for ( i => part in emittedSubParts ) { - if ( part.indexStart == currentIndexStart ) { - currentSubParts = i; - break; - } - currentMaterialOffset += part.lodIndexCount.length + 1; - } - if ( currentSubParts < 0 ) { - currentSubParts = emittedSubParts.length; - emittedSubParts.push( primitiveSubPart.clone() ); - } - } - } - var maxInstanceID = ( instanceCount + 1 ) * 2; - if ( instanceOffsetsCpu.length < maxInstanceID * 4 ) { - var next = haxe.io.Bytes.alloc(Std.int(instanceOffsetsCpu.length*3/2)); - next.blit(0, instanceOffsetsCpu, 0, instanceOffsetsCpu.length); - instanceOffsetsCpu = next; - } - instanceOffsetsCpu.setInt32((instanceCount * 2 + 0) * 4, currentMaterialOffset); - instanceOffsetsCpu.setInt32((instanceCount * 2 + 1) * 4, currentSubParts); - } else { - if( primitiveSubBytes == null ) { - primitiveSubBytes = haxe.io.Bytes.alloc(128); - instanced.commands = null; - } - if( primitiveSubBytes.length < (instanceCount+1) * 20 ) { - var next = haxe.io.Bytes.alloc(Std.int(primitiveSubBytes.length*3/2)); - next.blit(0, primitiveSubBytes, 0, instanceCount * 20); - primitiveSubBytes = next; - } - var p = instanceCount * 20; - primitiveSubBytes.setInt32(p, ps.indexCount); - primitiveSubBytes.setInt32(p + 4, 1); - primitiveSubBytes.setInt32(p + 8, ps.indexStart); - primitiveSubBytes.setInt32(p + 12, ps.baseVertex); - primitiveSubBytes.setInt32(p + 16, 0); + + if( primitiveSubBytes == null ) { + primitiveSubBytes = haxe.io.Bytes.alloc(128); + instanced.commands = null; + } + if( primitiveSubBytes.length < (instanceCount+1) * 20 ) { + var next = haxe.io.Bytes.alloc(Std.int(primitiveSubBytes.length*3/2)); + next.blit(0, primitiveSubBytes, 0, instanceCount * 20); + primitiveSubBytes = next; } - } else if (calcBounds) + var p = instanceCount * 20; + primitiveSubBytes.setInt32(p, ps.indexCount); + primitiveSubBytes.setInt32(p + 4, 1); + primitiveSubBytes.setInt32(p + 8, ps.indexStart); + primitiveSubBytes.setInt32(p + 12, ps.baseVertex); + primitiveSubBytes.setInt32(p + 16, 0); + } else if(calcBounds) instanced.addInstanceBounds(worldPosition == null ? absPos : worldPosition); var p = dataPasses; while( p != null ) { @@ -646,127 +317,25 @@ class MeshBatch extends MultiMaterial { instanceCount++; } - public function disposeBuffers( useAllocator : Bool = true ) { + public function disposeBuffers() { if( instanceCount == 0 ) return; var p = dataPasses; - if ( useAllocator ) { - var alloc = hxd.impl.Allocator.get(); - while( p != null ) { - for ( b in p.buffers ) - alloc.disposeBuffer(b); - p.buffers.resize(0); - p = p.next; - } - } else { - while( p != null ) { - for ( b in p.buffers ) - b.dispose(); - p = p.next; - } + var alloc = hxd.impl.Allocator.get(); + while( p != null ) { + for ( b in p.buffers ) + b.dispose(); + p = p.next; } } static var BATCH_START_FMT = hxd.BufferFormat.make([{ name : "Batch_Start", type : DFloat }]); - static var INDIRECT_DRAW_ARGUMENTS_FMT = hxd.BufferFormat.make([{ name : "", type : DVec4 }, { name : "", type : DFloat }]); - static var INSTANCE_OFFSETS_FMT = hxd.BufferFormat.make([{ name : "", type : DFloat }]); override function sync(ctx:RenderContext) { super.sync(ctx); if( instanceCount == 0 ) return; - flush(ctx); - } - - function addComputeShaders( pass : h3d.mat.Pass ) {} - - public function flush(ctx:RenderContext) { var p = dataPasses; var alloc = hxd.impl.Allocator.get(); var psBytes = primitiveSubBytes; - - var prim = @:privateAccess instanced.primitive; - var hmd = Std.downcast(prim, h3d.prim.HMDModel); - var materialCount = materials.length; - var lodCount = ( enableLOD ) ? prim.lodCount() : 1; - - if ( enableLOD || enableGPUCulling ) { - if ( emittedSubParts != null ) { - var upload = needUpload; - var vertex = instanceCount * 2; - if ( instanceOffsetsGpu == null || instanceOffsetsGpu.isDisposed() || vertex > instanceOffsetsGpu.vertices ) { - if ( instanceOffsetsGpu != null) - alloc.disposeBuffer( instanceOffsetsGpu ); - instanceOffsetsGpu = alloc.allocBuffer( vertex, INSTANCE_OFFSETS_FMT, UniformReadWrite ); - upload = true; - } - if ( upload ) - instanceOffsetsGpu.uploadBytes( instanceOffsetsCpu, 0, vertex ); - - if ( matInfos == null ) { - materialCount = 0; - var tmpSubPartInfos = alloc.allocFloats( 2 * emittedSubParts.length ); - var pos = 0; - for ( subPart in emittedSubParts ) { - var lodCount = subPart.lodIndexCount.length + 1; - tmpSubPartInfos[pos++] = lodCount; - tmpSubPartInfos[pos++] = subPart.bounds.dimension() * 0.5; - materialCount += lodCount; - } - subPartsInfos = alloc.ofFloats( tmpSubPartInfos, hxd.BufferFormat.VEC4_DATA, Uniform ); - alloc.disposeFloats(tmpSubPartInfos); - - var tmpMatInfos = alloc.allocFloats( 4 * ( materialCount + emittedSubParts.length ) ); - pos = 0; - for ( subPart in emittedSubParts ) { - var lodConfig = subPart.lodConfig; - tmpMatInfos[pos++] = subPart.indexCount; - tmpMatInfos[pos++] = subPart.indexStart; - tmpMatInfos[pos++] = ( 0 < lodConfig.length ) ? lodConfig[0] : 0.0; - pos++; - for ( i in 0...subPart.lodIndexCount.length ) { - tmpMatInfos[pos++] = subPart.lodIndexCount[i]; - tmpMatInfos[pos++] = subPart.lodIndexStart[i]; - tmpMatInfos[pos++] = ( i + 1 < lodConfig.length ) ? lodConfig[i + 1] : 0.0; - pos++; - } - } - - matInfos = alloc.ofFloats( tmpMatInfos, hxd.BufferFormat.VEC4_DATA, Uniform ); - alloc.disposeFloats(tmpMatInfos); - } - } else if ( matInfos == null ) { - if ( enableLOD ) { - var tmpMatInfos = alloc.allocFloats( 4 * materialCount * lodCount ); - matInfos = alloc.allocBuffer( materialCount * lodCount, hxd.BufferFormat.VEC4_DATA, Uniform ); - var lodConfig = hmd.getLodConfig(); - var startIndex : Int = 0; - for ( i => lod in @:privateAccess hmd.lods ) { - for ( j in 0...materialCount ) { - var indexCount = lod.indexCounts[j]; - var matIndex = i + j * lodCount; - tmpMatInfos[matIndex * 4 + 0] = indexCount; - tmpMatInfos[matIndex * 4 + 1] = startIndex; - tmpMatInfos[matIndex * 4 + 2] = ( i < lodConfig.length ) ? lodConfig[i] : 0.0; - startIndex += indexCount; - } - } - matInfos.uploadFloats( tmpMatInfos, 0, materialCount * lodCount ); - alloc.disposeFloats( tmpMatInfos ); - } else { - var tmpMatInfos = alloc.allocFloats( 4 * materialCount ); - matInfos = alloc.allocBuffer( materialCount, hxd.BufferFormat.VEC4_DATA, Uniform ); - var pos : Int = 0; - for ( i in 0...materials.length ) { - var matInfo = prim.getMaterialIndexes(i); - tmpMatInfos[pos++] = matInfo.count; - tmpMatInfos[pos++] = matInfo.start; - pos += 2; - } - matInfos.uploadFloats( tmpMatInfos, 0, materialCount ); - alloc.disposeFloats( tmpMatInfos ); - } - } - } - while( p != null ) { var index = 0; var start = 0; @@ -776,30 +345,17 @@ class MeshBatch extends MultiMaterial { var count = instanceCount - start; if( count > p.maxInstance ) count = p.maxInstance; - - var maxVertexCount = ( mustCalcBufferFormat ) ? p.maxInstance : ( useStorageBuffer ? MAX_STORAGE_BUFFER_ELEMENTS : MAX_BUFFER_ELEMENTS ); - var vertexCount = Std.int( count * (( 4 * p.paramsCount ) / p.bufferFormat.stride) ); - var vertexCountAllocated = #if js Std.int( MAX_BUFFER_ELEMENTS * 4 / p.bufferFormat.stride ) #else hxd.Math.imin( hxd.Math.nextPOT( vertexCount ), maxVertexCount ) #end; - - if( buf == null || buf.isDisposed() || buf.vertices < vertexCountAllocated ) { - var bufferFlags : hxd.impl.Allocator.BufferFlags = useStorageBuffer ? UniformReadWrite : UniformDynamic; - if ( buf != null ) - alloc.disposeBuffer(buf); - buf = alloc.allocBuffer( vertexCountAllocated, p.bufferFormat,bufferFlags ); + if( buf == null || buf.isDisposed() ) { + buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,hxd.BufferFormat.VEC4_DATA,UniformDynamic); p.buffers[index] = buf; upload = true; } if( upload ) - buf.uploadFloats(p.data, start * p.paramsCount * 4, vertexCount); + buf.uploadFloats(p.data, start * p.paramsCount * 4, count * p.paramsCount); if( psBytes != null ) { - if( p.instanceBuffers == null ) - p.instanceBuffers = []; + if( p.instanceBuffers == null ) p.instanceBuffers = []; var buf = p.instanceBuffers[index]; - if ( buf != null && buf.commandCount != count ) { - buf.dispose(); - buf = null; - } - if( buf == null ) { + if( buf == null /*|| buf.isDisposed()*/ ) { buf = new h3d.impl.InstanceBuffer(); var sub = psBytes.sub(start*20,count*20); for( i in 0...count ) @@ -808,75 +364,14 @@ class MeshBatch extends MultiMaterial { p.instanceBuffers[index] = buf; } } - - var commandCountAllocated = hxd.Math.imin( hxd.Math.nextPOT( count ), p.maxInstance ); - - if ( enableLOD || enableGPUCulling ) { - if ( p.commandBuffers == null) { - p.commandBuffers = []; - p.countBuffers = []; - } - var buf = p.commandBuffers[index]; - var cbuf = p.countBuffers[index]; - if ( buf == null ) { - buf = alloc.allocBuffer( commandCountAllocated, INDIRECT_DRAW_ARGUMENTS_FMT, UniformReadWrite ); - cbuf = alloc.allocBuffer( 1, hxd.BufferFormat.VEC4_DATA, UniformReadWrite ); - p.commandBuffers[index] = buf; - p.countBuffers[index] = cbuf; - } - else if ( buf.vertices < commandCountAllocated ) { - alloc.disposeBuffer( buf ); - buf = alloc.allocBuffer( commandCountAllocated, INDIRECT_DRAW_ARGUMENTS_FMT, UniformReadWrite ); - p.commandBuffers[index] = buf; - } - } start += count; index++; } - if ( ( enableLOD || enableGPUCulling ) ) { - var computeShader; - if( p.computePass == null ) { - computeShader = new ComputeIndirect(); - var computePass = new h3d.mat.Pass("batchUpdate"); - computePass.addShader(computeShader); - addComputeShaders(computePass); - p.computePass = computePass; - } else { - computeShader = p.computePass.getShader(ComputeIndirect); - } - - computeShader.ENABLE_LOD = enableLOD; - computeShader.ENABLE_CULLING = enableGPUCulling; - computeShader.ENABLE_DISTANCE_CLIPPING = maxDistance >= 0; - computeShader.radius = prim.getBounds().dimension() * 0.5; - computeShader.maxDistance = maxDistance; - computeShader.matInfos = matInfos; - computeShader.lodCount = lodCount; - computeShader.materialCount = materialCount; - computeShader.MAX_MATERIAL_COUNT = 16; - while ( materialCount * lodCount > computeShader.MAX_MATERIAL_COUNT ) - computeShader.MAX_MATERIAL_COUNT = computeShader.MAX_MATERIAL_COUNT + 16; - - if ( emittedSubParts != null ) { - computeShader.USING_SUB_PART = true; - computeShader.subPartCount = emittedSubParts.length; - computeShader.subPartInfos = subPartsInfos; - computeShader.instanceOffsets = instanceOffsetsGpu; - computeShader.MAX_SUB_PART_BUFFER_ELEMENT_COUNT = 16; - var maxSubPartsElement = hxd.Math.ceil( emittedSubParts.length / 2 ); - while ( maxSubPartsElement > computeShader.MAX_SUB_PART_BUFFER_ELEMENT_COUNT ) - computeShader.MAX_SUB_PART_BUFFER_ELEMENT_COUNT = computeShader.MAX_SUB_PART_BUFFER_ELEMENT_COUNT + 16; - } - - if ( enableGPUCulling ) - computeShader.frustum = ctx.getCameraFrustumBuffer(); - - } while( p.buffers.length > index ) - alloc.disposeBuffer( p.buffers.pop() ); + alloc.disposeBuffer(p.buffers.pop()); p = p.next; } - if( psBytes != null || enableLOD || enableGPUCulling ) { + if( psBytes != null ) { var offsets = @:privateAccess instanced.primitive.resolveBuffer("Batch_Start"); if( offsets == null || offsets.vertices < instanceCount || offsets.isDisposed() ) { if( offsets != null ) { @@ -899,17 +394,10 @@ class MeshBatch extends MultiMaterial { while( true ) { if( p.pass == ctx.drawPass.pass ) { var bufferIndex = ctx.drawPass.index & 0xFFFF; - if ( useStorageBuffer ) - p.shader.Batch_StorageBuffer = p.buffers[bufferIndex]; - else - p.shader.Batch_Buffer = p.buffers[bufferIndex]; + p.shader.Batch_Buffer = p.buffers[bufferIndex]; if( p.instanceBuffers == null ) { - var count = hxd.Math.imin( instanceCount - p.maxInstance * bufferIndex, p.maxInstance ); + var count = instanceCount - p.maxInstance * bufferIndex; instanced.commands.setCommand(count,p.indexCount,p.indexStart); - if ( p.commandBuffers != null && p.commandBuffers.length > 0 ) { - @:privateAccess instanced.commands.data = p.commandBuffers[bufferIndex].vbuf; - @:privateAccess instanced.commands.countBuffer = p.countBuffers[bufferIndex].vbuf; - } } else instanced.commands = p.instanceBuffers[bufferIndex]; break; @@ -928,36 +416,11 @@ class MeshBatch extends MultiMaterial { var p = dataPasses; while( p != null ) { var pass = p.pass; - - // Triggers upload - if ( enableGPUCulling ) - ctx.getCameraFrustumBuffer(); - // check that the pass is still enable var material = materials[p.matIndex]; if( material != null && material.getPass(pass.name) != null ) { - var emittedCount = 0; - for( i => buf in p.buffers ) { + for( i in 0...p.buffers.length ) ctx.emitPass(pass, this).index = i | (p.matIndex << 16); - if ( p.commandBuffers != null && p.commandBuffers.length > 0 ) { - var count = hxd.Math.imin( instanceCount - p.maxInstance * i, p.maxInstance); - var computeShader = p.computePass.getShader(ComputeIndirect); - computeShader.instanceData = buf; - computeShader.matIndex = p.matIndex; - computeShader.commandBuffer = p.commandBuffers[i]; - if ( countBytes == null ) { - countBytes = haxe.io.Bytes.alloc(4*4); - countBytes.setInt32(0, 0); - } - p.countBuffers[i].uploadBytes(countBytes, 0, 1); - computeShader.countBuffer = p.countBuffers[i]; - computeShader.startInstanceOffset = emittedCount; - computeShader.ENABLE_COUNT_BUFFER = isCountBufferAllowed(); - ctx.computeList(@:privateAccess p.computePass.shaders); - ctx.computeDispatch(count); - emittedCount += count; - } - } } p = p.next; } diff --git a/h3d/scene/MultiMaterial.hx b/h3d/scene/MultiMaterial.hx index 994361f051..bf865d91c2 100644 --- a/h3d/scene/MultiMaterial.hx +++ b/h3d/scene/MultiMaterial.hx @@ -24,7 +24,6 @@ class MultiMaterial extends Mesh { } override function emit( ctx : RenderContext ) { - calcScreenRatio(ctx); for( i in 0...materials.length ) { var m = materials[i]; if( m != null ) @@ -53,8 +52,8 @@ class MultiMaterial extends Mesh { override function draw( ctx : RenderContext ) { if( materials.length > 1 ) - primitive.selectMaterial(ctx.drawPass.index, primitive.screenRatioToLod(curScreenRatio)); - primitive.render(ctx.engine); + primitive.selectMaterial(ctx.drawPass.index); + super.draw(ctx); } } \ No newline at end of file diff --git a/h3d/scene/Object.hx b/h3d/scene/Object.hx index 14814c7fdb..4dfde06e37 100644 --- a/h3d/scene/Object.hx +++ b/h3d/scene/Object.hx @@ -17,7 +17,6 @@ enum abstract ObjectFlags(Int) { public var FFixedPosition = 0x2000; public var FFixedPositionSynced = 0x4000; public var FAlwaysSync = 0x8000; - public var FDrawn = 0x10000; public inline function new(value) { this = value; } @@ -37,14 +36,9 @@ enum abstract ObjectFlags(Int) { class Object { static inline var ROT2RAD = -0.017453292519943295769236907684886; - static inline var NO_VELOCITY = -1; - static inline var VELOCITY = 0; var flags : ObjectFlags; var lastFrame : Int; - - public var currentAnimation(default, null) : h3d.anim.Animation; - var children : Array; /** @@ -57,6 +51,42 @@ class Object { **/ public var numChildren(get, never) : Int; + /** + The name of the object, can be used to retrieve an object within a tree by using `getObjectByName` (default null) + **/ + public var name : Null; + + /** + The x position of the object relative to its parent. + **/ + public var x(default,set) : Float; + + /** + The y position of the object relative to its parent. + **/ + public var y(default, set) : Float; + + /** + The z position of the object relative to its parent. + **/ + public var z(default, set) : Float; + + /** + The amount of scaling along the X axis of this object (default 1.0) + **/ + public var scaleX(default,set) : Float; + + /** + The amount of scaling along the Y axis of this object (default 1.0) + **/ + public var scaleY(default, set) : Float; + + /** + The amount of scaling along the Z axis of this object (default 1.0) + **/ + public var scaleZ(default,set) : Float; + + /** Is the object and its children are displayed on screen (default true). **/ @@ -64,6 +94,23 @@ class Object { var allocated(get,set) : Bool; + /** + Follow a given object or joint as if it was our parent. Ignore defaultTransform when set. + **/ + public var follow(default, set) : Object; + + /** + When follow is set, only follow the position and ignore both scale and rotation. + **/ + public var followPositionOnly(get, set) : Bool; + + /** + This is an additional optional transformation that is performed before other local transformations. + It is used by the animation system. + **/ + public var defaultTransform(default, set) : h3d.Matrix; + public var currentAnimation(default, null) : h3d.anim.Animation; + /** Inform that the object is not to be displayed and his animation doesn't have to be sync. Unlike visible, this doesn't apply to children unless inheritCulled is set to true. **/ @@ -118,11 +165,6 @@ class Object { **/ public var alwaysSync(get, set) : Bool; - /** - When set, the object has been drawn during previous frame. Useful for temporal effects such as temporal antialiasing. - **/ - public var drawn(get, set) : Bool; - /** When set, collider shape will be used for automatic frustum culling. If `inheritCulled` is true, collider will be inherited to children unless they have their own collider set. @@ -139,64 +181,11 @@ class Object { **/ var cullingColliderInherited(get, set) : Bool; - /** - The x position of the object relative to its parent. - **/ - public var x(default,set) : Float; - - /** - The y position of the object relative to its parent. - **/ - public var y(default, set) : Float; - - /** - The z position of the object relative to its parent. - **/ - public var z(default, set) : Float; - - /** - The amount of scaling along the X axis of this object (default 1.0) - **/ - public var scaleX(default,set) : Float; - - /** - The amount of scaling along the Y axis of this object (default 1.0) - **/ - public var scaleY(default, set) : Float; - - /** - The amount of scaling along the Z axis of this object (default 1.0) - **/ - public var scaleZ(default,set) : Float; - var absPos : h3d.Matrix; - var prevAbsPos : h3d.Matrix; - var prevAbsPosFrame : Int = NO_VELOCITY; var invPos : h3d.Matrix; var qRot : h3d.Quat; var posChanged(get,set) : Bool; - /** - Follow a given object or joint as if it was our parent. Ignore defaultTransform when set. - **/ - public var follow(default, set) : Object; - - /** - When follow is set, only follow the position and ignore both scale and rotation. - **/ - public var followPositionOnly(get, set) : Bool; - - /** - This is an additional optional transformation that is performed before other local transformations. - It is used by the animation system. - **/ - public var defaultTransform(default, set) : h3d.Matrix; - - /** - The name of the object, can be used to retrieve an object within a tree by using `getObjectByName` (default null) - **/ - public var name : Null; - /** Create a new empty object, and adds it to the parent object if not null. **/ @@ -228,7 +217,6 @@ class Object { inline function get_cullingColliderInherited() return flags.has(FCullingColliderInherited); inline function get_fixedPosition() return flags.has(FFixedPosition); inline function get_alwaysSync() return flags.has(FAlwaysSync); - inline function get_drawn() return flags.has(FDrawn); inline function set_posChanged(b) return flags.set(FPosChanged, b || follow != null); inline function set_culled(b) return flags.set(FCulled, b); inline function set_visible(b) return flags.set(FVisible,b); @@ -244,7 +232,6 @@ class Object { inline function set_cullingColliderInherited(b) return flags.set(FCullingColliderInherited, b); inline function set_fixedPosition(b) return flags.set(FFixedPosition, b); inline function set_alwaysSync(b) return flags.set(FAlwaysSync, b); - inline function set_drawn(b) return flags.set(FDrawn, b); /** Create an animation instance bound to the object, set it as currentAnimation and play it. @@ -683,21 +670,7 @@ class Object { return follow = v; } - function calcPrevAbsPos() { - if ( prevAbsPosFrame == NO_VELOCITY ) - prevAbsPos = null; - else if ( prevAbsPosFrame < hxd.Timer.frameCount ) { - prevAbsPosFrame = hxd.Timer.frameCount; - if ( prevAbsPos == null ) - prevAbsPos = absPos.clone(); - else - prevAbsPos.load(absPos); - } - } - function calcAbsPos() { - calcPrevAbsPos(); - qRot.toMatrix(absPos); // prepend scale absPos._11 *= scaleX; @@ -735,7 +708,6 @@ class Object { function syncRec( ctx : RenderContext ) { #if sceneprof h3d.impl.SceneProf.mark(this); #end - #if heaps_prefetch untyped $prefetch(children.length, 2); #end if( currentAnimation != null ) { var old = parent; var dt = ctx.elapsedTime; @@ -783,7 +755,6 @@ class Object { break; if( c.lastFrame != ctx.frame ) { if( changed ) c.posChanged = true; - #if heaps_prefetch untyped $prefetch(children[p+1], 2); #end c.syncRec(ctx); } // if the object was removed, let's restart again. @@ -826,22 +797,11 @@ class Object { for( c in children ) c.posChanged = true; } - - var prevForcedScreenRatio : Float = ctx.forcedScreenRatio; - if ( !drawn || !ctx.computeVelocity || fixedPosition || culled ) - prevAbsPosFrame = NO_VELOCITY; - else if ( prevAbsPosFrame == NO_VELOCITY ) - prevAbsPosFrame = VELOCITY; - calcPrevAbsPos(); - - if( !culled || ctx.computingStatic ) { + if( !culled || ctx.computingStatic ) emit(ctx); - drawn = false; - } for( c in children ) c.emitRec(ctx); - ctx.forcedScreenRatio = prevForcedScreenRatio; } inline function set_x(v) { diff --git a/h3d/scene/RenderContext.hx b/h3d/scene/RenderContext.hx index fb1d1554c6..9bf32012da 100644 --- a/h3d/scene/RenderContext.hx +++ b/h3d/scene/RenderContext.hx @@ -17,7 +17,6 @@ class RenderContext extends h3d.impl.RenderContext { public var drawPass : h3d.pass.PassObject; public var pbrLightPass : h3d.mat.Pass; public var computingStatic : Bool; - public var computeVelocity : Bool; public var lightSystem : h3d.scene.LightSystem; public var extraShaders : hxsl.ShaderList; @@ -25,7 +24,6 @@ class RenderContext extends h3d.impl.RenderContext { public var debugCulling : Bool; public var wasContextLost : Bool; public var cullingCollider : h3d.col.Collider; - public var forcedScreenRatio : Float = -1; @global("camera.view") var cameraView : h3d.Matrix; @global("camera.zNear") var cameraNear : Float; @@ -36,27 +34,20 @@ class RenderContext extends h3d.impl.RenderContext { @global("camera.projFlip") var cameraProjFlip : Float; @global("camera.viewProj") var cameraViewProj : h3d.Matrix; @global("camera.inverseViewProj") var cameraInverseViewProj : h3d.Matrix; - @global("camera.previousViewProj") var cameraPreviousViewProj : h3d.Matrix; - @global("camera.jitterOffsets") var cameraJitterOffsets : h3d.Vector4; @global("global.time") var globalTime : Float; @global("global.pixelSize") var pixelSize : h3d.Vector; @global("global.modelView") var globalModelView : h3d.Matrix; @global("global.modelViewInverse") var globalModelViewInverse : h3d.Matrix; - @global("global.previousModelView") var globalPreviousModelView : h3d.Matrix; var allocPool : h3d.pass.PassObject; var allocFirst : h3d.pass.PassObject; - var tmpComputeLink = new hxsl.ShaderList(null,null); - var computeLink : hxsl.ShaderList; + var computeLink = new hxsl.ShaderList(null,null); var cachedShaderList : Array; var cachedPassObjects : Array; var cachedPos : Int; var passes : Array; var lights : Light; - var cameraFrustumBuffer : h3d.Buffer = null; - var cameraFrustumUploaded : Bool = false; - public function new(scene) { super(); this.scene = scene; @@ -73,10 +64,6 @@ class RenderContext extends h3d.impl.RenderContext { cameraProj = cam.mproj; cameraPos = cam.pos; cameraProjDiag = new h3d.Vector4(cam.mproj._11,cam.mproj._22,cam.mproj._33,cam.mproj._44); - if ( cameraPreviousViewProj == null ) - cameraPreviousViewProj = cam.m.clone(); - if (cameraJitterOffsets == null) - cameraJitterOffsets = new h3d.Vector4( 0.0, 0.0, 0.0, 0.0 ); cameraViewProj = cam.m; cameraInverseViewProj = camera.getInverseViewProj(); } @@ -107,7 +94,6 @@ class RenderContext extends h3d.impl.RenderContext { lights = null; cachedPos = 0; visibleFlag = true; - forcedScreenRatio = -1; time += elapsedTime; frame++; setCurrent(); @@ -160,25 +146,16 @@ class RenderContext extends h3d.impl.RenderContext { return sl; } - public function computeList(list : hxsl.ShaderList) { - if ( computeLink != null ) - throw "Use computeDispatch to dispatch computeList"; - computeLink = list; - } + public function computeDispatch( shader : hxsl.Shader, x = 1, y = 1, z = 1 ) { - public function computeDispatch( ?shader : hxsl.Shader, x = 1, y = 1, z = 1 ) { var prev = h3d.impl.RenderContext.get(); if( prev != this ) start(); // compile shader globals.resetChannels(); - if ( shader != null ) { - tmpComputeLink.s = shader; - computeLink = tmpComputeLink; - } - for ( s in computeLink ) - s.updateConstants(globals); + shader.updateConstants(globals); + computeLink.s = shader; var rt = hxsl.Cache.get().link(computeLink, Compute); // upload buffers engine.driver.selectShader(rt); @@ -186,15 +163,12 @@ class RenderContext extends h3d.impl.RenderContext { buf.grow(rt); fillGlobals(buf, rt); engine.uploadShaderBuffers(buf, Globals); - fillParams(buf, rt, computeLink, true); + fillParams(buf, rt, computeLink); engine.uploadShaderBuffers(buf, Params); engine.uploadShaderBuffers(buf, Textures); engine.uploadShaderBuffers(buf, Buffers); engine.driver.computeDispatch(x,y,z); - @:privateAccess engine.dispatches++; - if ( computeLink == tmpComputeLink ) - tmpComputeLink.s = null; - computeLink = null; + computeLink.s = null; if( prev != this ) { done(); @@ -207,34 +181,6 @@ class RenderContext extends h3d.impl.RenderContext { lights = l; } - public function getCameraFrustumBuffer() { - if ( cameraFrustumBuffer == null ) - cameraFrustumBuffer = hxd.impl.Allocator.get().allocBuffer( 6, hxd.BufferFormat.VEC4_DATA, UniformDynamic ); - - if ( !cameraFrustumUploaded ) { - inline function fillBytesWithPlane( buffer : haxe.io.Bytes, startPos : Int, plane : h3d.col.Plane ) { - buffer.setFloat( startPos, @:privateAccess plane.nx ); - buffer.setFloat( startPos + 4, @:privateAccess plane.ny ); - buffer.setFloat( startPos + 8, @:privateAccess plane.nz ); - buffer.setFloat( startPos + 12, @:privateAccess plane.d ); - } - - var tmp = haxe.io.Bytes.alloc( 16 * 6 ); - var frustum = camera.frustum; - fillBytesWithPlane( tmp, 0, frustum.pleft ); - fillBytesWithPlane( tmp, 16, frustum.pright ); - fillBytesWithPlane( tmp, 32, frustum.ptop ); - fillBytesWithPlane( tmp, 48, frustum.pbottom ); - fillBytesWithPlane( tmp, 64, frustum.pfar ); - fillBytesWithPlane( tmp, 80, frustum.pnear ); - - cameraFrustumBuffer.uploadBytes(tmp, 0, 6); - cameraFrustumUploaded = true; - } - - return cameraFrustumBuffer; - } - public function uploadParams() { fillParams(shaderBuffers, drawPass.shader, drawPass.shaders); engine.uploadShaderBuffers(shaderBuffers, Params); @@ -266,19 +212,7 @@ class RenderContext extends h3d.impl.RenderContext { } passes = []; lights = null; - - cameraFrustumUploaded = false; - - cameraPreviousViewProj.load(cameraViewProj); - computeVelocity = false; - clearCurrent(); } - override public function dispose() { - super.dispose(); - if ( cameraFrustumBuffer != null ) - hxd.impl.Allocator.get().disposeBuffer( cameraFrustumBuffer ); - } - } \ No newline at end of file diff --git a/h3d/scene/Renderer.hx b/h3d/scene/Renderer.hx index 7c89012402..c8ffa70d0c 100644 --- a/h3d/scene/Renderer.hx +++ b/h3d/scene/Renderer.hx @@ -28,11 +28,6 @@ class Renderer extends hxd.impl.AnyProps { var backToFront : h3d.pass.PassList -> Void; var debugging = false; - #if editor - public var showEditorGuides = false; - public var showEditorOutlines = true; - #end - public var effects : Array = []; public var renderMode : RenderMode = Default; @@ -103,28 +98,13 @@ class Renderer extends hxd.impl.AnyProps { for( p in passes ) { var z = p.obj.absPos._41 * cam._13 + p.obj.absPos._42 * cam._23 + p.obj.absPos._43 * cam._33 + cam._43; var w = p.obj.absPos._41 * cam._14 + p.obj.absPos._42 * cam._24 + p.obj.absPos._43 * cam._34 + cam._44; - p.depth = w > 0.0 ? z / w : - z / w; + p.depth = z / w; } if( frontToBack ) - passes.sort( - function(p1, p2) { - if ( p1.pass.layer != p2.pass.layer ) - return p1.pass.layer - p2.pass.layer; - if ( p1.depth == p2.depth ) - return 0; - return p1.depth > p2.depth ? 1 : -1; - } - ); + passes.sort(function(p1, p2) return p1.pass.layer == p2.pass.layer ? (p1.depth > p2.depth ? 1 : -1) : p1.pass.layer - p2.pass.layer); + else - passes.sort( - function(p1, p2) { - if ( p1.pass.layer != p2.pass.layer ) - return p1.pass.layer - p2.pass.layer; - if ( p1.depth == p2.depth ) - return 0; - return p1.depth < p2.depth ? 1 : -1; - } - ); + passes.sort(function(p1, p2) return p1.pass.layer == p2.pass.layer ? (p1.depth > p2.depth ? -1 : 1) : p1.pass.layer - p2.pass.layer); } inline function clear( ?color, ?depth, ?stencil ) { @@ -196,12 +176,6 @@ class Renderer extends hxd.impl.AnyProps { public function start() { } - public function startEffects() { - for ( e in effects ) - if ( e.enabled ) - e.start(this); - } - public function process( passes : Array ) { hasSetTarget = false; for( p in allPasses ) diff --git a/h3d/scene/Scene.hx b/h3d/scene/Scene.hx index 4f6769852b..e707de33db 100644 --- a/h3d/scene/Scene.hx +++ b/h3d/scene/Scene.hx @@ -20,11 +20,6 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I **/ public var renderer(default,set) : Renderer; - public var offsetX : Float = 0; - public var offsetY : Float = 0; - public var ratioX : Float = 1; - public var ratioY : Float = 1; - var ctx : RenderContext; var interactives : Array; @:allow(h3d.scene.Interactive) @@ -124,14 +119,9 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I return null; if( hitInteractives.length == 0 ) { - var x = event.relX - offsetX; - var y = event.relY - offsetY; - - var width = ratioX * window.width; - var height = ratioY * window.height; - var screenX = (x / width - 0.5) * 2; - var screenY = -(y / height - 0.5) * 2; + var screenX = (event.relX / window.width - 0.5) * 2; + var screenY = -(event.relY / window.height - 0.5) * 2; var p0 = camera.unproject(screenX, screenY, 0); var p1 = camera.unproject(screenX, screenY, 1); var r = h3d.col.Ray.fromPoints(p0.toPoint(), p1.toPoint()); @@ -189,10 +179,9 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I var wfactor = 0.; // adjust result with better precision - if( i.preciseShape != null || !i.bestMatch ) { - if( !i.isAbsoluteShape ) - r.transform(m); - var hit = (i.preciseShape ?? i.shape).rayIntersection(r, true); + if( i.preciseShape != null ) { + r.transform(m); + var hit = i.preciseShape.rayIntersection(r, i.bestMatch); if( hit > 0 ) { var hitPoint = r.getPoint(hit); i.hitPoint.x = hitPoint.x; @@ -205,8 +194,7 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I var p = i.hitPoint.clone(); p.w = 1; - if( !i.isAbsoluteShape ) - p.transform3x4(i.absPos); + p.transform3x4(i.absPos); p.project(camera.m); i.hitPoint.w = p.z + wfactor; } @@ -343,16 +331,13 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I ctx.start(); renderer.start(); - renderer.startEffects(); #if sceneprof h3d.impl.SceneProf.begin("sync", ctx.frame); #end - mark("sync"); syncRec(ctx); #if sceneprof h3d.impl.SceneProf.end(); h3d.impl.SceneProf.begin("emit", ctx.frame); #end - mark("emit"); emitRec(ctx); #if sceneprof h3d.impl.SceneProf.end(); #end @@ -380,7 +365,7 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I renderer.process(passes); // check that passes have been rendered - #if (debug && !editor) + #if debug if( !ctx.computingStatic && checkPasses) for( p in passes ) if( !p.rendered ) @@ -399,10 +384,6 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I } } - public dynamic function mark(name : String) { - @:privateAccess renderer.mark(name); - } - var prevDB : h3d.mat.Texture; var prevEngine = null; /** diff --git a/h3d/scene/Skin.hx b/h3d/scene/Skin.hx index db9c73c0be..a2640e01e3 100644 --- a/h3d/scene/Skin.hx +++ b/h3d/scene/Skin.hx @@ -8,7 +8,6 @@ class Joint extends Object { super(null); name = j.name; this.skin = skin; - lastFrame = -2; // force first sync // fake parent this.parent = skin; this.index = j.index; @@ -38,30 +37,31 @@ class Joint extends Object { // check if one of our parents has changed // we don't have a posChanged flag since the Joint // is not actualy part of the hierarchy - var p : h3d.scene.Object = skin; + var p = parent; while( p != null ) { - if( p.posChanged) { - update(); + if( p.posChanged ) { + // save the inverse absPos that was used to build the joints absPos + if( skin.jointsAbsPosInv == null ) { + skin.jointsAbsPosInv = new h3d.Matrix(); + skin.jointsAbsPosInv.zero(); + } + if( skin.jointsAbsPosInv._44 == 0 ) + skin.jointsAbsPosInv.inverse3x4(parent.absPos); + parent.syncPos(); + lastFrame = -1; break; } p = p.parent; } - - if( lastFrame != skin.lastFrame) { + if( lastFrame != skin.lastFrame ) { lastFrame = skin.lastFrame; absPos.load(skin.currentAbsPose[index]); + if( skin.jointsAbsPosInv != null && skin.jointsAbsPosInv._44 != 0 ) { + absPos.multiply3x4(absPos, skin.jointsAbsPosInv); + absPos.multiply3x4(absPos, parent.absPos); + } } } - - /** - Force the update of the position of this joint - **/ - @:access(h3d.scene.Skin) - public function update() { - skin.getAbsPos(); - skin.syncJoints(); - lastFrame = -1; - } } class Skin extends MultiMaterial { @@ -72,13 +72,14 @@ class Skin extends MultiMaterial { var currentPalette : Array; var splitPalette : Array>; var jointsUpdated : Bool; + var jointsAbsPosInv : h3d.Matrix; var paletteChanged : Bool; var skinShader : h3d.shader.SkinBase; var jointsGraphics : Graphics; + var additivePose : Array; public var showJoints : Bool; public var enableRetargeting : Bool = true; - public var prevEnableRetargeting : Bool = true; public function new(s, ?mat, ?parent) { super(null, mat, parent); @@ -113,11 +114,9 @@ class Skin extends MultiMaterial { var pt = j.offsets.getMin(); if ( m != null ) { pt.transform(m); - if( relativeTo != null ) pt.transform(relativeTo); b.addSpherePos(pt.x, pt.y, pt.z, j.offsetRay * scale); var pt = j.offsets.getMax(); pt.transform(m); - if( relativeTo != null ) pt.transform(relativeTo); b.addSpherePos(pt.x, pt.y, pt.z, j.offsetRay * scale); } } @@ -170,10 +169,14 @@ class Skin extends MultiMaterial { return skinData; } - public function setJointRelPosition( name : String, pos : h3d.Matrix ) { + public function setJointRelPosition( name : String, pos : h3d.Matrix, additive = false ) { var j = skinData.namedJoints.get(name); if( j == null ) return; - currentRelPose[j.index] = pos; + if( additive ) { + if( additivePose == null ) additivePose = []; + additivePose[j.index] = pos; + } else + currentRelPose[j.index] = pos; jointsUpdated = true; } @@ -251,16 +254,19 @@ class Skin extends MultiMaterial { m.multiply3x4inline(r, absPos); else m.multiply3x4inline(r, currentAbsPose[j.parent.index]); + if( additivePose != null ) { + var a = additivePose[id]; + if( a != null ) m.multiply3x4inline(a, m); + } if( bid >= 0 ) currentPalette[bid].multiply3x4inline(j.transPos, m); } skinShader.bonesMatrixes = currentPalette; + if( jointsAbsPosInv != null ) jointsAbsPosInv._44 = 0; // mark as invalid jointsUpdated = false; - prevEnableRetargeting = enableRetargeting; } override function emit( ctx : RenderContext ) { - calcScreenRatio(ctx); syncJoints(); // In case sync was not called because of culling (eg fixedPosition) if( splitPalette == null ) super.emit(ctx); @@ -303,7 +309,7 @@ class Skin extends MultiMaterial { } else { var i = ctx.drawPass.index; skinShader.bonesMatrixes = splitPalette[i]; - primitive.selectMaterial(i, primitive.screenRatioToLod(curScreenRatio)); + primitive.selectMaterial(i); ctx.uploadParams(); primitive.render(ctx.engine); } diff --git a/h3d/scene/pbr/Environment.hx b/h3d/scene/pbr/Environment.hx index 331c06ec7d..f6b1d18bb4 100644 --- a/h3d/scene/pbr/Environment.hx +++ b/h3d/scene/pbr/Environment.hx @@ -187,6 +187,7 @@ class CubeToPanorama extends h3d.shader.ScreenShader { @param var source : SamplerCube; function fragment() { + var PI = 3.1415926; var fovX = PI * 2; var fovY = PI; var hOffset = (2.0 * PI - fovX) * 0.5; diff --git a/h3d/scene/pbr/LightBuffer.hx b/h3d/scene/pbr/LightBuffer.hx index 0211cebdda..a1167187f8 100644 --- a/h3d/scene/pbr/LightBuffer.hx +++ b/h3d/scene/pbr/LightBuffer.hx @@ -1,7 +1,5 @@ package h3d.scene.pbr; -import h3d.pass.CascadeShadowMap; - class LightBuffer { public var defaultForwardShader = new h3d.shader.pbr.DefaultForward(); @@ -12,12 +10,10 @@ class LightBuffer { var MAX_DIR_SHADOW = 1; var MAX_SPOT_SHADOW = 2; var MAX_POINT_SHADOW = 2; - var MAX_CASCADE_COUNT = 4; var pointLightsShadow : Array = []; var spotLightsShadow : Array = []; var dirLightsShadow : Array = []; - var cascadeLight : DirLight; var pointLights : Array = []; var spotLights : Array = []; var dirLights : Array = []; @@ -26,7 +22,6 @@ class LightBuffer { final POINT_LIGHT_INFO_SIZE = 3; final SPOT_LIGHT_INFO_SIZE = 8; final DIR_LIGHT_INFO_SIZE = 5; - final CASCADE_SHADOW_INFO_SIZE = 13; public function new() { @@ -39,21 +34,18 @@ class LightBuffer { size += MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE; size += MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE; size += MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE; - size += CASCADE_SHADOW_INFO_SIZE; size = hxd.Math.imax(1, size); // Avoid empty buffer lightInfos = new hxd.FloatBuffer(size * stride); defaultForwardShader.lightInfos = new h3d.Buffer(size, hxd.BufferFormat.make([{ name : "uniformData", type : DVec4 }]), [UniformBuffer, Dynamic]); defaultForwardShader.BUFFER_SIZE = size; defaultForwardShader.dirLightStride = DIR_LIGHT_INFO_SIZE * MAX_DIR_LIGHT; defaultForwardShader.pointLightStride = POINT_LIGHT_INFO_SIZE * MAX_POINT_LIGHT; - defaultForwardShader.spotLightStride = SPOT_LIGHT_INFO_SIZE * MAX_SPOT_LIGHT; } public function setBuffers( s : h3d.shader.pbr.DefaultForward ) { s.lightInfos = defaultForwardShader.lightInfos; s.dirLightStride = defaultForwardShader.dirLightStride; s.pointLightStride = defaultForwardShader.pointLightStride; - s.spotLightStride = defaultForwardShader.spotLightStride; s.cameraPosition = defaultForwardShader.cameraPosition; s.emissivePower = defaultForwardShader.emissivePower; s.BUFFER_SIZE = defaultForwardShader.BUFFER_SIZE; @@ -64,7 +56,6 @@ class LightBuffer { s.DIR_SHADOW_COUNT = defaultForwardShader.DIR_SHADOW_COUNT; s.POINT_SHADOW_COUNT = defaultForwardShader.POINT_SHADOW_COUNT; s.SPOT_SHADOW_COUNT = defaultForwardShader.SPOT_SHADOW_COUNT; - s.CASCADE_COUNT = defaultForwardShader.CASCADE_COUNT; for( i in 0 ... defaultForwardShader.POINT_SHADOW_COUNT ) s.pointShadowMaps[i] = defaultForwardShader.pointShadowMaps[i]; @@ -72,8 +63,6 @@ class LightBuffer { s.spotShadowMaps[i] = defaultForwardShader.spotShadowMaps[i]; for( i in 0 ... defaultForwardShader.DIR_SHADOW_COUNT ) s.dirShadowMaps[i] = defaultForwardShader.dirShadowMaps[i]; - for ( i in 0... defaultForwardShader.CASCADE_COUNT ) - s.cascadeShadowMaps[i] = defaultForwardShader.cascadeShadowMaps[i]; s.USE_INDIRECT = defaultForwardShader.USE_INDIRECT; if( s.USE_INDIRECT ) { @@ -86,14 +75,14 @@ class LightBuffer { } } - function fillFloats( b : hxd.FloatBuffer, f1 : Float, f2 : Float, f3 : Float, f4 : Float, i : Int ) { + inline function fillFloats( b : hxd.FloatBuffer, f1 : Float, f2 : Float, f3 : Float, f4 : Float, i : Int ) { b[i+0] = f1; b[i+1] = f2; b[i+2] = f3; b[i+3] = f4; } - function fillVector( b : hxd.FloatBuffer, v : h3d.Vector, i : Int ) { + inline function fillVector( b : hxd.FloatBuffer, v : h3d.Vector, i : Int ) { b[i+0] = v.r; b[i+1] = v.g; b[i+2] = v.b; @@ -127,7 +116,6 @@ class LightBuffer { pointLightsShadow = []; spotLightsShadow = []; dirLightsShadow = []; - cascadeLight = null; pointLights = []; spotLights = []; dirLights = []; @@ -146,17 +134,14 @@ class LightBuffer { if (dirLightCount + dirShadowCount < MAX_DIR_LIGHT) { var hasShadow = dl.shadows != null && dl.shadows.enabled && dl.shadows.mode != None && shadows; if (hasShadow && dirShadowCount < MAX_DIR_SHADOW) { - var cascade = Std.downcast(dl.shadows, CascadeShadowMap); - if ( cascade != null ) - cascadeLight = dl; - else - dirLightsShadow.push(dl); + dirLightsShadow.push(dl); dirShadowCount++; } else { dirLights.push(dl); dirLightCount++; } } + } var pl = Std.downcast(l, PointLight); @@ -201,13 +186,13 @@ class LightBuffer { var p : h3d.scene.pbr.Renderer.RenderProps = pbrRenderer.props; var s = defaultForwardShader; + s.cameraPosition = ctx.camera.pos; s.emissivePower = p.emissive * p.emissive; s.pointLightCount = 0; s.spotLightCount = 0; s.dirLightCount = 0; - s.CASCADE_COUNT = 0; // Safe Reset for( i in 0 ... lightInfos.length ) @@ -222,8 +207,8 @@ class LightBuffer { var i = li * DIR_LIGHT_INFO_SIZE * 4; var pbr = @:privateAccess dl.pbr; fillVector(lightInfos, pbr.lightColor, i); - lightInfos[i+3] = 1.0; fillVector(lightInfos, pbr.lightDir, i+4); + lightInfos[i+3] = 1.0; lightInfos[i+7] = dl.shadows.bias; s.dirShadowMaps[li] = dl.shadows.getShadowTex(); var mat = dl.shadows.getShadowProj(); @@ -238,8 +223,8 @@ class LightBuffer { var i = (li + dirLightsShadow.length) * DIR_LIGHT_INFO_SIZE * 4; var pbr = @:privateAccess dl.pbr; fillVector(lightInfos, pbr.lightColor, i); - lightInfos[i+3] = -1.0; fillVector(lightInfos, pbr.lightDir, i+4); + lightInfos[i+3] = -1.0; } // Point Light With Shadows @@ -311,29 +296,6 @@ class LightBuffer { lightInfos[i+14] = -1.0; } - // Cascade shadows - if ( cascadeLight != null ) { - var offset = (MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE + MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE + MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE) * 4 ; - var i = offset; - var pbr = @:privateAccess cascadeLight.pbr; - fillVector(lightInfos, pbr.lightColor, i); - lightInfos[i+3] = 0.0; - fillVector(lightInfos, pbr.lightDir, i+4); - lightInfos[i+7] = 0.0; - var cascadeShadow = cast(cascadeLight.shadows, CascadeShadowMap); - var shadowMaps = cascadeShadow.getShadowTextures(); - s.CASCADE_COUNT = cascadeShadow.cascade; - var mat = cascadeShadow.cascadeViewProj; - fillFloats(lightInfos, mat._11, mat._21, mat._31, mat._41, i+8); - fillFloats(lightInfos, mat._12, mat._22, mat._32, mat._42, i+12); - fillFloats(lightInfos, mat._13, mat._23, mat._33, mat._43, i+16); - for ( index in 0...cascadeShadow.cascade ) { - s.cascadeShadowMaps[index] = shadowMaps[index]; - fillVector(lightInfos, cascadeShadow.getCascadeScale(index).toVector(), i + 20 + index * 8); - fillVector(lightInfos, cascadeShadow.getCascadeOffset(index).toVector(), i + 24 + index * 8); - } - } - s.dirLightCount = dirLights.length; s.pointLightCount = pointLights.length; s.spotLightCount = spotLights.length; @@ -347,7 +309,6 @@ class LightBuffer { pointLightsShadow = []; spotLightsShadow = []; dirLightsShadow = []; - cascadeLight = null; var pbrIndirect = @:privateAccess pbrRenderer.pbrIndirect; s.USE_INDIRECT = pbrRenderer.env != null && pbrIndirect.irrLut != null; diff --git a/h3d/scene/pbr/LightSystem.hx b/h3d/scene/pbr/LightSystem.hx index f644cb083c..122227ea6d 100644 --- a/h3d/scene/pbr/LightSystem.hx +++ b/h3d/scene/pbr/LightSystem.hx @@ -44,6 +44,9 @@ class LightSystem extends h3d.scene.LightSystem { public function drawScreenLights( r : h3d.scene.Renderer, lightPass : h3d.pass.ScreenFx, shadows : Bool = true ) { var plight = @:privateAccess ctx.lights; + var currentTarget = ctx.engine.getCurrentTarget(); + var width = currentTarget == null ? ctx.engine.width : currentTarget.width; + var height = currentTarget == null ? ctx.engine.height : currentTarget.height; while( plight != null ) { var light = Std.downcast(plight, h3d.scene.pbr.Light); if( light != null && light.primitive == null ) { diff --git a/h3d/scene/pbr/Renderer.hx b/h3d/scene/pbr/Renderer.hx index 2989220509..b0f966c77f 100644 --- a/h3d/scene/pbr/Renderer.hx +++ b/h3d/scene/pbr/Renderer.hx @@ -62,9 +62,6 @@ class DepthCopy extends h3d.shader.ScreenShader { } class Renderer extends h3d.scene.Renderer { - - public static final LIGHTMAP_STENCIL = 0x80; - var slides = new h3d.pass.ScreenFx(new h3d.shader.pbr.Slides()); var pbrOut = new h3d.pass.ScreenFx(new h3d.shader.ScreenShader()); var tonemap = new h3d.pass.ScreenFx(new h3d.shader.pbr.ToneMapping()); @@ -90,10 +87,8 @@ class Renderer extends h3d.scene.Renderer { depth : (null:h3d.mat.Texture), hdr : (null:h3d.mat.Texture), ldr : (null:h3d.mat.Texture), - velocity : (null:h3d.mat.Texture), }; - public var cullingDistanceFactor : Float = 0.0; public var skyMode : SkyMode = Hide; public var toneMode : TonemapMap = Reinhard; public var displayMode : DisplayMode = Pbr; @@ -101,11 +96,6 @@ class Renderer extends h3d.scene.Renderer { public var exposure(get,set) : Float; var debugShadowMapIndex = 1; - #if editor - var outline = new h3d.pass.ScreenFx(new hide.Renderer.ScreenOutline()); - var outlineBlur = new h3d.pass.Blur(4); - #end - static var ALPHA : hxsl.Output = Swiz(Value("output.color"),[W]); var output = new h3d.pass.Output("default",[ Value("output.color"), @@ -116,8 +106,7 @@ class Renderer extends h3d.scene.Renderer { #else Vec4([Value("output.metalness"), Value("output.roughness"), Value("output.emissive"), ALPHA]), #end - Vec4([Value("output.depth"),Const(0), Const(0), ALPHA /* ? */]), - Vec4([Value("output.velocity", 2), Const(0), Const(0)]) + Vec4([Value("output.depth"),Const(0), Const(0), ALPHA /* ? */]) ]); var decalsOutput = new h3d.pass.Output("decals",[ Vec4([Swiz(Value("output.color"),[X,Y,Z]), Value("output.albedoStrength",1)]), @@ -142,11 +131,6 @@ class Renderer extends h3d.scene.Renderer { Value("output.color"), Vec4([Value("output.depth"),Const(0),Const(0),h3d.scene.pbr.Renderer.ALPHA]) ]); - var colorDepthVelocityOutput = new h3d.pass.Output("colorDepthVelocityOutput",[ - Value("output.color"), - Vec4([Value("output.depth"),Const(0),Const(0),h3d.scene.pbr.Renderer.ALPHA]), - Vec4([Value("output.velocity", 2), Const(0), Const(0)]) - ]); public function new(?env) { super(); @@ -158,18 +142,18 @@ class Renderer extends h3d.scene.Renderer { pbrOut.pass.setBlendMode(Add); pbrOut.pass.stencil = new h3d.mat.Stencil(); pbrOut.pass.stencil.setOp(Keep, Keep, Keep); - pbrOut.pass.stencil.setFunc(NotEqual, LIGHTMAP_STENCIL, LIGHTMAP_STENCIL, LIGHTMAP_STENCIL); // ignore already drawn volumetricLightMap areas + pbrOut.pass.stencil.setFunc(NotEqual, 0x80, 0x80, 0x80); // ignore already drawn volumetricLightMap areas allPasses.push(output); allPasses.push(defaultPass); allPasses.push(decalsOutput); allPasses.push(colorDepthOutput); - allPasses.push(colorDepthVelocityOutput); allPasses.push(emissiveDecalsOutput); allPasses.push(new h3d.pass.Shadows(null)); refreshProps(); - #if editor - cullingDistanceFactor = hide.Ide.inst.ideConfig.cullingDistanceFactor; - #end + } + + override function dispose() { + super.dispose(); } override function addShader(s:hxsl.Shader) { @@ -187,10 +171,6 @@ class Renderer extends h3d.scene.Renderer { return output; case "decal" #if MRT_low , "emissiveDecal" #end: return decalsOutput; - #if editor - case "highlight", "highlightBack": - return defaultPass; - #end } return super.getPassByName(name); } @@ -229,41 +209,15 @@ class Renderer extends h3d.scene.Renderer { }); } - inline function cullPassesWithDistance( passes : h3d.pass.PassList, f : h3d.col.Collider -> Bool ) { - var prevCollider = null; - var prevResult = true; - passes.filter(function(p) { - var col = p.obj.cullingCollider; - if( col == null ) - return true; - if( col != prevCollider ) { - prevCollider = col; - prevResult = f(col); - if ( prevResult ) { - var dim = col.dimension() * cullingDistanceFactor; - dim = dim * dim; - prevResult = dim > ctx.camera.pos.distanceSq(p.obj.getAbsPos().getPosition()); - } - } - return prevResult; - }); - } - override function draw( name : String ) { var passes = get(name); - if ( cullingDistanceFactor > 0.0 ) - cullPassesWithDistance(passes, function(col) return col.inFrustum(ctx.camera.frustum)); - else - cullPasses(passes, function(col) return col.inFrustum(ctx.camera.frustum)); + cullPasses(passes, function(col) return col.inFrustum(ctx.camera.frustum)); defaultPass.draw(passes); passes.reset(); } function renderPass(p:h3d.pass.Output, passes, ?sort) { - if ( cullingDistanceFactor > 0.0 ) - cullPassesWithDistance(passes, function(col) return col.inFrustum(ctx.camera.frustum)); - else - cullPasses(passes, function(col) return col.inFrustum(ctx.camera.frustum)); + cullPasses(passes, function(col) return col.inFrustum(ctx.camera.frustum)); p.draw(passes, sort); passes.reset(); } @@ -277,14 +231,17 @@ class Renderer extends h3d.scene.Renderer { if( ctx.lightSystem != null ) ctx.lightSystem.drawPasses = ctx.engine.drawCalls - count; end(); - if (ls != null) { - while (ls.lightingShaders.length != 0) - ls.lightingShaders.pop(); - ls.lightBuffer.sync(ctx); + var pbrLightSystem : h3d.scene.pbr.LightSystem = cast ctx.lightSystem; + if (pbrLightSystem != null) { + while (pbrLightSystem.lightingShaders.length != 0) { + pbrLightSystem.lightingShaders.pop(); + } + pbrLightSystem.lightBuffer.sync(ctx); } begin(Lighting); if ( displayMode == Performance ) { + var ls = Std.downcast(getLightSystem(), h3d.scene.pbr.LightSystem); var s = new h3d.shader.pbr.Light.Performance(); performance.shader.gradient = getLightingPerformanceGradient(); s.maxLights = performance.shader.gradient.width - 1; @@ -319,25 +276,17 @@ class Renderer extends h3d.scene.Renderer { performance.shader.hdrMap = perf; } - beforeIndirect(); mark("Indirect Lighting"); - doIndirectLighting(); - afterIndirect(); - - end(); - } - - function doIndirectLighting() { - if( !renderLightProbes() && indirectEnv && env != null && env.power > 0.0 ) { + if( !renderLightProbes() && indirectEnv && env != null && env.power > 0.0 ) { pbrProps.isScreen = true; pbrIndirect.drawIndirectDiffuse = true; pbrIndirect.drawIndirectSpecular = true; pbrOut.render(); } + + end(); } - function beforeIndirect() {} - function afterIndirect() {} function beforeFullScreenLights() {} function afterFullScreenLights() {} @@ -386,12 +335,7 @@ class Renderer extends h3d.scene.Renderer { } function begin( step : h3d.impl.RendererFX.Step ) { - switch (step) { - case Custom(n): - mark(n); - default: - mark(step.getName()); - } + mark(step.getName()); for( f in effects ) if( f.enabled ) @@ -415,48 +359,7 @@ class Renderer extends h3d.scene.Renderer { } } - function renderEditorOutline() { - #if editor - if (showEditorGuides) { - renderPass(defaultPass, get("debuggeom"), backToFront); - renderPass(defaultPass, get("debuggeom_alpha"), backToFront); - } - - if (showEditorOutlines) { - var outlineTex = allocTarget("outline", true); - ctx.engine.pushTarget(outlineTex); - clear(0); - draw("highlightBack"); - draw("highlight"); - ctx.engine.popTarget(); - var outlineBlurTex = allocTarget("outlineBlur", false); - outline.pass.setBlendMode(Alpha); - outlineBlur.apply(ctx, outlineTex, outlineBlurTex); - outline.shader.texture = outlineBlurTex; - outline.render(); - } - #end - } - - function renderEditorOverlay() { - #if editor - renderPass(defaultPass, get("overlay"), backToFront); - renderPass(defaultPass, get("ui"), backToFront); - #end - } - function end() { - #if editor - switch( currentStep ) { - case MainDraw: - case BeforeTonemapping: - renderEditorOutline(); - case Overlay: - renderEditorOverlay(); - default: - } - #end - for( f in effects ) if( f.enabled ) f.end(this, currentStep); @@ -489,8 +392,6 @@ class Renderer extends h3d.scene.Renderer { textures.depth = allocTarget("depth", true, 1., R32F); textures.hdr = allocTarget("hdrOutput", true, 1, #if MRT_low RGB10A2 #else RGBA16F #end); textures.ldr = allocTarget("ldrOutput"); - if ( ctx.computeVelocity ) - textures.velocity = allocTarget("velocity", true, 1., RG16F ); } public function getPbrDepth() { @@ -504,7 +405,6 @@ class Renderer extends h3d.scene.Renderer { ctx.setGlobal("occlusionMap", { texture : textures.pbr, channel : hxsl.Channel.B }); ctx.setGlobal("hdrMap", textures.hdr); ctx.setGlobal("ldrMap", textures.ldr); - ctx.setGlobal("velocity", textures.velocity); ctx.setGlobal("global.time", ctx.time); ctx.setGlobal("camera.position", ctx.camera.pos); ctx.setGlobal("camera.inverseViewProj", ctx.camera.getInverseViewProj()); @@ -626,12 +526,9 @@ class Renderer extends h3d.scene.Renderer { } function getPbrRenderTargets( depth : Bool ) { - var targets = [textures.albedo, textures.normal, textures.pbr #if !MRT_low , textures.other #end]; if ( depth ) - targets.push(getPbrDepth()); - if ( ctx.computeVelocity ) - targets.push(textures.velocity); - return targets; + return [textures.albedo, textures.normal, textures.pbr #if !MRT_low , textures.other #end, getPbrDepth()]; + return [textures.albedo, textures.normal, textures.pbr #if !MRT_low , textures.other #end]; } override function render() { @@ -762,17 +659,23 @@ class Renderer extends h3d.scene.Renderer { debugging = true; hxd.Window.getInstance().addEventTarget(onEvent); } - renderEditorOutline(); - renderEditorOverlay(); + #if editor + renderPass(defaultPass, get("overlay"), backToFront); + renderPass(defaultPass, get("ui"), backToFront); + #end case Performance: if( enableFXAA ) { - mark("FXAA"); + mark("FXAA"); fxaa.apply(ldr); - } else + } + else { copy(ldr, null); + } performance.render(); - renderEditorOutline(); - renderEditorOverlay(); + #if editor + renderPass(defaultPass, get("overlay"), backToFront); + renderPass(defaultPass, get("ui"), backToFront); + #end } if( debugging && displayMode != Debug ) { debugging = false; diff --git a/h3d/shader/Base2d.hx b/h3d/shader/Base2d.hx index 6eb5cbf3d2..d292fc2b47 100644 --- a/h3d/shader/Base2d.hx +++ b/h3d/shader/Base2d.hx @@ -2,81 +2,113 @@ package h3d.shader; class Base2d extends hxsl.Shader { - static var SRC = { - - @input var input : { - var position : Vec2; - var uv : Vec2; - var color : Vec4; - }; - - var output : { - var position : Vec4; - var color : Vec4; - }; - - @global var time : Float; - @param var zValue : Float; - @param var texture : Sampler2D; - - var spritePosition : Vec4; - var absolutePosition : Vec4; - var pixelColor : Vec4; - var textureColor : Vec4; - @var var calculatedUV : Vec2; - - @const var isRelative : Bool; - @param var color : Vec4; - @param var absoluteMatrixA : Vec3; - @param var absoluteMatrixB : Vec3; - @param var filterMatrixA : Vec3; - @param var filterMatrixB : Vec3; - @const var hasUVPos : Bool; - @param var uvPos : Vec4; - - @const var killAlpha : Bool; - @const var pixelAlign : Bool; - @param var halfPixelInverse : Vec2; - @param var viewportA : Vec3; - @param var viewportB : Vec3; - - var outputPosition : Vec4; - - function __init__() { - spritePosition = vec4(input.position, zValue, 1); - if( isRelative ) { - absolutePosition.x = vec3(spritePosition.xy,1).dot(absoluteMatrixA); - absolutePosition.y = vec3(spritePosition.xy,1).dot(absoluteMatrixB); - absolutePosition.zw = spritePosition.zw; - } else - absolutePosition = spritePosition; - calculatedUV = hasUVPos ? input.uv * uvPos.zw + uvPos.xy : input.uv; - pixelColor = isRelative ? color * input.color : input.color; - textureColor = texture.get(calculatedUV); - pixelColor *= textureColor; - } - - function vertex() { - // transform from global to render texture coordinates - var tmp = vec3(absolutePosition.xy, 1); - tmp = vec3(tmp.dot(filterMatrixA), tmp.dot(filterMatrixB), 1); - // transform to viewport - outputPosition = vec4( - tmp.dot(viewportA), - tmp.dot(viewportB), - absolutePosition.zw - ); - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb219690(v=vs.85).aspx - if( pixelAlign ) outputPosition.xy -= halfPixelInverse; - output.position = outputPosition; - } - - function fragment() { - if( killAlpha && pixelColor.a < 0.001 ) discard; - output.color = pixelColor; - } - - }; + static var SRC = { + @input var input : { + var position : Vec2; + var uv : Vec2; + var color : Vec4; + }; + var output : { + var position : Vec4; + var color : Vec4; + }; + + @global var time : Float; + @param var zValue : Float; + @param var texture : Sampler2D; + + var spritePosition : Vec4; + var absolutePosition : Vec4; + var pixelColor : Vec4; + var textureColor : Vec4; + @var var calculatedUV : Vec2; + + @const var isRelative : Bool; + @param var color : Vec4; + @param var absoluteMatrixA : Vec3; + @param var absoluteMatrixB : Vec3; + @param var filterMatrixA : Vec3; + @param var filterMatrixB : Vec3; + @const var hasUVPos : Bool; + @param var uvPos : Vec4; + + @const var killAlpha : Bool; + @const var pixelAlign : Bool; + @param var halfPixelInverse : Vec2; + @param var viewportA : Vec3; + @param var viewportB : Vec3; + + var outputPosition : Vec4; + + function __init__() { + spritePosition = vec4(input.position, zValue, 1); + + // Calculate absolute position + if (isRelative) { + var tempVec = vec3(spritePosition.xy, 1); + absolutePosition = vec4( + tempVec.dot(absoluteMatrixA), + tempVec.dot(absoluteMatrixB), + spritePosition.z, + spritePosition.w + ); + } else { + absolutePosition = spritePosition; + } + + // Calculate UV coordinates + if (hasUVPos) { + calculatedUV = input.uv * uvPos.zw + uvPos.xy; + } else { + calculatedUV = input.uv; + } + + // Calculate color + if (isRelative) { + pixelColor = color * input.color; + } else { + pixelColor = input.color; + } + + // Sample texture color and multiply + textureColor = texture.get(calculatedUV); + pixelColor *= textureColor; + } + + function vertex() { + // Transform absolute position to render texture coordinates + var tempVec = vec3(absolutePosition.xy, 1); + var filteredPos = vec3( + tempVec.dot(filterMatrixA), + tempVec.dot(filterMatrixB), + 1 + ); + + // Transform to viewport space + outputPosition = vec4( + filteredPos.dot(viewportA), + filteredPos.dot(viewportB), + absolutePosition.z, + absolutePosition.w + ); + + // Pixel alignment correction + if (pixelAlign) { + outputPosition.xy -= halfPixelInverse; + } + + output.position = outputPosition; + } + + function fragment() { + // Discard if alpha is too low + if (killAlpha && pixelColor.a < 0.001) { + discard; + } + + output.color = pixelColor; + } + }; } \ No newline at end of file diff --git a/h3d/shader/BaseMesh.hx b/h3d/shader/BaseMesh.hx index 237d135bb0..e61fc2fb8d 100644 --- a/h3d/shader/BaseMesh.hx +++ b/h3d/shader/BaseMesh.hx @@ -11,12 +11,10 @@ class BaseMesh extends hxsl.Shader { var projFlip : Float; var projDiag : Vec3; var viewProj : Mat4; - var previousViewProj : Mat4; var inverseViewProj : Mat4; var zNear : Float; var zFar : Float; @var var dir : Vec3; - var jitterOffsets : Vec4; }; @global var global : { @@ -24,7 +22,6 @@ class BaseMesh extends hxsl.Shader { var pixelSize : Vec2; @perObject var modelView : Mat4; @perObject var modelViewInverse : Mat4; - @perObject var previousModelView : Mat4; }; @input var input : { @@ -38,25 +35,19 @@ class BaseMesh extends hxsl.Shader { var depth : Float; var normal : Vec3; var worldDist : Float; - var velocity : Vec2; }; var relativePosition : Vec3; var transformedPosition : Vec3; - var previousTransformedPosition : Vec3; var pixelTransformedPosition : Vec3; var transformedNormal : Vec3; var projectedPosition : Vec4; - var previousProjectedPosition : Vec4; var pixelColor : Vec4; var depth : Float; - var ndcPosition : Vec2; - var previousNdcPosition : Vec2; var screenUV : Vec2; var specPower : Float; var specColor : Vec3; var worldDist : Float; - var pixelVelocity : Vec2; @param var color : Vec4; @range(0,100) @param var specularPower : Float; @@ -68,8 +59,6 @@ class BaseMesh extends hxsl.Shader { relativePosition = input.position; transformedPosition = relativePosition * global.modelView.mat3x4(); projectedPosition = vec4(transformedPosition, 1) * camera.viewProj; - previousTransformedPosition = relativePosition * global.previousModelView.mat3x4(); - previousProjectedPosition = vec4(previousTransformedPosition, 1) * camera.previousViewProj; transformedNormal = (input.normal * global.modelView.mat3()).normalize(); camera.dir = (camera.position - transformedPosition).normalize(); pixelColor = color; @@ -83,15 +72,10 @@ class BaseMesh extends hxsl.Shader { function __init__fragment() { transformedNormal = transformedNormal.normalize(); // same as __init__, but will force calculus inside fragment shader, which limits varyings - ndcPosition = projectedPosition.xy / projectedPosition.w; - previousNdcPosition = previousProjectedPosition.xy / previousProjectedPosition.w; - screenUV = screenToUv(ndcPosition); + screenUV = screenToUv(projectedPosition.xy / projectedPosition.w); depth = projectedPosition.z / projectedPosition.w; // in case it's used in vertex : we don't want to interpolate in screen space specPower = specularPower; specColor = specularColor * specularAmount; - ndcPosition -= camera.jitterOffsets.xy; - previousNdcPosition -= camera.jitterOffsets.zw; - pixelVelocity = ( previousNdcPosition - ndcPosition ) * vec2(0.5, -0.5); } function vertex() { @@ -104,7 +88,6 @@ class BaseMesh extends hxsl.Shader { output.depth = depth; output.normal = #if MRT_low packNormal(transformedNormal).rgb #else transformedNormal #end; output.worldDist = worldDist; - output.velocity = pixelVelocity; } }; diff --git a/h3d/shader/Blur.hx b/h3d/shader/Blur.hx index dfdcc65ae8..da46cbb94d 100644 --- a/h3d/shader/Blur.hx +++ b/h3d/shader/Blur.hx @@ -4,7 +4,8 @@ class Blur extends ScreenShader { static var SRC = { - @param var inverseProj : Mat4; + @param var cameraInverseViewProj : Mat4; + @param var texture : Sampler2D; @param var depthTexture : Sampler2D; @param @const var Quality : Int; @@ -18,7 +19,8 @@ class Blur extends ScreenShader { @param var fixedColor : Vec4; @param @const var isDepthDependant : Bool; - @param var depthThreshold : Float = 1.0; + @param @const var hasNormal : Bool; + @param var normalTexture : Sampler2D; @param @const var isCube : Bool; @param var cubeTexture : SamplerCube; @@ -26,32 +28,19 @@ class Blur extends ScreenShader { function fragment() { if( isDepthDependant ) { - var dimensions = texture.size(); - var invDimensions = 1.0 / dimensions; - var coord = fragCoord.xy; - var fragUV = coord.xy * invDimensions; - var p = getViewPosition(fragUV); - var c = texture.get(fragUV); + var pcur = getPosition(input.uv); + var ccur = texture.get(input.uv); var color = vec4(0, 0, 0, 0); - - var isEdge = false; - - @unroll for( i in -Quality...Quality + 1 ) { - var curCoord = floor(coord + ( pixel * dimensions ) * vec2(1.0) * i) + vec2(0.5); - var nearestUV = curCoord * invDimensions; - var pcur = getViewPosition(nearestUV); - var d = abs(pcur.z - p.z); - isEdge = isEdge || ( d > depthThreshold ); - } - + var ncur = unpackNormal(normalTexture.get(input.uv)); @unroll for( i in -Quality + 1...Quality ) { - var curCoord = floor(coord + ( pixel * dimensions ) * offsets[i < 0 ? -i : i] * i) + vec2(0.5); - var nearestUV = curCoord * invDimensions; - var uv = fragUV + pixel * offsets[i < 0 ? -i : i] * i; - var ccur = texture.get( ( isEdge ) ? nearestUV : uv ); - var pcur = getViewPosition(nearestUV); - var d = abs(pcur.z - p.z); - c = ( d > depthThreshold ) ? c : ccur; + var uv = input.uv + pixel * offsets[i < 0 ? -i : i]; + var c = texture.get(uv); + var p = getPosition(uv); + var d = (p - pcur).dot(p - pcur); + var n = unpackNormal(normalTexture.get(uv)); + + c = mix(ccur, c, ncur.dot(n)); + c = mix(c, ccur, ((d - 0.001).max(0.) * 100000).min(1.)); color += c * values[i < 0 ? -i : i]; } pixelColor = color; @@ -80,9 +69,9 @@ class Blur extends ScreenShader { } } - function getViewPosition( uv : Vec2 ) : Vec3 { - var depth = depthTexture.get(uv).r; - var temp = vec4(uvToScreen(uv), depth, 1) * inverseProj; + function getPosition( uv : Vec2 ) : Vec3 { + var depth = unpack(depthTexture.get(uv)); + var temp = vec4(uvToScreen(uv), depth, 1) * cameraInverseViewProj; var originWS = temp.xyz / temp.w; return originWS; } diff --git a/h3d/shader/CascadeShadow.hx b/h3d/shader/CascadeShadow.hx index 12a03fc316..bc678ea87d 100644 --- a/h3d/shader/CascadeShadow.hx +++ b/h3d/shader/CascadeShadow.hx @@ -6,112 +6,70 @@ class CascadeShadow extends DirShadow { var pixelColor : Vec4; - @global var camera : { - var view : Mat4; - } - - @const(5) var CASCADE_COUNT : Int; + @const(5) var CASCADE_COUNT:Int; @const var DEBUG : Bool; - @const var BLEND : Bool; @param var cascadeShadowMaps : Array; - @param var cascadeViewProj : Mat3x4; - @param var cascadeTransitionFraction : Float; - @param var cascadeScales : Array; - @param var cascadeOffsets : Array; + @param var cascadeProjs : Array; @param var cascadeDebugs : Array; + @param var cascadeBias : Array; - var texelSize : Vec2; - - function shadowPCF( shadowUv : Vec2, zMax : Float, c : Int ) : Float { - var shadow = 1.0; - var rot = rand(transformedPosition.x + transformedPosition.y + transformedPosition.z) * 3.14 * 2; - var cosR = cos(rot); - var sinR = sin(rot); - var sampleStrength = 1.0 / PCF_SAMPLES; - var offScale = texelSize * pcfScale; - for(i in 0...PCF_SAMPLES) { - var offset = poissonDisk[i].xy * offScale; - offset = vec2(cosR * offset.x - sinR * offset.y, cosR * offset.y + sinR * offset.x); - var depth = cascadeShadowMaps[c].getLod(shadowUv + offset, 0).r; - shadow -= (zMax > depth) ? sampleStrength : 0.0; + function inside(pos : Vec3) : Bool { + if ( abs(pos.x) < 1.0 && abs(pos.y) < 1.0 && abs(pos.z) < 1.0 ) { + return true; + } else { + return false; } - return shadow; - } - - function shadowESM( shadowUv : Vec2, zMax : Float, c : Int ) : Float { - var depth = cascadeShadowMaps[c].get(shadowUv).r; - var delta = depth.min(zMax) - zMax; - return exp(shadowPower * delta).saturate(); - } - - function shadowBase( shadowUv : Vec2, zMax : Float, c : Int ) : Float { - var depth = cascadeShadowMaps[c].get(shadowUv).r; - return zMax > depth ? 0 : 1; - } - - function sampleShadow( shadowPos : Vec3, c : Int) : Float { - var zMax = shadowPos.z.saturate(); - var shadowUv = shadowPos.xy; - shadowUv.y = 1.0 - shadowUv.y; - - if( USE_PCF ) - return shadowPCF(shadowUv, zMax, c); - else if( USE_ESM ) - return shadowESM(shadowUv, zMax, c); - else - return shadowBase(shadowUv, zMax, c); } function fragment() { - if ( enable ) { - var shadowValue = 1.0; - var color = vec3(0); - texelSize = 1.0 / shadowRes; - - var vPos = vec4(transformedPosition, 1.0) * camera.view; - vPos /= vPos.w; - - var found = false; - @unroll for ( i in 0...CASCADE_COUNT ) { - if ( !found && vPos.z <= cascadeScales[i].w ) { - found = true; - - var shadowPos0 = transformedPosition * cascadeViewProj; - var shadowPos = ( i == 0 ) ? shadowPos0 : shadowPos0 * cascadeScales[i].xyz + cascadeOffsets[i].xyz; - shadowValue = sampleShadow(shadowPos, i); - color = cascadeDebugs[i].rgb; - - if ( BLEND ) { - var blendEnd = cascadeScales[i].w; - var blendSize = blendEnd * cascadeTransitionFraction; - var blendStart = blendEnd - blendSize; - var blendFactor = ( vPos.z - blendStart ) / blendSize; - - if ( blendFactor > 0.0 ) { - if ( i != CASCADE_COUNT - 1 ) { - var nextShadowPos = shadowPos0 * cascadeScales[i + 1].xyz + cascadeOffsets[i + 1].xyz; - var nextShadow = sampleShadow(nextShadowPos, i + 1); - shadowValue = nextShadow * blendFactor + shadowValue * (1 - blendFactor); - - var nextColor = cascadeDebugs[i + 1].rgb; - color = nextColor * blendFactor + color * (1 - blendFactor); - - } else { - shadowValue = blendFactor + shadowValue * (1 - blendFactor); - color = color * (1 - blendFactor); - } + if( enable ) { + shadow = 1.0; + var texelSize = 1.0/shadowRes; + @unroll for ( c in 0...CASCADE_COUNT ) { + var shadowPos = transformedPosition * cascadeProjs[c]; + + if ( inside(shadowPos) ) { + shadow = 1.0; + var zMax = shadowPos.z.saturate(); + var shadowUv = screenToUv(shadowPos.xy); + var bias = cascadeBias[c]; + if( USE_PCF ) { + var rot = rand(transformedPosition.x + transformedPosition.y + transformedPosition.z) * 3.14 * 2; + var cosR = cos(rot); + var sinR = sin(rot); + var sampleStrength = 1.0 / PCF_SAMPLES; + var offScale = texelSize * pcfScale; + for(i in 0...PCF_SAMPLES) { + var offset = poissonDisk[i].xy * offScale; + offset = vec2(cosR * offset.x - sinR * offset.y, cosR * offset.y + sinR * offset.x); + var depth = cascadeShadowMaps[c].getLod(shadowUv + offset, 0).r; + shadow -= (zMax - bias > depth) ? sampleStrength : 0.0; } } + else if( USE_ESM ) { + var depth = cascadeShadowMaps[c].get(shadowUv).r; + var delta = (depth + bias).min(zMax) - zMax; + shadow = exp(shadowPower * delta).saturate(); + } + else { + var depth = cascadeShadowMaps[c].get(shadowUv).r; + shadow -= zMax - bias > depth ? 1 : 0; + } } } + } - if ( DEBUG ) - pixelColor = vec4(color, 1.0); - else { - shadow = shadowValue; - dirShadow = shadow; + if ( DEBUG ) { + pixelColor = vec4(0.0, 0.0, 0.0, 1.0); + @unroll for ( c in 0...CASCADE_COUNT ) { + var shadowPos = transformedPosition * cascadeProjs[c]; + if ( inside(shadowPos) ) { + pixelColor.rgb = cascadeDebugs[c].rgb; + } } } + shadow = saturate(shadow); + dirShadow = shadow; } } } \ No newline at end of file diff --git a/h3d/shader/Parallax.hx b/h3d/shader/Parallax.hx index 92a442710b..5a4fd4482f 100644 --- a/h3d/shader/Parallax.hx +++ b/h3d/shader/Parallax.hx @@ -33,7 +33,7 @@ class Parallax extends hxsl.Shader { if( maxLayers == 0 ) calculatedUV += viewNS.xy * heightMap.get(calculatedUV) * amount; else { - var numLayers = mix(float(maxLayers), float(minLayers), saturate(abs(viewNS.z))); + var numLayers = mix(float(maxLayers), float(minLayers), abs(viewNS.z)); var layerDepth = 1 / numLayers; var curLayerDepth = 0.; var delta = (viewNS.xy / viewNS.z) * amount / numLayers; diff --git a/h3d/shader/SAO.hx b/h3d/shader/SAO.hx index 571c95be20..ec40c67ead 100644 --- a/h3d/shader/SAO.hx +++ b/h3d/shader/SAO.hx @@ -8,14 +8,9 @@ class SAO extends ScreenShader { static var SRC = { - @global var camera : { - var position : Vec3; - }; - - @range(4,30) @const(127) var numSamples : Int; + @range(4,30) @const var numSamples : Int; @range(1,10) @const(16) var numSpiralTurns : Int; @const var useWorldUV : Bool; - @const var USE_FADE : Bool = false; @ignore @param var depthTexture : Channel; @ignore @param var normalTexture : Channel3; @@ -31,8 +26,8 @@ class SAO extends ScreenShader { @ignore @param var screenRatio : Vec2; @ignore @param var fovTan : Float; - @param var fadeStart : Float; - @param var fadeEnd : Float; + @ignore @param var microOcclusion : Channel; + @param var microOcclusionIntensity : Float; function sampleAO(uv : Vec2, position : Vec3, normal : Vec3, radiusSS : Float, tapIndex : Int, rotationAngle : Float) : Float { // returns a unit vector and a screen-space radius for the tap on a unit disk @@ -88,10 +83,7 @@ class SAO extends ScreenShader { occlusion = 1.0 - occlusion / float(numSamples); occlusion = pow(occlusion, 1.0 + intensity).saturate(); - if ( USE_FADE ) { - var dist = distance(origin, camera.position); - occlusion = mix(occlusion, 1.0, saturate((dist - fadeStart) / (fadeEnd - fadeStart))); - } + occlusion *= mix(1, microOcclusion.get(vUV).r, microOcclusionIntensity); output.color = vec4(occlusion.xxx, 1.); } diff --git a/h3d/shader/Skin.hx b/h3d/shader/Skin.hx index 89adbe1cf0..c2f8b1a437 100644 --- a/h3d/shader/Skin.hx +++ b/h3d/shader/Skin.hx @@ -12,7 +12,6 @@ class Skin extends SkinBase { }; var transformedTangent : Vec4; - var previousTransformedPosition : Vec3; function vertex() { transformedPosition = @@ -30,7 +29,6 @@ class Skin extends SkinBase { transformedNormal += (input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * w4; } - previousTransformedPosition = transformedPosition; transformedNormal = normalize(transformedNormal); } diff --git a/h3d/shader/pbr/DefaultForward.hx b/h3d/shader/pbr/DefaultForward.hx index 34944f74fd..8afd274a2e 100644 --- a/h3d/shader/pbr/DefaultForward.hx +++ b/h3d/shader/pbr/DefaultForward.hx @@ -4,15 +4,7 @@ class DefaultForward extends hxsl.Shader { static var SRC = { - @global var camera : { - var view : Mat4; - var viewProj : Mat4; - var position : Vec3; - var inverseViewProj : Mat4; - } - - @const(4) var CASCADE_COUNT:Int; - @const(2) var DIR_SHADOW_COUNT:Int; + @const(16) var DIR_SHADOW_COUNT:Int; @const(16) var POINT_SHADOW_COUNT:Int; @const(16) var SPOT_SHADOW_COUNT:Int; @@ -31,10 +23,8 @@ class DefaultForward extends hxsl.Shader { @param var spotLightCount : Int; @param var dirLightStride : Int; @param var pointLightStride : Int; - @param var spotLightStride : Int; // ShadowMaps - @param var cascadeShadowMaps : Array; @param var dirShadowMaps : Array; @param var pointShadowMaps : Array; @param var spotShadowMaps : Array; @@ -201,41 +191,6 @@ class DefaultForward extends hxsl.Shader { return directLighting(fallOff * lightColor * fallOffInfoAngle, delta.normalize()); } - function evaluateCascadeLight() : Vec3 { - var i = dirLightStride + pointLightStride + spotLightStride; - var lightColor = lightInfos[i].rgb; - var lightDir = lightInfos[i+1].xyz; - - return directLighting(lightColor, lightDir); - } - - function inside(pos : Vec3) : Bool { - if ( abs(pos.x) < 1.0 && abs(pos.y) < 1.0 && abs(pos.z) < 1.0 ) - return true; - else - return false; - } - - function evaluateCascadeShadow() : Float { - var i = dirLightStride + pointLightStride + spotLightStride; - var shadow = 1.0; - var shadowProj = mat3x4(lightInfos[i + 2], lightInfos[i + 3], lightInfos[i + 4]); - - @unroll for ( c in 0...CASCADE_COUNT ) { - var cascadeScale = lightInfos[i + 5 + 2 * c]; - var shadowPos0 = transformedPosition * shadowProj; - var shadowPos = i == 0 ? shadowPos0 : shadowPos0 * cascadeScale.xyz + lightInfos[i + 6 + 2 * c].xyz; - if ( inside(shadowPos) ) { - var zMax = saturate(shadowPos.z); - var shadowUv = shadowPos.xy; - shadowUv.y = 1.0 - shadowUv.y; - var depth = cascadeShadowMaps[c].get(shadowUv.xy).r; - shadow -= zMax > depth ? 1.0 : 0.0; - } - } - return saturate(shadow); - } - function evaluateLighting() : Vec3 { var lightAccumulation = vec3(0); @@ -243,49 +198,26 @@ class DefaultForward extends hxsl.Shader { F0 = mix(pbrSpecularColor, albedoGamma, metalness); // Dir Light With Shadow - @unroll for( l in 0 ... DIR_SHADOW_COUNT ) { - var c = evaluateDirLight(l); - if ( dot(c, c) > 1e-6 ) - c *= evaluateDirShadow(l); - lightAccumulation += c; - } + @unroll for( l in 0 ... DIR_SHADOW_COUNT ) + lightAccumulation += evaluateDirLight(l) * evaluateDirShadow(l); // Dir Light - var start = DIR_SHADOW_COUNT; - if ( CASCADE_COUNT > 0 ) - start++; - @unroll for( l in start ... dirLightCount + DIR_SHADOW_COUNT ) + @unroll for( l in DIR_SHADOW_COUNT ... dirLightCount + DIR_SHADOW_COUNT ) lightAccumulation += evaluateDirLight(l); // Point Light With Shadow - @unroll for( l in 0 ... POINT_SHADOW_COUNT ) { - var c = evaluatePointLight(l); - if ( dot(c, c) > 1e-6 ) - c *= evaluatePointShadow(l); - lightAccumulation += c; - } + @unroll for( l in 0 ... POINT_SHADOW_COUNT ) + lightAccumulation += evaluatePointLight(l) * evaluatePointShadow(l); // Point Light @unroll for( l in POINT_SHADOW_COUNT ... pointLightCount + POINT_SHADOW_COUNT ) lightAccumulation += evaluatePointLight(l); // Spot Light With Shadow - @unroll for( l in 0 ... SPOT_SHADOW_COUNT ) { - var c = evaluateSpotLight(l); - if ( dot(c, c) > 1e-6 ) - c *= evaluateSpotShadow(l); - lightAccumulation += c; - } + @unroll for( l in 0 ... SPOT_SHADOW_COUNT ) + lightAccumulation += evaluateSpotLight(l) * evaluateSpotShadow(l); // Spot Light @unroll for( l in SPOT_SHADOW_COUNT ... spotLightCount + SPOT_SHADOW_COUNT ) lightAccumulation += evaluateSpotLight(l); - // Cascade shadows - if ( CASCADE_COUNT > 0 ) { - var c = evaluateCascadeLight(); - if ( dot(c, c) > 1e-6 ) - c *= evaluateCascadeShadow(); - lightAccumulation += c; - } - // Indirect only support the main env from the scene at the moment if( USE_INDIRECT ) lightAccumulation += indirectLighting(); diff --git a/h3d/shader/pbr/VolumeDecal.hx b/h3d/shader/pbr/VolumeDecal.hx index 79315311bc..5078ece839 100644 --- a/h3d/shader/pbr/VolumeDecal.hx +++ b/h3d/shader/pbr/VolumeDecal.hx @@ -150,6 +150,7 @@ class DecalPBR extends BaseDecal { @param var albedoTexture : Sampler2D; @param var normalTexture : Sampler2D; + @param var pbrTexture : Sampler2D; function __init__vertex() { diff --git a/haxelib.json b/haxelib.json index db4af11fcb..8bf213f992 100644 --- a/haxelib.json +++ b/haxelib.json @@ -1,10 +1,14 @@ { - "name" : "heaps", - "url" : "http://heaps.io", - "license" : "BSD", - "description" : "The GPU Game Framework", - "version" : "2.0.0", - "releasenote" : "See CHANGELOG.md", - "contributors" : ["ncannasse"], - "dependencies" : { "format" : "" } + "name": "heaps", + "url": "http://heaps.io", + "license": "BSD", + "description": "The GPU Game Framework", + "version": "2.0.5", + "releasenote": "See CHANGELOG.md", + "contributors": [ + "ncannasse" + ], + "dependencies": { + "format": "" + } } \ No newline at end of file diff --git a/hxd/App.hx b/hxd/App.hx index 9ec115dfbb..0e7eef6381 100644 --- a/hxd/App.hx +++ b/hxd/App.hx @@ -69,8 +69,9 @@ class App implements h3d.IDrawable { if( new2D != null ) { sevents.removeScene(s2d); sevents.addScene(scene, 0); - } else if( new3D != null ) { - sevents.removeScene(s3d); + } else { + if( new3D != null ) + sevents.removeScene(s3d); sevents.addScene(scene); } if( disposePrevious ) { @@ -113,7 +114,6 @@ class App implements h3d.IDrawable { if( disposePrevious ) this.s2d.dispose(); this.s2d = s2d; - s2d.mark = mark; } function setScene3D( s3d : h3d.scene.Scene, disposePrevious = true ) { @@ -129,10 +129,6 @@ class App implements h3d.IDrawable { s2d.render(e); } - function mark(name : String) { - s3d.mark(name); - } - function setup() { var initDone = false; engine.onReady = staticHandler; @@ -144,7 +140,6 @@ class App implements h3d.IDrawable { }; s3d = new h3d.scene.Scene(); s2d = new h2d.Scene(); - s2d.mark = mark; sevents = new hxd.SceneEvents(); sevents.addScene(s2d); sevents.addScene(s3d); diff --git a/hxd/BufferFormat.hx b/hxd/BufferFormat.hx index efb288da8b..65c434ef39 100644 --- a/hxd/BufferFormat.hx +++ b/hxd/BufferFormat.hx @@ -371,15 +371,6 @@ class BufferFormat { } static var ALL_FORMATS = new Map>(); - - public static function fromID( uid : Int ) { - for( fl in ALL_FORMATS ) - for( f in fl ) - if( f.uid == uid ) - return f; - return null; - } - public static function make( inputs : Array ) { var names = []; for( b in inputs ) @@ -440,11 +431,8 @@ class BufferFormat { return i >= 0 ? i : (0x7F + i) | 0x80; } - public static function floatS8to32( v : Int ) : Float { - if ( v & 0x80 != 0 ) - return -1*(0x7F-(v&0x7F))/128; - else - return (v&0x7F)/128; + public static function floatS8to32( v : Int ) { + return (v & 0x80 != 0 ? -1 : 1) * ((v&0x7F)/127); } public static function float32toU8( v : Float ) : Int { diff --git a/hxd/PixelFormat.hx b/hxd/PixelFormat.hx index 6ad043c686..c4cac1a075 100644 --- a/hxd/PixelFormat.hx +++ b/hxd/PixelFormat.hx @@ -23,9 +23,26 @@ enum PixelFormat { RG16U; RGB16U; RGBA16U; - S3TC( v : Int ); + ASTC( ?v : Int ); + ETC( ?v : Int ); + S3TC( ?v : Int ); Depth16; Depth24; Depth24Stencil8; - Depth32; +} + +enum abstract ASTC_FORMAT(Int) from Int to Int { + final RGBA_4x4 = 0x93B0; +} + +enum abstract DXT_FORMAT(Int) from Int to Int { + final RGB_DXT1 = 0x83F0; + final RGBA_DXT1 = 0x83F1; + final RGBA_DXT3 = 0x83F2; + final RGBA_DXT5 = 0x83F3; +} + +enum abstract ETC_FORMAT(Int) from Int to Int { + final RGB_ETC1 = 0x8D64; + final RGBA_ETC2 = 0x9278; } \ No newline at end of file diff --git a/hxd/Pixels.hx b/hxd/Pixels.hx index 735a5ec4f4..aaf156ae5c 100644 --- a/hxd/Pixels.hx +++ b/hxd/Pixels.hx @@ -391,6 +391,8 @@ class Pixels { this.bytes = nbytes; case [S3TC(a),S3TC(b)] if( a == b ): + case [ASTC(a),ASTC(b)] if( a == b ): + case [ETC(a),ETC(b)] if( a == b ): // nothing #if (hl && hl_ver >= "1.10") @@ -452,7 +454,7 @@ class Pixels { } } - public function getPixelF(x, y, ?v:h3d.Vector4) : h3d.Vector4 { + public function getPixelF(x, y, ?v:h3d.Vector4) { if( v == null ) v = new h3d.Vector4(); var p = ((x + y * width) * bytesPerPixel) + offset; @@ -538,6 +540,22 @@ class Pixels { return switch( format ) { case S3TC(_): (((height + 3) >> 2) << 2) * calcStride(width, format); + case ASTC(n): + var w = ((width + 3) >> 2) << 2; + var h = ((height + 3) >> 2) << 2; + (w >> 2) * (h >> 2) * 16; + case ETC(n): + if (n == 0) { // RGB_ETC1_Format or RGB_ETC2_Format + var w = ((width + 3) >> 2) << 2; + var h = ((height + 3) >> 2) << 2; + (w >> 2) * (h >> 2) * 8; + } else if (n == 1 || n == 2) { // RGBA_ETC2_EAC_Format + var w = ((width + 3) >> 2) << 2; + var h = ((height + 3) >> 2) << 2; + (w >> 2) * (h >> 2) * 16; + } else { + throw "Unsupported ETC format"; + } default: height * calcStride(width, format); } @@ -559,14 +577,24 @@ class Pixels { case RGB32F: 12; case RGB10A2: 4; case RG11B10UF: 4; + case ASTC(n): + var blocks = ((width + 3) >> 2) * 16; + blocks << 4; + case ETC(n): + if (n == 0) { // ETC1 and ETC2 RGB + ((width + 3) >> 2) << 3; + } else if (n == 1) { // ETC2 EAC RGBA + ((width + 3) >> 2) << 4; + } else { + throw "Unsupported ETC format"; + } case S3TC(n): var blocks = (width + 3) >> 2; if( n == 1 || n == 4 ) return blocks << 1; return blocks << 2; - case Depth16: 2; - case Depth24: 3; - case Depth24Stencil8, Depth32: 4; + case Depth16, Depth24, Depth24Stencil8: + throw "Not a pixel format"; } } @@ -605,13 +633,13 @@ class Pixels { channel.toInt() * 4; case RGB10A2, RG11B10UF: throw "Bit packed format"; - case S3TC(_), Depth16, Depth24, Depth24Stencil8, Depth32: + case S3TC(_), ASTC(_), ETC(_), Depth16, Depth24, Depth24Stencil8: throw "Not supported"; } } public static function alloc( width, height, format : PixelFormat ) { - return new Pixels(width, height, haxe.io.Bytes.alloc(calcDataSize(width, height, format)), format); + return new Pixels(width, height, haxe.io.Bytes.alloc(calcDataSize(width, height, format)), format); } /** diff --git a/hxd/SceneEvents.hx b/hxd/SceneEvents.hx index 200583b60a..d135750928 100644 --- a/hxd/SceneEvents.hx +++ b/hxd/SceneEvents.hx @@ -360,7 +360,6 @@ class SceneEvents { e.relY = oy; if( !e.propagate ) continue; - e.propagate = false; } emitEvent(e); diff --git a/hxd/System.hl.hx b/hxd/System.hl.hx index d97bac6c34..fa0186aa6a 100644 --- a/hxd/System.hl.hx +++ b/hxd/System.hl.hx @@ -58,14 +58,17 @@ class System { loopFunc = f; } - static function mainLoop() { + static function mainLoop() : Bool { // process events #if usesys - haxe.System.emitEvents(@:privateAccess hxd.Window.dispatchEvent); + if( !haxe.System.emitEvents(@:privateAccess hxd.Window.inst.event) ) + return false; #elseif hldx - dx.Loop.processEvents(@:privateAccess hxd.Window.dispatchEvent); + if( !dx.Loop.processEvents(@:privateAccess hxd.Window.inst.onEvent) ) + return false; #elseif hlsdl - sdl.Sdl.processEvents(@:privateAccess hxd.Window.dispatchEvent); + if( !sdl.Sdl.processEvents(@:privateAccess hxd.Window.inst.onEvent) ) + return false; #end // loop @@ -84,6 +87,7 @@ class System { hl.Profile.event(-2); // resume #end } + return true; } public static function start( init : Void -> Void ) : Void { @@ -124,14 +128,6 @@ class System { timeoutTick(); haxe.Timer.delay(runMainLoop, 0); } - - static function isAlive() { - #if usesys - return true; - #else - return hxd.Window.hasWindow(); - #end - } static function runMainLoop() { #if (haxe_ver >= 4.1) @@ -142,7 +138,7 @@ class System { #if ( target.threaded && (haxe_ver >= 4.2) && heaps_unsafe_events) var eventRecycle = []; #end - while( isAlive() ) { + while( true ) { #if !heaps_no_error_trap try { hl.Api.setErrorHandler(reportError); // set exception trap @@ -160,7 +156,7 @@ class System { @:privateAccess haxe.MainLoop.tick(); #end - mainLoop(); + if( !mainLoop() ) break; #if !heaps_no_error_trap } catch( e : Dynamic ) { hl.Api.setErrorHandler(null); @@ -463,12 +459,7 @@ class System { static function __init__() { #if !usesys - #if (haxe_ver >= 4.1) - var reportError = function(e:Dynamic) reportError((e is haxe.Exception)?e:new haxe.Exception(Std.string(e),null,e)); - #else - var reportError = function(e) reportError(e); - #end - hl.Api.setErrorHandler(reportError); // initialization error + hl.Api.setErrorHandler(function(e) reportError(e)); // initialization error sentinel = new hl.UI.Sentinel(30, function() throw "Program timeout (infinite loop?)"); #end #if ( target.threaded && (haxe_ver >= 4.2) ) diff --git a/hxd/Window.hl.hx b/hxd/Window.hl.hx index 630b8f4fe0..15c5359479 100644 --- a/hxd/Window.hl.hx +++ b/hxd/Window.hl.hx @@ -39,14 +39,11 @@ private class NativeDroppedFile extends hxd.DropFileEvent.DroppedFile { //@:coreApi class Window { - static var WINDOWS : Array = []; - var resizeEvents : List Void>; var eventTargets : List Void>; var dropTargets : List Void>; var dropFiles : Array; - public var id : Int; public var width(get, never) : Int; public var height(get, never) : Int; public var mouseX(get, never) : Int; @@ -116,31 +113,16 @@ class Window { final dxFlags = if (!fixed) dx.Window.RESIZABLE else 0; window = new dx.Window(title, width, height, dx.Window.CW_USEDEFAULT, dx.Window.CW_USEDEFAULT, dxFlags); #end - WINDOWS.push(this); - #if multidriver - id = window.id; - #end } public dynamic function onClose() : Bool { return true; } - public dynamic function onMove() : Void { - } - public dynamic function onMouseModeChange( from : MouseMode, to : MouseMode ) : Null { return null; } - public function close() { - if( !WINDOWS.remove(this) ) - return; - #if (multidriver && (hlsdl || hldx)) - window.destroy(); - #end - } - public function event( e : hxd.Event ) : Void { for( et in eventTargets ) et(e); @@ -323,26 +305,11 @@ class Window { startMouseX = curMouseX; startMouseY = curMouseY; - + return mouseMode = v; } - #if usesys - - function get_vsync() : Bool return haxe.System.vsync; - - function set_vsync( b : Bool ) : Bool { - return haxe.System.vsync = b; - } - - function get_isFocused() : Bool return true; - - function onEvent( e : Event ) : Bool { - event(e); - return true; - } - - #elseif (hldx||hlsdl) + #if (hldx||hlsdl) function get_vsync() : Bool return window.vsync; @@ -396,11 +363,6 @@ class Window { event(new Event(EOver)); case Leave: event(new Event(EOut)); - case Close: - return onCloseEvent(); - case Move: - if( onMove != null ) - onMove(); default: } case MouseDown if (!hxd.System.getValue(IsTouch)): @@ -512,7 +474,7 @@ class Window { #end eh = new Event(ERelease, e.mouseX, e.mouseY); eh.touchId = e.fingerId; - + #elseif hldx case KeyDown: eh = new Event(EKeyDown); @@ -553,23 +515,14 @@ class Window { for ( dt in dropTargets ) dt(event); dropFiles = null; #end - #if !hlsdl // hlsdl post both Close+Quit case Quit: - return onCloseEvent(); - #end + return onClose(); default: } if( eh != null ) event(eh); return true; } - function onCloseEvent() { - var ret = onClose(); - if( ret ) - close(); - return ret; - } - static function initChars() : Void { inline function addKey(sdl, keyCode) { @@ -663,6 +616,16 @@ class Window { addKey(sdl, keys.get(sdl)); } + #elseif usesys + + function get_vsync() : Bool return haxe.System.vsync; + + function set_vsync( b : Bool ) : Bool { + return haxe.System.vsync = b; + } + + function get_isFocused() : Bool return true; + #else function get_vsync() : Bool return true; @@ -673,11 +636,6 @@ class Window { function get_isFocused() : Bool return false; - function onEvent( e : Event ) : Bool { - event(e); - return true; - } - #end function get_displayMode() : DisplayMode { @@ -843,32 +801,8 @@ class Window { return ""; } - public function setCurrent() { - inst = this; - #if hlsdl - window.renderTo(); - #end - } - static var inst : Window = null; public static function getInstance() : Window { return inst; } - - public static function hasWindow() { - return WINDOWS.length > 0; - } - - static function dispatchEvent( e ) { - #if multidriver - if( false ) @:privateAccess WINDOWS[0].onEvent(e); // typing - for( w in WINDOWS ) - if( e.windowId == w.id ) - return w.onEvent(e); - return true; - #else - return inst.onEvent(e); - #end - } - } diff --git a/hxd/Window.hx b/hxd/Window.hx index ac68afe7b1..9c394b0dcf 100644 --- a/hxd/Window.hx +++ b/hxd/Window.hx @@ -114,9 +114,6 @@ class Window { public function setCursorPos( x : Int, y : Int, emitEvent : Bool = false ) : Void { throw "Not implemented"; } - - public function setCurrent() { - } static var inst : Window = null; public static function getInstance() : Window { diff --git a/hxd/fmt/bfnt/FontParser.hx b/hxd/fmt/bfnt/FontParser.hx index 885d5dd572..070d276854 100644 --- a/hxd/fmt/bfnt/FontParser.hx +++ b/hxd/fmt/bfnt/FontParser.hx @@ -55,6 +55,12 @@ class FontParser { font.lineHeight = Std.parseInt(xml.node.common.att.lineHeight); font.baseLine = Std.parseInt(xml.node.common.att.base); + // This node contains SDF Metadata + if(xml.hasNode.distanceField) { + font.distanceRange = Std.parseInt(xml.node.distanceField.att.distanceRange); + font.fieldType = Std.string(xml.node.distanceField.att.fieldType); + } + for ( p in xml.node.pages.elements ) { if ( p.att.id == "0" ) { resolveTileWithFallback(p.att.file); diff --git a/hxd/fmt/fbx/BaseLibrary.hx b/hxd/fmt/fbx/BaseLibrary.hx index 3cdbaef4e4..f01c9719f6 100644 --- a/hxd/fmt/fbx/BaseLibrary.hx +++ b/hxd/fmt/fbx/BaseLibrary.hx @@ -271,7 +271,7 @@ class BaseLibrary { // scale on geometry if( geometryScaleFactor != 1 ) { - for( g in this.root.getAll("Objects.Geometry.Vertices").concat(this.root.getAll("Objects.Geometry.Shape.Vertices")) ) { + for( g in this.root.getAll("Objects.Geometry.Vertices") ) { var v = toFloats(g); for( i in 0...v.length ) v[i] = v[i] / geometryScaleFactor; @@ -296,11 +296,6 @@ class BaseLibrary { var v = p.props[idx].toFloat(); p.props[idx] = PFloat(v / scaleFactor); } - case "GeometricTranslation": - for( idx in [4,5,6] ) { - var v = p.props[idx].toFloat(); - p.props[idx] = PFloat(v / scaleFactor); - } default: } } @@ -407,15 +402,9 @@ class BaseLibrary { convertPoints(v.getFloats()); for( v in g.getAll("LayerElementNormal.Normals") ) convertPoints(v.getFloats()); - } - for ( s in root.getAll("Objects.Geometry.Shape") ) { - for ( v in s.getAll("Vertices") ) - convertPoints(v.getFloats()); - for ( v in s.getAll("Normals") ) - convertPoints(v.getFloats()); - for ( v in s.getAll("Tangents") ) + for( v in g.getAll("LayerElementTangent.Tangents") ) convertPoints(v.getFloats()); - for ( v in s.getAll("Binormals") ) + for( v in g.getAll("LayerElementBinormal.Binormals") ) convertPoints(v.getFloats()); } } diff --git a/hxd/fmt/fbx/Geometry.hx b/hxd/fmt/fbx/Geometry.hx index 477968befe..24195ea9c1 100644 --- a/hxd/fmt/fbx/Geometry.hx +++ b/hxd/fmt/fbx/Geometry.hx @@ -90,7 +90,7 @@ class Geometry { var uv = getUVs(); var uv2 = g.getUVs(); if( uv.length != uv2.length ) - throw "Different UV layer (" + uv2.length + " should be " + uv.length + ")" + "in file " + lib.fileName; + throw "Different UV layer (" + uv2.length + " should be " + uv.length + ")"; for( i in 0...uv.length ) { var uv = uv[i]; var uv2 = uv2[i]; @@ -191,37 +191,22 @@ class Geometry { return points; } - public function getNormals(?matrix) { - if( matrix == null ) matrix = getGeomMatrix(); - if( matrix != null && matrix.isIdentity() ) matrix = null; - var normals = processVectors("LayerElementNormal", "Normals"); - var outNormals = []; - var tmp = new h3d.Vector(); - for( i in 0...Std.int(normals.length/3) ) { - var x = normals[i*3]; - var y = normals[i*3+1]; - var z = normals[i*3+2]; - if( matrix != null ) { - tmp.set(x,y,z); - tmp.transform3x3(matrix); - tmp.normalize(); - x = tmp.x; - y = tmp.y; - z = tmp.z; - } - outNormals.push(x); - outNormals.push(y); - outNormals.push(z); - } + public function getNormals() { + return processVectors("LayerElementNormal", "Normals"); + } - return outNormals; + public function getTangents( opt = false ) { + return processVectors("LayerElementTangent", "Tangents", opt); + } + + public function getBinormals( opt = false ) { + return processVectors("LayerElementBinormal", "Binormals", opt); } function processVectors( layer, name, opt = false ) { var vect = root.get(layer + "." + name, opt); if( vect == null ) return null; var nrm = vect.getFloats(); - // if by-vertice (Maya in some cases, unless maybe "Split per-Vertex Normals" is checked) // let's reindex based on polygon indexes if( root.get(layer+".MappingInformationType").props[0].toString() == "ByVertice" ) { @@ -235,19 +220,6 @@ class Geometry { } nrm = nout; } - - // Blender after version 4.X export normals with index to direct inforamtion type - if( root.get(layer+".ReferenceInformationType").props[0].toString() == "IndexToDirect" ) { - var nout = []; - var nrmIdx = root.get(layer+".NormalsIndex").getInts(); - for (i in 0...nrmIdx.length) { - nout.push(nrm[nrmIdx[i] * 3]); - nout.push(nrm[nrmIdx[i] * 3 + 1]); - nout.push(nrm[nrmIdx[i] * 3 + 2]); - } - nrm = nout; - } - return nrm; } @@ -281,7 +253,7 @@ class Geometry { case "GeometricTranslation": trans = new h3d.col.Point(p.props[4].toFloat() * (lib.leftHand ? -1 : 1), p.props[5].toFloat(), p.props[6].toFloat()); case "GeometricRotation": - rot = new h3d.col.Point(p.props[4].toFloat() * Math.PI / 180, p.props[5].toFloat() * Math.PI / 180, (p.props[6].toFloat() * Math.PI / 180) * (lib.leftHand ? -1 : 1)); + rot = new h3d.col.Point(p.props[4].toFloat() * Math.PI / 180, p.props[5].toFloat() * Math.PI / 180, p.props[6].toFloat() * Math.PI / 180); default: } if( rot == null && trans == null ) @@ -298,4 +270,5 @@ class Geometry { } return m; } + } \ No newline at end of file diff --git a/hxd/fmt/fbx/HMDOut.hx b/hxd/fmt/fbx/HMDOut.hx index 6374af3a68..67ecdb2f4e 100644 --- a/hxd/fmt/fbx/HMDOut.hx +++ b/hxd/fmt/fbx/HMDOut.hx @@ -40,7 +40,7 @@ class HMDOut extends BaseLibrary { var index = geom.getIndexes(); if ( index.vidx.length > 0 && uvs[0] == null ) @:privateAccess - throw "Need UVs to build tangents" + (geom.lib != null ? ' in ${geom.lib.fileName}' : ''); + throw "Need UVs to build tangents" + geom.lib != null ? ' in ${geom.lib.fileName}' : ''; #if (hl && !hl_disable_mikkt) var m = new hl.Format.Mikktspace(); @@ -115,7 +115,7 @@ class HMDOut extends BaseLibrary { var ret = try Sys.command("mikktspace",[fileName,outFile]) catch( e : Dynamic ) -1; if( ret != 0 ) { sys.FileSystem.deleteFile(fileName); - throw "Failed to call 'mikktspace' executable required to generate tangent data. Please ensure it's in your PATH"+(filePath == null ? "" : ' ($filePath)'); + throw "Failed to call 'mikktspace' executable required to generate tangent data. Please ensure it's in your PATH"; } var bytes = sys.io.File.getBytes(outFile); var size = index.vidx.length*4; @@ -181,16 +181,16 @@ class HMDOut extends BaseLibrary { } } - public static inline function writePrec( d : haxe.io.BytesOutput, v : Float, p : Precision ) { + inline function writePrec( v : Float, p : Precision ) { switch( p ) { - case F32: writeFloat(d, v); - case F16: d.writeUInt16(hxd.BufferFormat.float32to16(v,true)); - case S8: d.writeByte(hxd.BufferFormat.float32toS8(v)); - case U8: d.writeByte(BufferFormat.float32toU8(v)); + case F32: writeFloat(v); + case F16: dataOut.writeUInt16(hxd.BufferFormat.float32to16(v,true)); + case S8: dataOut.writeByte(hxd.BufferFormat.float32toS8(v)); + case U8: dataOut.writeByte(BufferFormat.float32toU8(v)); } } - public static inline function precisionSize(p:Precision) { + inline function precisionSize(p:Precision) { return switch( p ) { case F32: 4; case F16: 2; @@ -198,28 +198,20 @@ class HMDOut extends BaseLibrary { } } - public static inline function flushPrec( d : haxe.io.BytesOutput, p : Precision, count : Int ) { + inline function flushPrec( p : Precision, count : Int ) { var b = (count * precisionSize(p)) & 3; switch( b ) { case 0: case 1: - d.writeUInt16(0); - d.writeByte(0); + dataOut.writeUInt16(0); + dataOut.writeByte(0); case 2: - d.writeUInt16(0); + dataOut.writeUInt16(0); case 3: - d.writeByte(0); + dataOut.writeByte(0); } } - public static function remapPrecision(inputName : String) { - if ( inputName == "tangent" ) - return "normal"; - if ( inputName.indexOf("uv") == 0 ) - return "uv"; - return inputName; - } - function buildGeom( geom : hxd.fmt.fbx.Geometry, skin : h3d.anim.Skin, dataOut : haxe.io.BytesOutput, genTangents : Bool ) { var g = new Geometry(); @@ -228,7 +220,6 @@ class HMDOut extends BaseLibrary { var uvs = geom.getUVs(); var colors = geom.getColors(); var mats = geom.getMaterials(); - var index = geom.getPolygons(); // remove empty color data if( colors != null ) { @@ -293,20 +284,11 @@ class HMDOut extends BaseLibrary { ibufs.push([]); } - var shapes = geom.getRoot().getAll("Shape"); - var shapeIndexes = []; // Indexes of vertex used in blendshapes - var remappedShapes = []; - for ( sIdx => s in shapes ) { - shapeIndexes.push(s.get("Indexes").getInts()); - remappedShapes.push([]); - for (i in 0...shapeIndexes[sIdx].length) - remappedShapes[remappedShapes.length - 1].push([]); - } - g.bounds = new h3d.col.Bounds(); var stride = g.vertexFormat.stride; var tmpBuf = new hxd.impl.TypedArray.Float32Array(stride); var vertexRemap = new Array(); + var index = geom.getPolygons(); var count = 0, matPos = 0, stri = 0; var lookup = new Map(); var tmp = new h3d.col.Point(); @@ -399,26 +381,17 @@ class HMDOut extends BaseLibrary { vids = []; lookup.set(itotal, vids); } - var inBlendShape = false; - for ( s in shapeIndexes ) { - if ( s.contains(vidx) ) { - inBlendShape = true; - break; - } - } - if ( !inBlendShape ) { // vertices referenced by blend shapes can't be merged - for( vid in vids ) { - var same = true; - var p = vid * stride; - for( i in 0...stride ) - if( vbuf[p++] != tmpBuf[i] ) { - same = false; - break; - } - if( same ) { - found = vid; + for( vid in vids ) { + var same = true; + var p = vid * stride; + for( i in 0...stride ) + if( vbuf[p++] != tmpBuf[i] ) { + same = false; break; } + if( same ) { + found = vid; + break; } } if( found == null ) { @@ -428,16 +401,7 @@ class HMDOut extends BaseLibrary { vbuf.push(tmpBuf[i]); vids.push(found); } - vertexRemap.push(found); - - for ( s in 0...shapeIndexes.length ) { - for (idx in 0...shapeIndexes[s].length) { - if (shapeIndexes[s][idx] == vidx) { - remappedShapes[s][idx].push(found); - } - } - } } // by-skin-group index @@ -481,49 +445,49 @@ class HMDOut extends BaseLibrary { g.vertexPosition = dataOut.length; if( lowPrecConfig == null ) { for( i in 0...vbuf.length ) - writeFloat(dataOut, vbuf[i]); + writeFloat(vbuf[i]); } else { for( index in 0...Std.int(vbuf.length / stride) ) { var i = index * stride; - writePrec(dataOut, vbuf[i++], ppos); - writePrec(dataOut, vbuf[i++], ppos); - writePrec(dataOut, vbuf[i++], ppos); - flushPrec(dataOut, ppos,3); + writePrec(vbuf[i++], ppos); + writePrec(vbuf[i++], ppos); + writePrec(vbuf[i++], ppos); + flushPrec(ppos,3); if( normals != null ) { - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - flushPrec(dataOut, pnormal,3); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + flushPrec(pnormal,3); } if( tangents != null ) { - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - flushPrec(dataOut, pnormal,3); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + flushPrec(pnormal,3); } for( k in 0...uvs.length ) { - writePrec(dataOut, vbuf[i++], puv); - writePrec(dataOut, vbuf[i++], puv); - flushPrec(dataOut, puv,2); + writePrec(vbuf[i++], puv); + writePrec(vbuf[i++], puv); + flushPrec(puv,2); } if( colors != null ) { - writePrec(dataOut, vbuf[i++], pcolor); - writePrec(dataOut, vbuf[i++], pcolor); - writePrec(dataOut, vbuf[i++], pcolor); - flushPrec(dataOut, pcolor,3); + writePrec(vbuf[i++], pcolor); + writePrec(vbuf[i++], pcolor); + writePrec(vbuf[i++], pcolor); + flushPrec(pcolor,3); } if( skin != null ) { - writePrec(dataOut, vbuf[i++], pweight); - writePrec(dataOut, vbuf[i++], pweight); - writePrec(dataOut, vbuf[i++], pweight); - flushPrec(dataOut, pweight,3); - writeFloat(dataOut, vbuf[i++]); + writePrec(vbuf[i++], pweight); + writePrec(vbuf[i++], pweight); + writePrec(vbuf[i++], pweight); + flushPrec(pweight,3); + writeFloat(vbuf[i++]); } if( generateNormals ) { - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - flushPrec(dataOut, pnormal,3); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + writePrec(vbuf[i++], pnormal); + flushPrec(pnormal,3); } if( i != (index + 1) * stride ) throw "assert"; @@ -554,109 +518,6 @@ class HMDOut extends BaseLibrary { if( skin != null && skin.isSplit() ) matMap = null; - for ( i in 0...shapes.length ) { - var remapped = remappedShapes[i]; - var s = shapes[i]; - var shape = new BlendShape(); - shape.name = s.name; - shape.geom = -1; - var indexes = s.get("Indexes").getFloats();//shapeIndexes[i]; - var verts = s.get("Vertices").getFloats(); - var normals = s.get("Normals").getFloats(); - var uvs = s.get("UVs", true)?.getFloats(); - var colors = s.get("Colors", true)?.getFloats(); - format = []; - addFormat("position", DVec3, ppos); - if( normals != null ) - addFormat("normal", DVec3, pnormal); - if( tangents != null ) - addFormat("tangent", DVec3, pnormal); - if( uvs != null ) - addFormat("uv", DVec2, puv); - if( colors != null ) - addFormat("color", DVec3, pcolor); - shape.indexCount = remapped.length; - shape.vertexCount = indexes.length; - shape.vertexFormat = hxd.BufferFormat.make(format); - shape.vertexPosition = dataOut.length; - - vbuf = new hxd.FloatBuffer(); - for ( i in 0...shape.vertexCount ) { - vbuf.push(verts[i * 3]); - vbuf.push(verts[i * 3 + 1]); - vbuf.push(verts[i * 3 + 2]); - if ( normals != null ) { - vbuf.push(normals[i * 3]); - vbuf.push(normals[i * 3 + 1]); - vbuf.push(normals[i * 3 + 2]); - } - if ( uvs != null ) { - vbuf.push(uvs[i * 2]); - vbuf.push(uvs[i * 2 + 1]); - } - if ( colors != null ) { - vbuf.push(colors[i * 3]); - vbuf.push(colors[i * 3 + 1]); - vbuf.push(colors[i * 3 + 1]); - } - } - if( lowPrecConfig == null ) { - for( i in 0...vbuf.length ) - writeFloat(dataOut, vbuf[i]); - } else { - for( index in 0...Std.int(vbuf.length / stride) ) { - var i = index * stride; - writePrec(dataOut, vbuf[i++], ppos); - writePrec(dataOut, vbuf[i++], ppos); - writePrec(dataOut, vbuf[i++], ppos); - flushPrec(dataOut, ppos,3); - if( normals != null ) { - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - flushPrec(dataOut, pnormal,3); - } - if( tangents != null ) { - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - writePrec(dataOut, vbuf[i++], pnormal); - flushPrec(dataOut, pnormal,3); - } - for( k in 0...uvs.length ) { - writePrec(dataOut, vbuf[i++], puv); - writePrec(dataOut, vbuf[i++], puv); - flushPrec(dataOut, puv,2); - } - if( colors != null ) { - writePrec(dataOut, vbuf[i++], pcolor); - writePrec(dataOut, vbuf[i++], pcolor); - writePrec(dataOut, vbuf[i++], pcolor); - flushPrec(dataOut, pcolor,3); - } - if( i != (index + 1) * stride ) - throw "assert"; - } - } - - shape.remapPosition = dataOut.length; - for ( i in 0...remapped.length ) { - for (j in 0...remapped[i].length) { - var toWrite = remapped[i][j]; - - // We don't support models vertex count > 2^32 - 1 because we use - // the 32th bit for a flag to indicate that it is the last index - // affected by this offset - if (toWrite > Math.pow(2, 32) - 1) - throw ("Not supported, too much vertex"); - - if (j == remapped[i].length -1) - toWrite = toWrite | (1 << 31); - - dataOut.writeInt32(toWrite); - } - } - d.shapes.push(shape); - } return { g : g, materials : matMap }; } @@ -917,10 +778,6 @@ class HMDOut extends BaseLibrary { gdata = { gid : d.geometries.length, materials : geom.materials }; d.geometries.push(geom.g); hgeom.set(g.getId(), gdata); - for ( s in d.shapes ) { - if (s.geom == -1) - s.geom = gdata.gid; - } } model.geometry = gdata.gid; @@ -1019,8 +876,8 @@ class HMDOut extends BaseLibrary { return p; } - public static inline function writeFloat(d : haxe.io.BytesOutput, f : Float ) { - d.writeFloat( f == 0 ? 0 : f ); // prevent negative zero + inline function writeFloat( f : Float ) { + dataOut.writeFloat( f == 0 ? 0 : f ); // prevent negative zero } function writeFrame( o : h3d.anim.LinearAnimation.LinearObject, fid : Int ) { @@ -1028,31 +885,31 @@ class HMDOut extends BaseLibrary { if( o.frames != null ) { var f = o.frames[fid]; if( o.hasPosition ) { - writeFloat(dataOut, f.tx); - writeFloat(dataOut, f.ty); - writeFloat(dataOut, f.tz); + writeFloat(f.tx); + writeFloat(f.ty); + writeFloat(f.tz); } if( o.hasRotation ) { var ql = Math.sqrt(f.qx * f.qx + f.qy * f.qy + f.qz * f.qz + f.qw * f.qw); if( ql * f.qw < 0 ) ql = -ql; // make sure normalized qw > 0 - writeFloat(dataOut, round(f.qx / ql)); - writeFloat(dataOut, round(f.qy / ql)); - writeFloat(dataOut, round(f.qz / ql)); + writeFloat(round(f.qx / ql)); + writeFloat(round(f.qy / ql)); + writeFloat(round(f.qz / ql)); } if( o.hasScale ) { - writeFloat(dataOut, f.sx); - writeFloat(dataOut, f.sy); - writeFloat(dataOut, f.sz); + writeFloat(f.sx); + writeFloat(f.sy); + writeFloat(f.sz); } } if( o.uvs != null ) { - writeFloat(dataOut, o.uvs[fid<<1]); - writeFloat(dataOut, o.uvs[(fid<<1)+1]); + writeFloat(o.uvs[fid<<1]); + writeFloat(o.uvs[(fid<<1)+1]); } if( o.alphas != null ) - writeFloat(dataOut, o.alphas[fid]); + writeFloat(o.alphas[fid]); if( o.propValues != null ) - writeFloat(dataOut, o.propValues[fid]); + writeFloat(o.propValues[fid]); } function makeAnimation( anim : h3d.anim.Animation ) { @@ -1087,21 +944,21 @@ class HMDOut extends BaseLibrary { if( d.version < 3 ) { for( f in obj.frames ) { if( o.flags.has(HasPosition) ) { - writeFloat(dataOut, f.tx); - writeFloat(dataOut, f.ty); - writeFloat(dataOut, f.tz); + writeFloat(f.tx); + writeFloat(f.ty); + writeFloat(f.tz); } if( o.flags.has(HasRotation) ) { var ql = Math.sqrt(f.qx * f.qx + f.qy * f.qy + f.qz * f.qz + f.qw * f.qw); if( f.qw < 0 ) ql = -ql; - writeFloat(dataOut, round(f.qx / ql)); - writeFloat(dataOut, round(f.qy / ql)); - writeFloat(dataOut, round(f.qz / ql)); + writeFloat(round(f.qx / ql)); + writeFloat(round(f.qy / ql)); + writeFloat(round(f.qz / ql)); } if( o.flags.has(HasScale) ) { - writeFloat(dataOut, f.sx); - writeFloat(dataOut, f.sy); - writeFloat(dataOut, f.sz); + writeFloat(f.sx); + writeFloat(f.sy); + writeFloat(f.sz); } } } @@ -1111,14 +968,14 @@ class HMDOut extends BaseLibrary { if( count == 0 ) count = obj.uvs.length>>1 else if( count != obj.uvs.length>>1 ) throw "assert"; if( d.version < 3 ) for( f in obj.uvs ) - writeFloat(dataOut, f); + writeFloat(f); } if( obj.alphas != null ) { o.flags.set(HasAlpha); if( count == 0 ) count = obj.alphas.length else if( count != obj.alphas.length ) throw "assert"; if( d.version < 3 ) for( f in obj.alphas ) - writeFloat(dataOut, f); + writeFloat(f); } if( obj.propValues != null ) { o.flags.set(HasProps); @@ -1126,7 +983,7 @@ class HMDOut extends BaseLibrary { if( count == 0 ) count = obj.propValues.length else if( count != obj.propValues.length ) throw "assert"; if( d.version < 3 ) for( f in obj.propValues ) - writeFloat(dataOut, f); + writeFloat(f); } if( count == 0 ) throw "assert"; // no data ? @@ -1172,7 +1029,6 @@ class HMDOut extends BaseLibrary { d.materials = []; d.models = []; d.animations = []; - d.shapes = []; dataOut = new haxe.io.BytesOutput(); diff --git a/hxd/fmt/fbx/Writer.hx b/hxd/fmt/fbx/Writer.hx index ce2cea92f1..02d3e41f0e 100644 --- a/hxd/fmt/fbx/Writer.hx +++ b/hxd/fmt/fbx/Writer.hx @@ -3,128 +3,14 @@ package hxd.fmt.fbx; import hxd.fmt.fbx.Data; import hxd.fmt.hmd.Data; -typedef ExportParams = { - forward: String, - forwardSign: String, - up: String, - upSign: String, -} - - class Writer { - static var unsuported : Array = [ - h3d.scene.Interactive, - h3d.scene.Box, - #if hide hrt.prefab.fx.Emitter.EmitterObject #end - ]; - - var out: haxe.io.Output; + var out:haxe.io.Output; + var version:Int; public function new(out) { this.out = out; } - function getPrimitiveInfos(prim : h3d.prim.Primitive) @:privateAccess { - var infos : { - ?vertexFormat : BufferFormat, - ?vertexBuffer : Array, - ?indexesBuffer : Array, - ?lib : hxd.fmt.hmd.Library - }; - - infos = {}; - - var hmd = Std.downcast(prim, h3d.prim.HMDModel); - if (hmd != null) { - infos.vertexFormat = @:privateAccess hmd.data.vertexFormat; - var bufs = hmd.getDataBuffers(infos.vertexFormat); - infos.indexesBuffer = [for (i in 0...bufs.indexes.length) bufs.indexes[i]]; - infos.vertexBuffer = [for (i in 0...bufs.vertexes.length) bufs.vertexes[i]]; - infos.lib = hmd.lib; - return infos; - } - - var polyPrim = Std.downcast(prim, h3d.prim.Polygon); - if (polyPrim != null) { - var format = hxd.BufferFormat.POS3D; - if( polyPrim.normals != null ) - format = format.append("normal", DVec3); - if( polyPrim.tangents != null ) - format = format.append("tangent", DVec3); - if( polyPrim.uvs != null ) - format = format.append("uv", DVec2); - - infos.vertexFormat = format; - - var buf = new Array(); - for (k in 0...polyPrim.points.length) { - var p = polyPrim.points[k]; - buf.push(p.x); - buf.push(p.y); - buf.push(p.z); - if( polyPrim.normals != null ) { - var n = polyPrim.normals[k]; - buf.push(n.x); - buf.push(n.y); - buf.push(n.z); - } - if( polyPrim.tangents != null ) { - var t = polyPrim.tangents[k]; - buf.push(t.x); - buf.push(t.y); - buf.push(t.z); - } - if( polyPrim.uvs != null ) { - var t = polyPrim.uvs[k]; - buf.push(t.u); - buf.push(t.v); - } - } - - infos.vertexBuffer = buf; - - if (polyPrim.idx != null) - infos.indexesBuffer = [for (i in 0...polyPrim.indexes.count) polyPrim.idx[i]]; - else - infos.indexesBuffer = [for(i in 0...polyPrim.points.length) i]; - - return infos; - } - - return null; - } - - function resolvePathImpl( modelPath : String, filePath : String ) { - #if editor - inline function exists(path) return File.exists(path); - var fullPath = hide.Ide.inst.getPath(filePath); - if( exists(fullPath) ) - return fullPath; - - // swap drive letter - if( fullPath.charAt(1) == ":" && fullPath.charAt(0) != hide.Ide.inst.projectDir.charAt(0) ) { - fullPath = hide.Ide.inst.projectDir.charAt(0) + fullPath.substr(1); - if( exists(fullPath) ) - return fullPath; - } - - if( modelPath == null ) - return null; - - filePath = filePath.split("\\").join("/"); - modelPath = hide.Ide.inst.getPath(modelPath); - - var path = modelPath.split("/"); - path.pop(); - var relToModel = path.join("/") + "/" + filePath.split("/").pop(); - if( exists(relToModel) ) - return relToModel; - - return null; - #end - return filePath; - } - function getTabFormat(depth:Int) { return '${StringTools.rpad("", '\t', depth)}'; } @@ -165,30 +51,28 @@ class Writer { case PInts(v): { - var strBuf : StringBuf = new StringBuf(); - strBuf.add('*${v.length} {\n'); - strBuf.add('${getTabFormat(depth + 1)}a: '); + var res = '*${v.length} {\n'; + res += '${getTabFormat(depth + 1)}a: '; for (idx => i in v) { - strBuf.add('${idx != 0 ? ',' : ''}${i}'); + res += '${idx != 0 ? ',' : ''}${i}'; } - strBuf.add('\n${getTabFormat(depth)}}'); - return strBuf.toString(); + res += '\n${getTabFormat(depth)}}'; + return res; } case PFloats(v): { - var strBuf : StringBuf = new StringBuf(); - strBuf.add('*${v.length} {\n'); - strBuf.add('${getTabFormat(depth + 1)}a: '); + var res = '*${v.length} {\n'; + res += '${getTabFormat(depth + 1)}a: '; for (idx => i in v) { - strBuf.add('${idx != 0 ? ',' : ''}${i}'); + res += '${idx != 0 ? ',' : ''}${i}'; } - strBuf.add('\n${getTabFormat(depth)}}'); - return strBuf.toString(); + res += '\n${getTabFormat(depth)}}'; + return res; } default: @@ -197,97 +81,118 @@ class Writer { } function writeHeader() { - // This header is mandatory for most importers to define the fbx version - // of the file var fbxVersion = "7.3.0"; out.writeString('; FBX ${fbxVersion} project file\n'); out.writeString('; Copyright (C) 1997-2010 Autodesk Inc. and/or its licensors.\n'); out.writeString('; All rights reserved.\n'); out.writeString('; ----------------------------------------------------\n'); out.writeString('\n'); + + writeNode(buildHeaderNode()); } - function buildHeaderExtension() : FbxNode { + function buildTimeStampNode() : FbxNode{ var date = Date.now(); - var header : FbxNode = { name: "FBXHeaderExtension", props: null, childs: [ - { name: "FBXHeaderVersion", props: [PInt(1003)], childs: null }, - { name: "FBXVersion", props: [PInt(7003)], childs: null }, - { name: "CreationTimeStamp", props: null, childs:[ - { name: "Version", props:[PInt(1000)], childs:null }, - { name: "Year", props:[PInt(date.getFullYear())], childs:null }, - { name: "Month", props:[PInt(date.getMonth())], childs:null }, - { name: "Day", props:[PInt(date.getDay())], childs:null }, - { name: "Hour", props:[PInt(date.getHours())], childs:null }, - { name: "Minute", props:[PInt(date.getMinutes())], childs:null }, - { name: "Second", props:[PInt(date.getSeconds())], childs:null }, - { name: "Millisecond", props:[PInt(0)], childs:null} - ] }, - { name: "Creator", props: [PString("")], childs: null }, - { name: "SceneInfo", props: [PString("SceneInfo::GlobalInfo"), PString("UserData")], childs: [ - { name : "Type", props: [PString("UserData")], childs: null }, - { name : "Version", props: [PInt(100)], childs: null }, - { name:"MetaData", props:null, childs: [ - { name:"Version", props:[PInt(100)], childs: [] }, - { name:"Title", props:[PString("")], childs: [] }, - { name:"Subject", props:[PString("")], childs: [] }, - { name:"Author", props:[PString("")], childs: [] }, - { name:"Keywords", props:[PString("")], childs: [] }, - { name:"Revision", props:[PString("")], childs: [] }, - { name:"Comment", props:[PString("")], childs: [] } - ] }, - { name:"Properties70", props: null, childs:[ - { name:"P", props:[PString("DocumentUrl"), PString("KString"), PString("Url"), PString(""), PString("C:\\")], childs:null }, - { name:"P", props:[PString("SrcDocumentUrl"), PString("KString"), PString("Url"), PString(""), PString("C:\\")], childs:null }, - { name:"P", props:[PString("Original"), PString("Compound"), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("Original|ApplicationVendor"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("Original|ApplicationName"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("Original|ApplicationVersion"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("Original|DateTime_GMT"), PString("DateTime"), PString(""), PString(""), PString("01/01/1970 00:00:00.000")], childs:null }, - { name:"P", props:[PString("Original|FileName"), PString("KString"), PString(""), PString(""), PString("/foobar.fbx")], childs:null }, - { name:"P", props:[PString("LastSaved"), PString("Compound"), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("LastSaved|ApplicationVendor"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("LastSaved|ApplicationVersion"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - { name:"P", props:[PString("LastSaved|DateTime_GMT"), PString("DateTime"), PString(""), PString(""), PString("01/01/1970 00:00:00.000")], childs:null }, - { name:"P", props:[PString("Original|ApplicationNativeFile"), PString("KString"), PString(""), PString(""), PString("")], childs:null }, - ] } - ] } ] - }; + var tsVersion : FbxNode = {name: "Version", props:[PInt(1000)], childs:null}; + var tsYear : FbxNode = {name: "Year", props:[PInt(date.getFullYear())], childs:null}; + var tsMonth : FbxNode = {name: "Month", props:[PInt(date.getMonth())], childs:null}; + var tsDay : FbxNode = {name: "Day", props:[PInt(date.getDay())], childs:null}; + var tsHour : FbxNode = {name: "Hour", props:[PInt(date.getHours())], childs:null}; + var tsMinutes : FbxNode = {name: "Minute", props:[PInt(date.getMinutes())], childs:null}; + var tsSeconds : FbxNode = {name: "Second", props:[PInt(date.getSeconds())], childs:null}; + var tsMilliseconds : FbxNode = {name: "Millisecond", props:[PInt(0)], childs:null}; + var ts : FbxNode = {name: "CreationTimeStamp", props:null, childs:[tsVersion, tsYear, tsMonth, tsDay, tsHour, tsMinutes, tsSeconds, tsMilliseconds]}; + return ts; + } + + function buildSceneInfoMetaDataNode() : FbxNode { + var version : FbxNode = {name:"Version", props:[PInt(100)], childs: []}; + var title : FbxNode = {name:"Title", props:[PString("")], childs: []}; + var subject : FbxNode = {name:"Subject", props:[PString("")], childs: []}; + var author : FbxNode = {name:"Author", props:[PString("")], childs: []}; + var keywords : FbxNode = {name:"Keywords", props:[PString("")], childs: []}; + var revision : FbxNode = {name:"Revision", props:[PString("")], childs: []}; + var comment : FbxNode = {name:"Comment", props:[PString("")], childs: []}; + + var metadata : FbxNode = {name:"MetaData", props:null, childs: [version, title, subject, author, keywords, revision, comment]}; + return metadata; + } + function buildSceneInfoPropertiesNode() : FbxNode { + var properties : FbxNode = {name:"Properties70", props: null, childs:[ + {name:"P", props:[PString("DocumentUrl"), PString("KString"), PString("Url"), PString(""), PString("C:\\")], childs:null}, + {name:"P", props:[PString("SrcDocumentUrl"), PString("KString"), PString("Url"), PString(""), PString("C:\\")], childs:null}, + {name:"P", props:[PString("Original"), PString("Compound"), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("Original|ApplicationVendor"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("Original|ApplicationName"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("Original|ApplicationVersion"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("Original|DateTime_GMT"), PString("DateTime"), PString(""), PString(""), PString("01/01/1970 00:00:00.000")], childs:null}, + {name:"P", props:[PString("Original|FileName"), PString("KString"), PString(""), PString(""), PString("/foobar.fbx")], childs:null}, + {name:"P", props:[PString("LastSaved"), PString("Compound"), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("LastSaved|ApplicationVendor"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("LastSaved|ApplicationVersion"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + {name:"P", props:[PString("LastSaved|DateTime_GMT"), PString("DateTime"), PString(""), PString(""), PString("01/01/1970 00:00:00.000")], childs:null}, + {name:"P", props:[PString("Original|ApplicationNativeFile"), PString("KString"), PString(""), PString(""), PString("")], childs:null}, + ]}; + + return properties; + } + + function buildSceneInfoNode() : FbxNode { + var type : FbxNode = {name : "Type", props: [PString("UserData")], childs: null}; + var version : FbxNode = {name : "Version", props: [PInt(100)], childs: null}; + + var sceneInfo : FbxNode = {name: "SceneInfo", props:[PString("SceneInfo::GlobalInfo"), PString("UserData")], childs: [type, version, buildSceneInfoMetaDataNode(), buildSceneInfoPropertiesNode()]}; + return sceneInfo; + } + + function buildHeaderNode() : FbxNode { + var headerVersion : FbxNode = {name:"FBXHeaderVersion", props: [PInt(1003)], childs: null}; + var version : FbxNode = {name:"FBXVersion", props: [PInt(7003)], childs: null}; + var creator : FbxNode = {name:"Creator", props: [PString("")], childs: null}; + + var header : FbxNode = {name:"FBXHeaderExtension", props: null, childs: [headerVersion, version, buildTimeStampNode(), creator, buildSceneInfoNode()]}; return header; } - function buildGlobalSettings() { - var globalSettings : FbxNode = { name:"GlobalSettings", props: null, childs: [ - { name:"Version", props: [PInt(1000)], childs: null }, - { name:"Properties70", props: null, childs: [ - { name: "P", props: [PString("UpAxis"), PString("int"), PString("Integer"), PString(""), PInt(2) ], childs:null }, - { name: "P", props: [PString("UpAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, - { name: "P", props: [PString("FrontAxis"), PString("int"), PString("Integer"), PString(""), PInt(0) ], childs:null }, - { name: "P", props: [PString("FrontAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, - { name: "P", props: [PString("CoordAxis"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, - { name: "P", props: [PString("CoordAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, - { name: "P", props: [PString("OriginalUpAxis"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, - { name: "P", props: [PString("OriginalUpAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, - { name: "P", props: [PString("UnitScaleFactor"), PString("double"), PString("Number"), PString(""), PInt(100) ], childs:null }, - { name: "P", props: [PString("OriginalUnitScaleFactor"), PString("double"), PString("Number"), PString(""), PInt(1) ], childs:null }, - { name: "P", props: [PString("AmbientColor"), PString("ColorRGB"), PString("Color"), PString(""), PInt(0), PInt(0), PInt(0) ], childs:null }, - { name: "P", props: [PString("DefaultCamera"), PString("KString"), PString(""), PString(""), PString("Producer Perspective") ], childs:null }, - { name: "P", props: [PString("TimeMode"), PString("enum"), PString(""), PString(""), PInt(11) ], childs:null }, - { name: "P", props: [PString("TimeSpanStart"), PString("Ktime"), PString("Time"), PString(""), PInt(0) ], childs:null }, - { name: "P", props: [PString("TimeSpanStop"), PString("Ktime"), PString("Time"), PString(""), PInt(0) ], childs:null }, - { name: "P", props: [PString("CustomFrameRate"), PString("double"), PString("Number"), PString(""), PInt(24) ], childs:null }, - ] } + function buildProperties() { + var properties : FbxNode = { name:"Properties70", props: null, childs: [ + { name: "P", props: [PString("UpAxis"), PString("int"), PString("Integer"), PString(""), PInt(2) ], childs:null }, + { name: "P", props: [PString("UpAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, + { name: "P", props: [PString("FrontAxis"), PString("int"), PString("Integer"), PString(""), PInt(0) ], childs:null }, + { name: "P", props: [PString("FrontAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, + { name: "P", props: [PString("CoordAxis"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, + { name: "P", props: [PString("CoordAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, + { name: "P", props: [PString("OriginalUpAxis"), PString("int"), PString("Integer"), PString(""), PInt(-1) ], childs:null }, + { name: "P", props: [PString("OriginalUpAxisSign"), PString("int"), PString("Integer"), PString(""), PInt(1) ], childs:null }, + { name: "P", props: [PString("UnitScaleFactor"), PString("double"), PString("Number"), PString(""), PInt(100) ], childs:null }, + { name: "P", props: [PString("OriginalUnitScaleFactor"), PString("double"), PString("Number"), PString(""), PInt(1) ], childs:null }, + { name: "P", props: [PString("AmbientColor"), PString("ColorRGB"), PString("Color"), PString(""), PInt(0), PInt(0), PInt(0) ], childs:null }, + { name: "P", props: [PString("DefaultCamera"), PString("KString"), PString(""), PString(""), PString("Producer Perspective") ], childs:null }, + { name: "P", props: [PString("TimeMode"), PString("enum"), PString(""), PString(""), PInt(11) ], childs:null }, + { name: "P", props: [PString("TimeSpanStart"), PString("Ktime"), PString("Time"), PString(""), PInt(0) ], childs:null }, + { name: "P", props: [PString("TimeSpanStop"), PString("Ktime"), PString("Time"), PString(""), PInt(0) ], childs:null }, + { name: "P", props: [PString("CustomFrameRate"), PString("double"), PString("Number"), PString(""), PInt(24) ], childs:null }, ] }; + return properties; + } + function buildGlobalSettings() { + var version : FbxNode = {name:"Version", props: [PInt(1000)], childs: null}; + var properties : FbxNode = buildProperties(); + + var globalSettings : FbxNode = {name:"GlobalSettings", props: null, childs: [version, properties]}; return globalSettings; } function buildDefinitions(objects: Array) { - var materialsIds = new Array(); - var defCount = 1; + var defGlobalSettings : FbxNode = { name:"ObjectType", props:[PString("GlobalSettings")], childs: [ + { name: "Count", props: [PInt(1)], childs: null } + ] }; + var meshCount = 0; + var materialsIds = new Array(); var materialCount = 0; - var textureCount = 0; var meshes = []; for (o in objects) { @@ -300,23 +205,9 @@ class Writer { materialsIds.push(m.name); materialCount += m.getMaterials().length; - - if (m.material.texture != null) - textureCount++; - - if (m.material.normalMap != null) - textureCount++; - - if (m.material.specularTexture != null) - textureCount++; } } - var defGlobalSettings : FbxNode = { name:"ObjectType", props:[PString("GlobalSettings")], childs: [ - { name: "Count", props: [PInt(1)], childs: null } - ] }; - defCount += 1; - var modelCount = meshCount; var defModel : FbxNode = { name:"ObjectType", props:[PString("Model")], childs: [ { name: "Count", props: [PInt(modelCount)], childs: null }, @@ -396,7 +287,6 @@ class Writer { ] } ] } ] }; - defCount += modelCount; var geometryCount = meshCount; var defGeometry : FbxNode = { name:"ObjectType", props:[PString("Geometry")], childs: [ @@ -412,7 +302,6 @@ class Writer { ] } ] } ] }; - defCount += geometryCount; var defMaterial : FbxNode = { name:"ObjectType", props:[PString("Material")], childs: [ { name: "Count", props: [PInt(materialCount)], childs: null }, @@ -443,8 +332,8 @@ class Writer { ]} ] }, ] }; - defCount += materialCount; + var defCount = modelCount + geometryCount + materialCount + 1; var definitions : FbxNode = { name:"Definitions", props: null, childs: [ { name: "Version", props: [PInt(100)], childs: null }, { name: "Count", props: [PInt(defCount)], childs: null }, @@ -454,196 +343,122 @@ class Writer { defMaterial ]}; - if ( textureCount != 0 ) { - var defTexture : FbxNode = { name:"ObjectType", props:[PString("Texture")], childs: [ - { name: "Count", props: [PInt(textureCount)], childs: null }, - { name: "PropertyTemplate", props: [PString("FbxFileTexture")], childs: [ - { name:"Properties70", props: null, childs: [ - { name: "P", props: [PString("TextureTypeUse"), PString("enum"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Texture alpha"), PString("Number"), PString(""), PString("A"), PFloat(1)], childs: null }, - { name: "P", props: [PString("CurrentMappingType"), PString("enum"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("WrapModeU"), PString("enum"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("WrapModeV"), PString("enum"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("UVSwap"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("PremultiplyAlpha"), PString("bool"), PString(""), PString(""), PInt(1)], childs: null }, - { name: "P", props: [PString("Translation"), PString("Vector"), PString(""), PString("A"), PFloat(0), PFloat(0), PFloat(0)], childs: null }, - { name: "P", props: [PString("Rotation"), PString("Vector"), PString(""), PString("A"), PFloat(0), PFloat(0), PFloat(0)], childs: null }, - { name: "P", props: [PString("Scaling"), PString("Vector"), PString(""), PString("A"), PFloat(1), PFloat(1), PFloat(1)], childs: null }, - { name: "P", props: [PString("TextureRotationPivot"), PString("Vector3D"), PString("Vector"), PString(""), PFloat(0), PFloat(0), PFloat(0)], childs: null }, - { name: "P", props: [PString("TextureScalingPivot"), PString("Vector3D"), PString("Vector"), PString(""), PFloat(0), PFloat(0), PFloat(0)], childs: null }, - { name: "P", props: [PString("CurrentTextureBlendMode"), PString("enum"), PString(""), PString(""), PInt(1)], childs: null }, - { name: "P", props: [PString("UVSet"), PString("KString"), PString(""), PString(""), PString("default")], childs: null }, - { name: "P", props: [PString("UseMaterial"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("UseMipMap"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - ]} - ] }, - ] }; - defCount += textureCount; - - var videoCount = textureCount; - var defVideo : FbxNode = { name:"ObjectType", props:[PString("FbxVideo")], childs: [ - { name: "Count", props: [PInt(videoCount)], childs: null }, - { name: "PropertyTemplate", props: [PString("FbxFileTexture")], childs: [ - { name:"Properties70", props: null, childs: [ - { name: "P", props: [PString("ImageSequence"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("ImageSequenceOffset"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("FrameRate"), PString("double"), PString("Number"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("LastFrame"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Width"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Height"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Path"), PString("KString"), PString("XRefUrl"), PString(""), PString("")], childs: null }, - { name: "P", props: [PString("StartFrame"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("StopFrame"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("PlaySpeed"), PString("double"), PString("Number"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Offset"), PString("Ktime"), PString("Time"), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("InterlaceMode"), PString("enum"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("FreeRunning"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("Loop"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - { name: "P", props: [PString("AccessMode"), PString("bool"), PString(""), PString(""), PInt(0)], childs: null }, - ]} - ] }, - ] }; - - defCount += videoCount; - - definitions.childs.push(defTexture); - definitions.childs.push(defVideo); - } - return definitions; } - function buildObjects(objects: Array, params : Dynamic, objectRegistry : Array) { + function buildObjects(objects: Array, objectTreeRoot : Dynamic, params : Dynamic, usedMaterials : Array) { var objectsNode : FbxNode = { name: "Objects", props: null, childs: [] }; - var nextFreeId = 1; + var input = { objectsNode : objectsNode, nextFreeId : 1, usedMaterials : usedMaterials}; - function getUniqueId() { - nextFreeId++; - return nextFreeId - 1; - } + function buildObject(object : h3d.scene.Object, input : Dynamic, isRoot : Bool, params : Dynamic) { + // Define uniques ids for representing model, geometry and material node + var modelId = input.nextFreeId; + var geometryId = input.nextFreeId + 1; - function buildObject(object : h3d.scene.Object, params : Dynamic, isRoot : Bool) { - var modelId = getUniqueId(); - var modelTransform = object.getTransform(); + input.nextFreeId += 2; + + var mesh = Std.downcast(object, h3d.scene.Mesh); + var vertices = new Array(); + var normals = new Array(); + var uvs = new Array(); + var indexes = new Array(); + + if (mesh != null) { + var hmdModel = Std.downcast(mesh.primitive, h3d.prim.HMDModel); + var bufs = @:privateAccess hmdModel.getDataBuffers(hmdModel.data.vertexFormat); + + var idxVertex = 0; + while (idxVertex < bufs.vertexes.length) { + vertices.push(-bufs.vertexes[idxVertex]); // Change left hand to right hand + vertices.push(bufs.vertexes[idxVertex + 1]); + vertices.push(bufs.vertexes[idxVertex + 2]); + + normals.push(-bufs.vertexes[idxVertex + 3]); + normals.push(bufs.vertexes[idxVertex + 4]); + normals.push(bufs.vertexes[idxVertex + 5]); + + uvs.push(bufs.vertexes[idxVertex + 6]); + uvs.push(bufs.vertexes[idxVertex + 7]); + + @:privateAccess idxVertex += hmdModel.data.vertexFormat.stride; + } + + var idxIndex = 0; + while (idxIndex < bufs.indexes.length) { + // We have to flip the order of vertex to change the facing direction of the triangle (because we swapped x axis + // earlier to change from left hand to right hand) + indexes.push(bufs.indexes[idxIndex + 1]); + indexes.push(bufs.indexes[idxIndex]); + + // This is because the last index that close the polygon (in our case, we work with triangles, so the third) + // need to be increased by one and then set to negative. + // (This is because original index is XOR'ed with -1.) + // We also need to keep indexes in range of vertices length + indexes.push( -1 * (bufs.indexes[idxIndex + 2] + 1)); + + idxIndex += 3; + } + } + + var t = object.getTransform(); - // We add some extra rotations to the default transform to handle - // the export on different axis if (isRoot) { - var q = new h3d.Quat(); + var r = new h3d.Quat(); if (params.forward == "0" && params.forwardSign== "1" && params.up == "2" && params.upSign == "1") - q.initRotation(0,0,0); + r.initRotation(0,0,0); else if (params.forward == "0" && params.forwardSign== "-1" && params.up == "2" && params.upSign == "1") - q.initRotation(0,0,Math.degToRad(90)); + r.initRotation(0,0,Math.degToRad(90)); else throw "Export params not yet implemented"; - modelTransform = modelTransform.multiplied(q.toMatrix()); + t = t.multiplied(r.toMatrix()); } - // Apply the default transform of the mode on the current transform to - // get the real transform if (object.defaultTransform != null) - modelTransform = object.defaultTransform.multiplied(modelTransform); + t = object.defaultTransform.multiplied(t); - // Convert left hand matrix to right hand matrix - modelTransform._12 = -modelTransform._12; - modelTransform._13 = -modelTransform._13; - modelTransform._21 = -modelTransform._21; - modelTransform._31 = -modelTransform._31; - modelTransform._41 = -modelTransform._41; + t._12 = -t._12; + t._13 = -t._13; + t._21 = -t._21; + t._31 = -t._31; + t._41 = -t._41; - // The model node is used for every object, not only those we have mesh var model : FbxNode = { name:"Model", props: [PInt(modelId), PString('Model::${object.name}'), PString("Mesh")], childs:[ { name:"Version", props:[ PInt(232)], childs:null }, { name:"Properties70", props: null, childs: [ { name:"P", props:[PString("InheritType"), PString("enum"), PString(""), PString(""), PInt(1)], childs: null }, { name:"P", props:[PString("DefaultAttributeIndex"), PString("int"), PString("Integer"), PString(""), PInt(0)], childs: null }, - { name:"P", props:[PString("Lcl Translation"), PString("Lcl Translation"), PString(""), PString("A"), PFloat(modelTransform.getPosition().x), PFloat(modelTransform.getPosition().y), PFloat(modelTransform.getPosition().z)], childs: null }, - { name:"P", props:[PString("Lcl Rotation"), PString("Lcl Rotation"), PString(""), PString("A"), PFloat(Math.radToDeg(modelTransform.getEulerAngles().x)), PFloat(Math.radToDeg(modelTransform.getEulerAngles().y)), PFloat(Math.radToDeg(modelTransform.getEulerAngles().z))], childs: null }, - { name:"P", props:[PString("Lcl Scaling"), PString("Lcl Scaling"), PString(""), PString("A"), PFloat(modelTransform.getScale().x), PFloat(modelTransform.getScale().y), PFloat(modelTransform.getScale().z)], childs: null }, + { name:"P", props:[PString("Lcl Translation"), PString("Lcl Translation"), PString(""), PString("A"), PFloat(t.getPosition().x), PFloat(t.getPosition().y), PFloat(t.getPosition().z)], childs: null }, + { name:"P", props:[PString("Lcl Rotation"), PString("Lcl Rotation"), PString(""), PString("A"), PFloat(Math.radToDeg(t.getEulerAngles().x)), PFloat(Math.radToDeg(t.getEulerAngles().y)), PFloat(Math.radToDeg(t.getEulerAngles().z))], childs: null }, + { name:"P", props:[PString("Lcl Scaling"), PString("Lcl Scaling"), PString(""), PString("A"), PFloat(t.getScale().x), PFloat(t.getScale().y), PFloat(t.getScale().z)], childs: null }, ]} ] }; - objectsNode.childs.push(model); + input.objectsNode.childs.push(model); - var mesh = Std.downcast(object, h3d.scene.Mesh); if (mesh == null) return; - var vertices = new Array(); - var normals = new Array(); - var uvs = new Array>(); - var indexes = new Array(); - - var infos = getPrimitiveInfos(mesh.primitive); - var idxVertex = 0; - - // Fill mesh informations that will be required in the fbx file - while (idxVertex < infos.vertexBuffer.length) { - var curIndex = idxVertex; - vertices.push(-infos.vertexBuffer[curIndex]); // Convert left hand X coordinate to right hand X coordinate - vertices.push(infos.vertexBuffer[curIndex + 1]); - vertices.push(infos.vertexBuffer[curIndex + 2]); - curIndex += 3; - - if (infos.vertexFormat.hasInput("normal")) { - normals.push(-infos.vertexBuffer[curIndex]); // Convert left hand X coordinate to right hand X coordinate - normals.push(infos.vertexBuffer[curIndex + 1]); - normals.push(infos.vertexBuffer[curIndex + 2]); - curIndex += 3; - } - - // Tangent export isn't supported at the moment - if (infos.vertexFormat.hasInput("tangent")) - curIndex += 3; - - var uvIdx = 0; - var uvInput = 'uv${ uvIdx == 0 ? "" : '${uvIdx + 1}'}'; - while(infos.vertexFormat.hasInput(uvInput)) { - if (uvs.length < uvIdx + 1) - uvs.push(new Array()); - - uvs[uvIdx].push(infos.vertexBuffer[curIndex]); - uvs[uvIdx].push(1 - infos.vertexBuffer[curIndex + 1]); - curIndex += 2; - uvIdx++; - uvInput = 'uv${ uvIdx == 0 ? "" : '${uvIdx + 1}'}'; - } - - idxVertex += infos.vertexFormat.stride; - } - - var idxIndex = 0; - while (idxIndex < infos.indexesBuffer.length) { - // We have to flip the order of vertex to change the facing direction of the triangle (because we changed X axis - // sign earlier to change from left hand to right hand) - - // /!\ This is because the last index that close the polygon (in our case, we work with triangles, so the third) - // need to be increased by one and then set to negative. - // (This is because original index is XOR'ed with -1.) - indexes.push(infos.indexesBuffer[idxIndex + 1]); - indexes.push(infos.indexesBuffer[idxIndex]); - indexes.push( -1 * (infos.indexesBuffer[idxIndex + 2] + 1)); - - idxIndex += 3; - } - var meshMaterials = mesh.getMaterials(); - var materialIndexes = []; + var mats = new Array(); for (idx => mat in meshMaterials ) { - var materialId = -1; + var hmdModel = Std.downcast(mesh.primitive, h3d.prim.HMDModel); + var materialId = input.nextFreeId; // Only write material once in the fbx file - for (i in 0...objectRegistry.length) { - if (objectRegistry[i].name == "__mat"+mat.name) { - materialId = objectRegistry[i].id; + var matId = -1; + for (i in 0...input.usedMaterials.length) + if (input.usedMaterials[i].matName == mat.name) { + matId = input.usedMaterials[i].matId; break; } - } - if (materialId == -1) { - materialId = getUniqueId(); + if (matId != -1) { + materialId = matId; + } + else { + input.nextFreeId += 1; var material : FbxNode = { name:"Material", props: [PInt(materialId), PString('Material::${mat.name}'), PString("")], childs:[ { name: "Version", props: [PInt(102)], childs: null }, @@ -671,87 +486,15 @@ class Writer { ] }, ] }; - objectsNode.childs.push(material); - } - - objectRegistry.push({ name: "__mat"+mat.name, type: "O", id: materialId, parentId: modelId, property: null }); - - if (infos.lib == null) - continue; - - var hmd = Std.downcast(mesh.primitive, h3d.prim.HMDModel); - if (hmd != null) { - var materials = hmd.getMaterialIndexes(idx); - for (i in 0...Std.int(materials.count / 3)) - materialIndexes.push(idx); + input.objectsNode.childs.push(material); } - // Building mat textures - var textures = new Array(); - for (matData in infos.lib.header.materials) { - if (matData.name == mat.name) { - if (matData.diffuseTexture != null) - @:privateAccess textures.push({ name: matData.diffuseTexture.substr(matData.diffuseTexture.lastIndexOf("/") + 1), path: resolvePathImpl(infos.lib.resource.entry.path ,matData.diffuseTexture), property: "DiffuseColor" }); - if (matData.normalMap != null) - @:privateAccess textures.push({ name: matData.normalMap.substr(matData.normalMap.lastIndexOf("/") + 1), path: resolvePathImpl(infos.lib.resource.entry.path ,matData.normalMap), property: "NormalMap" }); - if (matData.specularTexture != null) - @:privateAccess textures.push({ name : matData.specularTexture.substr(matData.specularTexture.lastIndexOf("/") + 1), path: resolvePathImpl(infos.lib.resource.entry.path ,matData.specularTexture), property: "SpecularColor" }); - } - } - - for (t in textures) { - var textureId = getUniqueId(); - var textureProperty = '${ t.property == "DiffuseColor" ? "base_color_texture" : (t.property == "NormalMap" ? "normalmap_texture" : "specular_texture") }'; - var texture : FbxNode = { name:"Texture", props: [PInt(textureId), PString('Texture::${textureProperty}'), PString("Clip")], childs:[ - { name: "Type", props: [PString("TextureVideoClip")], childs: null }, - { name: "Version", props: [PInt(202)], childs: null }, - { name: "TextureName", props: [PString('Texture::${textureProperty}')], childs: null }, - { name: "Properties70", props: null, childs: [ - { name:"P", props: [PString("UseMaterial"), PString("bool"), PString(""), PString(""), PInt(1)], childs: null }, - { name:"P", props: [PString("AlphaSource"), PString("enum"), PString(""), PString(""), PInt(2)], childs: null }, - ] }, - { name: "Media", props: [PString('Video::${t.name}')], childs: null }, - { name: "Filename", props: [PString(t.path)], childs: null }, - { name: "RelativeFilename", props: [PString("")], childs: null }, - { name: "ModelUVTranslation", props: [PFloat(0), PFloat(0)], childs: null }, - { name: "ModelUVScaling", props: [PFloat(1), PFloat(1)], childs: null }, - { name: "Texture_Alpha_Source", props: [PString("None")], childs: null }, - { name: "Cropping", props: [PInt(0), PInt(0), PInt(0), PInt(0)], childs: null }, - ] }; - - - objectsNode.childs.push(texture); - objectRegistry.push({ name: "texture_"+t.name, type: "P", id: textureId, parentId: materialId, property: t.property }); - - // If texture isn't registered in fbx file, we should add it (it is registered as "Video" object) - var videoId = -1; - for (idx in 0...objectRegistry.length) { - if (objectRegistry[idx].name == "__video"+t.name) { - videoId = objectRegistry[idx].id; - break; - } - } - - if (videoId == -1) { - videoId = getUniqueId(); - var video : FbxNode = { name:"Video", props: [PInt(videoId), PString('Video::${t.name}'), PString("Clip")], childs:[ - { name: "Type", props: [PString("Clip")], childs: null }, - { name: "Properties70", props: null, childs: [ - { name:"P", props: [PString("KString"), PString("XRefUrl"), PString(""), PString(t.path)], childs: null }, - ] }, - { name: "UseMipMap", props: [PInt(0)], childs: null }, - { name: "Filename", props: [PString(t.path)], childs: null }, - { name: "RelativeFilename", props: [PString("")], childs: null }, - ] }; - - objectsNode.childs.push(video); - } - - objectRegistry.push({ name: "__video"+t.name, type: "O", id: videoId, parentId: textureId, property: null }); - } + input.usedMaterials.push( { matName : mat.name, matId : materialId, parentModelId : modelId} ); + var matIndexes = hmdModel.getMaterialIndexes(idx); + for (i in 0...Std.int(matIndexes.count / 3)) + mats.push(idx); } - var geometryId = getUniqueId(); var geometry : FbxNode = { name:"Geometry", props: [PInt(geometryId), PString('Geometry::${mesh.name}'), PString("Mesh")], childs:[ { name:"Vertices", props: [PFloats(vertices)], childs: null}, { name:"PolygonVertexIndex", props: [PInts(indexes)], childs: null}, @@ -762,34 +505,21 @@ class Writer { { name: "MappingInformationType", props: [ PString("ByVertice") ], childs: null }, { name: "ReferenceInformationType", props: [ PString("Direct") ], childs: null }, { name: "Normals", props: [ PFloats(normals) ], childs: null }, - ]} - ] }; - - if (materialIndexes.length > 0) { - geometry.childs.push({ name:"LayerElementMaterial", props: [PInt(0)], childs: [ + ]}, + { name:"LayerElementUV", props: [PInt(0)], childs: [ + { name: "Version", props: [ PInt(101) ], childs: null }, + { name: "Name", props: [ PString("UVMap") ], childs: null }, + { name: "MappingInformationType", props: [ PString("ByVertice") ], childs: null }, + { name: "ReferenceInformationType", props: [ PString("Direct") ], childs: null }, + { name: "UV", props: [ PFloats(uvs) ], childs: null }, + ]}, + { name:"LayerElementMaterial", props: [PInt(0)], childs: [ { name: "Version", props: [ PInt(101) ], childs: null }, { name: "Name", props: [ PString("") ], childs: null }, { name: "MappingInformationType", props: [ PString("ByPolygon") ], childs: null }, { name: "ReferenceInformationType", props: [ PString("IndexToDirect") ], childs: null }, - { name: "Materials", props: [ PInts(materialIndexes) ], childs: null }, - ]}); - } - - // Add all uv maps in layer elements - for (idx => uv in uvs) { - geometry.childs.push( - { name:"LayerElementUV", props: [PInt(idx)], childs: [ - { name: "Version", props: [ PInt(101) ], childs: null }, - { name: "Name", props: [ PString("UVMap"+idx) ], childs: null }, - { name: "MappingInformationType", props: [ PString("ByVertice") ], childs: null }, - { name: "ReferenceInformationType", props: [ PString("Direct") ], childs: null }, - { name: "UV", props: [ PFloats(uv) ], childs: null }, - ]} - ); - } - - // Build all layers (we're currently building several layers only to support several uvs maps) - geometry.childs.push( + { name: "Materials", props: [ PInts(mats) ], childs: null }, + ]}, { name:"Layer", props: [PInt(0)], childs: [ { name: "Version", props: [ PInt(100) ], childs: null }, { name: "LayerElement", props: null, childs: [ @@ -805,56 +535,52 @@ class Writer { { name: "TypedIndex", props: [ PInt(0) ], childs: null }, ] }, ]} - ); - - for (idx => uv in uvs) { - if (idx == 0) - continue; - - geometry.childs.push( - { name:"Layer", props: [PInt(idx)], childs: [ - { name: "Version", props: [ PInt(100) ], childs: null }, - { name: "LayerElement", props: null, childs: [ - { name: "Type", props: [ PString("LayerElementUV") ], childs: null }, - { name: "TypedIndex", props: [ PInt(idx) ], childs: null }, - ] }, - ]} - ); - } + ] }; - objectsNode.childs.push(geometry); - objectRegistry.push({ name: "geometry" ,type: "O", id: geometryId, parentId: modelId, property: null }); + input.objectsNode.childs.push(geometry); } - function build(objects: Array, parentId : Int) { + function build(objects: Array, input : Dynamic, parent : Dynamic) { for (o in objects) { - var objectId = nextFreeId; + // We're not supporting anything except meshes for now + // var mesh = Std.downcast(o, h3d.scene.Mesh); + // if (mesh == null) + // continue; - // Register object in object registry for future usage (add connections for example) - objectRegistry.push({ name: o.name, type : "O", id: nextFreeId, parentId: parentId, property: null }); + var objectLeaf = { id: input.nextFreeId, children : []}; + parent.children.push(objectLeaf); - // Build current object and his children recusively - buildObject(o, params, parentId == 0); - build(@:privateAccess o.children, objectId); + buildObject(o, input, parent.id == 0, params); + build(@:privateAccess o.children, input, objectLeaf); } } - build(objects, 0); - return objectsNode; + build(objects, input, objectTreeRoot); + return input.objectsNode; } - function buildConnections(objectRegistry : Array) { - var connections : FbxNode = { name:"Connections", props: null, childs: [] }; + function buildConnections(objectTree : Dynamic, usedMaterials : Array) { + // C stands for "Connection" + // OO stands for "Object to Object" meaning the connection is between two objects + // Then there's ids of object that are linked - for (o in objectRegistry) { - var connection = { name:"C", props: [ PString("O"+o.type), PInt(o.id), PInt(o.parentId) ], childs: null }; + var connections : FbxNode = { name:"Connections", props: null, childs: []}; - if (o.property != null) - connection.props.push(PString(o.property)); + function addConnexion(parentId : Int, objectTree : Dynamic) { + if (objectTree.id != 0) { + connections.childs.push({ name:"C", props: [ PString("OO"), PInt(objectTree.id), PInt(parentId) ], childs: null }); + connections.childs.push({ name:"C", props: [ PString("OO"), PInt(objectTree.id + 1), PInt(objectTree.id) ], childs: null }); + } - connections.childs.push(connection); + for (idx in 0...objectTree.children.length) + addConnexion(objectTree.id, objectTree.children[idx]); } + addConnexion(-1, objectTree); + + for (idx in 0...usedMaterials.length) + connections.childs.push({ name:"C", props: [ PString("OO"), PInt(usedMaterials[idx].matId), PInt(usedMaterials[idx].parentModelId) ], childs: null }); + return connections; } @@ -863,90 +589,18 @@ class Writer { var header = new haxe.io.BytesOutput(); out = header; - var objectRegistry = new Array(); - writeHeader(); - writeNode(buildHeaderExtension()); writeNode(buildGlobalSettings()); - writeNode(buildDefinitions(objects)); - writeNode(buildObjects(objects, params, objectRegistry)); - writeNode(buildConnections(objectRegistry)); + writeNode(buildDefinitions(cast objects)); + + var objectTreeRoot = { id: 0, children: [] }; + var usedMaterials = new Array(); + writeNode(buildObjects(cast objects, objectTreeRoot, params, usedMaterials)); + writeNode(buildConnections(objectTreeRoot, usedMaterials)); var bytes = header.getBytes(); out = old; out.write(bytes); } - - public function export(toExport: Array, destinationPath: String, callb : Void -> Void, ?params : ExportParams) { - if (this.out == null) - this.out = new haxe.io.BytesOutput(); - - function clean( obj : h3d.scene.Object ) : h3d.scene.Object { - var supported = true; - for (c in unsuported) { - if (Std.isOfType(obj, c)) { - supported = false; - break; - } - } - - if (!supported || !obj.visible) - return null; - - var o = new h3d.scene.Object(); - var multiMat = Std.downcast(obj, h3d.scene.MultiMaterial); - if (multiMat != null) - o = new h3d.scene.MultiMaterial(multiMat.primitive, multiMat.materials); - else { - var m = Std.downcast(obj, h3d.scene.Mesh); - if (Std.isOfType(m?.primitive, h3d.prim.HMDModel) || Std.isOfType(m?.primitive, h3d.prim.Cube)) - o = new h3d.scene.Mesh(m.primitive, m.material); - } - - o.x = obj.x; - o.y = obj.y; - o.z = obj.z; - o.scaleX = obj.scaleX; - o.scaleY = obj.scaleY; - o.scaleZ = obj.scaleZ; - @:privateAccess o.qRot.load(@:privateAccess obj.qRot); - o.name = obj.name; - o.follow = obj.follow; - o.followPositionOnly = obj.followPositionOnly; - o.visible = obj.visible; - if( obj.defaultTransform != null ) - o.defaultTransform = obj.defaultTransform.clone(); - for( c in @:privateAccess obj.children ) { - var c2 = clean(c); - if (c2 == null) - continue; - @:privateAccess c2.parent = o; - @:privateAccess o.children.push(c2); - } - - return o; - } - - // Clean a bit the object hierarchy to remove non-needed objects - // (Interactibles for example) - var roots = new Array(); - for (o in toExport) { - var t = clean(o); - if (t != null) - roots.push(t); - } - - // Handle the export of selection into a fbx file - if (destinationPath != null) { - var out = new haxe.io.BytesOutput(); - new hxd.fmt.fbx.Writer(out).write(roots, params); - #if js - hxd.File.saveBytes(destinationPath, out.getBytes()); - #else - sys.io.File.saveBytes(destinationPath, out.getBytes()); - #end - callb(); - } - } } diff --git a/hxd/fmt/hmd/Data.hx b/hxd/fmt/hmd/Data.hx index e04d332c5d..f4277db655 100644 --- a/hxd/fmt/hmd/Data.hx +++ b/hxd/fmt/hmd/Data.hx @@ -78,18 +78,6 @@ class Geometry { } } -class BlendShape { - public var name : String; - public var geom : Index; - public var vertexCount : Int; - public var vertexFormat : hxd.BufferFormat; - public var vertexPosition : DataPosition; - public var indexCount : DataPosition; - public var remapPosition : DataPosition; - public function new() { - } -} - class Material { public var name : String; @@ -195,7 +183,7 @@ class Animation { class Data { - public static inline var CURRENT_VERSION = 4; + public static inline var CURRENT_VERSION = 3; public var version : Int; public var props : Properties; @@ -203,7 +191,6 @@ class Data { public var materials : Array; public var models : Array; public var animations : Array; - public var shapes : Array; public var dataPosition : Int; public var data : haxe.io.Bytes; diff --git a/hxd/fmt/hmd/Library.hx b/hxd/fmt/hmd/Library.hx index 2bd2b2180f..f9712c6a0a 100644 --- a/hxd/fmt/hmd/Library.hx +++ b/hxd/fmt/hmd/Library.hx @@ -1,5 +1,4 @@ package hxd.fmt.hmd; -import h3d.prim.HMDModel; import hxd.fmt.hmd.Data; private class FormatMap { @@ -19,8 +18,8 @@ private class FormatMap { private class ContextShared extends hrt.prefab.ContextShared { var customLoadTexture : String -> h3d.mat.Texture; - public function new(loadTexture : String -> h3d.mat.Texture, ?root3d: h3d.scene.Object = null) { - super(root3d); + public function new(loadTexture : String -> h3d.mat.Texture, ?res : hxd.res.Resource) { + super(res); this.customLoadTexture = loadTexture; } @@ -267,32 +266,12 @@ class Library { return buf; } - function makePrimitive( model : Model ) { - var id : Int = model.geometry; + function makePrimitive( id : Int ) { var p = cachedPrimitives[id]; if( p != null ) return p; - - var lodInfos = getLODInfos( model ); - if ( lodInfos.lodLevel > 0) { - for ( m in header.models ) - if ( m.name != null && StringTools.contains(m.name, lodInfos.modelName) && StringTools.contains(m.name, "LOD0")) - return null; - throw "No LOD0 found for " + lodInfos.modelName + " in " + resource.name; - } - - var lods : Array = null; - if (lodInfos.lodLevel == 0 ) { - lods = findLODs( lodInfos.modelName, model ); - patchLodsMaterials(model, lods); - } - - p = new h3d.prim.HMDModel(header.geometries[id], header.dataPosition, this, lods); + p = new h3d.prim.HMDModel(header.geometries[id], header.dataPosition, this); p.incref(); // Prevent from auto-disposing cachedPrimitives[id] = p; - - if (lodInfos.lodLevel == 0) - h3d.prim.ModelDatabase.current.loadModelProps(model.name, p); - return p; } @@ -316,8 +295,8 @@ class Library { try { if ( setupMaterialLibrary(loadTexture, mat, hxd.res.Loader.currentInstance.load((props:Dynamic).__ref).toPrefab(), (props:Dynamic).name) ) return mat; - } catch( e : Dynamic ) {} - props = mat.getDefaultModelProps(); + } catch( e : Dynamic ) { + } } #end if( m.diffuseTexture != null ) { @@ -384,76 +363,6 @@ class Library { return def; } - public function getLODInfos( model : Model ) : { lodLevel : Int , modelName : String } { - var modelName : String = model.name; - var keyword = h3d.prim.HMDModel.lodExportKeyword; - if ( modelName == null || modelName.length <= keyword.length ) - return { lodLevel : -1, modelName : null }; - - // Test prefix - if ( modelName.substr(0, keyword.length) == keyword) { - var parsedInt = Std.parseInt(modelName.substr( keyword.length, 1 )); - if (parsedInt != null) { - if ( Std.parseInt( modelName.substr( keyword.length + 1, 1 ) ) != null ) - throw 'Did not expect a second number after LOD in ${modelName}'; - return { lodLevel : parsedInt, modelName : modelName.substr(keyword.length) }; - } - } - - // Test suffix - var maxCursor = modelName.length - keyword.length - 1; - if ( modelName.substr( maxCursor, keyword.length ) == keyword ) { - var parsedInt = Std.parseInt( modelName.charAt( modelName.length - 1) ); - if ( parsedInt != null ) { - return { lodLevel : parsedInt, modelName : modelName.substr( 0, maxCursor ) }; - } - } - - return { lodLevel : -1, modelName : null }; - } - - public function findLODs( modelName : String, lod0 : Model ) : Array { - if ( modelName == null ) - return null; - var lods : Array = []; - for ( curModel in header.models ) { - var lodInfos = getLODInfos( curModel ); - if ( lodInfos.lodLevel < 1 ) - continue; - if ( lodInfos.modelName == modelName ) { - if ( lods[lodInfos.lodLevel - 1] != null ) - throw 'Multiple LODs with the same level : ${curModel.name}'; - lods[lodInfos.lodLevel - 1] = curModel; - } - } - - return lods; - } - - public function patchLodsMaterials( lod0 : Model, lods : Array) { - for (model in lods) { - for (m in model.materials) { - if (lod0.materials.contains(m)) - continue; - throw 'Model ${model.name} has a material that isn\'t used by ${lod0.name}. This is not supported.'; - } - - // Patch materials when lods have different materials, otherwise some indexCounts will be null - var geom = header.geometries[model.geometry]; - var indexCounts = []; - var j = 0; - for ( i in 0...lod0.materials.length ) { - if (lod0.materials[i] == model.materials[j]) { - indexCounts[i] = geom.indexCounts[j]; - j++; - } - else - indexCounts[i] = 0; - } - geom.indexCounts = indexCounts; - } - } - #if !dataOnly public function makeObject( ?loadTexture : String -> h3d.mat.Texture ) : h3d.scene.Object { if( loadTexture == null ) @@ -466,9 +375,7 @@ class Library { if( m.geometry < 0 ) { obj = new h3d.scene.Object(); } else { - var prim = makePrimitive(m); - if (prim == null) - continue; + var prim = makePrimitive(m.geometry); if( m.skin != null ) { var skinData = makeSkin(m.skin, header.geometries[m.geometry]); skinData.primitive = prim; @@ -746,7 +653,7 @@ class Library { var vidx = data.indexes[idx]; var p = vidx * formatStride; var x = vbuf[p]; - if( Math.isNaN(x) ) { + if( x != x ) { // already processed continue; } @@ -857,9 +764,19 @@ class Library { if (materialContainer == null) materialContainer = new h3d.scene.Mesh(null, mat, null); - var shared = new ContextShared(loadTexture, materialContainer); + #if prefab2 + var shared = new ContextShared(loadTexture, null); + materialContainer.material = mat; - m.make(shared); + m.make(materialContainer,shared); + #else + var ctx = new hrt.prefab.Context(); + ctx.shared = new ContextShared(loadTexture, null); + + materialContainer.material = mat; + ctx.local3d = materialContainer; + m.make(ctx); + #end // Ensure there is no leak with this materialContainer.material = null; diff --git a/hxd/fmt/hmd/Reader.hx b/hxd/fmt/hmd/Reader.hx index 5c8ed9246b..56016f3ee5 100644 --- a/hxd/fmt/hmd/Reader.hx +++ b/hxd/fmt/hmd/Reader.hx @@ -142,21 +142,17 @@ class Reader { i = new haxe.io.BytesInput(i.read(d.dataPosition-12)); d.props = readProps(); - inline function makeFormat() { + for( k in 0...i.readInt32() ) { + var g = new Geometry(); + g.props = readProps(); + g.vertexCount = i.readInt32(); var stride = i.readByte(); - var format = hxd.BufferFormat.make([for( k in 0...i.readByte() ) { + g.vertexFormat = hxd.BufferFormat.make([for( k in 0...i.readByte() ) { var name = readCachedName(); var type = i.readByte(); new GeometryFormat(name, @:privateAccess GeometryDataFormat.fromInt(type&15), @:privateAccess hxd.BufferFormat.Precision.fromInt(type>>4)); }]); - if ( stride != format.stride ) throw "assert"; - return format; - } - for( k in 0...i.readInt32() ) { - var g = new Geometry(); - g.props = readProps(); - g.vertexCount = i.readInt32(); - g.vertexFormat = makeFormat(); + if( stride != g.vertexFormat.stride ) throw "assert"; g.vertexPosition = i.readInt32(); var subCount = i.readByte(); if( subCount == 0xFF ) subCount = i.readInt32(); @@ -233,24 +229,6 @@ class Reader { d.animations.push(a); } - if ( d.version >= 4 ) { - var shapeLength : Int; - shapeLength = i.readInt32(); - d.shapes = []; - for ( k in 0...shapeLength ) { - var s = new BlendShape(); - s.name = readName(); - s.geom = i.readInt32() - 1; - s.vertexCount = i.readInt32(); - s.vertexFormat = makeFormat(); - s.vertexPosition = i.readInt32(); - s.indexCount = i.readInt32(); - s.remapPosition = i.readInt32(); - d.shapes.push(s); - } - } - - return d; } diff --git a/hxd/fmt/hmd/Writer.hx b/hxd/fmt/hmd/Writer.hx index ae2d658d1d..09b7aa51c0 100644 --- a/hxd/fmt/hmd/Writer.hx +++ b/hxd/fmt/hmd/Writer.hx @@ -110,19 +110,16 @@ class Writer { writeProps(d.props); - function writeFormat(format : hxd.BufferFormat) { - out.writeByte(format.stride); - out.writeByte(@:privateAccess format.inputs.length); - for( f in format.getInputs() ) { - writeName(f.name); - out.writeByte(f.type.toInt() | (f.precision.toInt() << 4)); - } - } out.writeInt32(d.geometries.length); for( g in d.geometries ) { writeProps(g.props); out.writeInt32(g.vertexCount); - writeFormat(g.vertexFormat); + out.writeByte(g.vertexFormat.stride); + out.writeByte(@:privateAccess g.vertexFormat.inputs.length); + for( f in g.vertexFormat.getInputs() ) { + writeName(f.name); + out.writeByte(f.type.toInt() | (f.precision.toInt() << 4)); + } out.writeInt32(g.vertexPosition); if( g.indexCounts.length >= 0xFF ) { out.writeByte(0xFF); @@ -199,17 +196,6 @@ class Writer { } } - out.writeInt32(d.shapes.length); - for ( s in d.shapes ) { - writeName(s.name); - out.writeInt32(s.geom + 1); - out.writeInt32(s.vertexCount); - writeFormat(s.vertexFormat); - out.writeInt32(s.vertexPosition); - out.writeInt32(s.indexCount); - out.writeInt32(s.remapPosition); - } - var bytes = header.getBytes(); out = old; diff --git a/hxd/fmt/pak/FileSystem.hx b/hxd/fmt/pak/FileSystem.hx index b82979e1c2..3ed4203687 100644 --- a/hxd/fmt/pak/FileSystem.hx +++ b/hxd/fmt/pak/FileSystem.hx @@ -1,5 +1,8 @@ package hxd.fmt.pak; +#if !macro import hxd.fs.FileEntry; +import hxd.fs.LoadedBitmap; +#end #if (sys || nodejs) import sys.io.File; import sys.io.FileInput; @@ -46,7 +49,7 @@ class FileSeek { @:allow(hxd.fmt.pak.FileSystem) @:access(hxd.fmt.pak.FileSystem) -private class PakEntry extends FileEntry { +private class PakEntry extends hxd.fs.FileEntry { var fs : FileSystem; var parent : PakEntry; @@ -110,6 +113,27 @@ private class PakEntry extends FileEntry { return tot; } + override function loadBitmap( onLoaded : (bmp:hxd.fs.LoadedBitmap, ?texture:h3d.mat.Texture) -> Void ) : Void { + #if js + final ext = file.name.substring(file.name.indexOf('.')+1).toLowerCase(); + if(ext=='png') { + + hxd.res.Ktx2.Ktx2Decoder.getTexture(new haxe.io.BytesInput(getBytes()), texture -> { + onLoaded(null, texture); + }); + + } + // TODO: Test jpg + // else { + // var img = new js.html.Image(); + // img.onload = () -> onLoaded(new hxd.fs.LoadedBitmap(img)); + // img.src = 'data:$mime;base64,' + haxe.crypto.Base64.encode(getBytes()); + //} + #else + throw "Not implemented"; + #end + } + override function exists( name : String ) { if( subs != null ) for( c in subs ) @@ -118,7 +142,8 @@ private class PakEntry extends FileEntry { return false; } - override function get( name : String ) : FileEntry { + override function get( name : String ) : hxd.fs.FileEntry { + trace('name: ${name}'); if( subs != null ) for( c in subs ) if( c.name == name ) @@ -127,7 +152,7 @@ private class PakEntry extends FileEntry { } override function iterator() { - return new hxd.impl.ArrayIterator(cast subs); + return new hxd.impl.ArrayIterator(cast subs); } } @@ -145,6 +170,7 @@ class FileSystem implements hxd.fs.FileSystem { public var totalReadCount = 0; public function new() { + trace('new pak fs'); dict = new Map(); var f = new Data.File(); f.name = ""; @@ -159,6 +185,7 @@ class FileSystem implements hxd.fs.FileSystem { } public function loadPak( file : String ) { + trace('file: ${file}'); var index = files.length; files.push({ path : file, inputs : [] }); var s = getFile(index); @@ -185,10 +212,14 @@ class FileSystem implements hxd.fs.FileSystem { files.push(info); var pak = new Reader(file).readHeader(); if( pak.root.isDirectory ) { - for( f in pak.root.content ) + for( f in pak.root.content ) { + trace('f.name: ${f.name}'); addRec(root, f.name, f, index, pak.headerSize); - } else + } + } else { +trace('pak.root.name: ${pak.root.name}'); addRec(root, pak.root.name, pak.root, index, pak.headerSize); + } } public function dispose() { @@ -236,6 +267,7 @@ class FileSystem implements hxd.fs.FileSystem { ent.pakFile = pakFile; } else { ent = new PakEntry(this, parent, f, pakFile); + trace('addRec path: ${path}'); dict.set(path, ent); parent.subs.push(ent); } @@ -246,11 +278,12 @@ class FileSystem implements hxd.fs.FileSystem { f.dataPosition += delta; } - public function getRoot() : FileEntry { + public function getRoot() : hxd.fs.FileEntry { return root; } - public function get( path : String ) : FileEntry { + public function get( path : String ) : hxd.fs.FileEntry { + trace('path: ${path} ${dict.exists(path)}'); var f = dict.get(path); if( f == null ) throw new hxd.res.NotFound(path); return f; @@ -260,7 +293,8 @@ class FileSystem implements hxd.fs.FileSystem { return dict.exists(path); } - public function dir( path : String ) : Array { + public function dir( path : String ) : Array { + trace('path: ${path}'); var f = dict.get(path); if( f == null ) throw new hxd.res.NotFound(path); if( !f.isDirectory ) throw path+" is not a directory"; diff --git a/hxd/fmt/pak/Loader.hx b/hxd/fmt/pak/Loader.hx index 343e7adcaa..dd797429a0 100644 --- a/hxd/fmt/pak/Loader.hx +++ b/hxd/fmt/pak/Loader.hx @@ -14,6 +14,7 @@ class Loader extends h2d.Object { this.s2d = s2d; this.bg = new h2d.Graphics(this); this.onDone = onDone; + trace('init loader'); if( hxd.res.Loader.currentInstance == null ) hxd.res.Loader.currentInstance = new hxd.res.Loader(new FileSystem()); fs = Std.downcast(hxd.res.Loader.currentInstance.fs, FileSystem); @@ -35,6 +36,7 @@ class Loader extends h2d.Object { } override function sync(ctx:h2d.RenderContext) { + trace('sync loader'); super.sync(ctx); if( cur == null ) { @@ -63,6 +65,7 @@ class Loader extends h2d.Object { fs.addPak(new FileSystem.FileInput(bytes)); resCount++; cur = null; + trace('name:$name loaded'); } catch( e : Dynamic ) { cur.onError(e); } diff --git a/hxd/fs/BytesFileSystem.hx b/hxd/fs/BytesFileSystem.hx index 27013f44a0..0c7f1320db 100644 --- a/hxd/fs/BytesFileSystem.hx +++ b/hxd/fs/BytesFileSystem.hx @@ -33,7 +33,7 @@ class BytesFileEntry extends FileEntry { haxe.Timer.delay(onReady, 1); } - override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void { + override function loadBitmap( onLoaded : (bmp:LoadedBitmap, ?texture:h3d.mat.Texture) -> Void ) : Void { #if js var mime = switch fullPath.extension().toLowerCase() { case 'jpg' | 'jpeg': 'image/jpeg'; diff --git a/hxd/fs/Convert.hx b/hxd/fs/Convert.hx index b23fdd97be..4b1e73a626 100644 --- a/hxd/fs/Convert.hx +++ b/hxd/fs/Convert.hx @@ -16,10 +16,6 @@ class Convert { public var dstPath:String; public var originalFilename:String; public var srcBytes:haxe.io.Bytes; - /* - The calculated hash for the input source file content. - */ - public var hash : String; public function new(sourceExts, destExt) { this.sourceExts = sourceExts == null ? null : sourceExts.split(","); @@ -243,6 +239,7 @@ class CompressIMG extends Convert { } override function convert() { + trace('convert'); var resizedImagePath:String = null; var mips = hasParam("mips") && getParam("mips") == true; if (hasParam("size")) { @@ -309,9 +306,7 @@ class CompressIMG extends Convert { command("texconv", args); sys.FileSystem.deleteFile(tmpFile); tmpPath.ext = "tmp.DDS"; - var p = tmpPath.toString(); - if ( sys.FileSystem.exists(p) ) - sys.FileSystem.rename(p, dstPath); + sys.FileSystem.rename(tmpPath.toString(), dstPath); return; } var path = new haxe.io.Path(srcPath); @@ -403,13 +398,4 @@ class ConvertBinJSON extends Convert { static var _ = [Convert.register(new ConvertBinJSON("json,prefab,l3d", "hbson"))]; } - -class ConvertSVGToMSDF extends Convert { - override function convert() { - var size = hasParam("size") ? getParam("size") : 128; - command("msdfgen.exe", ["-svg", srcPath, "-size", '$size', '$size', "-autoframe", "-o", dstPath]); - } - - static var _ = Convert.register(new ConvertSVGToMSDF("svg", "png")); -} #end diff --git a/hxd/fs/EmbedFileSystem.hx b/hxd/fs/EmbedFileSystem.hx index 03de9ba413..6f872477cb 100644 --- a/hxd/fs/EmbedFileSystem.hx +++ b/hxd/fs/EmbedFileSystem.hx @@ -47,7 +47,7 @@ private class EmbedEntry extends FileEntry { #end } - override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void { + override function loadBitmap( onLoaded : (bpm:LoadedBitmap, ?texture:h3d.mat.Texture) -> Void ) : Void { #if js // directly get the base64 encoded data from resources var rawData = null; diff --git a/hxd/fs/FileConverter.hx b/hxd/fs/FileConverter.hx index d9a45b3ffc..3c1a89baec 100644 --- a/hxd/fs/FileConverter.hx +++ b/hxd/fs/FileConverter.hx @@ -26,16 +26,13 @@ typedef ConvertCommand = { class FileConverter { - // Date implementation has a second resolution on some platforms. - public static final FILE_TIME_PRECISION = 1000; - public var configuration(default,null) : String; var baseDir : String; var tmpDir : String; var configs : Map = new Map(); var defaultConfig : ConvertConfig; - var cache : Map, milliseconds : Null }>>; + var cache : Map }>>; var cacheTime : Float; static var extraConfigs:Array = []; @@ -71,8 +68,7 @@ class FileConverter { var defaultCfg : Dynamic = { "fs.convert" : { "fbx" : { "convert" : "hmd", "priority" : -1 }, - "fnt" : { "convert" : "bfnt", "priority" : -1 }, - "svg" : { "convert" : "png", "priority" : -1 } + "fnt" : { "convert" : "bfnt", "priority" : -1 } } }; for ( conf in extraConfigs ) { @@ -185,17 +181,11 @@ class FileConverter { } function loadConfig( dir : String ) : ConvertConfig { - return getConfig(configs, defaultConfig, dir, function(fullObj) { - return makeConfig(fullObj); - }); - } - - function getConfig(cachedConfigs : Map, defaultConfig : Dynamic, dir : String, makeConfig : Dynamic -> Dynamic) : Dynamic { - var c = cachedConfigs.get(dir); + var c = configs.get(dir); if( c != null ) return c; var dirPos = dir.lastIndexOf("/"); - var parent = dir == "" ? defaultConfig : getConfig(cachedConfigs, defaultConfig, dirPos < 0 ? "" : dir.substr(0,dirPos), (fullObj) -> makeConfig(fullObj)); - var propsFile = (dir == "" ? baseDir : baseDir + dir + "/") +"props.json"; + var parent = dir == "" ? defaultConfig : loadConfig(dirPos < 0 ? "" : dir.substr(0,dirPos)); + var propsFile = (dir == "" ? baseDir : baseDir + dir + "/")+"props.json"; if( !sys.FileSystem.exists(propsFile) ) { c = parent; } else { @@ -204,7 +194,7 @@ class FileConverter { var fullObj = mergeRec(parent.obj, obj); c = makeConfig(fullObj); } - cachedConfigs.set(dir, c); + configs.set(dir, c); return c; } @@ -306,7 +296,6 @@ class FileConverter { time : 0, hash : "", ver: conv.version, - milliseconds : #if js 0 #else null #end }; entry.push(match); } @@ -315,23 +304,16 @@ class FileConverter { if( !sys.FileSystem.exists(fullPath) ) throw "Missing "+fullPath; - var fileTime = getFileTime(fullPath); - var time = std.Math.floor(fileTime / FILE_TIME_PRECISION); - #if js - var milliseconds = std.Math.floor(fileTime) - time * FILE_TIME_PRECISION; - #else - var milliseconds = null; - #end + var time = std.Math.floor(getFileTime(fullPath) / 1000); var alreadyGen = sys.FileSystem.exists(fullOutPath) && match.ver == conv.version #if disable_res_cache && false #end; - if( alreadyGen && match.time == time #if js && (match.milliseconds == null || match.milliseconds == milliseconds ) #end ) + if( alreadyGen && match.time == time ) return; // not changed (time stamp) var content = hxd.File.getBytes(fullPath); var hash = haxe.crypto.Sha1.make(content).toHex(); if( alreadyGen && match.hash == hash ) { match.time = time; - match.milliseconds = milliseconds; saveCache(); return; // not changed (hash) } @@ -343,33 +325,29 @@ class FileConverter { conv.srcBytes = content; conv.originalFilename = e.name; conv.params = params; - conv.hash = hash; onConvert(conv); - executeConvert(conv); - conv.hash = null; + var prev = hxd.System.allowTimeout; + hxd.System.allowTimeout = false; + conv.convert(); + if( prev ) hxd.System.timeoutTick(); + hxd.System.allowTimeout = prev; conv.srcPath = null; conv.dstPath = null; conv.srcBytes = null; conv.originalFilename = null; + #if !macro + hxd.System.timeoutTick(); + #end if( !sys.FileSystem.exists(fullOutPath) ) throw "Converted output file "+fullOutPath+" was not created"; match.ver = conv.version; match.time = time; - match.milliseconds = milliseconds; match.hash = hash; saveCache(); } - dynamic function executeConvert( conv : Convert ) { - var prev = hxd.System.allowTimeout; - hxd.System.allowTimeout = false; - conv.convert(); - if( prev ) hxd.System.timeoutTick(); - hxd.System.allowTimeout = prev; - } - } #end diff --git a/hxd/fs/FileEntry.hx b/hxd/fs/FileEntry.hx index 0aca12ee5e..f4c5a9f7ce 100644 --- a/hxd/fs/FileEntry.hx +++ b/hxd/fs/FileEntry.hx @@ -9,6 +9,7 @@ class FileEntry { public var size(get, never) : Int; public var isDirectory(get, never) : Bool; public var isAvailable(get, never) : Bool; + var watchOnChangedHistory : Array Void>>; public function getBytes() : haxe.io.Bytes return null; public function readBytes( out : haxe.io.Bytes, outPos : Int, pos : Int, len : Int ) : Int { throw "readBytes() not implemented"; } @@ -48,11 +49,8 @@ class FileEntry { public function open() return @:privateAccess new FileInput(this); public function load( ?onReady : Void -> Void ) : Void { if( !isAvailable ) throw "load() not implemented"; else if( onReady != null ) onReady(); } - public function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void { throw "loadBitmap() not implemented"; } + public function loadBitmap( onLoaded : (bmp:LoadedBitmap, ?texture:h3d.mat.Texture) -> Void ) : Void { throw "loadBitmap() not implemented"; } public function watch( onChanged : Null Void> ) { } - #if multidriver - public function unwatch( id : Int ) { } - #end public function exists( name : String ) : Bool return false; public function get( name : String ) : FileEntry return null; diff --git a/hxd/fs/LocalFileSystem.hx b/hxd/fs/LocalFileSystem.hx index f7eaef96ea..174c5fd72e 100644 --- a/hxd/fs/LocalFileSystem.hx +++ b/hxd/fs/LocalFileSystem.hx @@ -45,7 +45,7 @@ class LocalEntry extends FileEntry { #end } - override function loadBitmap( onLoaded : hxd.fs.LoadedBitmap -> Void ) : Void { + override function loadBitmap( onLoaded :( bmp:LoadedBitmap, ?texture:h3d.mat.Texture )-> Void ) : Void { #if js #if (multidriver && !macro) var engine = h3d.Engine.getCurrent(); // hide @@ -92,32 +92,19 @@ class LocalEntry extends FileEntry { case ".tmp" if( this == fs.root ): continue; default: - var entry = fs.open(relPath == null ? f : relPath + "/" + f,false); - if( entry != null ) - arr.push(entry); + arr.push(fs.open(relPath == null ? f : relPath + "/" + f,false)); } } return new hxd.impl.ArrayIterator(arr); } var watchCallback : Void -> Void; - - /* - When a resource is load, we add a watcher on it and wtih callback to call - when this resource is modified. The problem is that in case several engine works - in parallel, and if the same resource is loaded by different engine, we have - to reload this resource for each engine when the file is modified (resulting - in one file watcher with multiple callback). - */ - #if multidriver var watchByEngine : Array Void>>; #end - #if (hl && (hl_ver >= version("1.12.0")) && !usesys) var watchHandle : hl.uv.Fs; var lastChanged : Float = 0; var onChangedDelay : haxe.Timer; #else var watchTime : Float; - var lastCheck : { fileTime : Float, stampTime : Float }; #end static var WATCH_INDEX = 0; @@ -158,33 +145,19 @@ class LocalEntry extends FileEntry { } var lockFile = tmpDir+"/"+w.file.split("/").pop()+".lock"; if( sys.FileSystem.exists(lockFile) ) return; - if( !w.isDirectory ) { - try { - #if nodejs - var cst = js.node.Fs.constants; - var path = w.file; - #if (multidriver && !macro) // Hide - // Fix searching path in hide/bin folder - path = hide.Ide.inst.getPath(path); - #end - var fid = js.node.Fs.openSync(path, cast (cst.O_APPEND | 0x10000000)); - js.node.Fs.closeSync(fid); - #elseif hl - if( fileIsLocked(@:privateAccess Sys.getPath(w.file)) ) - return; - #end - }catch( e : Dynamic ) return; - } + if( !w.isDirectory ) + try { + #if nodejs + var cst = js.node.Fs.constants; + var fid = js.node.Fs.openSync(w.file, cast (cst.O_RDONLY | cst.O_EXCL | 0x10000000)); + js.node.Fs.closeSync(fid); + #elseif hl + if( fileIsLocked(@:privateAccess Sys.getPath(w.file)) ) + return; + #end + }catch( e : Dynamic ) return; #end - var stampTime = haxe.Timer.stamp(); - if ( w.lastCheck == null || w.lastCheck.fileTime != t ) { - w.lastCheck = { fileTime : t, stampTime : stampTime }; - return; - } - if ( stampTime < w.lastCheck.stampTime + FileConverter.FILE_TIME_PRECISION * 0.001 ) - return; - w.watchTime = t; w.watchCallback(); } @@ -195,13 +168,11 @@ class LocalEntry extends FileEntry { if( watchCallback != null ) { WATCH_LIST.remove(this); watchCallback = null; - #if multidriver watchByEngine = null; #end #if (hl && (hl_ver >= version("1.12.0")) && !usesys) watchHandle.close(); watchHandle = null; #end } - return; } if( watchCallback == null ) { @@ -222,11 +193,6 @@ class LocalEntry extends FileEntry { #end w.watchCallback = null; WATCH_LIST.remove(w); - - #if multidriver - if (w.watchByEngine != null) - this.watchByEngine = w.watchByEngine.copy(); - #end } WATCH_LIST.push(this); } @@ -252,59 +218,22 @@ class LocalEntry extends FileEntry { watchTime = getModifTime(); #end - #if multidriver - if (watchByEngine == null) - watchByEngine = []; - var engine = h3d.Engine.getCurrent(); - if ( engine != null ) - watchByEngine[engine.id] = onChanged; - #end + if (watchOnChangedHistory == null) + watchOnChangedHistory = new Array Void>>(); + watchOnChangedHistory.push(onChanged); watchCallback = function() { - #if (js && multidriver && !macro) - try { - fs.convert.run(this); - } catch ( e : Dynamic ) { - hide.Ide.inst.quickMessage('Failed convert for ${name}, trying again'); - // Convert failed, let's mark this watch as not performed. - watchTime = -1; - lastCheck = null; - return; - } - #else fs.convert.run(this); - #end - - #if multidriver - if (watchByEngine == null) - return; - var idx = watchByEngine.length - 1; + var idx = watchOnChangedHistory.length -1; while (idx >= 0) { - if (watchByEngine[idx] != null) - watchByEngine[idx](); + if (watchOnChangedHistory[idx] != null) + watchOnChangedHistory[idx](); idx--; } - #else - onChanged(); - #end } } - #if multidriver - override function unwatch(id : Int) { - if ( watchByEngine == null || watchByEngine.length <= id ) - return; - watchByEngine[id] = null; - var i = watchByEngine.length; - while ( i > 0 ) { - if ( watchByEngine[i-1] != null ) - break; - i--; - } - watchByEngine.resize(i); - } - #end } class LocalFileSystem implements FileSystem { @@ -316,7 +245,7 @@ class LocalFileSystem implements FileSystem { static var isWindows = Sys.systemName() == "Windows"; public static var FILES_CHECK_MAX = 5; - public function new( dir : String, configuration : String, ?storagePath ) { + public function new( dir : String, configuration : String ) { baseDir = dir; if( configuration == null ) configuration = "default"; @@ -324,7 +253,7 @@ class LocalFileSystem implements FileSystem { #if macro var exePath = null; #else - var pr = storagePath != null ? storagePath : Sys.programPath(); + var pr = Sys.programPath(); var exePath = pr == null ? null : pr.split("\\").join("/").split("/"); #end @@ -425,11 +354,8 @@ class LocalFileSystem implements FileSystem { throw new NotFound(baseDir + path); var files = sys.FileSystem.readDirectory(baseDir + path); var r : Array = []; - for(f in files) { - var entry = open(path + "/" + f, false); - if( entry != null ) - r.push(entry); - } + for(f in files) + r.push(open(path + "/" + f, false)); return r; } diff --git a/hxd/fs/MultiFileSystem.hx b/hxd/fs/MultiFileSystem.hx index a6649a6aff..5b9d8936cf 100644 --- a/hxd/fs/MultiFileSystem.hx +++ b/hxd/fs/MultiFileSystem.hx @@ -17,7 +17,7 @@ private class MultiFileEntry extends FileEntry { override function open() return el[0].open(); override function load( ?onReady : Void -> Void ) : Void return el[0].load(onReady); - override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void return el[0].loadBitmap(onLoaded); + override function loadBitmap( onLoaded : (bmp:LoadedBitmap, ?texture:h3d.mat.Texture) -> Void ) : Void return el[0].loadBitmap(onLoaded); override function watch( onChanged : Null < Void -> Void > ) { for( e in el ) e.watch(onChanged); diff --git a/hxd/impl/AllocPos.hx b/hxd/impl/AllocPos.hx index c9b4958af4..92a15f4125 100644 --- a/hxd/impl/AllocPos.hx +++ b/hxd/impl/AllocPos.hx @@ -1,20 +1,15 @@ package hxd.impl; -class AllocPos { +typedef AllocPos = #if track_alloc AllocPosImpl #else {} #end - static var ENABLED : Bool = false; +#if track_alloc +class AllocPosImpl { public var position : String; public var stack : Array = []; public static var ENGINE_PACKAGES = ["h3d","hxd","h2d","haxe","sys","hrt" /* HIDE */]; - public static function make() { - if ( !ENABLED ) - return null; - return new AllocPos(); - } - - function new() { + public function new() { var curStack = haxe.CallStack.callStack(); curStack.shift(); for( s in curStack ) { @@ -39,4 +34,5 @@ class AllocPos { if( position == null ) position = stack[0]; } -} \ No newline at end of file +} +#end diff --git a/hxd/impl/Allocator.hx b/hxd/impl/Allocator.hx index a0899e9660..4b7116dfe6 100644 --- a/hxd/impl/Allocator.hx +++ b/hxd/impl/Allocator.hx @@ -4,8 +4,6 @@ enum abstract BufferFlags(Int) { public var Dynamic = 0; public var Static = 1; public var UniformDynamic = 2; - public var UniformReadWrite = 3; - public var Uniform = 4; public inline function toInt() : Int { return this; } @@ -24,13 +22,11 @@ class Allocator { case Static: null; case Dynamic: [Dynamic]; case UniformDynamic: [UniformBuffer,Dynamic]; - case UniformReadWrite: [UniformBuffer, ReadWriteBuffer]; - case Uniform: [UniformBuffer]; }); } public function ofFloats( v : hxd.FloatBuffer, format : hxd.BufferFormat, flags : BufferFlags = Dynamic ) { - var nvert = Math.ceil(v.length / format.stride); + var nvert = Std.int(v.length / format.stride); return ofSubFloats(v, nvert, format, flags); } diff --git a/hxd/impl/AnyProps.hx b/hxd/impl/AnyProps.hx index f4e8a94775..84b5a34db1 100644 --- a/hxd/impl/AnyProps.hx +++ b/hxd/impl/AnyProps.hx @@ -21,7 +21,7 @@ class AnyProps { public function refreshProps() { } - #if (editor && js) + #if editor public function editProps() { return new js.jquery.JQuery('

No properties for this object

'); } diff --git a/hxd/impl/AsyncLoader.hx b/hxd/impl/AsyncLoader.hx index 4e560ee7b6..027a782387 100644 --- a/hxd/impl/AsyncLoader.hx +++ b/hxd/impl/AsyncLoader.hx @@ -111,3 +111,29 @@ class NodeLoader implements AsyncLoader { } #end + +#if (js && !macro) +class PakLoader implements AsyncLoader { + + var fs : hxd.fmt.pak.FileSystem; + + public function new() { + fs = Std.downcast(hxd.res.Loader.currentInstance.fs, hxd.fmt.pak.FileSystem); + if( fs == null ) throw "Loader should be pak filesystem"; + } + + public function isSupported( img : hxd.res.Image ) { + return switch( img.getFormat() ) { + case Ktx2, Png, Jpg: true; + default: false; + } + } + + public function load( img : hxd.res.Image ) { + trace('img.entry.path: ${img.entry.path}'); + final data = fs.get(img.entry.path).getBytes(); + @:privateAccess img.asyncLoad(data); + } + +} +#end diff --git a/hxd/impl/CacheAllocator.hx b/hxd/impl/CacheAllocator.hx index 8a79bfe8b3..9d8cf8b68c 100644 --- a/hxd/impl/CacheAllocator.hx +++ b/hxd/impl/CacheAllocator.hx @@ -1,10 +1,9 @@ package hxd.impl; import hxd.impl.Allocator; -@:allow(hxd.impl.CacheAllocator) private class Cache { - var available : Array = []; - var disposed : Array = []; + public var available : Array = []; + public var disposed : Array = []; public var lastUse : Float = haxe.Timer.stamp(); public var onDispose : T -> Void; @@ -34,6 +33,7 @@ private class Cache { if( b == null ) b = disposed.pop(); if( b == null ) return false; if( onDispose != null ) onDispose(b); + lastUse += 1; return true; } } @@ -51,12 +51,7 @@ class CacheAllocator extends Allocator { public var maxKeepTime = 60.; override function allocBuffer(vertices:Int, format:hxd.BufferFormat, flags:BufferFlags=Dynamic):h3d.Buffer { - if( vertices >= 65536 ) { - switch ( flags ) { - case UniformReadWrite: - default: throw "assert"; - } - } + if( vertices >= 65536 ) throw "assert"; checkFrame(); var id = flags.toInt() | (format.uid << 3) | (vertices << 16); var c = buffers.get(id); diff --git a/hxd/net/BinaryLoader.hx b/hxd/net/BinaryLoader.hx index d10a0c37fa..24bb581172 100644 --- a/hxd/net/BinaryLoader.hx +++ b/hxd/net/BinaryLoader.hx @@ -18,7 +18,7 @@ class BinaryLoader { throw msg; } - public function load() { + public function load(raw = false) { #if js var xhr = new js.html.XMLHttpRequest(); @@ -32,7 +32,7 @@ class BinaryLoader { onError(xhr.statusText); return; } - onLoaded(haxe.io.Bytes.ofData(xhr.response)); + onLoaded(raw ? xhr.response : haxe.io.Bytes.ofData(xhr.response)); } xhr.onprogress = function(e) { diff --git a/hxd/res/Config.hx b/hxd/res/Config.hx index eabee58d10..7d78f3c414 100644 --- a/hxd/res/Config.hx +++ b/hxd/res/Config.hx @@ -65,7 +65,7 @@ class Config { "ogg" => "wav", "mp3" => "wav", "l3d" => "bake", - "css" => "less,css.map", + "css" => "less", ]; public static function addPairedExtension( main, shadow) { diff --git a/hxd/res/Image.hx b/hxd/res/Image.hx index ddbba80fdc..4fd78de3ea 100644 --- a/hxd/res/Image.hx +++ b/hxd/res/Image.hx @@ -1,5 +1,7 @@ package hxd.res; + + enum abstract ImageFormat(Int) { var Jpg = 0; var Png = 1; @@ -8,6 +10,7 @@ enum abstract ImageFormat(Int) { var Dds = 4; var Raw = 5; var Hdr = 6; + var Ktx2 = 7; /* Tells if we might not be able to directly decode the image without going through a loadBitmap async call. @@ -35,6 +38,7 @@ enum abstract ImageFormat(Int) { case Dds: "DDS"; case Raw: "RAW"; case Hdr: "HDR"; + case Ktx2: "KTX2"; }; } } @@ -96,7 +100,8 @@ class Image extends Resource { inf = new ImageInfo(); var f = entry.open(); f.fetch(256); // should be enough to fit DDS header - var head = try f.readUInt16() catch (e:haxe.io.Eof) 0; + var headBytes = try f.read(2) catch (e:haxe.io.Eof) null; + var head = headBytes.getUInt16(0); switch (head) { case 0xD8FF: // JPG inf.dataFormat = Jpg; @@ -115,6 +120,8 @@ class Image extends Resource { } } case 0x5089: // PNG + trace('png'); + inf.dataFormat = Png; f.bigEndian = true; f.skip(6); // header @@ -126,7 +133,7 @@ class Image extends Resource { var colbits = f.readByte(); var colType = f.readByte(); inf.pixelFormat = switch ([colbits, colType]) { - case [8, _]: BGRA; // TODO : grayscale png image + case [4, _], [8, _]: BGRA; // TODO : grayscale png image case [16, 0]: R16U; case [16, 2]: RGBA16U; // RGB16U is not supported on DirectX ! case [16, 4]: RG16U; // gray + alpha @@ -241,7 +248,22 @@ class Image extends Resource { fid = "" + fourCC; throw entry.path + " has unsupported 4CC " + fid; } + #if js + case 0x4273: + throw 'Use .ktx2 files for GPU compressed textures instead of .basis'; + case 0x4BAB: + final ktx2 = hxd.res.Ktx2.readFile(new haxe.io.BytesInput(@:privateAccess f.cache)); + inf.pixelFormat = switch ktx2.dfd.colorModel { + case hxd.res.Ktx2.DFDModel.ETC1S: ETC(hxd.res.Ktx2.TranscoderFormat.ETC1); + case hxd.res.Ktx2.DFDModel.UASTC:ASTC(hxd.res.Ktx2.TranscoderFormat.ASTC_4x4); + default: throw 'Unsupported colorModel in ktx2 file ${ktx2.dfd.colorModel}'; + } + inf.mipLevels = ktx2.header.levelCount; + inf.width = ktx2.header.pixelWidth; + inf.height = ktx2.header.pixelHeight; + inf.dataFormat = Ktx2; + #end case 0x3F23: // HDR RADIANCE inf.dataFormat = Hdr; @@ -273,7 +295,7 @@ class Image extends Resource { inf.width = inf.height = size; default: - throw "Unsupported texture format " + entry.path; + throw "Unsupported texture format " + headBytes.toHex() + " " + entry.path; } f.close(); @@ -291,7 +313,6 @@ class Image extends Resource { } customCheckInfo(this); - return inf; } @@ -435,9 +456,13 @@ class Image extends Resource { case Hdr: var data = hxd.fmt.hdr.Reader.decode(entry.getBytes(), false); pixels = new hxd.Pixels(data.width, data.height, data.bytes, inf.pixelFormat); + case Ktx2: + var bytes = entry.getBytes(); + pixels = new hxd.Pixels(inf.width, inf.height, bytes, inf.pixelFormat); } if (fmt != null) pixels.convert(fmt); + return pixels; } @@ -502,24 +527,20 @@ class Image extends Resource { getInfo(); var bmp = new hxd.BitmapData(inf.width, inf.height); var pixels = getPixels(); + trace('pixels: ${pixels}'); bmp.setPixels(pixels); pixels.dispose(); return bmp; } function watchCallb() { - var prevInfo = inf; + var w = inf.width, h = inf.height; inf = null; - try { - getInfo(); - } catch ( e : Dynamic ) { - inf = prevInfo; - return; - } var s = getSize(); - if (prevInfo.width != s.width || prevInfo.height != s.height) + if (w != s.width || h != s.height) tex.resize(s.width, s.height); tex.realloc = null; + loadTexture(); } @@ -541,15 +562,23 @@ class Image extends Resource { } function loadTexture(?asyncData:haxe.io.Bytes) { + trace('getFormat().useLoadBitmap: ${getFormat().useLoadBitmap}'); if (getFormat().useLoadBitmap) { // use native decoding tex.flags.set(Loading); - entry.loadBitmap(function(bmp) { - var bmp = bmp.toBitmap(); + entry.loadBitmap(function(bmp, ?texture) { + trace('*** $texture'); tex.alloc(); - tex.uploadBitmap(bmp); - bmp.dispose(); - tex.realloc = () -> loadTexture(); + if(texture!=null) { + tex = texture; + } else { + var bmp = bmp.toBitmap(); + tex.uploadBitmap(bmp); + bmp.dispose(); + } + tex.realloc = () -> { + loadTexture(); + } tex.flags.unset(Loading); @:privateAccess if (tex.waitLoads != null) { var arr = tex.waitLoads; @@ -557,7 +586,6 @@ class Image extends Resource { for (f in arr) f(); } - if (ENABLE_AUTO_WATCH) watch(watchCallb); }); @@ -565,21 +593,24 @@ class Image extends Resource { } function load() { - if ((enableAsyncLoading || tex.flags.has(AsyncLoading)) && asyncData == null && ASYNC_LOADER != null && ASYNC_LOADER.isSupported(this)) + if ((enableAsyncLoading || tex.flags.has(AsyncLoading)) && asyncData == null && ASYNC_LOADER.isSupported(this)) { @:privateAccess { - tex.dispose(); - tex.format = RGBA; - tex.width = 1; - tex.height = 1; - tex.customMipLevels = 1; - tex.flags.set(Loading); - tex.alloc(); - tex.uploadPixels(BLACK_1x1); - tex.width = inf.width; - tex.height = inf.height; - ASYNC_LOADER.load(this); - tex.realloc = () -> loadTexture(); - return; + tex.dispose(); + tex.format = RGBA; + tex.width = 1; + tex.height = 1; + tex.customMipLevels = 1; + tex.flags.set(Loading); + tex.alloc(); + tex.uploadPixels(BLACK_1x1); + tex.width = inf.width; + tex.height = inf.height; + ASYNC_LOADER.load(this); + tex.realloc =() -> { + loadTexture(); + } + return; + } } var t0 = haxe.Timer.stamp(); // immediately loading the PNG is faster than going through loadBitmap @@ -587,9 +618,7 @@ class Image extends Resource { tex.alloc(); switch (inf.dataFormat) { case Dds: - var pos = 128; - if (inf.flags.has(Dxt10Header)) - pos += 20; + var pos = inf.flags.has(Dxt10Header) ? 148 : 128; for (layer in 0...tex.layerCount) { for (mip in 0...inf.mipOffset) { var w = (inf.width << inf.mipOffset) >> mip; @@ -610,10 +639,20 @@ class Image extends Resource { pos += size; } } + case Ktx2: + throw 'Ktx2 loading using heaps resource system not implemented'; + #if js + // TODO: Need to handle async loading of compressed textures + var bytes = asyncData == null ? entry.getBytes() : asyncData; + hxd.res.Ktx2.Ktx2Decoder.getTexture(new haxe.io.BytesInput(bytes), texture -> { + tex = texture; + }); + #end default: for (layer in 0...tex.layerCount) { for (mip in 0...inf.mipLevels) { var pixels = getPixels(tex.format, layer * inf.mipLevels + mip); + trace('pixels: ${pixels.dataSize}'); tex.uploadPixels(pixels, mip, layer); pixels.dispose(); } @@ -625,7 +664,9 @@ class Image extends Resource { #if hl Sys.println #else trace #end (fmtStr + " " + Std.int(time) + "." + (Std.int(time * 10) % 10) + "ms " + inf.width + "x" + inf.height + " " + entry.path); } - tex.realloc = () -> loadTexture(); + tex.realloc = () -> { + loadTexture(); + } if (ENABLE_AUTO_WATCH) watch(watchCallb); } @@ -658,18 +699,28 @@ class Image extends Resource { if (DEFAULT_FILTER != Linear) tex.filter = DEFAULT_FILTER; tex.setName(entry.path); + setupTextureFlags = t -> switch fmt { + case S3TC(_), ASTC(_), ETC(_): + t.flags.set(AsyncLoading); + t.flags.set(Loading); + t.flags.set(NoAlloc); + default: + } setupTextureFlags(tex); // DirectX12 texture array triggers an access violation. - if (tex.flags.has(IsArray) || !tex.flags.has(LazyLoading)) + if (tex.flags.has(IsArray) || !tex.flags.has(LazyLoading)) { loadTexture(); - else - tex.realloc = () -> loadTexture(); + } else { + tex.realloc =() -> { + loadTexture(); + } + } return tex; } public function toTile():h2d.Tile { - getInfo(); - return h2d.Tile.fromTexture(toTexture()).sub(0, 0, inf.width, inf.height); + final tex = toTexture(); + return h2d.Tile.fromTexture(tex).sub(0, 0, tex.width, tex.height); } public static dynamic function setupTextureFlags(tex:h3d.mat.Texture) {} diff --git a/hxd/res/Ktx2.hx b/hxd/res/Ktx2.hx new file mode 100644 index 0000000000..01e2255dea --- /dev/null +++ b/hxd/res/Ktx2.hx @@ -0,0 +1,1142 @@ +package hxd.res; +#if js +import haxe.io.UInt8Array; +using Lambda; +/** + Ktx2 file parser. +**/ +class Ktx2 { + static inline final BYTE_INDEX_ERROR = 'ktx2 files with a file size exceeding 32 bit address space is not supported'; + + /** + Read ktx2 file + + @param bytes BytesInput containing ktx2 file data + + @return Parsed ktx2 file + **/ + public static function readFile(bytes:haxe.io.BytesInput):Ktx2File { + final header = readHeader(bytes); + final levels = readLevels(bytes, header.levelCount); + final dfd = readDfd(bytes); + final file:Ktx2File = { + header: header, + levels: levels, + dfd: dfd, + data: new js.lib.Uint8Array(@:privateAccess bytes.b), + supercompressionGlobalData: null, + } + return file; + } + + public static function readHeader(bytes:haxe.io.BytesInput):KTX2Header { + final ktx2Id = [ + // '´', 'K', 'T', 'X', '2', '0', 'ª', '\r', '\n', '\x1A', '\n' + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A, + ]; + + final matching = ktx2Id.mapi(( i, id) -> id == bytes.readByte()); + + if(matching.contains(false)) { + throw 'Invalid KTX2 header'; + } + final header:KTX2Header = { + vkFormat: bytes.readInt32(), + typeSize: bytes.readInt32(), + pixelWidth: bytes.readInt32(), + pixelHeight: bytes.readInt32(), + pixelDepth: bytes.readInt32(), + layerCount: bytes.readInt32(), + faceCount: bytes.readInt32(), + levelCount: bytes.readInt32(), + supercompressionScheme: bytes.readInt32(), + + dfdByteOffset: bytes.readInt32(), + dfdByteLength: bytes.readInt32(), + kvdByteOffset: bytes.readInt32(), + kvdByteLength: bytes.readInt32(), + sgdByteOffset: { + final val = bytes.read(8).getInt64(0); + if(val.high>0) { + throw BYTE_INDEX_ERROR; + } + val.low; + }, + sgdByteLength: { + final val = bytes.read(8).getInt64(0); + if(val.high>0) { + throw BYTE_INDEX_ERROR; + } + val.low; + } + } + + if (header.pixelDepth > 0) { + throw 'Failed to parse KTX2 file - Only 2D textures are currently supported.'; + } + if (header.layerCount > 1) { + throw 'Failed to parse KTX2 file - Array textures are not currently supported.'; + } + if (header.faceCount > 1) { + throw 'Failed to parse KTX2 file - Cube textures are not currently supported.'; + } + return header; + } + + static function readLevels(bytes:haxe.io.BytesInput, levelCount:Int):Array { + levelCount = hxd.Math.imax(1, levelCount); + final length = levelCount * 3 * (2 * 4); + final level = bytes.read(length); + final levels:Array = []; + + while (levelCount-- > 0) { + levels.push({ + byteOffset: { + final val = level.getInt64(0); + if(val.high>0) { + throw BYTE_INDEX_ERROR; + } + val.low; + }, + byteLength:{ + final val = level.getInt64(8); + if(val.high>0) { + throw BYTE_INDEX_ERROR; + } + val.low; + }, + uncompressedByteLength: { + final val = level.getInt64(16); + if(val.high>0) { + throw BYTE_INDEX_ERROR; + } + val.low; + }, + }); + } + return levels; + } + + static function readDfd(bytes:haxe.io.BytesInput):KTX2DFD { + final totalSize = bytes.readInt32(); + final vendorId = bytes.readInt16(); + final descriptorType = bytes.readInt16(); + final versionNumber = bytes.readInt16(); + final descriptorBlockSize = bytes.readInt16(); + final numSamples = Std.int((descriptorBlockSize-24) / 16); + final dfdBlock:KTX2DFD = { + vendorId:vendorId, + descriptorType: descriptorType, + versionNumber: versionNumber, + descriptorBlockSize: descriptorBlockSize, + colorModel: bytes.readByte(), + colorPrimaries: bytes.readByte(), + transferFunction: bytes.readByte(), + flags: bytes.readByte(), + texelBlockDimension: { + x: bytes.readByte() + 1, + y: bytes.readByte() + 1, + z: bytes.readByte() + 1, + w: bytes.readByte() + 1, + }, + bytesPlane: [ + bytes.readByte() /* bytesPlane0 */, + bytes.readByte() /* bytesPlane1 */, + bytes.readByte() /* bytesPlane2 */, + bytes.readByte() /* bytesPlane3 */, + bytes.readByte() /* bytesPlane4 */, + bytes.readByte() /* bytesPlane5 */, + bytes.readByte() /* bytesPlane6 */, + bytes.readByte() /* bytesPlane7 */, + ], + numSamples: numSamples, + samples: [ + for (i in 0...numSamples) { + final bitOffset = bytes.readUInt16(); + final bitLength = bytes.readByte() + 1; + final channelType = bytes.readByte(); + final channelFlags = (channelType & 0xf0) >> 4; + final samplePosition = [ + bytes.readByte() /* samplePosition0 */, + bytes.readByte() /* samplePosition1 */, + bytes.readByte() /* samplePosition2 */, + bytes.readByte() /* samplePosition3 */, + ]; + final sampleLower = bytes.readUInt16() + bytes.readUInt16(); + final sampleUpper = bytes.readUInt16() + bytes.readUInt16(); + final sample:KTX2Sample = { + bitOffset: bitOffset, + bitLength: bitLength, + channelType: channelType & 0x0F, + channelFlags: channelFlags, + samplePosition: samplePosition, + sampleLower: sampleLower, + sampleUpper: sampleUpper, + }; + sample; + } + ], + } + return dfdBlock; + } +} + +class Ktx2Decoder { + public static var mscTranscoder:Dynamic; + public static var workerLimit = 4; + + static var _workerNextTaskID = 1; + static var _workerSourceURL:String; + static var _workerConfig:BasisWorkerConfig; + static var _workerPool:Array = []; + static var _transcoderPending:js.lib.Promise; + static var _transcoderBinary:haxe.io.Bytes; + + public static function getTexture(bytes:haxe.io.BytesInput, cb:(texture:h3d.mat.Texture) -> Void) { + createTexture(bytes, cb); + } + + static function detectSupport(fmt:KtxTranscodeTarget) { + final driver:h3d.impl.GlDriver = cast h3d.Engine.getCurrent().driver; + return { + astcSupported: driver.textureSupport.astc, + etc1Supported: driver.textureSupport.etc1, + etc2Supported: driver.textureSupport.etc2, + dxtSupported: driver.textureSupport.dxt, + } + } + + static function getWorker() { + return initTranscoder().then(val -> { + if (_workerPool.length < workerLimit) { + final worker = new js.html.Worker(_workerSourceURL); + final workerTask:WorkerTask = { + worker: worker, + callbacks: new haxe.ds.IntMap(), + taskCosts: new haxe.ds.IntMap(), + taskLoad: 0, + } + worker.postMessage({ + type: 'init', + config: _workerConfig, + transcoderBinary: _transcoderBinary, + }); + + worker.onmessage = e -> { + var message = e.data; + switch (message.type) { + case 'transcode': + workerTask.callbacks.get(message.id).resolve(message); + case 'error': + workerTask.callbacks.get(message.id).reject(message); + default: + throw 'Ktx2Loader: Unexpected message, "${message.type}"'; + } + }; + _workerPool.push(workerTask); + } else { + _workerPool.sort((a, b) -> a.taskLoad > b.taskLoad ? -1 : 1); + } + + return _workerPool[_workerPool.length - 1]; + }); + } + + static function createTexture(buffer:haxe.io.BytesInput, cb:(texture:h3d.mat.Texture) -> Void) { + final ktx = Ktx2.readFile(buffer); + + final w = ktx.header.pixelWidth; + final h = ktx.header.pixelHeight; + + final transcodeTarget = switch ktx.dfd.colorModel { + case hxd.res.Ktx2.DFDModel.ETC1S: + KtxTranscodeTarget.ETC1S({}, { + fmt: CompressedFormat.ETC1, + alpha: ktx.dfd.hasAlpha(), + needsPowerOfTwo: true, + }); + case hxd.res.Ktx2.DFDModel.UASTC: + KtxTranscodeTarget.UASTC({}, { + fmt: CompressedFormat.ASTC, + alpha: ktx.dfd.hasAlpha(), + needsPowerOfTwo: true, + }); + default: throw 'Unsupported colorModel in ktx2 file ${ktx.dfd.colorModel}'; + } + trace('detectSupport'); + + _workerConfig = detectSupport(transcodeTarget); + getWorker().then(task -> { + trace('got worker'); + final worker = task.worker; + final taskID = _workerNextTaskID++; + + final textureDone = new js.lib.Promise((resolve, reject) -> { + task.callbacks.set(taskID, { + resolve: resolve, + reject: reject, + }); + task.taskCosts.set(taskID, buffer.length); + task.taskLoad += task.taskCosts.get(taskID); + buffer.position = 0; + final bytes = buffer.readAll().getData(); + worker.postMessage({type: 'transcode', id: taskID, buffer: bytes}, [bytes]); + }); + + textureDone.then((message:BasisWorkerMessage)-> { + if(message.type == 'error') { + throw 'Unable to decode ktx2 file: ${message.error}'; + } + + final w = message.data.width; + final h = message.data.height; + + final create = (fmt:hxd.PixelFormat) -> { + if(ktx.header.faceCount > 1 || ktx.header.layerCount > 1) { + // TODO: Handle cube texture + throw 'Multi texture ktx2 files not supported'; + } + final face = message.data.faces[0]; + final mipmaps:Array = face.mipmaps; + final texture = new h3d.mat.Texture(w, h, null, fmt); + var level = 0; + for (mipmap in mipmaps) { + final bytes = haxe.io.Bytes.ofData(cast mipmap.data); + final pixels = new hxd.Pixels(mipmap.width, mipmap.height, bytes, fmt); + texture.uploadPixels(pixels, level); + level++; + } + if(mipmaps.length>1) { + texture.flags.set(MipMapped); + texture.mipMap = Linear; + } + texture; + } + final texture = switch message.data.format { + case EngineFormat.RGBA_ASTC_4x4_Format: + create(hxd.PixelFormat.ASTC(10)); + case EngineFormat.RGB_BPTC_UNSIGNED_Format, EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_S3TC_DXT5_Format: + create(hxd.PixelFormat.S3TC(3)); + case EngineFormat.RGB_ETC1_Format: + create(hxd.PixelFormat.ETC(0)); + case EngineFormat.RGBA_ETC2_EAC_Format: + create(hxd.PixelFormat.ETC(1)); + default: + throw 'Ktx2Loader: No supported format available.'; + } + + if (task != null && taskID > 0) { + task.taskLoad -= task.taskCosts.get(taskID); + task.callbacks.remove(taskID); + task.taskCosts.remove(taskID); + } + cb(texture); + }); + }); + + } + + static function initTranscoder() { + if (_transcoderBinary == null) { + // Load transcoder wrapper. + final jsLoader = new hxd.net.BinaryLoader('vendor/basis_transcoder.js'); + final jsContent = new js.lib.Promise((resolve, reject) -> { + jsLoader.onLoaded = resolve; + jsLoader.onError = reject; + jsLoader.load(); + }); + // Load transcoder WASM binary. + final binaryLoader = new hxd.net.BinaryLoader('vendor/basis_transcoder.wasm'); + final binaryContent = new js.lib.Promise((resolve, reject) -> { + binaryLoader.onLoaded = resolve; + binaryLoader.onError = reject; + binaryLoader.load(true); + }); + + + _transcoderPending = js.lib.Promise.all([jsContent, binaryContent]).then(arr -> { + final transcoder = arr[0].toString(); + final wasm = arr[1]; + final transcoderFormat = Type.getClassFields(TranscoderFormat).map(f -> '"$f": ${Reflect.field(TranscoderFormat, f)},\n').fold((curr, acc) -> '$acc\t$curr', '{\n') + '}'; + final basisFormat = Type.allEnums(BasisFormat).fold((curr, acc) -> '$acc\t"${curr.getName()}": ${curr.getIndex()},\n', '{\n') + '}'; + final engineFormat = Type.getClassFields(EngineFormat).map(f -> '"$f": ${Reflect.field(EngineFormat, f)},\n').fold((curr, acc) -> '$acc\t$curr', '{\n') + '}'; + final engineType = Type.getClassFields(EngineType).map(f -> '"$f": ${Reflect.field(EngineType, f)},\n').fold((curr, acc) -> '$acc\t$curr', '{\n') + '}'; + var body = [ + '/* constants */', + 'let _EngineFormat = $engineFormat', + 'let _EngineType = $engineType', + 'let _TranscoderFormat = $transcoderFormat', + 'let _BasisFormat = $basisFormat', + '/* basis_transcoder.js */', + transcoder, + '/* worker */', + basisWorker() + ].join('\n'); + + _workerSourceURL = js.html.URL.createObjectURL(new js.html.Blob([body])); + _transcoderBinary = wasm; + }); + + } + return _transcoderPending; + } + + /** + Get transcoder config according to priority (https://github.com/KhronosGroup/3D-Formats-Guidelines/blob/main/KTXDeveloperGuide.md) + **/ + /* + static public function getTranscoderConfig(target:KtxTranscodeTarget):KtxTranscodeConfig { + return switch target { + case ETC1S(options, caps): { + switch options { + case { forceRGBA: true}: + { + transcodeFormat: TranscodeTarget.RGBA32, + engineFormat: EngineFormat.RGBA8Format, + roundToMultiple4: false, + } + case _: + switch caps { + case {fmt: ETC2, alpha: true}: + { + transcodeFormat: TranscodeTarget.ETC2_RGBA, + engineFormat: EngineFormat.RGBA_ETC2_EAC_Format, + } + case {fmt: ETC2, alpha: false}: + { + transcodeFormat: TranscodeTarget.ETC1_RGB, + engineFormat: EngineFormat.RGB_ETC1_Format, + } + case {fmt: ETC1, alpha: false}: + { + transcodeFormat: TranscodeTarget.ETC1_RGB, + engineFormat: EngineFormat.RGB_ETC1_Format, + } + case {fmt: BPTC}: + { + transcodeFormat: TranscodeTarget.BC7_RGBA, + engineFormat: EngineFormat.RGBA_BPTC_Format, + } + case {fmt: S3TC, alpha: true}: + { + transcodeFormat: TranscodeTarget.BC3_RGBA, + engineFormat: EngineFormat.RGBA_S3TC_DXT5_Format, + } + case {fmt: S3TC, alpha: false}: + { + transcodeFormat: TranscodeTarget.BC1_RGB, + engineFormat: EngineFormat.RGBA_S3TC_DXT1_Format, + } + case _: + { + transcodeFormat: TranscodeTarget.RGBA32, + engineFormat: EngineFormat.RGBA8Format, + roundToMultiple4: false, + } + } + } + } + case UASTC(options, caps): { + switch options { + case {forceRGBA: true}: + { + transcodeFormat: TranscodeTarget.RGBA32, + engineFormat: EngineFormat.RGBAFormat, + roundToMultiple4: false, + } + case {forceR8: true}: + { + transcodeFormat: TranscodeTarget.R8, + engineFormat: EngineFormat.R8Format, + roundToMultiple4: false, + } + case {forceRG8: true}: + { + transcodeFormat: TranscodeTarget.RG8, + engineFormat: EngineFormat.RG8Format, + roundToMultiple4: false, + } + case {useRGBAIfASTCBC7NotAvailableWhenUASTC: true}: { + switch caps { + case {fmt:ASTC}: + { + transcodeFormat: TranscodeTarget.ASTC_4X4_RGBA, + engineFormat: EngineFormat.RGBA_ASTC_4x4_Format, + } + case {fmt:BPTC}: + { + transcodeFormat: TranscodeTarget.BC7_RGBA, + engineFormat: EngineFormat.RGBA_BPTC_Format, + } + case _: + { + transcodeFormat: TranscodeTarget.RGBA32, + engineFormat: EngineFormat.RGBA8Format, + roundToMultiple4: false, + } + } + } + case _: { + switch caps { + case {fmt:ASTC}: + { + transcodeFormat: TranscodeTarget.ASTC_4X4_RGBA, + engineFormat: EngineFormat.RGBA_ASTC_4x4_Format, + } + case {fmt:BPTC}: + { + transcodeFormat: TranscodeTarget.BC7_RGBA, + engineFormat: EngineFormat.RGBA_BPTC_Format, + } + case {fmt:ETC2, alpha: true}: + { + transcodeFormat: TranscodeTarget.ETC2_RGBA, + engineFormat: EngineFormat.RGBA_ETC2_EAC_Format, + } + case {fmt:ETC2, alpha: false}: + { + transcodeFormat: TranscodeTarget.ETC1_RGB, + engineFormat: EngineFormat.RGB_ETC2_Format, + } + case {fmt:ETC1}: + { + transcodeFormat: TranscodeTarget.ETC1_RGB, + engineFormat: EngineFormat.RGB_ETC1_Format, + } + case {fmt:S3TC, alpha: true}: + { + transcodeFormat: TranscodeTarget.BC3_RGBA, + engineFormat: EngineFormat.RGBA_S3TC_DXT5_Format, + } + case {fmt:S3TC, alpha: false}: + { + transcodeFormat: TranscodeTarget.BC1_RGB, + engineFormat: EngineFormat.RGBA_S3TC_DXT1_Format, + } + case _: + { + transcodeFormat: TranscodeTarget.RGBA32, + engineFormat: EngineFormat.RGBA8Format, + roundToMultiple4: false, + } + } + } + } + } + } + } +*/ +} + +typedef Ktx2File = { + header:KTX2Header, + levels:Array, + dfd:KTX2DFD, + data:js.lib.Uint8Array, + supercompressionGlobalData:KTX2SupercompressionGlobalData, +} + +enum abstract SuperCompressionScheme(Int) from Int to Int { + final NONE = 0; + final BASISLZ = 1; + final ZSTANDARD = 2; + final ZLIB = 3; +} + +enum abstract DFDModel(Int) from Int to Int { + final ETC1S = 163; + final UASTC = 166; +} + +enum abstract DFDChannel_ETC1S(Int) from Int to Int { + final RGB = 0; + final RRR = 3; + final GGG = 4; + final AAA = 15; +} + +enum abstract DFDChannel_UASTC(Int) from Int to Int { + final RGB = 0; + final RGBA = 3; + final RRR = 4; + final RRRG = 5; +} + +enum abstract DFDTransferFunction(Int) from Int to Int { + final LINEAR = 1; + final SRGB = 2; +} + +enum abstract SupercompressionScheme(Int) from Int to Int { + public final None = 0; + public final BasisLZ = 1; + public final ZStandard = 2; + public final ZLib = 3; +} + +/** @internal */ +@:structInit class KTX2Header { + public final vkFormat: Int; + public final typeSize: Int; + public final pixelWidth: Int; + public final pixelHeight: Int; + public final pixelDepth: Int; + public final layerCount: Int; + public final faceCount: Int; + public final levelCount: Int; + public final supercompressionScheme: Int; + public final dfdByteOffset: Int; + public final dfdByteLength: Int; + public final kvdByteOffset: Int; + public final kvdByteLength: Int; + public final sgdByteOffset: Int; + public final sgdByteLength: Int; + + public function needZSTDDecoder() { + return supercompressionScheme == SupercompressionScheme.ZStandard; + } +} + +/** @internal */ +typedef KTX2Level = { + /** + Byte offset. According to spec this should be 64 bit, but since a lot of byte code in haxe is using regular 32 bit Int for indexing, + supporting files to large to fit in 32bit space is complicated and should not be needed for individual game assets. + **/ + final byteOffset: Int; + final byteLength: Int; + final uncompressedByteLength: Int; +} + +typedef KTX2Sample = { + final bitOffset: Int; + final bitLength: Int; + final channelType: Int; + final channelFlags: Int; + final samplePosition: Array; + final sampleLower: Int; + final sampleUpper: Int; +} + +/** @internal */ +@:structInit class KTX2DFD { + public final vendorId: Int; + public final descriptorType: Int; + public final versionNumber: Int; + public final descriptorBlockSize: Int; + public final colorModel: Int; + public final colorPrimaries: Int; + public final transferFunction: Int; + public final flags: Int; + public final texelBlockDimension: { + x: Int, + y: Int, + z: Int, + w: Int, + }; + public final bytesPlane: Array; + public final numSamples: Int; + public final samples: Array; + + public function hasAlpha() { + return switch colorModel { + case hxd.res.Ktx2.DFDModel.ETC1S: + numSamples == 2 && (samples[0].channelType == DFDChannel_ETC1S.AAA || samples[1].channelType == DFDChannel_ETC1S.AAA); + case hxd.res.Ktx2.DFDModel.UASTC: + samples[0].channelType == DFDChannel_UASTC.RGBA; + default: throw 'Unsupported colorModel in ktx2 file ${colorModel}'; + } + } + + public function isInGammaSpace() { + return transferFunction == DFDTransferFunction.SRGB; + } +} + +/** @internal */ +typedef KTX2ImageDesc = { + final imageFlags: Int; + final rgbSliceByteOffset: Int; + final rgbSliceByteLength: Int; + final alphaSliceByteOffset: Int; + final alphaSliceByteLength: Int; +} + +/** @internal */ +typedef KTX2SupercompressionGlobalData = { + final endpointCount: Int; + final selectorCount: Int; + final endpointsByteLength: Int; + final selectorsByteLength: Int; + final tablesByteLength: Int; + final extendedByteLength: Int; + final imageDescs: Array; + final endpointsData: haxe.io.UInt8Array; + final selectorsData: haxe.io.UInt8Array; + final tablesData: haxe.io.UInt8Array; + final extendedData: haxe.io.UInt8Array; +} + +@:keep +class TranscoderFormat { + public static final ETC1 = 0; + public static final ETC2 = 1; + public static final BC1 = 2; + public static final BC3 = 3; + public static final BC4 = 4; + public static final BC5 = 5; + public static final BC7_M6_OPAQUE_ONLY = 6; + public static final BC7_M5 = 7; + + public static final ASTC_4x4 = 10; + public static final ATC_RGB = 11; + public static final ATC_RGBA_INTERPOLATED_ALPHA = 12; + public static final RGBA32 = 13; + public static final RGB565 = 14; + public static final BGR565 = 15; + public static final RGBA4444 = 16; + public static final BC6H = 22; + public static final RGB_HALF = 24; + public static final RGBA_HALF = 25; +} + +enum TranscoderType { + cTFETC1; + cTFETC2; // Not used + cTFBC1; + cTFBC3; + cTFBC4; // Not used + cTFBC5; // Not used + cTFBC7_M6_OPAQUE_ONLY; // Not used + cTFBC7_M5; // Not used + cTFPVRTC1_4_RGB; // Not used + cTFPVRTC1_4_RGBA; // Not used + cTFASTC_4x4; + cTFATC_RGB1; // Not used + cTFATC_RGBA_INTERPOLATED_ALPHA2; // Not used + cTFRGBA321; + cTFRGB5654; // Not used + cTFBGR5655; // Not used + cTFRGBA44446; // Not used +} + +@:keep +enum BasisFormat { + ETC1S; + UASTC; + UASTC_HDR; +} + +@:keep +class EngineFormat { + public static final RGBAFormat = 0x03FF; + public static final RGBA8Format = 0x8058; + public static final R8Format = 0x8229; + public static final RG8Format = 0x822b; + public static final RGBA_ASTC_4x4_Format = PixelFormat.ASTC_FORMAT.RGBA_4x4; + public static final RGB_BPTC_UNSIGNED_Format = 0x8e8f; + public static final RGBA_BPTC_Format =0x8e8c; + public static final RGB_S3TC_DXT1_Format = PixelFormat.DXT_FORMAT.RGB_DXT1; + public static final RGBA_S3TC_DXT1_Format = PixelFormat.DXT_FORMAT.RGBA_DXT1; + public static final RGBA_S3TC_DXT5_Format = PixelFormat.DXT_FORMAT.RGBA_DXT5; + public static final RGB_ETC1_Format = PixelFormat.ETC_FORMAT.RGB_ETC1; + public static final RGBA_ETC2_EAC_Format = PixelFormat.ETC_FORMAT.RGBA_ETC2; + public static final RGB_ETC2_Format = 0x9274; +} + +@:keep +class EngineType { + public static final UnsignedByteType = 1009; + public static final FloatType = 1015; + public static final HalfFloatType = 1016; +} + +/** + * Defines a mipmap level + */ +@:structInit class MipmapLevel { + /** + * The data of the mipmap level + */ + public var data: Null = null; + + /** + * The width of the mipmap level + */ + public final width: Int; + + /** + * The height of the mipmap level + */ + public final height: Int; +} + +enum KtxTranscodeTarget { + ETC1S(options:ETC1SDecoderOptions, caps:Ktx2Caps); + UASTC(options:UASTCDecoderOptions, caps:Ktx2Caps); +} + +@:structInit class KtxTranscodeConfig { + public final transcodeFormat:TranscodeTarget; + public final engineFormat:Int; +// public final basisFormat:EngineType; + public final engineType = EngineType.UnsignedByteType; + public final roundToMultiple4 = true; +} + + +@:structInit class Ktx2Caps { + public final fmt: CompressedFormat; + + public final alpha: Null = null; + + public final needsPowerOfTwo = true; +} +enum CompressedFormat { + ETC2; + ETC1; + S3TC; + ASTC; + BPTC; +} + +/** +* Options passed to the KTX2 decode function +*/ +@:structInit class UASTCDecoderOptions { + /** use RGBA format if ASTC and BC7 are not available as transcoded format */ + public final useRGBAIfASTCBC7NotAvailableWhenUASTC = false; + + /** force to always use (uncompressed) RGBA for transcoded format */ + public final forceRGBA = false; + + /** force to always use (uncompressed) R8 for transcoded format */ + public final forceR8 = false; + + /** force to always use (uncompressed) RG8 for transcoded format */ + public final forceRG8 = false; +} + +@:structInit class ETC1SDecoderOptions { + public final forceRGBA = false; +} + +enum TranscodeTarget { + ASTC_4X4_RGBA; + BC7_RGBA; + BC3_RGBA; + BC1_RGB; + ETC2_RGBA; + ETC1_RGB; + RGBA32; + R8; + RG8; +} + +typedef WorkerTask = { + worker:js.html.Worker, + callbacks:haxe.ds.IntMap<{resolve:(value:Dynamic) -> Void, reject:(reason:Dynamic) -> Void}>, + taskCosts:haxe.ds.IntMap, + taskLoad:Int, +} + +typedef BasisWorkerConfig = { + astcSupported: Bool, + etc1Supported: Bool, + etc2Supported: Bool, + dxtSupported: Bool, +} + +function basisWorker() { + return " + let config; + let transcoderPending; + let BasisModule; + + const EngineFormat = _EngineFormat; + const EngineType = _EngineType; + const TranscoderFormat = _TranscoderFormat; + const BasisFormat = _BasisFormat; + + self.addEventListener( 'message', function ( e ) { + const message = e.data; + switch ( message.type ) { + case 'init': + config = message.config; + init( message.transcoderBinary ); + break; + case 'transcode': + transcoderPending.then( () => { + try { + const { faces, buffers, width, height, hasAlpha, format, type, dfdFlags } = transcode( message.buffer ); + self.postMessage( { type: 'transcode', id: message.id, data: { faces, width, height, hasAlpha, format, type, dfdFlags } }, buffers ); + } catch ( error ) { + console.error( error ); + self.postMessage( { type: 'error', id: message.id, error: error.message } ); + } + } ); + break; + } + } ); + + function init( wasmBinary ) { + transcoderPending = new Promise( ( resolve ) => { + BasisModule = { wasmBinary, onRuntimeInitialized: resolve }; + BASIS( BasisModule ); // eslint-disable-line no-undef + } ).then( () => { + BasisModule.initializeBasis(); + if ( BasisModule.KTX2File === undefined ) { + console.warn( 'KTX2Loader: Please update Basis Universal transcoder.' ); + } + } ); + } + + function transcode( buffer ) { + const ktx2File = new BasisModule.KTX2File( new Uint8Array( buffer ) ); + function cleanup() { + ktx2File.close(); + ktx2File.delete(); + } + + if ( ! ktx2File.isValid() ) { + cleanup(); + throw new Error( 'KTX2Loader: Invalid or unsupported .ktx2 file' ); + } + let basisFormat; + if ( ktx2File.isUASTC() ) { + basisFormat = BasisFormat.UASTC; + } else if ( ktx2File.isETC1S() ) { + basisFormat = BasisFormat.ETC1S; + } else if ( ktx2File.isHDR() ) { + basisFormat = BasisFormat.UASTC_HDR; + } else { + throw new Error( 'KTX2Loader: Unknown Basis encoding' ); + } + const width = ktx2File.getWidth(); + const height = ktx2File.getHeight(); + const layerCount = ktx2File.getLayers() || 1; + const levelCount = ktx2File.getLevels(); + const faceCount = ktx2File.getFaces(); + const hasAlpha = ktx2File.getHasAlpha(); + const dfdFlags = ktx2File.getDFDFlags(); + const { transcoderFormat, engineFormat, engineType } = getTranscoderFormat( basisFormat, width, height, hasAlpha ); + if ( ! width || ! height || ! levelCount ) { + cleanup(); + throw new Error( `KTX2Loader: Invalid texture ktx2File:${JSON.stringify(ktx2File)} w:${width} h: ${height} levelCount:${levelCount}` ); + } + + if ( ! ktx2File.startTranscoding() ) { + cleanup(); + throw new Error( 'KTX2Loader: .startTranscoding failed' ); + } + + const faces = []; + const buffers = []; + + for ( let face = 0; face < faceCount; face ++ ) { + const mipmaps = []; + for ( let mip = 0; mip < levelCount; mip ++ ) { + const layerMips = []; + let mipWidth, mipHeight; + for ( let layer = 0; layer < layerCount; layer ++ ) { + const levelInfo = ktx2File.getImageLevelInfo( mip, layer, face ); + if ( face === 0 && mip === 0 && layer === 0 && ( levelInfo.origWidth % 4 !== 0 || levelInfo.origHeight % 4 !== 0 ) ) { + console.warn( 'KTX2Loader: ETC1S and UASTC textures should use multiple-of-four dimensions.' ); + } + + if ( levelCount > 1 ) { + mipWidth = levelInfo.origWidth; + mipHeight = levelInfo.origHeight; + } else { + // Handles non-multiple-of-four dimensions in textures without mipmaps. Textures with + // mipmaps must use multiple-of-four dimensions, for some texture formats and APIs. + // See mrdoob/three.js#25908. + mipWidth = levelInfo.width; + mipHeight = levelInfo.height; + } + + let dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) ); + const status = ktx2File.transcodeImage( dst, mip, layer, face, transcoderFormat, 0, - 1, - 1 ); + + if ( engineType === EngineType.HalfFloatType ) { + dst = new Uint16Array( dst.buffer, dst.byteOffset, dst.byteLength / Uint16Array.BYTES_PER_ELEMENT ); + } + + if ( ! status ) { + cleanup(); + throw new Error( 'KTX2Loader: .transcodeImage failed.' ); + } + layerMips.push( dst ); + } + const mipData = concat( layerMips ); + mipmaps.push( { data: mipData, width: mipWidth, height: mipHeight } ); + buffers.push( mipData.buffer ); + } + faces.push( { mipmaps, width, height, format: engineFormat, type: engineType } ); + } + cleanup(); + return { faces, buffers, width, height, hasAlpha, dfdFlags, format: engineFormat, type: engineType }; + } + // + + // Optimal choice of a transcoder target format depends on the Basis format (ETC1S, UASTC, or + // UASTC HDR), device capabilities, and texture dimensions. The list below ranks the formats + // separately for each format. Currently, priority is assigned based on: + // + // high quality > low quality > uncompressed + // + // Prioritization may be revisited, or exposed for configuration, in the future. + // + // Reference: https://github.com/KhronosGroup/3D-Formats-Guidelines/blob/main/KTXDeveloperGuide.md + const FORMAT_OPTIONS = [ + { + if: 'astcSupported', + basisFormat: [ BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4 ], + engineFormat: [ EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format ], + engineType: [ EngineType.UnsignedByteType ], + priorityETC1S: Infinity, + priorityUASTC: 1, + needsPowerOfTwo: false, + }, + { + if: 'bptcSupported', + basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5 ], + engineFormat: [ EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format ], + engineType: [ EngineType.UnsignedByteType ], + priorityETC1S: 3, + priorityUASTC: 2, + needsPowerOfTwo: false, + }, + { + if: 'dxtSupported', + basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.BC1, TranscoderFormat.BC3 ], + engineFormat: [ EngineFormat.RGBA_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format ], + engineType: [ EngineType.UnsignedByteType ], + priorityETC1S: 4, + priorityUASTC: 5, + needsPowerOfTwo: false, + }, + { + if: 'etc2Supported', + basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC2 ], + engineFormat: [ EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format ], + engineType: [ EngineType.UnsignedByteType ], + priorityETC1S: 1, + priorityUASTC: 3, + needsPowerOfTwo: false, + }, + { + if: 'etc1Supported', + basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.ETC1 ], + engineFormat: [ EngineFormat.RGB_ETC1_Format ], + engineType: [ EngineType.UnsignedByteType ], + priorityETC1S: 2, + priorityUASTC: 4, + needsPowerOfTwo: false, + }, + + // Uncompressed fallbacks. + + { + basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC ], + transcoderFormat: [ TranscoderFormat.RGBA32, TranscoderFormat.RGBA32 ], + engineFormat: [ EngineFormat.RGBAFormat, EngineFormat.RGBAFormat ], + engineType: [ EngineType.UnsignedByteType, EngineType.UnsignedByteType ], + priorityETC1S: 100, + priorityUASTC: 100, + needsPowerOfTwo: false, + }, + { + basisFormat: [ BasisFormat.UASTC_HDR ], + transcoderFormat: [ TranscoderFormat.RGBA_HALF ], + engineFormat: [ EngineFormat.RGBAFormat ], + engineType: [ EngineType.HalfFloatType ], + priorityHDR: 100, + needsPowerOfTwo: false, + } + ]; + + const OPTIONS = { + // TODO: For ETC1S we intentionally sort by _UASTC_ priority, preserving + // a historical accident shown to avoid performance pitfalls for Linux with + // Firefox & AMD GPU (RadeonSI). Further work needed. + // See https://github.com/mrdoob/three.js/pull/29730. + [ BasisFormat.ETC1S ]: FORMAT_OPTIONS + .filter( ( opt ) => opt.basisFormat.includes( BasisFormat.ETC1S ) ) + .sort( ( a, b ) => a.priorityUASTC - b.priorityUASTC ), + + [ BasisFormat.UASTC ]: FORMAT_OPTIONS + .filter( ( opt ) => opt.basisFormat.includes( BasisFormat.UASTC ) ) + .sort( ( a, b ) => a.priorityUASTC - b.priorityUASTC ), + + [ BasisFormat.UASTC_HDR ]: FORMAT_OPTIONS + .filter( ( opt ) => opt.basisFormat.includes( BasisFormat.UASTC_HDR ) ) + .sort( ( a, b ) => a.priorityHDR - b.priorityHDR ), + }; + + function getTranscoderFormat( basisFormat, width, height, hasAlpha ) { + const options = OPTIONS[ basisFormat ]; + for ( let i = 0; i < options.length; i++ ) { + const opt = options[ i ]; + if ( opt.if && ! config[ opt.if ] ) continue; + if ( ! opt.basisFormat.includes( basisFormat ) ) continue; + if ( hasAlpha && opt.transcoderFormat.length < 2 ) continue; + if ( opt.needsPowerOfTwo && ! ( isPowerOfTwo( width ) && isPowerOfTwo( height ) ) ) continue; + const transcoderFormat = opt.transcoderFormat[ hasAlpha ? 1 : 0 ]; + const engineFormat = opt.engineFormat[ hasAlpha ? 1 : 0 ]; + const engineType = opt.engineType[ 0 ]; + console.log(`transcoderFormat: ${JSON.stringify(transcoderFormat)}`); + + return { transcoderFormat, engineFormat, engineType }; + } + throw new Error( 'KTX2Loader: Failed to identify transcoding target.' ); + } + + function isPowerOfTwo( value ) { + if ( value <= 2 ) return true; + return ( value & ( value - 1 ) ) === 0 && value !== 0; + } + + /** Concatenates N byte arrays. */ + function concat( arrays ) { + if ( arrays.length === 1 ) return arrays[ 0 ]; + let totalByteLength = 0; + + for ( let i = 0; i < arrays.length; i ++ ) { + const array = arrays[ i ]; + totalByteLength += array.byteLength; + } + + const result = new Uint8Array( totalByteLength ); + let byteOffset = 0; + + for ( let i = 0; i < arrays.length; i ++ ) { + const array = arrays[ i ]; + result.set( array, byteOffset ); + byteOffset += array.byteLength; + } + + return result; + }"; +} + +@:structInit class BasisWorkerMessage { + public final id:String; + public final type = 'transcode'; + public final data:{ + faces:Array<{mipmaps:Array, width:Int, height:Int, format:Int, type:Int}>, + width:Int, + height:Int, + hasAlpha:Bool, + format:Int, + type:Int, + dfdFlags:Int, + }; + public final error:String = null; +} +#end \ No newline at end of file diff --git a/hxformat.json b/hxformat.json new file mode 100644 index 0000000000..5ab4a59519 --- /dev/null +++ b/hxformat.json @@ -0,0 +1 @@ +{ "disableFormatting": true } \ No newline at end of file diff --git a/hxsl/Ast.hx b/hxsl/Ast.hx index 0f995473e5..33b56716c1 100644 --- a/hxsl/Ast.hx +++ b/hxsl/Ast.hx @@ -3,15 +3,6 @@ package hxsl; enum BufferKind { Uniform; RW; - Partial; - RWPartial; -} - -enum TexDimension { - T1D; - T2D; - T3D; - TCube; } enum Type { @@ -25,14 +16,15 @@ enum Type { TMat4; TMat3x4; TBytes( size : Int ); - TSampler( dim : TexDimension, isArray : Bool ); - TRWTexture( dim : TexDimension, isArray : Bool, channels : Int ); - TMat2; + TSampler2D; + TSampler2DArray; + TSamplerCube; TStruct( vl : Array ); TFun( variants : Array ); TArray( t : Type, size : SizeDecl ); TBuffer( t : Type, size : SizeDecl, kind : BufferKind ); TChannel( size : Int ); + TMat2; } enum VecType { @@ -177,7 +169,6 @@ enum TExprDef { TSwitch( e : TExpr, cases : Array<{ values : Array, expr:TExpr }>, def : Null ); TWhile( e : TExpr, loop : TExpr, normalWhile : Bool ); TMeta( m : String, args : Array, e : TExpr ); - TField( e : TExpr, name : String ); } typedef TVar = { @@ -298,15 +289,6 @@ enum TGlobal { RoundEven; // compute SetLayout; - ImageStore; - ComputeVar_GlobalInvocation; - ComputeVar_LocalInvocation; - ComputeVar_WorkGroup; - ComputeVar_LocalInvocationIndex; - //ComputeVar_NumWorkGroups - no DirectX support - //ComputeVar_WorkGroupSize - no DirectX support - AtomicAdd; - GroupMemoryBarrier; } enum Component { @@ -330,7 +312,6 @@ class Tools { public static var SWIZ = Component.createAll(); public static var MAX_CHANNELS_BITS = 3; - public static var MAX_PARTIAL_MAPPINGS_BITS = 7; public static function allocVarId() { // in order to prevent compile time ids to conflict with runtime allocated ones @@ -342,26 +323,6 @@ class Tools { #end } - public static function getTexUVSize( dim : TexDimension, arr = false ) { - var size = switch( dim ) { - case T1D: 1; - case T2D: 2; - case T3D, TCube: 3; - } - if( arr ) size++; - return size; - } - - public static function getDimSize( dim : TexDimension, arr = false ) { - var size = switch( dim ){ - case T1D: 1; - case T2D, TCube: 2; - case T3D: 3; - } - if( arr ) size++; - return size; - } - public static function getName( v : TVar ) { if( v.qualifiers == null ) return v.name; @@ -403,15 +364,13 @@ class Tools { } case TChannel(_): return 3 + MAX_CHANNELS_BITS; - case TBuffer(_, _, Partial|RWPartial): - return MAX_PARTIAL_MAPPINGS_BITS; default: } return 0; } public static function isConst( v : TVar ) { - if( v.type.match(TChannel(_)|TBuffer(_,_,Partial|RWPartial)) ) + if( v.type.match(TChannel(_)) ) return true; if( v.qualifiers != null ) for( q in v.qualifiers ) @@ -448,9 +407,9 @@ class Tools { return false; } - public static function isTexture( t : Type ) { + public static function isSampler( t : Type ) { return switch( t ) { - case TSampler(_), TChannel(_), TRWTexture(_): + case TSampler2D, TSamplerCube, TSampler2DArray, TChannel(_): true; default: false; @@ -462,25 +421,19 @@ class Tools { case TVec(size, t): var prefix = switch( t ) { case VFloat: ""; - case VInt: "i"; - case VBool: "b"; + case VInt: "I"; + case VBool: "B"; } - prefix + "vec" + size; + prefix + "Vec" + size; case TStruct(vl):"{" + [for( v in vl ) v.name + " : " + toString(v.type)].join(",") + "}"; case TArray(t, s): toString(t) + "[" + (switch( s ) { case SConst(i): "" + i; case SVar(v): v.name; } ) + "]"; case TBuffer(t, s, k): var prefix = switch( k ) { - case Uniform: "Buffer"; - case RW: "RWBuffer"; - case Partial: "PartialBuffer"; - case RWPartial: "RWPartialBuffer"; + case Uniform: "buffer"; + case RW: "rwbuffer"; }; prefix+" "+toString(t) + "[" + (switch( s ) { case SConst(i): "" + i; case SVar(v): v.name; } ) + "]"; case TBytes(n): "Bytes" + n; - case TSampler(dim, arr): - "Sampler"+dim.getName().substr(1)+(arr ? "Array":""); - case TRWTexture(dim, arr,dims): - "RWTexture"+dim.getName().substr(1)+(arr ? "Array":"")+"<"+(dims == 1 ? "Float" : "Vec"+dims)+">"; default: t.getName().substr(1); } } @@ -521,13 +474,8 @@ class Tools { case TCall({ e : TGlobal(SetLayout) },_): return true; case TCall(e, pl): - switch( e.e ) { - case TGlobal( ImageStore | AtomicAdd | GroupMemoryBarrier ): + if( !e.e.match(TGlobal(_)) ) return true; - case TGlobal(g): - default: - return true; - } for( p in pl ) if( hasSideEffect(p) ) return true; @@ -544,8 +492,6 @@ class Tools { return hasSideEffect(e) || hasSideEffect(loop); case TMeta(_, _, e): return hasSideEffect(e); - case TField(e,_): - return hasSideEffect(e); } } @@ -575,7 +521,6 @@ class Tools { f(loop); case TConst(_), TVar(_), TGlobal(_), TDiscard, TContinue, TBreak: case TMeta(_, _, e): f(e); - case TField(e, _): f(e); } } @@ -597,7 +542,6 @@ class Tools { case TWhile(e, loop, normalWhile): TWhile(f(e), f(loop), normalWhile); case TConst(_), TVar(_), TGlobal(_), TDiscard, TContinue, TBreak: e.e; case TMeta(m, args, e): TMeta(m, args, f(e)); // don't map args - case TField(e, name): TField(f(e), name); } return { e : ed, t : e.t, p : e.p }; } @@ -616,7 +560,7 @@ class Tools { case TMat4: 16; case TMat3x4: 12; case TBytes(s): s; - case TBool, TString, TSampler(_), TRWTexture(_), TFun(_): 0; + case TBool, TString, TSampler2D, TSampler2DArray, TSamplerCube, TFun(_): 0; case TArray(t, SConst(v)), TBuffer(t, SConst(v),_): size(t) * v; case TArray(_, SVar(_)), TBuffer(_): 0; } diff --git a/hxsl/BatchShader.hx b/hxsl/BatchShader.hx index 5dd986c748..967845d798 100644 --- a/hxsl/BatchShader.hx +++ b/hxsl/BatchShader.hx @@ -4,10 +4,8 @@ class BatchShader extends hxsl.Shader { static var SRC = { @const var Batch_HasOffset : Bool; - @const var Batch_UseStorage : Bool; - @const(4096) var Batch_Count : Int; + @const(65536) var Batch_Count : Int; @param var Batch_Buffer : Buffer; - @param var Batch_StorageBuffer : RWBuffer; }; public var params : RuntimeShader.AllocParam; diff --git a/hxsl/Cache.hx b/hxsl/Cache.hx index 8b4d8ec972..0ae3ed25f3 100644 --- a/hxsl/Cache.hx +++ b/hxsl/Cache.hx @@ -302,7 +302,7 @@ class Cache { var prev = s; var splitter = new hxsl.Splitter(); - var sl = try splitter.split(s, mode == Batch ) catch( e : Error ) { e.msg += "\n\nin\n\n"+Printer.shaderToString(s); throw e; }; + var sl = try splitter.split(s) catch( e : Error ) { e.msg += "\n\nin\n\n"+Printer.shaderToString(s); throw e; }; // params tracking var paramVars = new Map(); @@ -442,7 +442,6 @@ class Cache { data = hl.Api.compact(data, null, 0, null); #end var textures = []; - var buffers = []; c.texturesCount = 0; for( g in flat.allocData.keys() ) { var alloc = flat.allocData.get(g); @@ -462,7 +461,7 @@ class Cache { } var ap = new AllocParam(a.v.name, a.pos, p.instance, p.index, a.v.type); switch( a.v.type ) { - case TArray(t,_) if( t.isTexture() ): + case TArray(t,_) if( t.isSampler() ): // hack to mark array of texture, see ShaderManager.fillParams ap.pos = -a.size; count += a.size; @@ -474,16 +473,22 @@ class Cache { for( i in 0...out.length - 1 ) out[i].next = out[i + 1]; switch( g.type ) { - case TArray(t, _) if( t.isTexture() ): + case TArray(t, _) if( t.isSampler() ): textures.push({ t : t, all : out }); c.texturesCount += count; case TArray(TVec(4, VFloat), SConst(size)): c.params = out[0]; c.paramsSize = size; - case TArray(TBuffer(_, _, kind), _): - for( outBuf in out ) - if ( outBuf != null ) - buffers.push(outBuf); + case TArray(TBuffer(_), _): + if( c.buffers == null ) { + c.buffers = out[0]; + c.bufferCount = out.length; + } else { + var p = c.buffers; + while( p.next != null ) p = p.next; + p.next = out[0]; + c.bufferCount += out.length; + } default: throw "assert"; } case Global: @@ -502,22 +507,7 @@ class Cache { } if( textures.length > 0 ) { // relink in order based on type - textures.sort(function(t1,t2) { - return switch ( [t1.t, t2.t] ) { - case [TSampler(t1, a1), TSampler(t2, a2)]: - if ( a1 != a2 ) - a1 ? 1 : -1; - else - t1.getIndex() - t2.getIndex(); - case [TRWTexture(t1, a1, _), TRWTexture(t2, a2, _)]: - if ( a1 != a2 ) - a1 ? 1 : -1; - else - t1.getIndex() - t2.getIndex(); - default : - t1.t.getIndex() - t2.t.getIndex(); - } - }); + textures.sort(function(t1,t2) return t1.t.getIndex() - t2.t.getIndex()); c.textures = textures[0].all[0]; for( i in 1...textures.length ) { var prevAll = textures[i-1].all; @@ -525,31 +515,12 @@ class Cache { prev.next = textures[i].all[0]; } } - if ( buffers.length > 0 ) { - buffers.sort(function(b1, b2) { - return switch ( [b1.type, b2.type] ) { - case [TBuffer(_, _, k1), TBuffer(_, _, k2)]: - k1.getIndex() - k2.getIndex(); - default: - b1.type.getIndex() - b2.type.getIndex(); - } - }); - } - var p = null; - for ( b in buffers ) { - if ( c.buffers == null ) { - c.buffers = b; - p = c.buffers; - } else { - p.next = b; - p = p.next; - } - } - c.bufferCount = buffers.length; if( c.globals == null ) c.globalsSize = 0; if( c.params == null ) c.paramsSize = 0; + if( c.buffers == null ) + c.bufferCount = 0; c.data = data; return c; } @@ -622,22 +593,18 @@ class Cache { hasOffset.qualifiers = [Const()]; inputOffset.qualifiers = [PerInstance(1)]; - var useStorage = declVar("Batch_UseStorage",TBool,Param); var vcount = declVar("Batch_Count",TInt,Param); - var vuniformBuffer = declVar("Batch_Buffer",TBuffer(TVec(4,VFloat),SVar(vcount),Uniform),Param); - var vstorageBuffer = declVar("Batch_StorageBuffer",TBuffer(TVec(4,VFloat),SConst(0),RW),Param); + var vbuffer = declVar("Batch_Buffer",TBuffer(TVec(4,VFloat),SVar(vcount),Uniform),Param); var voffset = declVar("Batch_Offset", TInt, Local); - var euniformBuffer = { e : TVar(vuniformBuffer), p : pos, t : vuniformBuffer.type }; - var estorageBuffer = { e : TVar(vstorageBuffer), p : pos, t : vstorageBuffer.type }; + var ebuffer = { e : TVar(vbuffer), p : pos, t : vbuffer.type }; var eoffset = { e : TVar(voffset), p : pos, t : voffset.type }; var tvec4 = TVec(4,VFloat); var countBits = 16; vcount.qualifiers = [Const(1 << countBits)]; - useStorage.qualifiers = [Const()]; s.data = { name : "batchShader_"+id, - vars : [vcount,hasOffset,useStorage,vuniformBuffer,vstorageBuffer,voffset,inputOffset], + vars : [vcount,hasOffset,vbuffer,voffset,inputOffset], funs : [], }; @@ -688,13 +655,8 @@ class Cache { var params = null; var used = []; - var added = []; function addParam(p:RuntimeShader.AllocParam) { - var pid = p.perObjectGlobal != null ? -p.perObjectGlobal.gid : p.instance * 1024 + p.index; - if( added.indexOf(pid) >= 0 ) - return; - added.push(pid); var size = switch( p.type ) { case TMat4: 4 * 4; case TVec(n,VFloat): n; @@ -773,11 +735,11 @@ class Cache { var parentVars = new Map(); var swiz = [[X],[Y],[Z],[W]]; - function readOffset( ebuffer, index : Int ) : TExpr { + function readOffset( index : Int ) : TExpr { return { e : TArray(ebuffer,{ e : TBinop(OpAdd,eoffset,{ e : TConst(CInt(index)), t : TInt, p : pos }), t : TInt, p : pos }), t : tvec4, p : pos }; } - function declareLocalVar( v : AllocParam ) { + function extractVar( v : AllocParam ) { var vreal : TVar = declVar(v.name, v.type, Local); if( v.perObjectGlobal != null ) { var path = v.perObjectGlobal.path.split("."); @@ -800,49 +762,43 @@ class Cache { } } s.data.vars.push(vreal); - return vreal; - } - - function extractVar( vreal, ebuffer, v : AllocParam ) { var index = (v.pos>>2); var extract = switch( v.type ) { case TMat4: { p : pos, t : v.type, e : TCall({ e : TGlobal(Mat4), t : TVoid, p : pos },[ - readOffset(ebuffer, index), - readOffset(ebuffer, index + 1), - readOffset(ebuffer, index + 2), - readOffset(ebuffer, index + 3), + readOffset(index), + readOffset(index + 1), + readOffset(index + 2), + readOffset(index + 3), ]) }; case TVec(4,VFloat): - readOffset(ebuffer, index); + readOffset(index); case TVec(3,VFloat): - { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),v.pos&3 == 0 ? [X,Y,Z] : [Y,Z,W]) }; + { p : pos, t : v.type, e : TSwiz(readOffset(index),v.pos&3 == 0 ? [X,Y,Z] : [Y,Z,W]) }; case TVec(2,VFloat): var swiz = switch( v.pos & 3 ) { case 0: [X,Y]; case 1: [Y,Z]; default: [Z,W]; } - { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),swiz) }; + { p : pos, t : v.type, e : TSwiz(readOffset(index),swiz) }; case TFloat: - { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),swiz[v.pos&3]) } + { p : pos, t : v.type, e : TSwiz(readOffset(index),swiz[v.pos&3]) } default: throw "assert"; } return { p : pos, e : TBinop(OpAssign, { e : TVar(vreal), p : pos, t : v.type }, extract), t : TVoid }; } - var exprsUniform = []; - var exprsStorage = []; + var exprs = []; var stride = used.length; var p = params; while( p != null ) { - var vreal = declareLocalVar(p); - exprsUniform.push(extractVar(vreal, euniformBuffer, p)); - exprsStorage.push(extractVar(vreal, estorageBuffer, p)); + exprs.push(extractVar(p)); p = p.next; } + var inits = []; inits.push({ @@ -868,35 +824,19 @@ class Cache { e : TBinop(OpAssignOp(OpMult),eoffset,{ e : TConst(CInt(stride)), t : TInt, p : pos }), }); - inits.push({ - p : pos, - e : TIf({ e : TVar(useStorage), t : TBool, p : pos },{ - p : pos, - e : TBlock(exprsStorage), - t : TVoid, - }, { - p : pos, - e : TBlock(exprsUniform), - t : TVoid, - }), - t : TVoid, - }); - var fv : TVar = declVar("init",TFun([]), Function); var f : TFunction = { kind : Init, ref : fv, args : [], ret : TVoid, - expr : { e : TBlock(inits), p : pos, t : TVoid }, + expr : { e : TBlock(inits.concat(exprs)), p : pos, t : TVoid }, }; s.data.funs.push(f); - s.consts = new SharedShader.ShaderConst(vcount,2,countBits+1); + s.consts = new SharedShader.ShaderConst(vcount,1,countBits+1); s.consts.globalId = 0; s.consts.next = new SharedShader.ShaderConst(hasOffset,0,1); s.consts.next.globalId = 0; - s.consts.next.next = new SharedShader.ShaderConst(useStorage,1,1); - s.consts.next.next.globalId = 0; return { shader : s, params : params, size : stride }; } diff --git a/hxsl/CacheFile.hx b/hxsl/CacheFile.hx index 545efb1321..00bd33da04 100644 --- a/hxsl/CacheFile.hx +++ b/hxsl/CacheFile.hx @@ -83,7 +83,7 @@ class CacheFile extends Cache { static var HEX = "0123456789abcdef"; - function load(showProgress=false) { + function load() { isLoading = true; var t0 = haxe.Timer.stamp(); var wait = []; @@ -113,26 +113,12 @@ class CacheFile extends Cache { } } if( wait.length > 0 ) { - var fullCount = wait.length; waitCount += wait.length; #if hlmulti - var t1 = haxe.Timer.stamp(); for( r in wait ) { - if (showProgress && (waitCount % 5 == 0 || waitCount <= 1)) { - var progress = Std.int((1 - (waitCount / fullCount)) * 1000) / 10; - Sys.print('$progress% \t(${fullCount - waitCount}/$fullCount) \r'); - } - addNewShader(r); hxd.System.timeoutTick(); } - if (showProgress) { - log(""); - var t = haxe.Timer.stamp() - t1; - var m = hxd.Math.round(t / 60); - var s = t - m * 60; - log('$waitCount generated in ${m}m ${hxd.Math.fmt(s)}s'); - } #else haxe.Timer.delay(function() { for( r in wait ) { @@ -153,16 +139,6 @@ class CacheFile extends Cache { } function resolveShader( name : String ) : hxsl.Shader { - if ( StringTools.endsWith(name, ".shgraph") ) { - #if hide - var shgraph : hrt.shgraph.ShaderGraph = try cast hxd.res.Loader.currentInstance.load(name).toPrefab().load() catch( e : hxd.res.NotFound ) null; - if (shgraph == null) - return null; - return shgraph.makeShaderInstance(); - #else - return null; - #end - } var cl = Type.resolveClass(name); if( cl == null ) return null; diff --git a/hxsl/CacheFileBuilder.hx b/hxsl/CacheFileBuilder.hx index b0622aebb5..3aed8985e1 100644 --- a/hxsl/CacheFileBuilder.hx +++ b/hxsl/CacheFileBuilder.hx @@ -5,7 +5,6 @@ enum CacheFilePlatform { OpenGL; PS4; XBoxOne; - XBoxOneGDK; XBoxSeries; NX; NXBinaries; @@ -21,9 +20,9 @@ private class CustomCacheFile extends CacheFile { super(true, true); } - override function load(showProgress=true) { + override function load() { allowSave = true; - super.load(showProgress); + super.load(); } override function addSource(r:RuntimeShader) { @@ -59,7 +58,6 @@ private class CustomCacheFile extends CacheFile { case OpenGL: "gl"; case PS4: "ps4"; case XBoxOne: "xboxone"; - case XBoxOneGDK: "xbogdk"; case XBoxSeries: "xbox"; case NX: "nx"; case NXBinaries: "nxbin"; @@ -108,6 +106,7 @@ class CacheFileBuilder { public function compileShader( r : RuntimeShader, rd : RuntimeShader.RuntimeShaderData ) : String { hasCompiled = true; + Sys.print("."); var s = generateShader(r, rd); if( s == null ) return null; @@ -184,7 +183,7 @@ class CacheFileBuilder { sys.FileSystem.deleteFile(tmpSrc); sys.FileSystem.deleteFile(tmpOut); return { code : code, bytes : data }; - case XBoxSeries, XBoxOneGDK: + case XBoxSeries: #if (hldx && dx12) if( !dxInitDone ) { var win = new dx.Window("", 800, 600); @@ -203,11 +202,7 @@ class CacheFileBuilder { code = serializeRootSignature + code; sys.io.File.saveContent(tmpSrc, code); var args = ["-rootsig-define", "ROOT_SIGNATURE", "-T", ( (rd.kind == Vertex) ? "vs_" : "ps_") + dxcShaderVersion,"-O3","-Fo", tmpOut, tmpSrc]; - var p; - if( platform == XBoxOneGDK ) - p = new sys.io.Process(Sys.getEnv("GXDKLatest")+ "bin\\XboxOne\\dxc.exe", args); - else - p = new sys.io.Process(Sys.getEnv("GXDKLatest")+ "bin\\Scarlett\\dxc.exe", args); + var p = new sys.io.Process(Sys.getEnv("GXDKLatest")+ "bin\\Scarlett\\dxc.exe", args); var error = p.stderr.readAll().toString(); var ecode = p.exitCode(); if( ecode != 0 ) @@ -261,10 +256,8 @@ class CacheFileBuilder { case "-lib": var lib = new format.hl.Reader().read(new haxe.io.BytesInput(sys.io.File.getBytes(getArg()))); for( s in lib.strings ) { - if( !StringTools.startsWith(s,"HXS") ) continue; + if( !StringTools.startsWith(s,"HXSL") ) continue; var data = try haxe.crypto.Base64.decode(s) catch( e : Dynamic ) continue; - if (data.length < 4 ) - continue; var len = data.get(3); var name = data.getString(4,len); builder.shaderLib.set(name, s); @@ -277,8 +270,6 @@ class CacheFileBuilder { builder.platforms.push(PS4); case "-xbox": builder.platforms.push(XBoxOne); - case "-xbogdk": - builder.platforms.push(XBoxOneGDK); case "-xbs": builder.platforms.push(XBoxSeries); case "-nx": diff --git a/hxsl/ChannelTexture.hx b/hxsl/ChannelTexture.hx index c190b222fe..4665f0f8ba 100644 --- a/hxsl/ChannelTexture.hx +++ b/hxsl/ChannelTexture.hx @@ -1,3 +1,3 @@ package hxsl; -typedef ChannelTexture = { texture : hxsl.Types.TextureChannel, channel : hxsl.Channel }; +typedef ChannelTexture = { texture : hxsl.Types.ChannelTextureType, channel : hxsl.Channel }; diff --git a/hxsl/Checker.hx b/hxsl/Checker.hx index 915f15ecee..a6d8b621dc 100644 --- a/hxsl/Checker.hx +++ b/hxsl/Checker.hx @@ -48,16 +48,6 @@ class Checker { var genFloat = [for( t in genType ) { args : [ { name : "value", type : t } ], ret : t } ]; var genFloat2 = [for( t in genType ) { args : [ { name : "a", type : t }, { name : "b", type : t } ], ret : t } ]; var genWithFloat = [for( t in genType ) { args : [ { name : "a", type : t }, { name : "b", type : TFloat } ], ret : t } ]; - var texDefs = [ - { dim : T1D, arr : false, uv : TFloat, iuv : TInt }, - { dim : T2D, arr : false, uv : vec2, iuv : ivec2 }, - { dim : T3D, arr : false, uv : vec3, iuv : ivec3 }, - { dim : TCube, arr : false, uv : vec3, iuv : ivec3 }, - { dim : T1D, arr : true, uv : vec2, iuv : ivec2 }, - { dim : T2D, arr : true, uv : vec3, iuv : ivec3 }, - { dim : TCube, arr : true, uv : vec4, iuv : ivec4 }, - ]; - var gvars = new Map(); for( g in Ast.TGlobal.createAll() ) { var def = switch( g ) { case Vec2, Vec3, Vec4, Mat2, Mat3, Mat3x4, Mat4, IVec2, IVec3, IVec4, BVec2, BVec3, BVec4: []; @@ -77,13 +67,33 @@ class Checker { case Cross: [ { args : [ { name : "a", type : vec3 }, { name : "b", type : vec3 } ], ret : vec3 } ]; case Texture: - [for( t in texDefs ) { args : [{ name : "tex", type : TSampler(t.dim,t.arr) }, { name : "uv", type : t.uv }], ret : vec4 }]; + [ + { args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 } ], ret : vec4 }, + { args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 } ], ret : vec4 }, + { args : [ { name : "tex", type : TSampler2DArray }, { name : "uv", type : vec3 } ], ret : vec4 }, + ]; case TextureLod: - [for( t in texDefs ) { args : [{ name : "tex", type : TSampler(t.dim,t.arr) }, { name : "uv", type : t.uv }, { name : "lod", type : TFloat }], ret : vec4 }]; + [ + { args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 }, { name : "lod", type : TFloat } ], ret : vec4 }, + { args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 }, { name : "lod", type : TFloat } ], ret : vec4 }, + { args : [ { name : "tex", type : TSampler2DArray }, { name : "uv", type : vec3 }, { name : "lod", type : TFloat } ], ret : vec4 }, + ]; case Texel: - [for( t in texDefs ) { args : [{ name : "tex", type : TSampler(t.dim,t.arr) }, { name : "pos", type : t.iuv }], ret : vec4 }]; + [ + { args : [ { name: "tex", type: TSampler2D }, { name: "pos", type: ivec2 } ], ret: vec4 }, + { args : [ { name: "tex", type: TSampler2DArray }, { name: "pos", type: ivec3 } ], ret: vec4 }, + { args : [ { name: "tex", type: TSampler2D }, { name: "pos", type: ivec2 }, { name: "lod", type: TInt } ], ret: vec4 }, + { args : [ { name: "tex", type: TSampler2DArray }, { name: "pos", type: ivec3 }, { name: "lod", type: TInt } ], ret: vec4 }, + ]; case TextureSize: - []; + [ + { args : [ { name: "tex", type: TSampler2D } ], ret: vec2 }, + { args : [ { name: "tex", type: TSampler2DArray } ], ret: vec3 }, + { args : [ { name: "tex", type: TSamplerCube } ], ret: vec2 }, + { args : [ { name: "tex", type: TSampler2D }, { name: "lod", type: TInt } ], ret: vec2 }, + { args : [ { name: "tex", type: TSampler2DArray }, { name: "lod", type: TInt } ], ret: vec3 }, + { args : [ { name: "tex", type: TSamplerCube }, { name: "lod", type: TInt } ], ret: vec2 }, + ]; case ToInt: [for( t in baseType ) { args : [ { name : "value", type : t } ], ret : TInt } ]; case ToFloat: @@ -172,48 +182,16 @@ class Checker { [{ args : [{ name : "screenPos", type : vec2 }], ret : vec2 }]; case UvToScreen: [{ args : [{ name : "uv", type : vec2 }], ret : vec2 }]; - case Trace, GroupMemoryBarrier: + case Trace: []; case FloatBitsToInt, FloatBitsToUint: [for( i => t in genType ) { args : [ { name: "x", type: t } ], ret: genIType[i] }]; case IntBitsToFloat, UintBitsToFloat: [for( i => t in genType ) { args : [ { name: "x", type: genIType[i] } ], ret: t }]; case SetLayout: - [ - { args : [{ name : "x", type : TInt },{ name : "y", type : TInt },{ name : "z", type : TInt }], ret : TVoid }, - { args : [{ name : "x", type : TInt },{ name : "y", type : TInt }], ret : TVoid }, - { args : [{ name : "x", type : TInt }], ret : TVoid } - ]; - case ImageStore: - []; + [{ args : [{ name : "x", type : TInt },{ name : "y", type : TInt },{ name : "z", type : TInt }], ret : TVoid }]; case VertexID, InstanceID, FragCoord, FrontFacing: null; - case AtomicAdd: - [{ args : [{ name : "buf", type : TBuffer(TInt, SConst(0), RW) },{ name : "index", type : TInt }, { name : "data", type : TInt }], ret : TInt }]; - case _ if( g.getName().indexOf("_") > 0 ): - var name = g.getName(); - var idx = name.indexOf("_"); - var vname = name.substr(0, idx); - vname = vname.charAt(0).toLowerCase() + vname.substr(1); - var vl = gvars.get(vname); - if( vl == null ) { - vl = []; - gvars.set(vname, vl); - } - var vt = switch( g ) { - case ComputeVar_GlobalInvocation, ComputeVar_LocalInvocation, ComputeVar_WorkGroup: - ivec3; - case ComputeVar_LocalInvocationIndex: - TInt; - default: - throw "Unknown type for global var "+g; - } - var fname = name.substr(idx+1); - fname = fname.charAt(0).toLowerCase() + fname.substr(1); - vl.push({ name : fname, type : vt }); - null; - default: - throw "Unsupported global "+g; } if( def != null ) globals.set(g.toString(), { t : TFun(def), g : g } ); @@ -222,16 +200,6 @@ class Checker { globals.set("instanceID", { t : TInt, g : InstanceID }); globals.set("fragCoord", { t : vec4, g : FragCoord }); globals.set("frontFacing", { t : TBool, g : FrontFacing }); - for( gname => vl in gvars ) - globals.set(gname, { t : TStruct([ - for( v in vl ) - { - name: v.name, - kind : Global, - type : v.type, - id : 0 - } - ]), g : null }); globals.set("int", globals.get("toInt")); globals.set("float", globals.get("toFloat")); globals.set("reflect", globals.get("lReflect")); @@ -358,7 +326,7 @@ class Checker { } function tryUnify( t1 : Type, t2 : Type ) { - if( t1.equals(t2) ) + if( t1 == t2 ) return true; switch( [t1, t2] ) { case [TVec(s1, t1), TVec(s2, t2)] if( s1 == s2 && t1 == t2 ): @@ -372,10 +340,6 @@ class Checker { return tryUnify(t1,t2); case [TChannel(n1), TChannel(n2)] if( n1 == n2 ): return true; - case [TSampler(dim1, arr1), TSampler(dim2, arr2)]: - return dim1 == dim2 && arr1 == arr2; - case [TRWTexture(dim1, arr1, chans1), TRWTexture(dim2, arr2, chans2)]: - return dim1 == dim2 && arr1 == arr2 && chans1 == chans2; default: } return false; @@ -404,11 +368,11 @@ class Checker { return; case Local if( v.qualifiers == null || v.qualifiers.indexOf(Final) < 0 ): return; - case Param, Local if( v.type.match(TBuffer(_,_,RW|RWPartial) | TRWTexture(_)) ): + case Param if( v.type.match(TBuffer(_,_,RW)) ): return; default: } - case TSwiz(e, _), TField(e, _): + case TSwiz(e, _): checkWrite(e); return; case TArray(e, _): @@ -501,7 +465,7 @@ class Checker { var v = vars.get(name); if( v != null ) { var canCall = switch( name ) { - case "vertex", "fragment", "main": false; + case "vertex", "fragment": false; default: !StringTools.startsWith(name,"__init__"); } if( !canCall ) @@ -510,7 +474,7 @@ class Checker { TVar(v); } else { var g = globals.get(name); - if( g != null && g.g != null ) { + if( g != null ) { type = g.t; TGlobal(g.g); } else { @@ -523,18 +487,6 @@ class Checker { } } } - case EField({ expr : EIdent(name) }, f) if( vars.get(name) == null && globals.get(name) != null && globals.get(name).g == null ): - switch( globals.get(name).t ) { - case TStruct(vl): - for( v in vl ) - if( v.name == f ) { - var g = name.charAt(0).toUpperCase()+name.substr(1)+"_"+f.charAt(0).toUpperCase()+f.substr(1); - return { e : TGlobal(Ast.TGlobal.createByName(g)), t : v.type, p : e.pos }; - } - error(name+" field should be "+[for( v in vl ) v.name].join("|"), e.pos); - default: - throw "assert"; - } case EField(e1, f): var e1 = typeExpr(e1, Value); var ef = fieldAccess(e1, f, with, e.pos); @@ -707,9 +659,12 @@ class Checker { case EArray(e1, e2): var e1 = typeExpr(e1, Value); var e2 = typeExpr(e2, With(TInt)); - unify(e2.t, TInt, e2.p); + switch( e2.t ) { + case TInt: + default: unify(e2.t, TInt, e2.p); + } switch( e1.t ) { - case TArray(t, size), TBuffer(t,size, Uniform), TBuffer(t,size, Partial): + case TArray(t, size), TBuffer(t,size,_): switch( [size, e2.e] ) { case [SConst(v), TConst(CInt(i))] if( i >= v ): error("Indexing outside array bounds", e.pos); @@ -718,18 +673,12 @@ class Checker { default: } type = t; - case TBuffer(t, size, _): - type = t; case TMat2: type = vec2; case TMat3: type = vec3; case TMat4, TMat3x4: type = vec4; - case TVec(_, VFloat): - type = TFloat; - case TVec(_, VInt): - type = TInt; default: error("Cannot index " + e1.t.toString() + " : should be an array", e.pos); } @@ -800,12 +749,6 @@ class Checker { var v = makeVar(v, e.pos); if( isImport && v.kind == Param ) continue; - - switch( v.type ) { - case TSampler(T3D, true), TRWTexture(T3D, true, _), TRWTexture(_,_,3): - error("Unsupported texture type", e.pos); - default: - } if( einit != null ) inits.push({ v : v, e : einit }); else if( v.qualifiers != null && v.qualifiers.indexOf(Final) >= 0 ) @@ -921,7 +864,8 @@ class Checker { if ( v.kind != Local ) error("Borrow should not have a type qualifier", pos); case Sampler(_): switch( v.type ) { - case TSampler(_), TArray(TSampler(_), _): + case TArray(t, _) if( t.isSampler() ): + case t if( t.isSampler() ): default: error("Sampler should be on sampler type or sampler array", pos); } case Ignore, Doc(_): @@ -947,6 +891,8 @@ class Checker { switch( t ) { case TArray(_): error("Multidimentional arrays are not allowed", pos); + case TStruct(_): + error("Array of structures are not allowed", pos); default: } var s = switch( size ) { @@ -996,19 +942,6 @@ class Checker { } } - function isParentArray( v : TVar ) { - if( v.parent == null ) - return false; - switch( v.parent.type ) { - case TStruct(_): - return isParentArray(v.parent); - case TBuffer(_), TArray(_): - return true; - default: - return false; - } - } - function fieldAccess( e : TExpr, f : String, with : WithType, pos : Position ) : FieldAccess { var ef = switch( e.t ) { case TStruct(vl): @@ -1020,8 +953,6 @@ class Checker { } if( found == null ) null; - else if( isParentArray(found) ) - { e : TField(e,f), t : found.type, p : pos }; else { e : TVar(found), t : found.type, p : pos }; default: @@ -1032,15 +963,14 @@ class Checker { var g = globals.get(f); if( g == null ) { var gl : TGlobal = switch( [f, e.t] ) { - case ["get", TSampler(_)]: Texture; + case ["get", TSampler2D|TSampler2DArray|TSamplerCube]: Texture; case ["get", TChannel(_)]: ChannelRead; - case ["getLod", TSampler(_)]: TextureLod; + case ["getLod", TSampler2D|TSampler2DArray|TSamplerCube]: TextureLod; case ["getLod", TChannel(_)]: ChannelReadLod; - case ["fetch"|"fetchLod", TSampler(_)]: Texel; + case ["fetch"|"fetchLod", TSampler2D|TSampler2DArray]: Texel; case ["fetch"|"fetchLod", TChannel(_)]: ChannelFetch; - case ["size", TSampler(_) | TRWTexture(_)]: TextureSize; + case ["size", TSampler2D|TSampler2DArray|TSamplerCube]: TextureSize; case ["size", TChannel(_)]: ChannelTextureSize; - case ["store", TRWTexture(_)]: ImageStore; default: null; } if( gl != null ) { @@ -1098,27 +1028,9 @@ class Checker { return null; } - function getSizeType(size,vtype) { - return switch( size ) { - case 1: vtype == VInt ? TInt : TFloat; - case 2: vtype == VInt ? ivec2 : vec2; - case 3: vtype == VInt ? ivec3 : vec3; - case 4: vtype == VInt ? ivec4 : vec4; - default: throw "assert"; - } - } - function specialGlobal( g : TGlobal, e : TExpr, args : Array, pos : Position ) : TExpr { var type = null; inline function checkLength(n,t) { - var skip = false; - if( args.length == 1 && (t == TInt || t == TFloat) ) { - switch( args[0].t ) { - case TVec(n2,t2) if( (t2 == VInt || t2 == VFloat) && n2 == n ): skip = true; - default: false; - } - } - if( skip ) return; var tsize = 0; for( a in args ) switch( a.t ) { @@ -1153,6 +1065,8 @@ class Checker { if( args.length == 1 ) { switch( args[0].t ) { case TInt, TFloat: + case TVec(n,VFloat): + if( n != 3 ) error("Invalid input vector length: "+n+" should be "+k, pos); default: checkLength(k,TInt); } @@ -1200,26 +1114,6 @@ class Checker { } case Trace: type = TVoid; - case ImageStore: - switch( ([for( a in args ) a.t]) ) { - case [TRWTexture(dim,arr,chans), uv, color]: - var szt = getSizeType(Tools.getDimSize(dim,arr),VInt); - unify(uv, szt, args[1].p); - unify(color, chans == 1 ? TFloat : TVec(chans,VFloat), args[2].p); - type = TVoid; - default: - error("Cannot apply " + g.toString() + " to these parameters", pos); - } - case TextureSize: - if( args.length != 1 ) - error("TextureSize() requires one single argument", pos); - switch( args[0].t ) { - case TSampler(dim,arr), TRWTexture(dim,arr,_): - type = getSizeType(Tools.getDimSize(dim,arr),VFloat); - default: - unify(args[0].t, TSampler(T2D,false), args[0].p); - type = vec2; - } default: } if( type == null ) diff --git a/hxsl/Dce.hx b/hxsl/Dce.hx index f6a45229f8..44b53bd248 100644 --- a/hxsl/Dce.hx +++ b/hxsl/Dce.hx @@ -47,7 +47,7 @@ class Dce { var i = get(v); if( v.kind == Input ) inputs.push(i); - if( v.kind == Output || v.type.match(TBuffer(_,_,RW) | TRWTexture(_)) ) + if( v.kind == Output || v.type.match(TBuffer(_,_,RW)) ) i.keep = true; } } @@ -148,13 +148,13 @@ class Dce { writeTo.pop(); if( isAffected.indexOf(v) < 0 ) isAffected.push(v); - case TBinop(OpAssign | OpAssignOp(_), { e : (TArray({ e: TVar(v) }, i) | TSwiz({ e : TArray({ e : TVar(v) }, i) },_) | TField({e : TArray({ e : TVar(v) }, i) }, _)) }, e): + case TBinop(OpAssign | OpAssignOp(_), { e : TArray({ e: TVar(v) }, i) }, e): var v = get(v); writeTo.push(v); check(i, writeTo, isAffected); check(e, writeTo, isAffected); writeTo.pop(); - if( isAffected.indexOf(v) < 0 ) + if ( isAffected.indexOf(v) < 0 ) isAffected.push(v); case TBlock(el): var noWrite = []; @@ -202,14 +202,6 @@ class Dce { } else { link(channelVars[cid], writeTo); } - case TCall({ e : TGlobal(ImageStore)}, [{ e : TVar(v) }, uv, val]): - var v = get(v); - writeTo.push(v); - check(uv, writeTo, isAffected); - check(val, writeTo, isAffected); - writeTo.pop(); - if( isAffected.indexOf(v) < 0 ) - isAffected.push(v); default: e.iter(check.bind(_, writeTo, isAffected)); } @@ -272,11 +264,6 @@ class Dce { if( !loop.hasSideEffect() ) return { e : TConst(CNull), t : e.t, p : e.p }; return { e : TFor(v, it, loop), p : e.p, t : e.t }; - case TMeta(m, args, em): - var em = mapExpr(em, isVar); - if( !isVar && !em.hasSideEffect() ) - return { e : TConst(CNull), t : e.t, p : e.p }; - return { e : TMeta(m, args, em), t : e.t, p : e.p }; default: return e.map(function(e) return mapExpr(e,true)); } diff --git a/hxsl/DynamicShader.hx b/hxsl/DynamicShader.hx index bfd737c6e8..dc65f489a3 100644 --- a/hxsl/DynamicShader.hx +++ b/hxsl/DynamicShader.hx @@ -35,10 +35,7 @@ class DynamicShader extends Shader { function addVarIndex(v:hxsl.Ast.TVar, ?access : Access, ?defObj : Dynamic ) { if( v.kind != Param ) return; - var isFloat = switch(v.type) { - case TFloat, TInt: access == null; - default: false; - } + var isFloat = v.type == TFloat && access == null; var vid = isFloat ? floats.length : values.length; if( access != null ) access = new Access(Structure, access.index, access.fields.copy()); @@ -92,10 +89,6 @@ class DynamicShader extends Shader { accesses.push(access == null ? new Access(isFloat?Float:Dynamic,vid,null) : access); } - public function getParamIndex( p : hxsl.Ast.TVar ) : Int { - return varIndexes.get(p.id); - } - override function getParamValue(index:Int) : Dynamic { var a = accesses[index]; switch( a.kind ) { @@ -170,24 +163,6 @@ class DynamicShader extends Shader { updateConstantsFinal(globals); } - public function getVariable( name : String ) { - var vid = varNames.get(name); - if( vid == null ) - return null; - return vid < 0 ? floats[-vid-1] : values[vid]; - } - - public function setVariable( name : String, value : Dynamic ) { - var vid = varNames.get(name); - if( vid == null ) - return false; - if( vid < 0 ) - floats[-vid-1] = value; - else - values[vid] = value; - return true; - } - @:keep public function hscriptGet( field : String ) : Dynamic { var vid = varNames.get(field); if( vid == null ) diff --git a/hxsl/Eval.hx b/hxsl/Eval.hx index c664c6e7ea..7eefe9c95e 100644 --- a/hxsl/Eval.hx +++ b/hxsl/Eval.hx @@ -15,7 +15,6 @@ class Eval { var constants : Map; var funMap : Map; var curFun : TFunction; - var mapped : Array = []; public function new() { varMap = new Map(); @@ -27,16 +26,16 @@ class Eval { constants.set(v.id, TConst(c)); } - function mapVar( v : TVar, isLocal : Bool ) { + function mapVar( v : TVar ) { var v2 = varMap.get(v); if( v2 != null ) - return v2; + return v == v2 ? v2 : mapVar(v2); if( v.parent != null ) { - mapVar(v.parent, isLocal); // always map parent first + mapVar(v.parent); // always map parent first v2 = varMap.get(v); if( v2 != null ) - return v == v2 ? v2 : mapVar(v2, isLocal); + return v == v2 ? v2 : mapVar(v2); } v2 = { @@ -46,15 +45,13 @@ class Eval { kind : v.kind, }; - if( v.parent != null ) v2.parent = mapVar(v.parent, isLocal); + if( v.parent != null ) v2.parent = mapVar(v.parent); if( v.qualifiers != null ) v2.qualifiers = v.qualifiers.copy(); varMap.set(v, v2); varMap.set(v2, v2); // make it safe to have multiple eval - if (isLocal) - mapped.push(v); switch( v2.type ) { case TStruct(vl): - v2.type = TStruct([for( v in vl ) mapVar(v, isLocal)]); + v2.type = TStruct([for( v in vl ) mapVar(v)]); case TArray(t, SVar(vs)), TBuffer(t, SVar(vs), _): var c = constants.get(vs.id); if( c != null ) @@ -69,7 +66,7 @@ class Eval { Error.t("Integer value expected for array size constant " + vs.name, null); } else { - var vs2 = mapVar(vs, isLocal); + var vs2 = mapVar(vs); v2.type = switch( v2.type ) { case TArray(_): TArray(t, SVar(vs2)); case TBuffer(_,_,kind): TBuffer(t, SVar(vs2), kind); @@ -81,17 +78,17 @@ class Eval { return v2; } - function checkTextureRec(t:Type) { - if( t.isTexture() ) + function checkSamplerRec(t:Type) { + if( t.isSampler() ) return true; switch( t ) { case TStruct(vl): for( v in vl ) - if( checkTextureRec(v.type) ) + if( checkSamplerRec(v.type) ) return true; return false; case TArray(t, _): - return checkTextureRec(t); + return checkSamplerRec(t); case TBuffer(_): return true; default: @@ -101,7 +98,7 @@ class Eval { function needsInline(f:TFunction) { for( a in f.args ) - if( checkTextureRec(a.type) ) + if( checkSamplerRec(a.type) ) return true; return false; } @@ -111,8 +108,8 @@ class Eval { for( f in s.funs ) { var f2 : TFunction = { kind : f.kind, - ref : mapVar(f.ref, false), - args : [for( a in f.args ) mapVar(a, false)], + ref : mapVar(f.ref), + args : [for( a in f.args ) mapVar(a)], ret : f.ret, expr : f.expr, }; @@ -127,7 +124,7 @@ class Eval { } return { name : s.name, - vars : [for( v in s.vars ) mapVar(v, false)], + vars : [for( v in s.vars ) mapVar(v)], funs : funs, }; } @@ -211,7 +208,7 @@ class Eval { var i = switch( args[0].e ) { case TConst(CInt(i)): i; default: Error.t("Cannot eval complex channel " + Printer.toString(args[0],true)+" "+constantsToString(), pos); throw "assert"; }; var channel = oldArgs[0]; channel = { e : switch( channel.e ) { - case TVar(v): TVar(mapVar(v, false)); + case TVar(v): TVar(mapVar(v)); default: throw "assert"; }, t : channel.t, p : channel.p }; var count = switch( channel.t ) { case TChannel(i): i; default: throw "assert"; }; @@ -264,7 +261,6 @@ class Eval { } function evalExpr( e : TExpr, isVal = true ) : TExpr { - var t = e.t; var d : TExprDef = switch( e.e ) { case TGlobal(_), TConst(_): e.e; case TVar(v): @@ -272,12 +268,11 @@ class Eval { if( c != null ) c; else { - var v2 = mapVar(v, false); - t = v2.type; + var v2 = mapVar(v); TVar(v2); } case TVarDecl(v, init): - TVarDecl(mapVar(v, true), init == null ? null : evalExpr(init)); + TVarDecl(mapVar(v), init == null ? null : evalExpr(init)); case TArray(e1, e2): var e1 = evalExpr(e1); var e2 = evalExpr(e2); @@ -285,10 +280,6 @@ class Eval { case [TArrayDecl(el),TConst(CInt(i))] if( i >= 0 && i < el.length ): el[i].e; default: - switch( e1.t ) { - case TArray(at, _), TBuffer(at,_,_): t = at; - default: - } TArray(e1, e2); } case TSwiz(e, r): @@ -322,7 +313,7 @@ class Eval { varMap.remove(v); undo.push(function() varMap.set(v, old)); } - var v2 = mapVar(v, false); + var v2 = mapVar(v); outExprs.push( { e : TVarDecl(v2, e), t : TVoid, p : e.p } ); } } @@ -342,7 +333,6 @@ class Eval { Error.t("Cannot eval non-static call expresssion '" + new Printer().exprString(c)+"'", c.p); } case TBlock(el): - var index = mapped.length; var out = []; var last = el.length - 1; for( i in 0...el.length ) { @@ -354,15 +344,6 @@ class Eval { out.push(e); } } - // unmap previous vars - while( mapped.length > index ) { - var v = mapped.pop(); - var v2 = varMap.get(v); - if (v2 != null ) { - varMap.remove(v); - varMap.remove(v2); - } - } if( out.length == 1 && curFun.kind != Init ) out[0].e else @@ -494,7 +475,7 @@ class Eval { case TDiscard: TDiscard; case TFor(v, it, loop): - var v2 = mapVar(v, true); + var v2 = mapVar(v); var it = evalExpr(it); var e = switch( it.e ) { case TBinop(OpInterval, { e : TConst(CInt(start)) }, { e : TConst(CInt(len)) } ) if( unrollLoops ): @@ -562,10 +543,8 @@ class Eval { e2 = evalExpr(e, isVal); } TMeta(name, args, e2); - case TField(e, name): - TField(evalExpr(e), name); }; - return { e : d, t : t, p : e.p } + return { e : d, t : e.t, p : e.p } } } diff --git a/hxsl/Flatten.hx b/hxsl/Flatten.hx index 7aa1e92e4c..03fde8054f 100644 --- a/hxsl/Flatten.hx +++ b/hxsl/Flatten.hx @@ -26,7 +26,6 @@ class Flatten { var params : Array; var outVars : Array; var varMap : Map; - var textureFormats : Array<{ dim : TexDimension, arr : Bool, rw : Int }>; public var allocData : Map< TVar, Array >; public function new() { @@ -36,7 +35,6 @@ class Flatten { globals = []; params = []; outVars = []; - textureFormats = []; varMap = new Map(); allocData = new Map(); for( v in s.vars ) @@ -50,19 +48,9 @@ class Flatten { pack(prefix + "Globals", Global, globals, VFloat); pack(prefix + "Params", Param, params, VFloat); var allVars = globals.concat(params); - textureFormats.sort(function(t1,t2) { - if ( t1.rw != t2.rw ) - return t1.rw - t2.rw; - if ( t1.arr != t2.arr ) - return t1.arr ? 1 : -1; - return t1.dim.getIndex() - t2.dim.getIndex(); - }); - for( t in textureFormats ) { - var name = t.dim == T2D ? "" : t.dim.getName().substr(1); - if( t.rw > 0 ) name = "RW"+name+t.rw; - if( t.arr ) name += "Array"; - packTextures(prefix + "Textures" + name, allVars, t.rw == 0 ? TSampler(t.dim, t.arr) : TRWTexture(t.dim, t.arr, t.rw)); - } + var textures = packTextures(prefix + "Textures", allVars, TSampler2D) + .concat(packTextures(prefix+"TexturesCube", allVars, TSamplerCube)) + .concat(packTextures(prefix+"TexturesArray", allVars, TSampler2DArray)); packBuffers("buffers", allVars, Uniform); packBuffers("rwbuffers", allVars, RW); var funs = [for( f in s.funs ) mapFun(f, mapExpr)]; @@ -83,55 +71,7 @@ class Flatten { }; } - static var SWIZ = [X,Y,Z,W]; - - function mkOp(e:TExpr,by:Int,f:Int->Int->Int,binop,pos) { - switch( e.e ) { - case TConst(CInt(i)): - return { e : TConst(CInt(f(i,by))), t : TInt, p : pos }; - default: - } - return { e : TBinop(binop,e,mkInt(by,pos)), t : TInt, p : pos }; - } - - function mkAdd(e,offset,pos) { - if( offset == 0 ) - return e; - return mkOp(e,offset,(x,y) -> x + y,OpAdd,pos); - } - - function mkMult( e : TExpr, by : Int, pos ) { - if( by == 1 ) - return e; - return mkOp(e,by,(x,y) -> x * y,OpMult,pos); - } - function mapExpr( e : TExpr ) : TExpr { - inline function getFieldPos(expr, name) { - var pos = -1; - switch( expr.t ) { - case TStruct(vl): - var cur = 0; - for( v in vl ) { - if( v.name == name ) { - pos = cur; - break; - } - cur += v.type.size(); - } - default: - } - if( pos < 0 ) throw "assert"; - return pos; - } - inline function readField(expr, pos, size) { - var idx = pos >> 2; - var arr : TExpr = optimize({ e : TArray(expr,{ e : TConst(CInt(idx)), p : e.p, t : TInt }), p : e.p, t : TVec(4,VFloat) }); - if( size == 4 && pos & 3 == 0 ) - return arr; - var sw = SWIZ.slice(pos&3,(pos&3) + size); - return { e : TSwiz(arr,sw), t : size == 1 ? TFloat : TVec(size,VFloat), p : e.p } - } e = switch( e.e ) { case TVar(v): var a = varMap.get(v); @@ -139,92 +79,25 @@ class Flatten { e else access(a, v.type, e.p, AIndex(a)); - case TArray( { e : TVar(v), p : vp }, eindex): + case TArray( { e : TVar(v), p : vp }, eindex) if( !eindex.e.match(TConst(CInt(_))) ): var a = varMap.get(v); - if( a == null || (!v.type.match(TBuffer(_)) && eindex.e.match(TConst(CInt(_)))) ) - e.map(mapExpr); + if( a == null ) + e else { switch( v.type ) { - case TArray(t, _) if( t.isTexture() ): + case TArray(t, _) if( t.isSampler() ): eindex = toInt(mapExpr(eindex)); access(a, t, vp, AOffset(a,1,eindex)); - case TBuffer(TInt|TFloat,_), TVec(_, VFloat|VInt): - e.map(mapExpr); - case TArray(t, _), TBuffer(t, _): + case TArray(t, _): var stride = varSize(t, a.t); - if( stride == 0 || (v.type.match(TArray(_)) && stride & 3 != 0) ) throw new Error("Dynamic access to an Array which size is not 4 components-aligned is not allowed", e.p); - stride = (stride + 3) >> 2; + if( stride == 0 || stride & 3 != 0 ) throw new Error("Dynamic access to an Array which size is not 4 components-aligned is not allowed", e.p); + stride >>= 2; eindex = toInt(mapExpr(eindex)); - access(a, t, vp, AOffset(a,stride, mkMult(eindex,stride,vp))); + access(a, t, vp, AOffset(a,stride, stride == 1 ? eindex : { e : TBinop(OpMult,eindex,mkInt(stride,vp)), t : TInt, p : vp })); default: throw "assert"; } } - case TVarDecl(v, init) if( v.type.match(TStruct(_))): - var size = Math.ceil(v.type.size() / 4); - var v2 : TVar = { - id : Tools.allocVarId(), - name : v.name, - type : TArray(TVec(4,VFloat),SConst(size)), - kind : v.kind, - }; - var a = new Alloc(v2,VFloat,0,0); - a.v = v; - varMap.set(v, a); - { e : TVarDecl(v2, init == null ? null : mapExpr(init)), t : TVoid, p : e.p } - case TField(expr, name): - var pos = getFieldPos(expr, name); - var expr = mapExpr(expr); - switch( e.t ) { - case TFloat: - readField(expr, pos, 1); - case TVec(size,VFloat), TBytes(size): - var idx = pos >> 2; - var idx2 = ((pos + size - 1) >> 2); - if( idx == idx2 ) - readField(expr, pos, size); - else { - var k = 4 - pos; - var type = switch(size) { - case 2: Vec2; - case 3: Vec3; - case 4: Vec4; - default: throw "assert"; - } - { e : TCall({ e : TGlobal(type), p : e.p, t : TVoid },[ - readField(expr, pos, k), - readField(expr, pos + 1, size - k) - ]), t : e.t, p : e.p } - } - case TMat4: - { e : TCall({ e : TGlobal(Mat4), p : e.p, t : TVoid },[ - readField(expr, pos, 4), - readField(expr, pos + 4, 4), - readField(expr, pos + 8, 4), - readField(expr, pos + 12, 4), - ]), t : e.t, p : e.p } - case TMat3x4: - { e : TCall({ e : TGlobal(Mat3x4), p : e.p, t : TVoid },[ - readField(expr, pos, 4), - readField(expr, pos + 4, 4), - readField(expr, pos + 8, 4), - ]), t : e.t, p : e.p } - default: - throw "Unsupported type "+e.t.toString(); - } - case TBinop(OpAssign, e1, e2) if ( e.t.match(TMat4) && e1.e.match(TField(_,_))): - switch ( e1 ) { - case {e : TField(expr, name), t : TMat4}: - var pos = getFieldPos(expr, name); - var expr = mapExpr(expr); - {e : TBlock([ - {e : TBinop(OpAssign, readField(expr, pos, 4), {e : TArray(e2, {e : TConst(CInt(0)), t : TInt, p : null}), t : TVec(4, VFloat), p : null}), t : e.t, p : e.p}, - {e : TBinop(OpAssign, readField(expr, pos + 4, 4), {e : TArray(e2, {e : TConst(CInt(1)), t : TInt, p : null}), t : TVec(4, VFloat), p : null}), t : e.t, p : e.p}, - {e : TBinop(OpAssign, readField(expr, pos + 8, 4), {e : TArray(e2, {e : TConst(CInt(2)), t : TInt, p : null}), t : TVec(4, VFloat), p : null}), t : e.t, p : e.p}, - {e : TBinop(OpAssign, readField(expr, pos + 12, 4), {e : TArray(e2, {e : TConst(CInt(3)), t : TInt, p : null}), t : TVec(4, VFloat), p : null}), t : e.t, p : e.p}, - ]), t : e.t, p : e.p} - default : throw "assert"; - } default: e.map(mapExpr); }; @@ -242,7 +115,7 @@ class Flatten { inline function readOffset( a : Alloc, stride : Int, delta : TExpr, index : Int, pos ) : TExpr { var index = (a.t == null ? a.pos : a.pos >> 2) + index; - var offset : TExpr = mkAdd(delta,index,pos); + var offset : TExpr = index == 0 ? delta : { e : TBinop(OpAdd, delta, mkInt(index, pos)), t : TInt, p : pos }; return { e : TArray({ e : TVar(a.g), t : a.g.type, p : pos }, offset), t : TVec(4,a.t), p:pos }; } @@ -273,20 +146,12 @@ class Flatten { var stride = Std.int(a.size / len); var earr = [for( i in 0...len ) { var a = new Alloc(a.g, a.t, a.pos + stride * i, stride); access(a, t, pos, AIndex(a)); }]; return { e : TArrayDecl(earr), t : t, p : pos }; - case TBuffer(_): - return { e : TVar(a.g), t : t, p : pos }; - case TStruct(vl): - var size = 0; - for( v in vl ) - size += varSize(v.type, a.t); - var stride = Math.ceil(size/4); - var earr = [for( i in 0...stride ) read(i, pos)]; - return { e : TArrayDecl(earr), t : TArray(TVec(4,VFloat),SConst(stride)), p : pos }; - case t if( t.isTexture() ): - var e = read(0, pos); - e.t = t; - return e; default: + if( t.isSampler() ) { + var e = read(0, pos); + e.t = t; + return e; + } var size = varSize(t, a.t); if( size > 4 ) return Error.t("Access not supported for " + t.toString(), null); @@ -295,6 +160,7 @@ class Flatten { // 0 size array : return vec4(0.) if( a.pos == -1 ) return { e : TCall({ e : TGlobal(Vec4), t : TFun([]), p : pos },[{ e : TConst(CFloat(0)), t : TFloat, p : pos }]), t : TVec(4,VFloat), p : pos }; + if( a.pos & 3 != 0 ) throw "assert"; } else { var sw = []; for( i in 0...size ) @@ -356,10 +222,10 @@ class Flatten { var samplers = []; for( v in vars ) { var count = 1; - if( !v.type.equals(t) ) { + if( v.type != t ) { switch( v.type ) { - case TChannel(_) if( t.match(TSampler(T2D,false)) ): - case TArray(t2,SConst(n)) if( t2.equals(t) ): + case TChannel(_) if( t == TSampler2D ): + case TArray(t2,SConst(n)) if( t2 == t ): count = n; default: continue; @@ -405,30 +271,11 @@ class Flatten { }; for( v in vars ) switch( v.type ) { - case TBuffer(t,SConst(size),k) if( kind == k ): - var stride = Math.ceil(t.size()/4); - var bt = switch( t ) { - case TInt|TFloat if( kind.match( RW|RWPartial ) ) : - v.type; - default: - // for buffers of complex types, let's perform our own remaping - // this ensure that there's no difference between buffer layout - // depending on the platform or compiler - TBuffer(TVec(4,VFloat),SConst(size * stride),k); - } - var vbuf : TVar = { - id : Tools.allocVarId(), - name : v.name, - type : bt, - kind : Param, - }; - // register an allocation that is required for filling the buffers - var a = new Alloc(vbuf, null, alloc.length, 1); - a.t = VFloat; + case TBuffer(_,_,k) if( kind == k ): + var a = new Alloc(g, null, alloc.length, 1); a.v = v; alloc.push(a); - varMap.set(v, a); - outVars.push(vbuf); + outVars.push(v); default: } g.type = TArray(TBuffer(TVoid,SConst(0),kind),SConst(alloc.length)); @@ -444,10 +291,10 @@ class Flatten { kind : kind, }; for( v in vars ) { - if( v.type.isTexture() || v.type.match(TBuffer(_)) ) + if( v.type.isSampler() || v.type.match(TBuffer(_)) ) continue; switch( v.type ) { - case TArray(t,_) if( t.isTexture() ): continue; + case TArray(t,_) if( t.isSampler() ): continue; default: } var size = varSize(v.type, t); @@ -498,61 +345,31 @@ class Flatten { return switch( v ) { case TFloat, TInt if( t == VFloat ): 1; case TVec(n, t2) if( t == t2 ): n; - case TBytes(n): n; case TMat4 if( t == VFloat ): 16; case TMat3, TMat3x4 if( t == VFloat ): 12; case TArray(at, SConst(n)): varSize(at, t) * n; - case TStruct(vl): - var size = 0; - for( v in vl ) - size += varSize(v.type, t); - size; default: throw v.toString() + " size unknown for type " + t; } } - function addTextureFormat(dim,arr,rw=0) { - for( f in textureFormats ) - if( f.dim == dim && f.arr == arr && f.rw == rw ) - return; - textureFormats.push({ dim : dim, arr : arr, rw : rw }); - } - function gatherVar( v : TVar ) { switch( v.type ) { case TStruct(vl): for( v in vl ) gatherVar(v); - return; - case TSampler(dim, arr), TRWTexture(dim, arr, _): - var rw = switch( v.type ) { - case TRWTexture(_,_,chans): chans; - default: 0; - } - addTextureFormat(dim,arr,rw); - case TChannel(_): - addTextureFormat(T2D,false); - case TArray(type, _): - switch ( type ) { - case TSampler(dim, arr): - addTextureFormat(dim, arr, 0); - case TRWTexture(dim, arr, chans): - addTextureFormat(dim, arr, chans); - default: - } default: - } - switch( v.kind ) { - case Global: - if( v.hasQualifier(PerObject) ) + switch( v.kind ) { + case Global: + if( v.hasQualifier(PerObject) ) + params.push(v); + else + globals.push(v); + case Param: params.push(v); - else - globals.push(v); - case Param: - params.push(v); - default: - outVars.push(v); + default: + outVars.push(v); + } } } diff --git a/hxsl/GlslOut.hx b/hxsl/GlslOut.hx index c48e8e820a..fd33e69bb8 100644 --- a/hxsl/GlslOut.hx +++ b/hxsl/GlslOut.hx @@ -68,14 +68,9 @@ class GlslOut { set(BVec4, "bvec4"); set(FragCoord, "gl_FragCoord"); set(FrontFacing, "gl_FrontFacing"); + set(FrontFacing, "gl_FrontFacing"); set(FloatBitsToUint, "_floatBitsToUint"); set(UintBitsToFloat, "_uintBitsToFloat"); - - set(ComputeVar_LocalInvocation, "ivec3(gl_LocalInvocationID)"); - set(ComputeVar_GlobalInvocation, "ivec3(gl_GlobalInvocationID)"); - set(ComputeVar_LocalInvocationIndex, "int(gl_LocalInvocationIndex)"); - set(ComputeVar_WorkGroup, "ivec3(gl_WorkGroupID)"); - for( g in gl ) KWDS.set(g, true); gl; @@ -99,6 +94,7 @@ class GlslOut { public var varNames : Map; public var glES : Null; public var version : Null; + public var precision : Null = null; /* Intel HD driver fix: @@ -135,12 +131,6 @@ class GlslOut { decls.push(s); } - function getSamplerType(dim:TexDimension,arr) { - var name = "sampler" + dim.getName().substr(1); - if( arr ) name += "Array"; - return name; - } - function addType( t : Type ) { switch( t ) { case TVoid: @@ -173,13 +163,14 @@ class GlslOut { case TMat3x4: decl(MAT34); add("_mat3x4"); - case TSampler(dim,arr): - var name = getSamplerType(dim,arr); - add(name); - if( isES && (arr || dim == T3D) ) - decl("precision lowp "+name+";"); - case TRWTexture(dim, arr, chans): - add("image"+dim.getName().substr(1)+(arr?"Array":"")); + case TSampler2D: + add("sampler2D"); + case TSampler2DArray: + add("sampler2DArray"); + if( isES ) + decl("precision lowp sampler2DArray;"); + case TSamplerCube: + add("samplerCube"); case TStruct(vl): add("struct { "); for( v in vl ) { @@ -195,7 +186,6 @@ class GlslOut { switch( size ) { case SVar(v): ident(v); - case SConst(0): case SConst(1) if( intelDriverFix ): add(2); case SConst(v): @@ -219,15 +209,14 @@ class GlslOut { add("["); switch( size ) { case SVar(v): ident(v); - case SConst(0): case SConst(1) if( intelDriverFix ): add(2); case SConst(n): add(n); } add("]"); case TBuffer(t, size, kind): switch( kind ) { - case Uniform, Partial: - case RW, RWPartial: + case Uniform: + case RW: add("rw_"); } add((isVertex ? "vertex_" : "") + "uniform_buffer"+(uniformBuffer++)); @@ -246,7 +235,7 @@ class GlslOut { function addValue( e : TExpr, tabs : String ) { switch( e.e ) { case TBlock(el): - var name = "_val" + (exprIds++); + var name = "val" + (exprIds++); var tmp = buf; buf = new StringBuf(); addType(e.t); @@ -306,18 +295,18 @@ class GlslOut { decl("vec3 unpackNormal( vec4 v ) { return normalize((v.xyz - vec3(0.5)) * vec3(2.)); }"); case Texture: switch( args[0].t ) { - case TSampler(T2D,_), TChannel(_) if( isES2 ): + case TSampler2D, TSampler2DArray, TChannel(_) if( isES2 ): return "texture2D"; - case TSampler(TCube,_) if( isES2 ): + case TSamplerCube if( isES2 ): return "textureCube"; default: } case TextureLod: switch( args[0].t ) { - case TSampler(T2D,_), TChannel(_) if( isES2 ): + case TSampler2D, TSampler2DArray, TChannel(_) if( isES2 ): decl("#extension GL_EXT_shader_texture_lod : enable"); return "texture2DLodEXT"; - case TSampler(TCube,_) if( isES2 ): + case TSamplerCube if( isES2 ): decl("#extension GL_EXT_shader_texture_lod : enable"); return "textureCubeLodEXT"; default: @@ -329,21 +318,16 @@ class GlslOut { // else return "texelFetch"; case TextureSize: - var sufix = ""; switch( args[0].t ) { - case TChannel(_): + case TSampler2D, TChannel(_): decl("vec2 _textureSize(sampler2D sampler, int lod) { return vec2(textureSize(sampler, lod)); }"); - case TSampler(dim,arr): - var size = Tools.getDimSize(dim,arr); - sufix = (arr?"Array":""); - var t = "sampler"+dim.getName().substr(1)+sufix; - decl('vec$size _texture${sufix}Size($t sampler, int lod) { return vec$size(textureSize(sampler, lod)); }'); - case TRWTexture(dim,arr,_): - var size = Tools.getDimSize(dim,arr); - return "vec"+size+"(imageSize"; + case TSamplerCube: + decl("vec2 _textureSize(samplerCube sampler, int lod) { return vec2(textureSize(sampler, lod)); }"); + case TSampler2DArray: + decl("vec3 _textureSize(sampler2DArray sampler, int lod) { return vec3(textureSize(sampler, lod)); }"); default: } - return '_texture${sufix}Size'; + return "_textureSize"; case Mod if( rt == TInt && isES ): decl("int _imod( int x, int y ) { return int(mod(float(x),float(y))); }"); return "_imod"; @@ -484,14 +468,6 @@ class GlslOut { add("clamp("); addValue(e, tabs); add(", 0., 1.)"); - case TCall( { e : TGlobal(AtomicAdd) }, args): - add("atomicAdd("); - addValue(args[0], tabs); - add("["); - addValue(args[1], tabs); - add("],"); - addValue(args[2], tabs); - add(")"); case TCall({ e : TGlobal(g = Texel) }, args): add(getFunName(g,args,e.t)); add("("); @@ -510,31 +486,14 @@ class GlslOut { add(getFunName(g,args,e.t)); add("("); addValue(args[0], tabs); - if( args.length != 1 ) { + if ( args.length != 1 ) { // with LOD argument add(", "); addValue(args[1], tabs); add(")"); - } else if( args[0].t.match(TRWTexture(_)) ) { - add("))"); } else { add(", 0)"); } - case TCall({ e : TGlobal(ImageStore) }, [tex, uv, color]): - var chans = switch( tex.t ) { - case TRWTexture(_, _, chans): chans; - default: throw "assert"; - } - // we can use function decl because of some GLSL compiler bug - add("imageStore("); - addValue(tex, tabs); - add(","); - addValue(uv, tabs); - add(","); - if( chans != 4 ) add("("); - addValue(color, tabs); - if( chans != 4 ) add(")"+(chans == 1 ? ".xx" : ".xyyy")); - add(")"); case TCall(v, args): switch( v.e ) { case TGlobal(g): @@ -657,10 +616,6 @@ class GlslOut { add(")"); case TMeta(_, _, e): addExpr(e, tabs); - case TField(val, name): - addExpr(val, tabs); - add("."); - add(name); } } @@ -717,19 +672,14 @@ class GlslOut { switch( v.kind ) { case Param, Global: switch( v.type ) { - case TBuffer(_, _, RW|RWPartial): - add("layout(std430) buffer "); case TBuffer(_, _, kind): add("layout(std140) "); switch( kind ) { - case Uniform, Partial: + case Uniform: add("uniform "); - case RW, RWPartial: + case RW: add("buffer "); } - case TArray(TRWTexture(_, _, chans), _): - var format = "rgba".substr(0, chans); - add('layout(${format}32f) uniform '); default: add("uniform "); } @@ -785,10 +735,6 @@ class GlslOut { case TGlobal(g): m.set(g,true); case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }, { e : TConst(CInt(y)) }, { e : TConst(CInt(z)) }]): computeLayout = [x,y,z]; - case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }, { e : TConst(CInt(y)) }]): - computeLayout = [x,y,1]; - case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }]): - computeLayout = [x,1,1]; default: hxsl.Tools.iter(e,collectGlobals.bind(m)); } } @@ -810,11 +756,21 @@ class GlslOut { isCompute = f.kind == Main; if( isCompute ) { - // no prec - } else if( isVertex ) + // No precision qualifiers needed for compute shaders + } else if( isVertex ) { + #if js + if( precision != null ) { + decl('precision $precision float;'); + decl('precision $precision int;'); + } else { + decl("precision highp float;"); + } + #else decl("precision highp float;"); - else + #end + } else { decl("precision mediump float;"); + } initVars(s); @@ -862,7 +818,7 @@ class GlslOut { if( isES ) decl("#version " + (version < 100 ? 100 : version) + (version > 150 ? " es" : "")); - else if( isCompute || version >= 430 ) + else if( isCompute ) decl("#version 430"); else if( version != null ) decl("#version " + (version > 150 ? 150 : version)); diff --git a/hxsl/HlslOut.hx b/hxsl/HlslOut.hx index d5feda8fbe..692d0870db 100644 --- a/hxsl/HlslOut.hx +++ b/hxsl/HlslOut.hx @@ -14,8 +14,8 @@ class Samplers { public function make( v : TVar, arr : Array ) : Array { var ntex = switch( v.type ) { - case TArray(t, SConst(k)) if( t.isTexture() ): k; - case t if( t.isTexture() ): 1; + case TArray(t, SConst(k)) if( t.isSampler() ): k; + case t if( t.isSampler() ): 1; default: return null; } @@ -49,7 +49,7 @@ class Samplers { class HlslOut { static var KWD_LIST = [ - "s_input", "s_output", "_in", "_out", "in", "out", "mul", "matrix", "vector", "export", "half", "half2", "half3", "half4", "float", "double", "line", "linear", "point", "precise", + "s_input", "s_output", "_in", "_out", "in", "out", "mul", "matrix", "vector", "export", "half", "float", "double", "line", "linear", "point", "precise", "sample" // pssl ]; static var KWDS = [for( k in KWD_LIST ) k => true]; @@ -70,6 +70,8 @@ class HlslOut { m.set(Fract, "frac"); m.set(Mix, "lerp"); m.set(Inversesqrt, "rsqrt"); + m.set(VertexID,"_in.vertexID"); + m.set(InstanceID,"_in.instanceID"); m.set(IVec2, "int2"); m.set(IVec3, "int3"); m.set(IVec4, "int4"); @@ -77,12 +79,12 @@ class HlslOut { m.set(BVec3, "bool3"); m.set(BVec4, "bool4"); m.set(FragCoord,"_in.__pos__"); + m.set(FrontFacing, "_in.isFrontFace"); m.set(FloatBitsToInt, "asint"); m.set(FloatBitsToUint, "asuint"); m.set(IntBitsToFloat, "asfloat"); m.set(UintBitsToFloat, "_uintBitsToFloat"); m.set(RoundEven, "round"); - m.set(GroupMemoryBarrier, "GroupMemoryBarrier"); for( g in m ) KWDS.set(g, true); m; @@ -90,6 +92,9 @@ class HlslOut { var SV_POSITION = "SV_POSITION"; var SV_TARGET = "SV_TARGET"; + var SV_VertexID = "SV_VertexID"; + var SV_InstanceID = "SV_InstanceID"; + var SV_IsFrontFace = "SV_IsFrontFace"; var STATIC = "static "; var CONST = "const "; var buf : StringBuf; @@ -116,19 +121,6 @@ class HlslOut { allNames = new Map(); } - function getSVName( g : TGlobal ) { - return switch( g ) { - case VertexID: "SV_VertexID"; - case InstanceID: "SV_InstanceID"; - case FrontFacing: "SV_IsFrontFace"; - case ComputeVar_GlobalInvocation: "SV_DispatchThreadID"; - case ComputeVar_LocalInvocation: "SV_GroupThreadID"; - case ComputeVar_WorkGroup: "SV_GroupID"; - case ComputeVar_LocalInvocationIndex: "SV_GroupIndex"; - default: null; - } - } - inline function add( v : Dynamic ) { buf.add(v); } @@ -175,8 +167,12 @@ class HlslOut { add("float4x4"); case TMat3x4: add("float4x3"); - case TSampler(_), TRWTexture(_): - add(getTexType(t)); + case TSampler2D: + add("Texture2D"); + case TSamplerCube: + add("TextureCube"); + case TSampler2DArray: + add("Texture2DArray"); case TStruct(vl): add("struct { "); for( v in vl ) { @@ -188,7 +184,14 @@ class HlslOut { add("function"); case TArray(t, size), TBuffer(t,size,_): addType(t); - addArraySize(size); + add("["); + switch( size ) { + case SVar(v): + ident(v); + case SConst(v): + add(v); + } + add("]"); case TChannel(n): add("channel" + n); } @@ -198,7 +201,6 @@ class HlslOut { add("["); switch( size ) { case SVar(v): ident(v); - case SConst(0): case SConst(n): add(n); } add("]"); @@ -206,7 +208,7 @@ class HlslOut { function addVar( v : TVar ) { switch( v.type ) { - case TArray(t, size), TBuffer(t,size,Uniform): + case TArray(t, size), TBuffer(t,size,_): addVar({ id : v.id, name : v.name, @@ -214,11 +216,6 @@ class HlslOut { kind : v.kind, }); addArraySize(size); - case TBuffer(t, size, RW): - add('RWStructuredBuffer<'); - addType(t); - add('> '); - ident(v); default: addType(v.type); add(" "); @@ -229,7 +226,7 @@ class HlslOut { function addValue( e : TExpr, tabs : String ) { switch( e.e ) { case TBlock(el): - var name = "_val" + (exprIds++); + var name = "val" + (exprIds++); var tmp = buf; buf = new StringBuf(); addType(e.t); @@ -290,110 +287,6 @@ class HlslOut { decl("float4 mod(float4 x, float4 y) { return x - y * floor(x/y); }"); } - function getTexType( t : Type ) { - return switch( t ) { - case TSampler(dim, arr): "Texture"+dim.getName().substr(1)+(arr?"Array":""); - case TRWTexture(dim, arr, chans): "RWTexture"+dim.getName().substr(1)+(arr?"Array":"")+"<"+(chans==1?"float":"float"+chans)+">"; - default: throw "assert"; - } - } - - function declGlobal( g : TGlobal, args : Array ) { - switch( g ) { - case Mat3x4: - // float4x3 constructor uses row-order, we want column order here - decl("float4x3 mat3x4( float4 a, float4 b, float4 c ) { float4x3 m; m._m00_m10_m20_m30 = a; m._m01_m11_m21_m31 = b; m._m02_m12_m22_m32 = c; return m; }"); - decl("float4x3 mat3x4( float4x4 m ) { float4x3 m2; m2._m00_m10_m20_m30 = m._m00_m10_m20_m30; m2._m01_m11_m21_m31 = m._m01_m11_m21_m31; m2._m02_m12_m22_m32 = m._m02_m12_m22_m32; return m2; }"); - case Mat4: - decl("float4x4 mat4( float4 a, float4 b, float4 c, float4 d ) { float4x4 m; m._m00_m10_m20_m30 = a; m._m01_m11_m21_m31 = b; m._m02_m12_m22_m32 = c; m._m03_m13_m23_m33 = d; return m; }"); - case Mat3: - decl("float3x3 mat3( float4x4 m ) { return (float3x3)m; }"); - decl("float3x3 mat3( float4x3 m ) { return (float3x3)m; }"); - decl("float3x3 mat3( float3 a, float3 b, float3 c ) { float3x3 m; m._m00_m10_m20 = a; m._m01_m11_m21 = b; m._m02_m12_m22 = c; return m; }"); - decl("float3x3 mat3( float c00, float c01, float c02, float c10, float c11, float c12, float c20, float c21, float c22 ) { float3x3 m = { c00, c10, c20, c01, c11, c21, c02, c12, c22 }; return m; }"); - case Mat2: - decl("float2x2 mat2( float4x4 m ) { return (float2x2)m; }"); - decl("float2x2 mat2( float4x3 m ) { return (float2x2)m; }"); - decl("float2x2 mat2( float3x3 m ) { return (float2x2)m; }"); - decl("float2x2 mat2( float2 a, float2 b ) { float2x2 m; m._m00_m10 = a; m._m01_m11 = b; return m; }"); - decl("float2x2 mat2( float c00, float c01, float c10, float c11 ) { float2x2 m = { c00, c10, c01, c11 }; return m; }"); - case Mod: - declMods(); - case Pow: - // negative power might not work - decl("#pragma warning(disable:3571)"); - case Pack: - decl("float4 pack( float v ) { float4 color = frac(v * float4(1, 255, 255.*255., 255.*255.*255.)); return color - color.yzww * float4(1. / 255., 1. / 255., 1. / 255., 0.); }"); - case Unpack: - decl("float unpack( float4 color ) { return dot(color,float4(1., 1. / 255., 1. / (255. * 255.), 1. / (255. * 255. * 255.))); }"); - case PackNormal: - decl("float4 packNormal( float3 n ) { return float4((n + 1.) * 0.5,1.); }"); - case UnpackNormal: - decl("float3 unpackNormal( float4 p ) { return normalize(p.xyz * 2. - 1.); }"); - case Atan: - decl("float atan( float y, float x ) { return atan2(y,x); }"); - case ScreenToUv: - decl("float2 screenToUv( float2 v ) { return v * float2(0.5, -0.5) + float2(0.5,0.5); }"); - case UvToScreen: - decl("float2 uvToScreen( float2 v ) { return v * float2(2.,-2.) + float2(-1., 1.); }"); - case DFdx: - decl("float dFdx( float v ) { return ddx(v); }"); - decl("float2 dFdx( float2 v ) { return ddx(v); }"); - decl("float3 dFdx( float3 v ) { return ddx(v); }"); - case DFdy: - decl("float dFdy( float v ) { return ddy(v); }"); - decl("float2 dFdy( float2 v ) { return ddy(v); }"); - decl("float3 dFdy( float3 v ) { return ddy(v); }"); - case UintBitsToFloat: - decl("float _uintBitsToFloat( int v ) { return asfloat(asuint(v)); }"); - decl("float2 _uintBitsToFloat( int2 v ) { return asfloat(asuint(v)); }"); - decl("float3 _uintBitsToFloat( int3 v ) { return asfloat(asuint(v)); }"); - decl("float4 _uintBitsToFloat( int4 v ) { return asfloat(asuint(v)); }"); - case AtomicAdd: - decl("int atomicAdd( RWStructuredBuffer buf, int index, int data ) { int val; InterlockedAdd(buf[index], data, val); return val; }"); - case TextureSize: - var tt = args[0].t; - var tstr = getTexType(tt); - switch( tt ) { - case TSampler(dim, arr) if( args.length > 1 ): - var size = Tools.getDimSize(dim, arr); - switch( size ) { - case 1: - decl('float textureSize($tstr tex, int lod) { float w; float levels; tex.GetDimensions((uint)lod,w,levels); return w; }'); - case 2: - decl('float2 textureSize($tstr tex, int lod) { float w; float h; float levels; tex.GetDimensions((uint)lod,w,h,levels); return float2(w, h); }'); - case 3: - decl('float3 textureSize($tstr tex, int lod) { float w; float h; float els; float levels; tex.GetDimensions((uint)lod,w,h,els,levels); return float3(w, h, els); }'); - } - case TSampler(dim,arr), TRWTexture(dim, arr, _): - var size = Tools.getDimSize(dim, arr); - switch( size ) { - case 1: - decl('float textureSize($tstr tex) { float w; tex.GetDimensions(w); return w; }'); - case 2: - decl('float2 textureSize($tstr tex) { float w; float h; tex.GetDimensions(w,h); return float2(w, h); }'); - case 3: - decl('float3 textureSize($tstr tex) { float w; float h; float els; tex.GetDimensions(w,h,els); return float3(w, h, els); }'); - } - default: - throw "assert"; - } - case Vec2 if( args.length == 1 && args[0].t == TFloat ): - decl("float2 vec2( float v ) { return float2(v,v); }"); - case Vec3 if( args.length == 1 && args[0].t == TFloat ): - decl("float3 vec3( float v ) { return float3(v,v,v); }"); - case Vec4 if( args.length == 1 && args[0].t == TFloat ): - decl("float4 vec4( float v ) { return float4(v,v,v,v); }"); - case IVec2 if( args.length == 1 && args[0].t.match(TInt | TFloat)): - decl("int2 ivec2( int v ) { return int2(v,v); }"); - case IVec3 if( args.length == 1 && args[0].t.match(TInt | TFloat)): - decl("int3 ivec3( int v ) { return int3(v,v,v); }"); - case IVec4 if( args.length == 1 && args[0].t.match(TInt | TFloat)): - decl("int4 ivec4( int v ) { return int4(v,v,v,v); }"); - default: - } - } - function addExpr( e : TExpr, tabs : String ) { switch( e.e ) { case TConst(c): @@ -444,21 +337,16 @@ class HlslOut { if( g == Texture && isVertex ) add(",0"); add(")"); - case TCall({ e : TGlobal(ImageStore) }, [tex,uv,color]): - addValue(tex, tabs); - add("["); - addValue(uv, tabs); - add("] = "); - addValue(color, tabs); case TCall({ e : TGlobal(g = (Texel)) }, args): addValue(args[0], tabs); add(".Load("); - switch( args[1].t ) { - case TSampler(dim,arr): - var size = Tools.getDimSize(dim, arr) + 1; - add("int"+size+"("); - default: - throw "assert"; + switch ( args[1].t ) { + case TSampler2D: + add("int3("); + case TSampler2DArray: + add("int4("); + default: + throw "assert"; } addValue(args[1],tabs); if ( args.length != 2 ) { @@ -469,13 +357,31 @@ class HlslOut { add(", 0"); } add("))"); + case TCall({ e : TGlobal(g = (TextureSize)) }, args): + decl("float2 textureSize(Texture2D tex, int lod) { float w; float h; float levels; tex.GetDimensions((uint)lod,w,h,levels); return float2(w, h); }"); + decl("float3 textureSize(Texture2DArray tex, int lod) { float w; float h; float els; float levels; tex.GetDimensions((uint)lod,w,h,els,levels); return float3(w, h, els); }"); + decl("float2 textureSize(TextureCube tex, int lod) { float w; float h; float levels; tex.GetDimensions((uint)lod,w,h,levels); return float2(w, h); }"); + add("textureSize("); + addValue(args[0], tabs); + if (args.length != 1) { + // With LOD argument + add(", "); + addValue(args[1],tabs); + } else { + add(", 0"); + } + add(")"); case TCall(e = { e : TGlobal(g) }, args): - declGlobal(g, args); - switch( [g,args] ) { - case [Vec2|Vec3|Vec4, [{ t : TFloat }]]: - add(g.getName().toLowerCase()); - case [IVec2|IVec3|IVec4, [{ t : TInt }]|[{ t : TFloat }]]: - add(g.getName().toLowerCase()); + switch( [g,args.length] ) { + case [Vec2, 1] if( args[0].t == TFloat ): + decl("float2 vec2( float v ) { return float2(v,v); }"); + add("vec2"); + case [Vec3, 1] if( args[0].t == TFloat ): + decl("float3 vec3( float v ) { return float3(v,v,v); }"); + add("vec3"); + case [Vec4, 1] if( args[0].t == TFloat ): + decl("float4 vec4( float v ) { return float4(v,v,v,v); }"); + add("vec4"); default: addValue(e,tabs); } @@ -487,6 +393,58 @@ class HlslOut { } add(")"); case TGlobal(g): + switch( g ) { + case Mat3x4: + // float4x3 constructor uses row-order, we want column order here + decl("float4x3 mat3x4( float4 a, float4 b, float4 c ) { float4x3 m; m._m00_m10_m20_m30 = a; m._m01_m11_m21_m31 = b; m._m02_m12_m22_m32 = c; return m; }"); + decl("float4x3 mat3x4( float4x4 m ) { float4x3 m2; m2._m00_m10_m20_m30 = m._m00_m10_m20_m30; m2._m01_m11_m21_m31 = m._m01_m11_m21_m31; m2._m02_m12_m22_m32 = m._m02_m12_m22_m32; return m2; }"); + case Mat4: + decl("float4x4 mat4( float4 a, float4 b, float4 c, float4 d ) { float4x4 m; m._m00_m10_m20_m30 = a; m._m01_m11_m21_m31 = b; m._m02_m12_m22_m32 = c; m._m03_m13_m23_m33 = d; return m; }"); + case Mat3: + decl("float3x3 mat3( float4x4 m ) { return (float3x3)m; }"); + decl("float3x3 mat3( float4x3 m ) { return (float3x3)m; }"); + decl("float3x3 mat3( float3 a, float3 b, float3 c ) { float3x3 m; m._m00_m10_m20 = a; m._m01_m11_m21 = b; m._m02_m12_m22 = c; return m; }"); + decl("float3x3 mat3( float c00, float c01, float c02, float c10, float c11, float c12, float c20, float c21, float c22 ) { float3x3 m = { c00, c10, c20, c01, c11, c21, c02, c12, c22 }; return m; }"); + case Mat2: + decl("float2x2 mat2( float4x4 m ) { return (float2x2)m; }"); + decl("float2x2 mat2( float4x3 m ) { return (float2x2)m; }"); + decl("float2x2 mat2( float3x3 m ) { return (float2x2)m; }"); + decl("float2x2 mat2( float2 a, float2 b ) { float2x2 m; m._m00_m10 = a; m._m01_m11 = b; return m; }"); + decl("float2x2 mat2( float c00, float c01, float c10, float c11 ) { float2x2 m = { c00, c10, c01, c11 }; return m; }"); + case Mod: + declMods(); + case Pow: + // negative power might not work + decl("#pragma warning(disable:3571)"); + case Pack: + decl("float4 pack( float v ) { float4 color = frac(v * float4(1, 255, 255.*255., 255.*255.*255.)); return color - color.yzww * float4(1. / 255., 1. / 255., 1. / 255., 0.); }"); + case Unpack: + decl("float unpack( float4 color ) { return dot(color,float4(1., 1. / 255., 1. / (255. * 255.), 1. / (255. * 255. * 255.))); }"); + case PackNormal: + decl("float4 packNormal( float3 n ) { return float4((n + 1.) * 0.5,1.); }"); + case UnpackNormal: + decl("float3 unpackNormal( float4 p ) { return normalize(p.xyz * 2. - 1.); }"); + case Atan: + decl("float atan( float y, float x ) { return atan2(y,x); }"); + case ScreenToUv: + decl("float2 screenToUv( float2 v ) { return v * float2(0.5, -0.5) + float2(0.5,0.5); }"); + case UvToScreen: + decl("float2 uvToScreen( float2 v ) { return v * float2(2.,-2.) + float2(-1., 1.); }"); + case DFdx: + decl("float dFdx( float v ) { return ddx(v); }"); + decl("float2 dFdx( float2 v ) { return ddx(v); }"); + decl("float3 dFdx( float3 v ) { return ddx(v); }"); + case DFdy: + decl("float dFdy( float v ) { return ddy(v); }"); + decl("float2 dFdy( float2 v ) { return ddy(v); }"); + decl("float3 dFdy( float3 v ) { return ddy(v); }"); + case UintBitsToFloat: + decl("float _uintBitsToFloat( int v ) { return asfloat(asuint(v)); }"); + decl("float2 _uintBitsToFloat( int2 v ) { return asfloat(asuint(v)); }"); + decl("float3 _uintBitsToFloat( int3 v ) { return asfloat(asuint(v)); }"); + decl("float4 _uintBitsToFloat( int4 v ) { return asfloat(asuint(v)); }"); + default: + } add(GLOBALS.get(g)); case TParenthesis(e): add("("); @@ -700,10 +658,6 @@ class HlslOut { } case TMeta(m, args, e): handleMeta(m, args, addExpr, e, tabs); - case TField(e, f): - addValue(e, tabs); - add("."); - add(f); } } @@ -746,15 +700,11 @@ class HlslOut { } } - function collectGlobals( m : Map, e : TExpr ) { + function collectGlobals( m : Map, e : TExpr ) { switch( e.e ) { - case TGlobal(g): m.set(g,e.t); + case TGlobal(g): m.set(g,true); case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }, { e : TConst(CInt(y)) }, { e : TConst(CInt(z)) }]): computeLayout = [x,y,z]; - case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }, { e : TConst(CInt(y)) }]): - computeLayout = [x,y,1]; - case TCall({ e : TGlobal(SetLayout) }, [{ e : TConst(CInt(x)) }]): - computeLayout = [x,1,1]; default: e.iter(collectGlobals.bind(m)); } } @@ -782,24 +732,12 @@ class HlslOut { for( v in s.vars ) if( v.kind == Input || (v.kind == Var && !isVertex) ) declVar("_in.", v); - for( g in foundGlobals.keys() ) { - var sv = getSVName(g); - if( sv == null ) continue; - add("\t"); - switch( g ) { - case InstanceID: - add("uint"); - default: - addType(foundGlobals.get(g)); - } - var name = g.getName().split("_").pop(); - name = name.charAt(0).toLowerCase()+name.substr(1); - add(" "+name); - add(" : "); - add(sv); - add(";\n"); - GLOBALS.set(g, "_in."+name); - } + if( foundGlobals.exists(VertexID) ) + add("\tuint vertexID : "+SV_VertexID+";\n"); + if( foundGlobals.exists(InstanceID) ) + add("\tuint instanceID : "+SV_InstanceID+";\n"); + if( foundGlobals.exists(FrontFacing) ) + add("\tbool isFrontFace : "+SV_IsFrontFace+";\n"); add("};\n\n"); if( !isCompute ) { @@ -828,22 +766,18 @@ class HlslOut { function initParams( s : ShaderData ) { var textures = []; var buffers = []; - var uavs = []; add('cbuffer _params : register(b${baseRegister+1}) {\n'); for( v in s.vars ) if( v.kind == Param ) { switch( v.type ) { - case TArray(TRWTexture(_), _): - uavs.push(v); - continue; - case TArray(t, _) if( t.isTexture() ): + case TArray(t, _) if( t.isSampler() ): textures.push(v); continue; case TBuffer(_): buffers.push(v); continue; default: - if( v.type.isTexture() ) { + if( v.type.isSampler() ) { textures.push(v); continue; } @@ -854,19 +788,29 @@ class HlslOut { } add("};\n\n"); - var regCount = baseRegister + 2; - for( b in buffers.concat(uavs) ) { + var bufCount = 0; + for( b in buffers ) { switch( b.type ) { - case TBuffer(t, size, Uniform): - add('cbuffer _buffer$regCount : register(b${regCount++}) { '); - addVar(b); - add("; };\n"); + case TBuffer(t, size, kind): + switch( kind ) { + case Uniform: + add('cbuffer _buffer$bufCount : register(b${bufCount+baseRegister+2}) { '); + addVar(b); + add("; };\n"); + bufCount++; + case RW: + add('RWStructuredBuffer<'); + addType(t); + add('> '); + ident(b); + add(' : register(u${bufCount+baseRegister+2});'); + bufCount++; + } default: - addVar(b); - add(' : register(u${regCount++});\n'); + throw "assert"; } } - if( buffers.length + uavs.length > 0 ) add("\n"); + if( bufCount > 0 ) add("\n"); var ctx = new Samplers(); var texCount = 0; diff --git a/hxsl/Linker.hx b/hxsl/Linker.hx index a1297427ca..f22fa63b18 100644 --- a/hxsl/Linker.hx +++ b/hxsl/Linker.hx @@ -296,6 +296,21 @@ class Linker { s.deps = new Map(); for( r in s.readVars ) buildDependency(s, r, s.writeMap.exists(r.id)); + // propagate fragment flag + if( s.vertex == null ) + for( d in s.deps.keys() ) + if( d.vertex == false ) { + debug(s.name + " marked as fragment because of " + d.name); + s.vertex = false; + break; + } + // propagate vertex flag + if( s.vertex ) + for( d in s.deps.keys() ) + if( d.vertex == null ) { + debug(d.name + " marked as vertex because of " + s.name); + d.vertex = true; + } } function collect( cur : ShaderInfos, out : Array, vertex : Bool ) { @@ -363,7 +378,6 @@ class Linker { init : [-3000], vert : [-2000], frag : [-1000], - main : [-2500], }; for( s in shadersData ) { for( f in s.funs ) { @@ -383,7 +397,6 @@ class Linker { var status : Null = switch( f.ref.name ) { case "__init__vertex": prio = initPrio.vert; true; case "__init__fragment": prio = initPrio.frag; false; - case "__init__main": prio = initPrio.main; false; default: prio = initPrio.init; null; } switch( f.expr.e ) { @@ -435,31 +448,11 @@ class Linker { break; } if( onlyParams ) { - debug("Force " + s.name + " into fragment since it only reads params"); + debug("Force " + s.name+" into fragment since it only reads params"); s.vertex = false; } } - for( s in shaders ) { - if ( s.deps == null) - continue; - // propagate fragment flag - if( s.vertex == null ) - for( d in s.deps.keys() ) - if( d.vertex == false ) { - debug(s.name + " marked as fragment because of " + d.name); - s.vertex = false; - break; - } - // propagate vertex flag - if( s.vertex ) - for( d in s.deps.keys() ) - if( d.vertex == null ) { - debug(d.name + " marked as vertex because of " + s.name); - d.vertex = true; - } - } - // collect needed dependencies var v = [], f = []; collect(entry, v, true); diff --git a/hxsl/MacroParser.hx b/hxsl/MacroParser.hx index f7a4dc2a7e..afe0900d98 100644 --- a/hxsl/MacroParser.hx +++ b/hxsl/MacroParser.hx @@ -83,21 +83,6 @@ class MacroParser { } } - function getTexDim( n : String, f : Ast.TexDimension -> Bool -> Ast.Type ) { - var arr = false; - if( StringTools.endsWith(n,"Array") ) { - arr = true; - n = n.substr(0,-5); - } - return switch( n ) { - case "1D": f(T1D,arr); - case "2D": f(T2D,arr); - case "3D": f(T3D,arr); - case "Cube": f(TCube,arr); - default: null; - } - } - public function parseType( t : ComplexType, pos : Position ) : Ast.Type { switch( t ) { case TPath( { pack : [], name : name, sub : null, params : [] } ): @@ -119,6 +104,9 @@ class MacroParser { case "Mat3x4": return TMat3x4; case "Mat2": return TMat2; case "String": return TString; + case "Sampler2D": return TSampler2D; + case "Sampler2DArray": return TSampler2DArray; + case "SamplerCube": return TSamplerCube; case "Bytes2": return TBytes(2); case "Bytes3": return TBytes(3); case "Bytes4": return TBytes(4); @@ -126,39 +114,8 @@ class MacroParser { case "Channel2": return TChannel(2); case "Channel3": return TChannel(3); case "Channel4": return TChannel(4); - case _ if( StringTools.startsWith(name,"Sampler") ): - var t = getTexDim(name.substr(7), (d,arr) -> TSampler(d,arr)); - if( t != null ) return t; - } - case TPath( { pack : [], name : name, sub : null, params : pl } ) if( StringTools.startsWith(name,"RWTexture") ): - var chans = switch( pl[0] ) { - case TPType(TPath({ pack : [], name : n, sub : null, params : [] })): - switch( n ) { - case "Float": 1; - case "Vec2": 2; - case "Vec3": 3; - case "Vec4": 4; - default: 0; - } - case null, _: 0; } - if( chans == 0 ) - error("Unsupported RWTexture parameter, should be Float|Vec2|Vec3|Vec4", pos); - var t = getTexDim(name.substr(9), (dim,arr) -> TRWTexture(dim,arr,chans)); - if( t != null ) - return t; - case TPath( { pack : [], name : name = ("RWBuffer"|"RWPartialBuffer"), sub : null, params : [t] } ): - var t = switch( t ) { - case TPType(t): parseType(t, pos); - default: null; - } - if( t != null ) - return switch( name ) { - case "RWBuffer": TBuffer(t,SConst(0),RW); - case "RWPartialBuffer": TBuffer(t,SConst(0),RWPartial); - default: throw "assert"; - } - case TPath( { pack : [], name : name = ("Array"|"Buffer"|"RWBuffer"|"PartialBuffer"|"RWPartialBuffer"), sub : null, params : [t, size] } ): + case TPath( { pack : [], name : name = ("Array"|"Buffer"|"RWBuffer"), sub : null, params : [t, size] } ): var t = switch( t ) { case TPType(t): parseType(t, pos); default: null; @@ -176,8 +133,6 @@ class MacroParser { case "Array": TArray(t, size); case "Buffer": TBuffer(t,size,Uniform); case "RWBuffer": TBuffer(t,size,RW); - case "PartialBuffer": TBuffer(t,size,Partial); - case "RWPartialBuffer": TBuffer(t,size,RWPartial); default: throw "assert"; } case TAnonymous(fl): diff --git a/hxsl/Macros.hx b/hxsl/Macros.hx index cf10c57123..8a4c2472de 100644 --- a/hxsl/Macros.hx +++ b/hxsl/Macros.hx @@ -23,13 +23,15 @@ class Macros { for( v in vl ) { fields.push({ pos : pos, name : v.name, kind : FVar(makeType(v.type)) }); if( v.type.match(TChannel(_)) ) - fields.push({ pos : pos, name : v.name+"Channel", kind : FVar(macro : hxsl.Channel) }); + fields.push({ pos : pos, name : v.name+"Channel", kind : FVar(macro : hxsl.Channel) }); } TAnonymous(fields); - case TSampler(_, false), TRWTexture(_,false,_): - macro : hxsl.Types.Texture; - case TSampler(_, true), TRWTexture(_,true,_): - macro : hxsl.Types.TextureArray; + case TSampler2D: + macro : hxsl.Types.Sampler2D; + case TSampler2DArray: + macro : hxsl.Types.Sampler2DArray; + case TSamplerCube: + macro : hxsl.Types.SamplerCube; case TMat2, TMat3, TMat3x4, TMat4: macro : hxsl.Types.Matrix; case TString: @@ -44,7 +46,7 @@ class Macros { var t = makeType(t); macro : Array<$t>; case TChannel(_): - macro : hxsl.Types.TextureChannel; + macro : hxsl.Types.ChannelTextureType; case TFun(_): throw "assert"; case TBuffer(_): @@ -244,7 +246,7 @@ class Macros { case TInt: exprs.push(macro { var v : Int = $p; - if( v >>> $v{ c.bits } != 0 ) throw $v{ c.v.name } +" is out of range " + v + ">" + $v{ (1 << c.bits) - 1 } + ", consider using @const(MAX_VALUE)"; + if( v >>> $v{ c.bits } != 0 ) throw $v{ c.v.name } +" is out of range " + v + ">" + $v{ (1 << c.bits) - 1 }; constBits |= v << $v{ c.pos }; }); case TBool: @@ -261,13 +263,6 @@ class Macros { if( $p == null ) $psel = Unknown else if( $psel == Unknown ) $defFormat; constBits |= ((globals.allocChannelID($p) << 3) | Type.enumIndex($psel)) << $v{ c.pos }; }); - case TBuffer(_,_,Partial|RWPartial): - var psel = getPath(c.v,"Format"); - exprs.push(macro { - if( $p == null ) throw "Partial buffer is not set"; - if( $p.format.uid >>> hxsl.Ast.Tools.MAX_PARTIAL_MAPPINGS_BITS != 0 ) throw "Buffer format is out of range"; - constBits |= $p.format.uid << $v{ c.pos }; - }); default: throw "assert"; } @@ -311,7 +306,7 @@ class Macros { args : [ { name : "index", type : macro : Int } ], expr : { expr : EBlock([ - { expr : ESwitch(macro index, [for( i in 0...tparams.length ) if( tparams[i] == TFloat || tparams[i] == TInt ) { values : [macro $v{i}], expr : macro return ${eparams[i]} }], macro {}), pos : pos }, + { expr : ESwitch(macro index, [for( i in 0...tparams.length ) if( tparams[i] == TFloat ) { values : [macro $v{i}], expr : macro return ${eparams[i]} }], macro {}), pos : pos }, macro return 0., ]), pos : pos, diff --git a/hxsl/NXGlslOut.hx b/hxsl/NXGlslOut.hx index 986029c4ca..e8485ef068 100644 --- a/hxsl/NXGlslOut.hx +++ b/hxsl/NXGlslOut.hx @@ -33,12 +33,12 @@ class NXGlslOut extends hxsl.GlslOut { hasGlobals = true; case Param: switch( v.type ) { - case TArray(t, _) if( t.isTexture() ): + case TArray(t, _) if( t.isSampler() ): super.initVar(v); default: hasParams = true; } - case Output: + case Output: if( !isVertex ) add('layout(location=${outIndex++}) '); super.initVar(v); default: @@ -55,7 +55,7 @@ class NXGlslOut extends hxsl.GlslOut { if( v.kind == Param ){ switch( v.type ) { case TBuffer(_): - case TArray(t, _) if( t.isTexture() ): + case TArray(t, _) if( t.isSampler() ): default: add("\t"); super.initVar(v); diff --git a/hxsl/Printer.hx b/hxsl/Printer.hx index 53cc87ffe0..e74e6f5fc1 100644 --- a/hxsl/Printer.hx +++ b/hxsl/Printer.hx @@ -299,10 +299,6 @@ class Printer { } add(" "); addExpr(e, tabs); - case TField(e, name): - addExpr(e, tabs); - add("."); - add(name); } } diff --git a/hxsl/Serializer.hx b/hxsl/Serializer.hx index 024fd635cc..cdd56a83da 100644 --- a/hxsl/Serializer.hx +++ b/hxsl/Serializer.hx @@ -11,7 +11,6 @@ class Serializer { var types : Array; var uid = 1; var tid = 1; - var version : Int; public function new() { } @@ -77,7 +76,7 @@ class Serializer { function writeType( t : Type ) { out.addByte(t.getIndex()); - switch( t ) { + switch (t) { case TVec(size, t): out.addByte(size | (t.getIndex() << 3)); case TBytes(size): @@ -102,18 +101,11 @@ class Serializer { } case TChannel(size): out.addByte(size); - case TSampler(dim, arr): - out.addByte((dim.getIndex() << 1) | (arr ? 1 : 0)); - case TRWTexture(dim, arr, chans): - out.addByte((dim.getIndex() << 3) | (arr ? 1 : 0) | ((chans - 1) << 1)); - case TVoid, TInt, TBool, TFloat, TString, TMat2, TMat3, TMat4, TMat3x4: - case __TUnused: - throw "assert"; + case TVoid, TInt, TBool, TFloat, TString, TMat2, TMat3, TMat4, TMat3x4, TSampler2D, TSampler2DArray, TSamplerCube: } } static var TVECS = new Map(); - static var TDIMS = hxsl.Ast.TexDimension.createAll(); function readType() : Type { return switch( input.readByte() ) { @@ -134,20 +126,9 @@ class Serializer { case 7: TMat4; case 8: TMat3x4; case 9: TBytes(input.readInt32()); - case 10 if( version == 0 ): TSampler(T2D, false); - case 11 if( version == 0 ): TSampler(T2D, true); - case 12 if( version == 0 ): TSampler(TCube, false); - case 18 if( version == 0 ): TMat2; - case 10: - var b = input.readByte(); - var dim = TDIMS[b>>1]; - TSampler(dim, b & 1 != 0); - case 11: - var b = input.readByte(); - var dim = TDIMS[b>>3]; - TRWTexture(dim, b & 1 != 0, ((b>>1)&3) + 1); - case 12: - TMat2; + case 10: TSampler2D; + case 11: TSampler2DArray; + case 12: TSamplerCube; case 13: var id = readVarInt(); var t = types[id]; @@ -173,6 +154,7 @@ class Serializer { TBuffer(t, v == null ? SConst(readVarInt()) : SVar(v), kind); case 17: TChannel(input.readByte()); + case 18: TMat2; default: throw "assert"; } @@ -320,9 +302,6 @@ class Serializer { writeString(m); writeArr(args, writeConst); writeExpr(e); - case TField(e,name): - writeExpr(e); - writeString(name); } writeType(e.t); // no position @@ -390,7 +369,6 @@ class Serializer { }), readExpr()); case 19: TWhile(readExpr(), readExpr(), input.readByte() != 0); case 20: TMeta(readString(), readArr(readConst), readExpr()); - case 21: TField(readExpr(), readString()); default: throw "assert"; } return { @@ -459,14 +437,11 @@ class Serializer { }; } - static var SIGN = 0x8C741D; // will be encoded to HXSL + static var SIGN = 0x8B741D; // will be encoded to HXSL public function unserialize( data : String ) : ShaderData { input = new haxe.io.BytesInput(haxe.crypto.Base64.decode(data,false)); - if( input.readByte() != (SIGN & 0xFF) || input.readByte() != (SIGN >> 8) & 0xFF ) - throw "Invalid HXSL data"; - version = input.readByte() - 0x8B; - if( version < 0 || version > 1 ) + if( input.readByte() != (SIGN & 0xFF) || input.readByte() != (SIGN >> 8) & 0xFF || input.readByte() != (SIGN >> 16) & 0xFF ) throw "Invalid HXSL data"; varMap = new Map(); types = []; diff --git a/hxsl/SharedShader.hx b/hxsl/SharedShader.hx index 7f54fa0b2c..ba2b4ca59d 100644 --- a/hxsl/SharedShader.hx +++ b/hxsl/SharedShader.hx @@ -69,114 +69,17 @@ class SharedShader { return if( i == null ) makeInstance(constBits) else i; } - function makeBufferType( v : TVar, tbuf : hxsl.Ast.Type, fmt : hxd.BufferFormat ) : hxsl.Ast.Type { - var name = v.name; - switch( tbuf ) { - case TStruct(vl): - var inputs = [for( i in fmt.getInputs() ) i]; - var vli : Array = []; - var p = 0; - while( p < inputs.length ) { - var i = inputs[p++]; - var name = i.name; - var t = switch( i.type ) { - case DVec2: TVec(2,VFloat); - case DVec3: TVec(3,VFloat); - case DVec4: TVec(4,VFloat); - case DFloat: TFloat; - case DBytes4: TBytes(4); - } - if( StringTools.endsWith(i.name,"__m0") ) { - var h = i.type.getSize(); - var w = 2; - while( inputs[p+w-1] != null && StringTools.endsWith(inputs[p+w-1].name,"__m"+w) ) - w++; - t = switch( [w,h] ) { - case [2,2]: TMat2; - case [3,3]: TMat3; - case [3,4]: TMat3x4; - case [4,4]: TMat4; - default: throw "Unsupported matrix format"; - } - name = i.name.substr(0,-4); - p += w - 1; - } - vli.push({ - id : Tools.allocVarId(), - name : name, - type : t, - kind : v.kind, - parent : v, - }); - } - for( v in vl ) { - var found = false; - for( v2 in vli ) - if( v.name == v2.name ) { - switch( [v.type, v2.type] ) { - case [TFloat, TFloat]: - case [TVec(a,VFloat), TVec(b,VFloat)] if( a <= b ): - default: - if( !v.type.equals(v2.type) ) - throw "Buffer "+data.name+"."+v.name+":"+v.type.toString()+" should be "+v2.type.toString(); - } - found = true; - break; - } - if( !found ) - throw "Buffer is missing "+data.name+"."+v.name+":"+v.type.toString(); - } - return TStruct(vli); - default: - throw "assert"; - } - } - function makeInstance( constBits : Int ) { var eval = new hxsl.Eval(); var c = consts; - var buffers : Array = []; while( c != null ) { - switch( c.v.type ) { - case TBool: - eval.setConstant(c.v, CBool((constBits >>> c.pos) & 1 != 0)); - case TInt, TChannel(_): - eval.setConstant(c.v, CInt((constBits >>> c.pos) & ((1 << c.bits) - 1))); - case TBuffer(t, size, kind): - var bits = (constBits >>> c.pos) & ((1 << c.bits) - 1); - var fmt = hxd.BufferFormat.fromID(bits); - var v : TVar = { - id : c.v.id, - name : c.v.name, - kind : c.v.kind, - type : null, - }; - var fullT = makeBufferType(v, t, fmt); - v.type = TBuffer(fullT, size, switch( kind ) { - case Partial: Uniform; - case RWPartial: RW; - default: throw "assert"; - }); - eval.varMap.set(c.v, v); - buffers.push(v); + eval.setConstant(c.v, switch( c.v.type ) { + case TBool: CBool((constBits >>> c.pos) & 1 != 0); + case TInt, TChannel(_): CInt((constBits >>> c.pos) & ((1 << c.bits) - 1)); default: throw "assert"; - } + }); c = c.next; } - for ( v in buffers ) { - switch ( v.type ) { - case TBuffer(t, SVar(vs), kind): - var c = @:privateAccess eval.constants.get(vs.id); - if ( c != null ) { - switch ( c ) { - case TConst(CInt(i)): - v.type = TBuffer(t, SConst(i), kind); - default: - } - } - default: - } - } eval.inlineCalls = true; eval.unrollLoops = UNROLL_LOOPS; var edata = eval.eval(data); diff --git a/hxsl/Splitter.hx b/hxsl/Splitter.hx index dac9fcbe56..3dad261d96 100644 --- a/hxsl/Splitter.hx +++ b/hxsl/Splitter.hx @@ -18,19 +18,15 @@ private class VarProps { class Splitter { var vars : Map; - var avars : Array; var varNames : Map; var varMap : Map; - var isBatchShader : Bool; - public function new() { } - public function split( s : ShaderData, isBatchShader : Bool ) : Array { - this.isBatchShader = isBatchShader; - var vfun = null, vvars = new Map(), avvars = []; - var ffun = null, fvars = new Map(), afvars = []; + public function split( s : ShaderData ) : Array { + var vfun = null, vvars = new Map(); + var ffun = null, fvars = new Map(); var isCompute = false; varNames = new Map(); varMap = new Map(); @@ -38,13 +34,11 @@ class Splitter { switch( f.kind ) { case Vertex, Main: vars = vvars; - avars = avvars; vfun = f; checkExpr(f.expr); if( f.kind == Main ) isCompute = true; case Fragment: vars = fvars; - avars = afvars; ffun = f; checkExpr(f.expr); default: @@ -52,9 +46,7 @@ class Splitter { } var vafterMap = []; - var length = avvars.length; - for( i in 0...length ) { - var inf = avvars[i]; + for( inf in Lambda.array(vvars) ) { var v = inf.v; if( inf.local ) continue; switch( v.kind ) { @@ -105,7 +97,7 @@ class Splitter { var finits = []; var todo = []; - for( inf in afvars ) { + for( inf in fvars ) { var v = inf.v; switch( v.kind ) { case Input: @@ -176,14 +168,8 @@ class Splitter { var fvars = [for( v in fvars ) if( !v.local ) v]; // make sure we sort the inputs the same way they were sent in inline function getId(v:VarProps) return v.origin == null ? v.v.id : v.origin.id; - inline function compare(v1:VarProps, v2:VarProps) { - var result = getId(v1) - getId(v2); - if ( result != 0 ) - return result; - return v1.v.id - v2.v.id; - } - vvars.sort(function(v1, v2) return compare(v1, v2)); - fvars.sort(function(v1, v2) return compare(v1, v2)); + vvars.sort(function(v1, v2) return getId(v1) - getId(v2)); + fvars.sort(function(v1, v2) return getId(v1) - getId(v2)); return isCompute ? [ { @@ -217,15 +203,12 @@ class Splitter { function checkVar( v : VarProps, vertex : Bool, vvars : Map, p ) { switch( v.v.kind ) { case Local if( v.requireInit ): - if ( v.origin.parent == null || (v.origin.parent.name != "global" && !isBatchShader) ) - throw new Error("Variable " + v.v.name + " is used without being initialized", p); + throw new Error("Variable " + v.v.name + " is used without being initialized", p); case Var: if( !vertex ) { var i = vvars.get(v.origin.id); - if( i != null && i.v.kind == Input ) - return; - if( v.requireInit && ( i == null || i.write == 0 ) ) - throw new Error("Varying " + v.v.name + " is not written by vertex shader",p); + if( i != null && i.v.kind == Input ) return; + if( i == null || i.write == 0 ) throw new Error("Varying " + v.v.name + " is not written by vertex shader",p); } default: } @@ -270,7 +253,6 @@ class Splitter { i = new VarProps(nv); i.origin = v; vars.set(v.id, i); - avars.push(i); } return i; } diff --git a/hxsl/Types.hx b/hxsl/Types.hx index f132f40815..b30209a2bd 100644 --- a/hxsl/Types.hx +++ b/hxsl/Types.hx @@ -6,12 +6,14 @@ typedef IVec = Array; typedef BVec = Array; typedef Matrix = h3d.Matrix; typedef Texture = h3d.mat.Texture; -typedef TextureArray = h3d.mat.TextureArray; -typedef TextureChannel = h3d.mat.Texture; +typedef Sampler2D = h3d.mat.Texture; +typedef Sampler2DArray = h3d.mat.TextureArray; +typedef SamplerCube = h3d.mat.Texture; +typedef ChannelTextureType = h3d.mat.Texture; typedef Buffer = h3d.Buffer; class ChannelTools { - public static inline function isPackedFormat( c : TextureChannel ) { + public static inline function isPackedFormat( c : ChannelTextureType ) { return c.format == h3d.mat.Texture.nativeFormat; } } \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000..9a969635c3 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "@hacksawstudios/heaps", + "version": "2.0.5", + "description": "_High Performance Game Framework_", + "main": "index.js", + "devDependencies": { + "@hacksawstudios/haxe-module-installer": "1.2.12", + "fs-extra": "11.2.0" + }, + "scripts": { + "postinstall": "npx @hacksawstudios/haxe-module-installer", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/HacksawStudios/heaps.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/HacksawStudios/heaps/issues" + }, + "homepage": "https://github.com/HacksawStudios/heaps#readme" +} \ No newline at end of file diff --git a/samples/Camera2D.hx b/samples/Camera2D.hx index d203411c69..0b574f1f01 100644 --- a/samples/Camera2D.hx +++ b/samples/Camera2D.hx @@ -1,7 +1,3 @@ -import h2d.col.Point; -import h2d.Tile; -import h2d.RenderContext; -import h2d.Object; import h3d.Engine; import h2d.TextInput; import h2d.Camera; @@ -17,7 +13,6 @@ class Camera2D extends SampleApp { var followCamera : Camera; var followPoint : Graphics; - var car : Car; var sliderAnchorX : h2d.Slider; var sliderCamAnchorX : h2d.Slider; @@ -106,14 +101,13 @@ class Camera2D extends SampleApp { } } - addText("Use arrow keys to move the green arrow"); - followPoint = new Graphics(); + addText("User arrow keys to move the green arrow"); + followPoint = new Graphics(s2d); followPoint.beginFill(0xff00); followPoint.moveTo(0, -5); followPoint.lineTo(5, 5); followPoint.lineTo(-5, 5); followPoint.setPosition(followX, followY); - s2d.add(followPoint, 0); // Anchor allows to adjust the position of camera target relative to it's top-left corner in scene viewport ratio. // 0.5 would ensure that whatever position camera points at would at the center of it's viewport. @@ -128,29 +122,20 @@ class Camera2D extends SampleApp { followCamera.follow = followPoint; followCamera.followRotation = true; - // Scene.camera property provides an alias to `Scene.cameras[0]`. + // Scene.camera proeprty provides an alias to `Scene.cameras[0]`. camera = s2d.camera; camera.setAnchor(0.5, 0.5); camera.setPosition(s2d.width * .5, s2d.height * .5); camera.layerVisible = (idx) -> idx == 0; // Marker for primary camera position - cameraPositionMarker = new h2d.Graphics(); + cameraPositionMarker = new h2d.Graphics(s2d); cameraPositionMarker.x= camera.x; cameraPositionMarker.y= camera.y; cameraPositionMarker.beginFill(0xff0000,0.5); cameraPositionMarker.drawRect(-10, -1, 20, 2); cameraPositionMarker.drawRect(-1, -10, 2, 20); cameraPositionMarker.endFill(); - s2d.add(cameraPositionMarker, 0); - - // Note that despite being on layer 0, and thus visible to both cameras - // car is visible only on the first camera, due to it not rendering - // to cameras other than the one assigned to it. - car = new Car(camera); - car.offsetX = followX - tmx.width * (tileSize / 2); - car.offsetY = followY - tmx.height * (tileSize / 2); - s2d.add(car, 0); addText("Camera controls"); sliderCamAnchorX=addSlider("Anchor X", function() { return camera.anchorX; }, function(v) { camera.anchorX = v; onCameraParameterChanged();}, 0, 1); @@ -162,7 +147,6 @@ class Camera2D extends SampleApp { sliderCamScaleX=addSlider("Scale X", function() { return camera.scaleX; }, function(v) { camera.scaleX = v; onCameraParameterChanged();}, 0, 5); sliderCamScaleY=addSlider("Scale Y", function() { return camera.scaleY; }, function(v) { camera.scaleY = v; onCameraParameterChanged();}, 0, 5); - addButton("Swap car camera", () -> car.camera = car.camera == followCamera ? camera : followCamera); } override function render(e:Engine) { @@ -199,73 +183,4 @@ class Camera2D extends SampleApp { new Camera2D(); } -} - -// An example of alternative way to hide/show objects on camera by checking `RenderContext.currentCamera` -class Car extends h2d.Bitmap { - - public var camera : Camera; - - var sprites : Array; - var path : Array = [ - new Point(3.8 * 16, 0), - new Point(3.8 * 16, 8.1 * 16), - new Point(16.8 * 16, 8.1 * 16), - new Point(16.8 * 16, 20.6 * 16), - new Point(0, 20.6 * 16), - ]; - var pathPos = 0; - public var offsetX: Float = 0; - public var offsetY: Float = 0; - - public function new( camera : Camera, ?parent : Object ) { - var tileset = Res.tiles.toTile(); - sprites = [ - tileset.sub(336, 236, 16, 20).center(), // down - tileset.sub(352, 236, 16, 20).center(), // up - tileset.sub(341, 256, 22, 16).center(), // left - tileset.sub(341, 272, 22, 16).center(), // right - ]; - super(sprites[0], parent); - this.camera = camera; - } - - override function drawRec( ctx : RenderContext ) { - // By checking `ctx.currentCamera` we can conditionally render parts of the object tree - // This allows for more flexibility compared to layer filtering, as it's possible - // to hide objects that are nested deep in the object tree. - if ( ctx.currentCamera != camera ) return; - super.drawRec(ctx); - } - - override function onAdd() { - super.onAdd(); - setPosition(path[0].x + offsetX, path[0].y + offsetY); - next(); - } - - function next() { - this.pathPos++; - if ( this.pathPos == this.path.length ) { - setPosition(path[0].x + offsetX, path[0].y + offsetY); - this.pathPos = 1; - } - var prev = path[this.pathPos-1]; - var next = path[this.pathPos]; - this.tile = if ( prev.x == next.x ) { - if ( next.y < prev.y ) sprites[1]; - else sprites[0]; - } else { - if ( next.x < prev.x ) sprites[2]; - else sprites[3]; - } - } - - override function sync( ctx : RenderContext ) { - var target = this.path[this.pathPos]; - this.x = hxd.Math.valueMove(this.x, target.x + offsetX, 64*ctx.elapsedTime); - this.y = hxd.Math.valueMove(this.y, target.y + offsetY, 64*ctx.elapsedTime); - if ( this.x == target.x+offsetX && this.y == target.y+offsetY ) next(); - super.sync(ctx); - } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..46289926c5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,47 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@hacksawstudios/haxe-module-installer@1.2.6": + version "1.2.6" + resolved "https://hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com:443/npm/hacksaw-client-artifacts/@hacksawstudios/haxe-module-installer/-/haxe-module-installer-1.2.6.tgz#fcb9c643a30f11200a647f437a9befce984ad5f4" + integrity sha512-2bWK3hAmF+bVMxxVRCez7OJIbBo+nu1JvUSXdeoaJVsgPtFBCKZ6dAfi7VqRHZo7IQJKaHFzI2seMKD5OFckmA== + dependencies: + fs-extra "10.1.0" + +fs-extra@10.1.0: + version "10.1.0" + resolved "https://hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com:443/npm/hacksaw-client-artifacts/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@11.1.0: + version "11.1.0" + resolved "https://hacksawgaming-281033247142.d.codeartifact.eu-west-1.amazonaws.com:443/npm/hacksaw-client-artifacts/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==