Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified packages/flame_3d/assets/shaders/spatial_material.shaderbundle
Binary file not shown.
Binary file modified packages/flame_3d/assets/shaders/unlit_material.shaderbundle
Binary file not shown.
74 changes: 61 additions & 13 deletions packages/flame_3d/bin/build_shaders.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';

/// Bundle a shader ('name'.frag & 'name'.vert) into a single shader bundle and
/// store it in the assets directory.
Expand All @@ -8,23 +9,68 @@ import 'dart:io';
/// Flutter might support auto-bundling themselves but until then we have to
/// do it manually.
///
/// Note: this script should be run from the root of the package:
/// packages/flame_3d
/// Run from the package root whose shaders are being built. When invoked
/// from a consumer package via `dart run flame_3d:build_shaders`, the
/// consumer's own `shaders/` is bundled. Every Dart dependency that ships a
/// top-level `shaders/` directory is added to impellerc's include path under
/// its package name, so shaders can `#include <pkg_name/foo.glsl>` against
/// any of them. `<flutter/...>` resolves to the engine builtins.
void main(List<String> arguments) async {
final root = Directory.current;
final assets = Directory.fromUri(root.uri.resolve('assets/shaders'));
final shaders = Directory.fromUri(root.uri.resolve('shaders'));
final packageShaderDirs = await _resolvePackageShaderDirs();

await compute(assets, shaders);
await compute(assets, shaders, packageShaderDirs);
if (arguments.contains('watch')) {
stdout.writeln('Running in watch mode');
shaders.watch(recursive: true).listen((event) {
compute(assets, shaders);
compute(assets, shaders, packageShaderDirs);
});
}
}

Future<void> compute(Directory assets, Directory shaders) async {
/// Returns every Dart dependency's top-level `shaders/` directory, so an
/// `#include <pkg_name/foo.glsl>` can resolve to
/// `<pkg-root>/shaders/pkg_name/foo.glsl`.
Future<List<Directory>> _resolvePackageShaderDirs() async {
final configUri = await Isolate.packageConfig;
if (configUri == null) {
throw Exception(
'Unable to locate package_config.json. Run `dart pub get` first.',
);
}

final configFile = File.fromUri(configUri);
final config =
jsonDecode(configFile.readAsStringSync()) as Map<String, dynamic>;
final packages = (config['packages'] as List).cast<Map<String, dynamic>>();
final result = <Directory>[];
for (final package in packages) {
final name = package['name'] as String;
if (name == 'flutter') {
// `flutter` ships no shader chunks; its includes come from the engine.
continue;
}

final rootUriRaw = package['rootUri'] as String;
final rootUri = configUri.resolve(
rootUriRaw.endsWith('/') ? rootUriRaw : '$rootUriRaw/',
);

final shaderDir = Directory.fromUri(rootUri.resolve('shaders/'));
if (shaderDir.existsSync()) {
result.add(shaderDir);
}
}
return result;
}

Future<void> compute(
Directory assets,
Directory shaders,
List<Directory> packageShaderDirs,
) async {
// Delete all the bundled shaders so we can replace them with new ones.
if (assets.existsSync()) {
assets.deleteSync(recursive: true);
Expand All @@ -44,6 +90,9 @@ Future<void> compute(Directory assets, Directory shaders) async {
.map((f) => f.path.split(Platform.pathSeparator).last.split('.').first)
.toSet();

final impellerC = await findImpellerC();
final engineShaderLib = impellerC.resolve('./shader_lib/').toFilePath();

for (final name in uniqueShaders) {
final bundle = {
'TextureFragment': {
Expand All @@ -57,14 +106,17 @@ Future<void> compute(Directory assets, Directory shaders) async {
};

stdout.writeln('Computing shader "$name"');
final impellerC = await findImpellerC();
final result = await Process.run(impellerC.toFilePath(), [
'--sl=${assets.path}${Platform.pathSeparator}$name.shaderbundle',
'--shader-bundle=${jsonEncode(bundle)}',
'--include=${shaders.path}',
for (final dir in packageShaderDirs) '--include=${dir.path}',
'--include=$engineShaderLib',
]);

if (result.exitCode != 0) {
return stderr.writeln(result.stderr);
stderr.writeln('Failed to compile shader "$name":\n${result.stderr}');
exitCode = 1;
}
}
}
Expand Down Expand Up @@ -126,7 +178,7 @@ Future<Uri> findImpellerC() async {
// ignore: do_not_use_environment
const impellercEnvVar = String.fromEnvironment('IMPELLERC');
if (impellercEnvVar != '') {
if (!doesFileExist(impellercEnvVar)) {
if (!File(impellercEnvVar).existsSync()) {
throw Exception(
'IMPELLERC environment variable is set, '
"but it doesn't point to a valid file!",
Expand All @@ -147,7 +199,7 @@ Future<Uri> findImpellerC() async {
final tried = <Uri>[];
for (final variant in _impellercLocations) {
final impellercPath = engineArtifactsDir.resolve(variant);
if (doesFileExist(impellercPath.toFilePath())) {
if (File(impellercPath.toFilePath()).existsSync()) {
found = impellercPath;
break;
}
Expand All @@ -161,7 +213,3 @@ Future<Uri> findImpellerC() async {

return found;
}

bool doesFileExist(String path) {
return File(path).existsSync();
}
15 changes: 14 additions & 1 deletion packages/flame_3d/lib/src/resources/material/unlit_material.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class UnlitMaterial extends Material {
super(
vertexShader: VertexShader.fromAsset(
'packages/flame_3d/assets/shaders/unlit_material.shaderbundle',
slots: ['VertexInfo'],
slots: ['VertexInfo', 'JointMatrices'],
),
fragmentShader: FragmentShader.fromAsset(
'packages/flame_3d/assets/shaders/unlit_material.shaderbundle',
Expand All @@ -45,8 +45,21 @@ class UnlitMaterial extends Material {
..setMatrix4('VertexInfo.view', context.view)
..setMatrix4('VertexInfo.projection', context.projection);

final jointTransforms = context.jointsInfo.jointTransforms;
Comment thread
wolfenrain marked this conversation as resolved.
if (jointTransforms.length > _maxJoints) {
throw Exception(
'At most $_maxJoints joints per surface are supported;'
' found ${jointTransforms.length}',
);
}
for (final (index, transform) in jointTransforms.indexed) {
vertexShader.setMatrix4('JointMatrices.joints[$index]', transform);
}

fragmentShader
..setTexture('albedoTexture', albedoTexture)
..setColor('Material.albedoColor', albedoColor);
}

static const _maxJoints = 16;
}
22 changes: 22 additions & 0 deletions packages/flame_3d/shaders/flame_3d/skinning.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef FLAME_SKINNING_GLSL_
#define FLAME_SKINNING_GLSL_

in vec4 vertexJoints;
in vec4 vertexWeights;

uniform JointMatrices {
mat4 joints[16];
} jointMatrices;

mat4 computeSkinMatrix() {
if (vertexWeights.x == 0.0 && vertexWeights.y == 0.0 && vertexWeights.z == 0.0 && vertexWeights.w == 0.0) {
return mat4(1.0);
}

return vertexWeights.x * jointMatrices.joints[int(vertexJoints.x)] +
vertexWeights.y * jointMatrices.joints[int(vertexJoints.y)] +
vertexWeights.z * jointMatrices.joints[int(vertexJoints.z)] +
vertexWeights.w * jointMatrices.joints[int(vertexJoints.w)];
}

#endif
19 changes: 2 additions & 17 deletions packages/flame_3d/shaders/spatial_material.vert
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec4 vertexColor;
in vec3 vertexNormal;
in vec4 vertexJoints;
in vec4 vertexWeights;

#include <flame_3d/skinning.glsl>
Comment thread
wolfenrain marked this conversation as resolved.

out vec2 fragTexCoord;
out vec4 fragColor;
Expand All @@ -18,21 +18,6 @@ uniform VertexInfo {
mat4 projection;
} vertex_info;

uniform JointMatrices {
mat4 joints[16];
} jointMatrices;

mat4 computeSkinMatrix() {
if (vertexWeights.x == 0.0 && vertexWeights.y == 0.0 && vertexWeights.z == 0.0 && vertexWeights.w == 0.0) {
return mat4(1.0);
}

return vertexWeights.x * jointMatrices.joints[int(vertexJoints.x)] +
vertexWeights.y * jointMatrices.joints[int(vertexJoints.y)] +
vertexWeights.z * jointMatrices.joints[int(vertexJoints.z)] +
vertexWeights.w * jointMatrices.joints[int(vertexJoints.w)];
}

void main() {
mat4 skinMatrix = computeSkinMatrix();
vec3 position = (skinMatrix * vec4(vertexPosition, 1.0)).xyz;
Expand Down
19 changes: 10 additions & 9 deletions packages/flame_3d/shaders/unlit_material.vert
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec4 vertexColor;
in vec3 vertexNormal;
in vec4 vertexJoints;
in vec4 vertexWeights;

#include <flame_3d/skinning.glsl>

out vec2 fragTexCoord;
out vec4 fragColor;
Expand All @@ -19,14 +19,15 @@ uniform VertexInfo {
} vertex_info;

void main() {
mat4 mvp = vertex_info.projection * vertex_info.view * vertex_info.model;
gl_Position = mvp * vec4(vertexPosition, 1.0);
mat4 skinMatrix = computeSkinMatrix();
vec3 position = (skinMatrix * vec4(vertexPosition, 1.0)).xyz;
vec3 normal = normalize((skinMatrix * vec4(vertexNormal, 0.0)).xyz);

mat4 modelViewProjection = vertex_info.projection * vertex_info.view * vertex_info.model;
gl_Position = modelViewProjection * vec4(position, 1.0);

fragTexCoord = vertexTexCoord;
fragColor = vertexColor;

// Pass through all vertex attributes so the compiler doesn't strip them,
// which would break the vertex buffer layout.
fragPosition = vertexPosition + vertexJoints.xyz * vertexWeights.x;
fragNormal = vertexNormal;
fragPosition = vec3(vertex_info.model * vec4(position, 1.0));
fragNormal = mat3(transpose(inverse(vertex_info.model))) * normal;
}
Loading