flutter3d
Showcase Changelog 38 packages API reference

Texture transform: the source

since 0.7.0 Formats and I/O

Regions the guide quotes: checker (line 19), transform (line 40), check (line 71).

1/// `withTextureTransform`: `KHR_texture_transform`, applied to a mesh's own
2/// texture coordinates.
3///
4/// Quoted by `texture_transform.md` and shown whole in the Source tab.
5library;
6
7import 'dart:typed_data';
8
9import 'package:flutter3d/flutter3d.dart';
10import 'package:flutter3d_showcase/src/demo/demo.dart';
11import 'package:vector_math/vector_math.dart';
13final class TextureTransformDemo extends ShowcaseDemo {
14 late final MeshData _plain;
15 late final MeshData _moved;
17 @override
18 Scene build(DemoContext context) {
19 // An 8x8 checker, small enough that scaling the transform below by
20 // four makes the tiling obvious.
21 final pixels = Uint8List(8 * 8 * 4);
22 for (var y = 0; y < 8; y++) {
23 for (var x = 0; x < 8; x++) {
24 final lit = ((x ~/ 2) + (y ~/ 2)).isEven;
25 final o = (y * 8 + x) * 4;
26 final value = lit ? 235 : 25;
27 pixels[o] = value;
28 pixels[o + 1] = value;
29 pixels[o + 2] = value;
30 pixels[o + 3] = 255;
31 }
32 }
33 final albedo = context.device.createTextureFromPixels(
34 width: 8,
35 height: 8,
36 format: TextureFormat.r8g8b8a8UNormInt,
37 pixels: ByteData.sublistView(pixels),
38 );
40 // `KHR_texture_transform`'s own order: scale, then rotate, then move.
41 // Scaling by four here tiles the checker four times across the plane
42 // instead of stretching one copy of it across the whole surface.
43 _plain = PlaneShape(width: 2, depth: 2).build();
44 final transform = TextureTransform(scale: Vector2(4.0, 4.0));
45 _moved = withTextureTransform(_plain, transform);
47 return Scene()
48 ..add(
49 MeshNode(
50 DeviceMesh.upload(context.device, _moved),
51 Material(albedo: albedo, roughness: 0.8, doubleSided: true),
52 name: 'floor',
53 ),
54 )
55 ..add(
56 LightNode(name: 'sun', intensity: 3.0)
57 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
58 );
59 }
61 @override
62 void configureView(DemoContext context) {
63 context.orbit
64 ..distance = 3.0
65 ..pitch = 0.9
66 ..yaw = 0.4;
67 }
69 @override
70 void verify(Scene scene, FrameResult frame) {
71 final layout = _plain.layout;
72 final stride = layout.floatsPerVertex;
73 final uvOffset = layout.floatOffsetOf(VertexLayout.texcoord.name);
74 // The plane's corner at U=1 should come back at U=4, the same
75 // arithmetic checked independently of whatever `withTextureTransform`
76 // did internally.
77 final cornerOffset = stride + uvOffset;
78 final sourceU = _plain.vertices[cornerOffset];
79 final movedU = _moved.vertices[cornerOffset];
80 if ((movedU - sourceU * 4.0).abs() > 1e-6 || sourceU == 0.0) {
81 throw StateError('the transform did not scale U by four');
82 }
83 if (_moved.vertices.length != _plain.vertices.length) {
84 throw StateError('the transform changed the vertex count');
85 }
86 if (frame.drawCalls < 1) {
87 throw StateError('the floor was not drawn');
88 }
89 }