Umbra - visual editor for shaders in Flutter

Related tags

Templates umbra
Overview

Umbra

coverage style: very good analysis License: MIT

Generated by the Very Good CLI πŸ€–

Umbra, visual editor for shaders in Flutter.


Getting Started πŸš€

This project contains 3 flavors:

  • development
  • staging
  • production

To run the desired flavor either use the launch configuration in VSCode/Android Studio or use the following commands:

# Development
$ flutter run --flavor development --target lib/main_development.dart

# Staging
$ flutter run --flavor staging --target lib/main_staging.dart

# Production
$ flutter run --flavor production --target lib/main_production.dart

*Umbra works on iOS, Android, Web, and Windows.


Running Tests πŸ§ͺ

To run all unit and widget tests use the following command:

$ flutter test --coverage --test-randomize-ordering-seed random

To view the generated coverage report you can use lcov.

# Generate Coverage Report
$ genhtml coverage/lcov.info -o coverage/

# Open Coverage Report
$ open coverage/index.html

Working with Translations 🌐

This project relies on flutter_localizations and follows the official internationalization guide for Flutter.

Adding Strings

  1. To add a new localizable string, open the app_en.arb file at lib/l10n/arb/app_en.arb.
{
    "@@locale": "en",
    "counterAppBarTitle": "Counter",
    "@counterAppBarTitle": {
        "description": "Text shown in the AppBar of the Counter Page"
    }
}
  1. Then add a new key/value and description
{
    "@@locale": "en",
    "counterAppBarTitle": "Counter",
    "@counterAppBarTitle": {
        "description": "Text shown in the AppBar of the Counter Page"
    },
    "helloWorld": "Hello World",
    "@helloWorld": {
        "description": "Hello World Text"
    }
}
  1. Use the new string
import 'package:umbra/l10n/l10n.dart';

@override
Widget build(BuildContext context) {
  final l10n = context.l10n;
  return Text(l10n.helloWorld);
}

Adding Supported Locales

Update the CFBundleLocalizations array in the Info.plist at ios/Runner/Info.plist to include the new locale.

    ...

    <key>CFBundleLocalizationskey>
	<array>
		<string>enstring>
		<string>esstring>
	array>

    ...

Adding Translations

  1. For each supported locale, add a new ARB file in lib/l10n/arb.
β”œβ”€β”€ l10n
β”‚   β”œβ”€β”€ arb
β”‚   β”‚   β”œβ”€β”€ app_en.arb
β”‚   β”‚   └── app_es.arb
  1. Add the translated strings to each .arb file:

app_en.arb

{
    "@@locale": "en",
    "counterAppBarTitle": "Counter",
    "@counterAppBarTitle": {
        "description": "Text shown in the AppBar of the Counter Page"
    }
}

app_es.arb

{
    "@@locale": "es",
    "counterAppBarTitle": "Contador",
    "@counterAppBarTitle": {
        "description": "Texto mostrado en la AppBar de la pΓ‘gina del contador"
    }
}

Updating bricks

When a brick gets changed the templates in umbra_core need to be updated, you can use the following command for that:

mason bundle -t dart ./bricks/<brick_name> -o packages/umbra_core/lib/src/<path_to_template>
Comments
  • fix: Unhandled Exception: 128: Not a supported op.

    fix: Unhandled Exception: 128: Not a supported op.

    Description

    A simple shader example compiled and generated successfully, however, fails on execution with the following error Unhandled Exception: 128: Not a supported op.

    Steps To Reproduce

    Sample shader

    uniform float time;
    
    const float Pi=3.14159;
    const int complexity=40;// More points of color.
    const float fluid_speed=3000.;// Drives speed, higher number will make it slower.
    const float color_intensity=.225;
    
    vec4 fragment(vec2 uv,vec2 fragCoord){
        vec2 p=(2.*gl_FragCoord.xy-resolution)/max(resolution.x,resolution.y);
        for(int i=1;i<complexity;i++)
        {
            vec2 newp=p+time*.0003;
            newp.x+=.6/float(i)*sin(float(i)*p.y+time/fluid_speed*float(i+2000))+.5;
            newp.y+=.6/float(i)*sin(float(i)*p.x+time/fluid_speed*float(i+1909))-.5;
            p=newp;
        }
        vec3 col=vec3(0.,color_intensity*sin(3.*p.x)+.25,color_intensity*sin(3.*p.x)+color_intensity);
        return vec4(col,1.);
    }
    

    Flutter code:

    import 'package:flutter/material.dart';
    import 'package:shader/shaders/hello_world.dart';
    
    void main() async {
      // Ensure bindings are initialized otherwise we can't user rootBundle.
      WidgetsFlutterBinding.ensureInitialized();
    
      runApp(const MaterialApp(home: MyApp()));
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      // Compile the binary into a Fragment program.
      var _value = 0.5;
      HelloWorld? program;
      @override
      void initState() {
        super.initState();
        initProgram();
      }
    
      Future<void> initProgram() async {
        program = await HelloWorld.compile();
        if (mounted) {
          setState(() {});
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                SizedBox(
                  width: 300,
                  child: Slider(
                    value: _value,
                    onChanged: (value) {
                      setState(() {
                        _value = value;
                      });
                    },
                  ),
                ),
                program == null
                    ? const CircularProgressIndicator()
                    : SizedBox(
                        width: 512,
                        height: 512,
                        child: FlutterShaderTest(
                          shader: program!.shader(
                            resolution: const Size.square(512),
                            time: _value,
                          ),
                        ),
                      ),
              ],
            ),
          ),
        );
      }
    }
    
    class FlutterShaderTest extends StatelessWidget {
      const FlutterShaderTest({Key? key, required this.shader}) : super(key: key);
    
      final Shader shader;
    
      @override
      Widget build(BuildContext context) {
        return CustomPaint(painter: ShaderPainter(shader));
      }
    }
    
    class ShaderPainter extends CustomPainter {
      ShaderPainter(this.shader) : _paint = Paint()..shader = shader;
    
      final Shader shader;
    
      final Paint _paint;
    
      @override
      void paint(Canvas canvas, Size size) {
        canvas.drawRect(const Rect.fromLTWH(0, 0, 512, 512), _paint);
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
    }
    

    Expected Behavior

    Output of the painter should look similar to this example: https://glslsandbox.com/e#81872.0

    Stacktrace

    Launching lib\main.dart on Windows in debug mode...
    Connecting to VM Service at ws://127.0.0.1:58165/tK7yafOyS04=/ws
    [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: 177: Not a supported op.
    #0      _Transpiler.parseInstruction (dart:_spirv/src/transpiler.dart:564:9)
    #1      _Transpiler.transpile (dart:_spirv/src/transpiler.dart:169:7)
    #2      transpile (dart:_spirv:73:5)
    #3      new FragmentProgram._ (dart:ui/painting.dart:3881:44)
    #4      FragmentProgram.compile.<anonymous closure> (dart:ui/painting.dart:3872:58)
    #5      new Future.<anonymous closure> (dart:async/future.dart:252:37)
    #6      _rootRun (dart:async/zone.dart:1418:47)
    #7      _CustomZone.run (dart:async/zone.dart:1328:19)
    #8      _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
    #9      _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
    #10     _rootRun (dart:async/zone.dart:1426:13)
    #11     _CustomZone.run (dart:async/zone.dart:1328:19)
    #12     _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1260:23)
    #13     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
    #14     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
    #15     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
    #16     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
    

    Additional comments

    I'm not new to shaders, but it's been at least 14 years since the last time I wrote one, back with glsl and GLUT when I was doing my Computer Graphics Specialization. I feel rusty and lost, like it is totally new for me and since Flutter added shader support I've been looking forward to get into this again, at least as a hobby. I found your work (it's amazing!) and I've been following you since that first medium article you wrote.

    Today, after a few months of being swamped in work I can finally give it a try and when trying to transcribe one simple example from GLSL Sandbox I get runtime errors that I can't figure out.

    I believe that the main issue is that I'm using glsl instructions that don't fully translate to spir-v (for which I have ZERO experience with).

    I've also tried to find information about which instructions from glsl are not available or have a different counterpart when translated to spir-v. If you can point me into the right direction, I'd be more than grateful! I don't really know which instruction is a not supported operation, I whish this tool was a bit more explicit on which line of the shader code is having an issue (I know that's almost impossible, but one can dream).

    Anyways, thanks again for your amazing work!

    bug 
    opened by nosmirck 6
  • docs: pub activate issue and restricted docs

    docs: pub activate issue and restricted docs

    Awesome package and can't wait to play around with it.

    The doc mentioned in the readme is giving a 404: https://github.com/wolfenrain/umbra/tree/main/docs

    And running dart pub global activate umbra_cli

    Gives:

    dart pub global activate umbra_cli
    Because pub global activate depends on umbra_cli any which doesn't exist
      (could not find package umbra_cli at https://pub.dartlang.org), version
      solving failed
    
    documentation 
    opened by HayesGordon 3
  • fix: installation of dependencies on Windows

    fix: installation of dependencies on Windows

    Description

    Installing the dependencies was giving me the following error: Bad state: No element #0 ListMixin.firstWhere (dart:collection/list.dart:167:5) #1 FileExtractor.extract (package:umbra_cli/src/workers/file_extractor.dart:26:26) ...

    I managed to track the problem to the fact that the FileExtractor was expecting a file named "install/bin/glslc" in the archive, but the actual name is "install/bin/glslc.exe" (for Windows). Also, the file was being saved as "glslc" and not as "glslc.exe", as it should on Windows.

    I just added the ".exe" to the end of the file names, when installing the dependencies and the platform is Windows. Creating and generating the shaders is working for me now.

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [x] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by ruivop 2
  • docs: linked the rules to follow while writing GLSL code

    docs: linked the rules to follow while writing GLSL code

    This may be helpful to let the developers know what their GLSL code should/shouldn't contain so that the generated SPIR-V properly runs on dart SPIR-V Transpiler.

    PS : sorry for the earlier PR which I had to close due to some issues.

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by rutvik110 2
  • chore(umbra_cli): v0.1.0-dev.8

    chore(umbra_cli): v0.1.0-dev.8

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • chore(umbra_flutter): v0.1.0-dev.4

    chore(umbra_flutter): v0.1.0-dev.4

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • chore(umbra): v0.1.0-dev.4

    chore(umbra): v0.1.0-dev.4

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • chore(umbra_cli): v0.1.0-dev.7

    chore(umbra_cli): v0.1.0-dev.7

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • chore(umbra_flutter): v0.1.0-dev.3

    chore(umbra_flutter): v0.1.0-dev.3

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • chore(umbra): v0.1.0-dev.3

    chore(umbra): v0.1.0-dev.3

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [x] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • feat: add support for `mat4` uniforms

    feat: add support for `mat4` uniforms

    Description

    Add support for mat4 uniforms.

    Type of Change

    • [x] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • fix: TranspileException (23: only float vectors are supported)

    fix: TranspileException (23: only float vectors are supported)

    I'm trying to compare two floats here as seen in below glsl code, y is pre-calculated or assigned.

    vec4 fragment(in vec2 uv,in vec2 fragCoord){
        
        float y=1.;
        
        vec3 finalColor=y==1.?vec3(.5176,.3059,.3059):vec3(0.);
        
        return vec4(finalColor.xyx,1.);    
    }
    

    The glsl code is compiled properly and binaries are generated but when trying to execute the shader in Flutter, it's throwing the following exception

    Exception has occurred.
    TranspileException (23: only float vectors are supported)
    

    I know the error is very straightforward but I'm not sure why it's throwing this error, seems like the portion where it's comparing this two values is not compiled properly or something else I'm not aware of. This is really messing with my head for some time, would love to get idea of what's going in here.

    Steps To Reproduce

    1. Go to https://github.com/rutvik110/my_shader
    2. Build the project

    Expected Behavior

    Shader binaries compiled successfully on flutter side and shader is shown.

    bug 
    opened by rutvik110 0
  • feat: consider using the Dart build system

    feat: consider using the Dart build system

    See https://github.com/dart-lang/build/

    You can define a builder that takes in a . glsl file and emits a Dart file. You get watch for free, plus pretty smart incremental builds, etc.

    Happy to chat with you about i!

    opened by kevmoo 1
  • feat: support shader hints for uniforms

    feat: support shader hints for uniforms

    Description

    Implementing shader hints for uniforms.

    Type of Change

    • [x] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by wolfenrain 0
  • feat: support shader hints for uniforms

    feat: support shader hints for uniforms

    Description

    As a developer I often want to pass a color to my shader like so:

    uniform vec4 myColor;
    
    vec4 fragment(vec2 uv, vec2 fragCoord) {
        return myColor;
    }
    

    But to pass a color we need to call the generated API like this:

    const aRealColor = Color(0xFF00FF00);
    
    ...
    
    myShader.shader(
       ...
      myColor: Vector4(
        aRealColor.red / 255,
        aRealColor.green / 255,
        aRealColor.blue / 255,
        aRealColor.alpha / 255,
      ),
    );
    

    Which, as you can see, still requires developers to correctly construct a Vector4 in the normalized space that shaders often expect for color values. It would be much more developer friendly if we could directly pass the Color value to the generated API and then Umbra makes sure it would be passed as a vec4.

    So the proposal is to add support for shader hints. Shader hints are indicators on how a uniform is going to be used. These shader hints can then tell Umbra that a uniform will hold a certain type of value and Umbra can then optimize the generated API based on those hints.

    This would allow Umbra to provide more functionality to the developers and potentially even be an extendable system so that custom hints can be created.

    The above example can be rewritten with shader hints like so:

    uniform vec4 myColor : hint_color;
    
    vec4 fragment(vec2 uv, vec2 fragCoord) {
        ...
    }
    

    And then the generated API for this shader would look like this:

    const aRealColor = Color(0xFF00FF00);
    
    ...
    
    myShader.shader(
       ...
      myColor: aRealColor
    );
    

    Requirements

    • [ ] Add support for at least the following shader hints:
      • [ ] hint_color
      • [ ] hint_range(min, max, [step])
    • [ ] Ensure that this is not a breaking change, shaders without hints should still compile as expected

    Additional Context

    This is proposal is based on shader hints from the Godot shading language.

    opened by wolfenrain 0
  • feat: instead of using the `glslc` command switch to `dart:ffi` and call `libshaderc` directly

    feat: instead of using the `glslc` command switch to `dart:ffi` and call `libshaderc` directly

    Description

    As a developer, I want to use umbra directly in my Flutter app and generate SPIR-V binaries on the fly. but right now this requires the glslc command to be installed which isn't useable on non-desktop platforms.

    If we use dart:ffi instead and use the underlying library of glslc called libshaderc we can then use Umbra in both Flutter and Dart applications, assuming that the correct lib files are provided.

    Requirements

    • [ ] being able to generate SPIR-V binaries in Flutter directly
    • [ ] have a way to have the libshaderc files provided for the given platform
    refactor 
    opened by wolfenrain 3
Releases(umbra_flutter-v0.1.0-dev.4)
Owner
Jochum van der Ploeg
Jochum van der Ploeg
Json editor - A json editor on flutter

Features Support add comment; Support show errors for invalid json text; Pretty

Chan Young 12 Nov 18, 2022
An app for analysing sound waves and building sounds from a visual sound wave graph

Wave This app was built for the Science Talent Search Victoria 2022. UPDATE: This project won a minor bursary prize in the Computer Programs category

William Herring 7 Dec 20, 2022
Generate secure passwords, check for exposed passwords, get visual feedback for password strength or get form validation with a minimum password strength required.

password_strength_checker Generate secure passwords, check for exposed passwords, get visual feedback for password strength or get form validation wit

Dario Varriale 6 Aug 8, 2023
WYSIWYG editor for Flutter with a rich set of supported formatting options. (WIP)

✨ rich_editor WYSIWYG editor for Flutter with a rich set of supported formatting options. Based on https://github.com/dankito/RichTextEditor, but for

Festus Olusegun 116 Dec 27, 2022
A Flutter material theme editor

Flutter Theme ⚠️ WARNING: This app is still under development so please expect bugs and missing features in the app. ⚠️ Inspired by Panache, a Flutter

null 0 Jan 2, 2022
A lightweight HTML-Richtext editor for Flutter

Flutter HTML Editor Flutter HTML Editor is a simple HTML-based Richtext editor, which is able to edit and parse a selected set of HTML tags into a Flu

Herry 14 Oct 19, 2022
A Flutter package that provides a WYSIWYG editor backed by flutter_inappwebview and the Summernote library.

Flutter Html Editor - Enhanced Flutter HTML Editor Enhanced is a text editor for Android, iOS, and Web to help write WYSIWYG HTML code with the Summer

Tanay Neotia 200 Dec 31, 2022
🎨 Flutter Material Theme editor

?? Panache A Flutter Material Theme editor. Panache helps you to create beautiful Material themes for your Flutter applications. Customize widgets col

Erick Ghaumez 1.7k Dec 30, 2022
A Flutter based code editor

rich_code_editor A simple package that supports creating code editors in Flutter. Flutter version supported: Flutter 1.22.5 Getting Started There are

Sovit 111 Dec 16, 2022
Rich text editor for Flutter

A rich text editor for Flutter FlutterQuill is a rich text editor and a Quill component for Flutter. This library is a WYSIWYG editor built for the mo

X Code 1.7k Jan 4, 2023
An online Dart editor with support for console, web, and Flutter apps

DartPad DartPad is a free, open-source online editor to help developers learn about Dart and Flutter. You can access it at dartpad.dev. What is it? Wh

Dart 1.4k Jan 4, 2023
Panache - 🎨 Flutter Material Theme editor

?? Panache A Flutter Material Theme editor. Panache helps you to create beautiful Material themes for your Flutter applications. Customize widgets col

Erick Ghaumez 1.7k Jan 3, 2023
An Instagram like text editor Flutter widget that helps you to change your text style.

TextEditor An instagram like text editor widget for flutter Show some ❀️ and star the repo to support the project Features Edit TextStyle object font

Mehdi Zarepour 68 Dec 16, 2022
FLUTTER API: Video Editor allows trim, crop, rotate and scale video with a super flexible UI Design

video_editor My other APIs Scroll Navigation Video Viewer Helpers Features Super flexible UI Design. Support actions: Crop Trim Scale Rotate Cover sel

Luis Felipe Murguia Ramos 214 Dec 26, 2022
Rich text editor for Flutter based on Delta format (Quill fork)

Visual Editor Visual Editor is a Rich Text editor for Flutter originally forked from Flutter Quill. The editor is built around the powerful Delta docu

Visual Space 190 Jan 7, 2023
MathCanvas - Graphical Math Equation Editor made by Flutter

Graphical Math Equation Editor made by Flutter. My goal is to provide a keyboard typing experience like a calculator. Test Web Page: https:

GongBJ 3 Jun 3, 2022
A simple tag editor for inputing tags in Flutter.

Super Tag Editor A simple tag editor for inputting tags with suggestion box Supported suggestion box Screen Shot 1 Screen Shot 2 ) Usage Add the packa

dab 4 Dec 6, 2022
Megalinks is an android app where we provide free resources available for video editing, like Scenepacks, project files of the big editor, tutorials, etc...

MegaLinks Megalinks is an android app where we provide free resources available for video editing, like Scenepacks, project files of the big editor, t

Vishal Rajesh Karangale 3 Jul 8, 2022
camilo velandia 69 Dec 30, 2022