Jump to content

Developer Insights #20 - Here there be Drag(ons)


Intercept Games

Recommended Posts

On 6/16/2023 at 2:07 PM, Nertea said:

It is partial and handles rotation but not translation (an artifact of how the system is set up). This one's a hard one, because players use the translation and rotation tools to do cool builds and make exciting things that are often decidedly unaerodynamic. There's a narrow line between simulation and gameplay which we toe a lot, and I would love to write more about - IMO one of the most interesting things about this game. Reality is often punishing, and the right amount of punishment is a tough thing to determine!

Fair enough. Was asking more from a stance of curiosity than attempting to poke holes in the process. Thanks for the additional insight.

Link to comment
Share on other sites

This looks a lot like many of my own e-mails I send out to my development and QA team after finding and fixing a bug.  I still do this, even though I know that about 80% of the readers stopped reading it after the first paragraph and skip to the last paragraph which summarizes the end result, but I still do it with the hopes that at least one of the readers finds it helpful by either providing context when testing it in QA or in finding and fixing some future bugs.  Plus, I know on at least one occasion where my boss told the entire team that, after reading the entire thing for the third time, he totally agreed with the product release delay in order to find and fix the issue.   

I know from experience that it can be frustrating getting unhelpful feedback from people, as you spend sometimes weeks trying to find the one line of the code that is causing the problem. I often hear the comment about finding a bug is like trying to find a needle in a haystack. However, If the cause of the problem stood out as obvious as this thing that is totally different from all of the other things around it, it would would make the job of debugging so much easier, even when your software has millions of lines of code.  However,  nobody writes code where the line of code with the bug in it looks any different than the lines before or after it.  If they knew it was going to be a bug when they wrote it, they would have fixed it in the first place.   So, in reality it is much closer to trying to find a very specific needle in a needlestack, sometimes when you don't even know which specific needle you are looking for.  It is very easy to skip right over the right needle, after sifting through thousands of other needles, already.

So, in similar fashion as my issue summaries of my own, I will summarize by saying, from one dev to another,  great job on explaining the way the system works and how you found the solution.  There are those of us out here that do read the whole thing and find the information useful.

Link to comment
Share on other sites

All great thoughts. Appreciate everyone who has take the time to write some reasoned, cogent suggestions. 

I'm not going to speak to whether any solution is better or worse, but all-vessel approaches have some advantages and some disadvantages. An advantage is indeed that you can occlude things you 'should' occlude quite easily. Your cargo bays and clipped parts are going to be much easier to treat, and whole-stack occlusion will be simpler. I will avoid saying for free because that is never a realistic word to use in software development. The probable biggest challenge is going to be dealing with parts that should not be occluded when they should be occluded. If you get 6 drag calculation images from each face of a large vessel, you're going to miss stuff. An quick obvious example here is biplane style wings - from 2 key plan views, we'll only see one wing! We would have to introduce edge cases to deal with this. We also consider scale. Fundamentally the drag cube approach only works well because we are confident that for most parts, we can fully represent each face's data by 3 numbers. Most of the time, a part is a weird cylinder or a funky box. If we try to snapshot  a whole vessel with our texture-based approach, we're going to get different drag values for different scales of an otherwise identical vessel due to the size of the textures used, and the drag contributions of smaller parts are going to start getting missed. We'd have to introduce scaling of the drag textures for different vessels, at minimum, and we're still going to get weird data at different step sizes. 

To sort those two problems, we probably actually need more than 6 drag views, and we need a consistent sampling grid that is independent of vessel scale... oh, that's basically now an expensive voxelization approach. Yikes - maybe just go all the way to voxels. Not strictly bad, but now a completely different aerodynamic approach, and one that needs to cater to recalculating voxel sets in response to flight events. 

Overall, I think some level of all-vessel or at least near-part approximations would indeed help treat some situations, but we've got to carefully design and balance implementing that versus other work we want to do. 

I also want to touch on the part clipping note here because it is a super-excellent illustration of one of the most common KSP design challenges. I love part clipping. I can make so many better looking, more optimized vessels than I could otherwise. That KSP1 update that introduced the transform tools? Game-changing for me and I suspect a ton of others. As someone who wants to clip a bunch of greebles together to make a cool probe, or... I was going to list a dozen use cases, but we all know it's great.

Clipping is physically nonsensical though (well, depends on the part, but on average, yeah). I can literally cram 8x the fuel into the same space just by setting my symmetry mode to 8x and hitting the Roll hotkey twice in the VAB. I recall a fun KSP1 exploit that involved 2 Mammoths, a cubic strut and a resulting 8-nozzle engine monstrosity which let you get twice the thrust in the same footprint. This isn't a big problem in space, clipping doesn't save you mass directly, you're still at Tsiolkovsky's mercy, but in atmosphere it has gameplay implications. It lets me fit more into a cargo bay than I should, as a quick example. In the current drag model, with some exceptions, part clipping doesn't change the aerodynamic behavior of parts. Jamming 8 fuel tanks into one still preserves their visibility to drag, so you've got a little penalty. 

Holistic all-vessel occlusion calculations would make this better. I'd then maybe be able to hide those 8 fuel tanks from drag. Now though... that becaomes fundamentally best way to build a rocket. There's no penalty for clipping, it gives me more fuel and better aerodynamics, and the most optimized design becomes the most clipped design, which to my mind is not ideal. If I wanted true physical realism, perfect occlusion calculations would need to be married with some way to stop players from doing that, which I absolutely don't want. Either way we go we end up with something that breaks reality in some way. It's a conundrum, and I'm really bringing it up because for me, it really focuses that physical reality versus excitement trade that I have to look at constantly when designing systems for KSP2. 

On 6/16/2023 at 11:10 AM, jclovis3 said:

I think now I am seeing another issue with this method of occlusion that may impact cargo bays and dynamic Fairings, which is that occlusion of a forward part is only tied to the one part behind it and that second part does not pass on the occlusion to the next part but simply provides only itself as the occlusion to the 3rd part.

We take a fairly different approach with fairings - when looking at this article, you can think of a closed fairing and its contents as a single part, from the perspective of the stack occlusion system. 

On 6/16/2023 at 12:26 PM, Pat20999 said:

Chris, is the new “torch drive” the salt water drive.

You know I can't answer that ;). 

I can say though that the definition of torch drive varies, and there are a number of different hypothetical propulsion techniques that might fit the bill. 

1 hour ago, 000PainKiller000 said:

How do liquid hydrogen tanks behave? I'm asking because it's not clear to me. Visually, when nothing is connected to them, they seems like a big streamlined orb, with a little metal skeleton. Mostly is shouldn't be a draggy part. However, i can see that in the front and back (and also on it's side) there are connection nodes. According to ksp1 logic, if those are not covered, they should have big draggy surfaces right? 

They're treated as any other part, so attaching nothing to the 'flat' front bit will create more drag if you don't have something rounded or pointy on it. Not a lot, because for those big tanks the difference between a flat surface and a slightly curved at 3 degree surface isn't a big change. One thing you'll see tough is that the LH2 spheres slow down faster purely because they're very light. You don't need a lot of force on a light object to change its velocity rapidly. 

Also, the basic set of equations that KSP2 uses are not tuned very differently than KSP1, and I do know that they have some trouble creating intuitive drag on blunt-ish bodies (a topic for someone more fluent in CFD than me). It may be worth a look from the team in the future, Making History did some custom compensations to make the round ball pods behave a little more intuitively as well. 

Link to comment
Share on other sites

On 6/17/2023 at 3:02 AM, asmi said:

The proper way to do it is to render vessel from the airflow's POV with orthographic projection and linear depth, use shader which captures depth into texture, and then run a simple compute shader over it to calculate a drag based on how much of a texture has depth written out (this is the simplest possible model). 

Could something like this be used to create a single drag cube for the entire craft, calculated at launch, staging, docking etc?

 

Edit: completely ignore this. Answered very well above.

Edited by Infinite deaths
Answered above
Link to comment
Share on other sites

On 6/20/2023 at 4:30 PM, Nertea said:

Holistic all-vessel occlusion calculations would make this better. I'd then maybe be able to hide those 8 fuel tanks from drag. Now though... that becaomes fundamentally best way to build a rocket. There's no penalty for clipping, it gives me more fuel and better aerodynamics, and the most optimized design becomes the most clipped design, which to my mind is not ideal. If I wanted true physical realism, perfect occlusion calculations would need to be married with some way to stop players from doing that, which I absolutely don't want. Either way we go we end up with something that breaks reality in some way. It's a conundrum, and I'm really bringing it up because for me, it really focuses that physical reality versus excitement trade that I have to look at constantly when designing systems for KSP2. 

That is ridiculous. The proper way to solve this problem is to disallow such clipping, and absolutely NOT to introduce yet another bug-o-dynamics instead of aerodynamics. Aerodynamics is - and should be - governed by the outer shape of the vessel, it doesn't and shouldn't care about internal construction. Besides, since you plan to only penalize clipping fuel tanks on top of each other by drag, does it mean that you have absolutely no problems with such "designs" when they escape atmosphere? Or you didn't think this far yet, and when you do, you add some other ridiculous penalty - like tenforld increase it phantom forces from buggy physics engine?

Honestly reading such dev posts makes me lose confidence in a technical competence and decision making of a dev team. The job of air drag system is to simulate drag, not to resolve shortcomings of VAB editor by inventing fake penalties which will leave players scratching their heads thinking "why the heck do I have so much drag?" when the answer is "you clipped one too many fuel tanks" (a face palm smile goes here). There is absolutely NO way in hell the player will ever figure it out, that has NO basis in reality and therefore should NOT exist.

So, pack up that existing drag system in a box, seal it and throw away that crap as far as you can, and implement this system properly. Existing system has so many holes and edge cases I can come up with right off top of my head, that I can't believe the tech leadership didn't shoot this idea down right from the get-go. I know I would've done exactly that.

Edited by asmi
Link to comment
Share on other sites

On 6/22/2023 at 1:39 PM, Infinite deaths said:

Could something like this be used to create a single drag cube for the entire craft, calculated at launch, staging, docking etc?

The problem with cubes - which devs don't seem to get judging by their posts, is that it only works somewhat well with smooth convex shapes, so that if your part/vessel flies 45° into the airstream, you can interpolate between drag area "up front" and "side" and get somewhat realistic value for drag. But when was the last time you've seen anyone makes a vessel with smooth convex shape?

Let's take a trivial example - an axis-aligned solid unit cube (1 meter in each dimension). If you precalculate it's drag surface from each cardinal direction, you get 1 sq. m.  for all directions (because a cube is axis-aligned). But what if you "fly" that cube by the edge into the airstream? You should get 1.41 sq. m. because the airstream will "see" two sides of a cube at 45° angle each, and total area will be sqrt(1^2 + 1^2) * 1 ≈ 1.41, but you can't get that value by interpolating between sides, as they both are 1 sq. m. And so the "cube-based" drag model will treat a cube as if it's a unit sphere, which of course is completely incorrect.

So if a model fails with a simple cube, can you imagine how it's going to work with more complex non-convex shapes? Well, you don't need to imagine - just fire up KSP2 and you will see that it's bevavior defies common sense in almost all cases.

Edited by asmi
Link to comment
Share on other sites

22 hours ago, asmi said:

That is ridiculous. The proper way to solve this problem is to disallow such clipping, and absolutely NOT to introduce yet another bug-o-dynamics instead of aerodynamics. Aerodynamics is - and should be - governed by the outer shape of the vessel, it doesn't and shouldn't care about internal construction. Besides, since you plan to only penalize clipping fuel tanks on top of each other by drag, does it mean that you have absolutely no problems with such "designs" when they escape atmosphere? Or you didn't think this far yet, and when you do, you add some other ridiculous penalty - like tenforld increase it phantom forces from buggy physics engine?

Honestly reading such dev posts makes me lose confidence in a technical competence and decision making of a dev team. The job of air drag system is to simulate drag, not to resolve shortcomings of VAB editor by inventing fake penalties which will leave players scratching their heads thinking "why the heck do I have so much drag?" when the answer is "you clipped one too many fuel tanks" (a face palm smile goes here). There is absolutely NO way in hell the player will ever figure it out, that has NO basis in reality and therefore should NOT exist.

So, pack up that existing drag system in a box, seal it and throw away that crap as far as you can, and implement this system properly. Existing system has so many holes and edge cases I can come up with right off top of my head, that I can't believe the tech leadership didn't shoot this idea down right from the get-go. I now I would've done exactly that.

I agree with this. Such clipping should not be allowed or should be penalized by simple logic. For example if a fuel tank is clipped into another fuel tank it should reduce its total capacity by the amount that it is clipped. Same thing if it is clipped into any other other solid part. For hollow parts maybe give the user the choice, but in those cases total fuel should still be reduced a bit. Aerodynamics should be calculated only based on the vessel's outer shape.

Edited by Antiglow
Link to comment
Share on other sites

1 hour ago, Antiglow said:

I agree with this. Such clipping should not be allowed or should be penalized by simple logic. For example if a fuel tank is clipped into another fuel tank it should reduce its total capacity by the amount that it is clipped. Same thing if it is clipped into any other other solid part. For hollow parts maybe give the user the choice, but in those cases total fuel should still be reduced a bit. Aerodynamics should be calculated only based on the vessel's outer shape.

My point is - whatever the situation with clipped fuel tanks is, it's not the job of aerodynamics to "penalize" for it. This idea to link clipped tanks with aerodynamics is so absurd that I question the sound judgment of whoever came up with it,  and also wonder what other similarly insane ideas found it's way into the codebase. That is a very depressing thought, and it gives credence to naysayers' claims that the tech team behind this project simply lacks technical expertise required to deliver the project with quality that most people expect.

Link to comment
Share on other sites

On 6/16/2023 at 10:02 PM, asmi said:

Actually a hacky crap that was described in the article will give you no end of headaches due to endless supply of edge cases. The proper way to do it is to render vessel from the airflow's POV with orthographic projection and linear depth, use shader which captures depth into texture, and then run a simple compute shader over it to calculate a drag based on how much of a texture has depth written out (this is the simplest possible model). More advanced model would calculate slopes (dz/dx and dz/dy) and drag values would be based on those slopes (this way more "aerodynamic" shapes would indeed be more aerodynamic and have less drag). This approach also provides a permanent fix for cargo bay occlusion problems as a free bonus with no further hacks required (because what's inside the bay will not render into depth texture at all, or will be overwritten by closer surfaces depending on how you set up your render pipeline and how good you are at culling stuff). And - most importantly - since this approach actually models how airflow works (ignoring mach effects and some other things like skin drag, flow separation, shock waves, etc.), it does not depend on part setup or vessel composition - it only cares about the shape which is presented to the airflow. Which is how it should be.

If you worry about possible performance issues, you can only run these calculations every X frames as opposed to every frame, and interpolate drag in between. This might even be a configurable option in the settings.

This was exactly my suggestion earlier in the thread, which was pushed back against.

 

On 6/19/2023 at 10:00 AM, asmi said:

No it's not possible, because the shape presented to the airstream is going to depend on a direction of that airstream relative to the vessel, and so it's not feasible to pre-calculate all possible directions in advance, especially since every time the craft changes, it will have to be re-calculated. Also real-time calculations will take into account things like flexing, which can also lead to a change in a shape presented into the airstream.

Rendering it constantly would be the best solution - and doable by the GPU.  But the current system doesn't render from the direction of airflow, so just using an orthographic cube map of the vessel which would better account for occlusion would still do better than the current solution which uses orthographic per-part and a hack to account for some occlusion.

Link to comment
Share on other sites

18 hours ago, RocketRockington said:

so just using an orthographic cube map of the vessel

That wouldn't be much better. For example, it would fare just as bad as a current system in the abovemention example of a solid axis-aligned unit cube - and for the same reason.

Link to comment
Share on other sites

On 6/24/2023 at 6:17 PM, asmi said:

My point is - whatever the situation with clipped fuel tanks is, it's not the job of aerodynamics to "penalize" for it. This idea to link clipped tanks with aerodynamics is so absurd that I question the sound judgment of whoever came up with it

I do not agree with this framing, it doesn't make sense to say that clipped parts are penalized by drag cubes. Under drag cubes, clipped parts are not linked to drag because the one has no influence on the other. Rather, it's the whole-vessel approach that you propose that rewards clipped parts, because now part clipping affects drag. And it rewards unduly in the extreme case of clipping fuel tanks and engines, unless another system is created to prevent or discourage this.

This will drastically affect the types of builds that players will be encouraged to use in atmosphere, encouraging spam of smaller clipped fuel tanks and engines to get unrealistic drag characteristics while inflating part count to boot. To avoid partclipped ascent vehicles becoming the norm, this means that next to implementing an entirely new whole-vessel (probably voxelization) approach to drag, you must also implement an anti-partclipping system. Even if this combination can be made performant and free of undesired artifacts, which I wouldn't consider a given, the cost of implementing it bug-free is going to be high and impact other engineering work on the game.

I do want the advantages that come naturally from such an approach, but it doesn't seem realistic to me given how much more there is to work on.

Edited by Lyneira
Link to comment
Share on other sites

I don't see why we should care about Part Clipping being used to make weird impossible crafts : if it's not part clipping, it'll be something else that non-RP, non realistic approach players, will use to break records. Phantom Forces, Kraken Drive, and other dirty alpacas.

What matters is to give the player that will play "normally" a good feel of confidence, to make them comfortable using this or that, to make sure it's repeatable, homogeneous, and representative of the reality, while keeping in mind that it's a game, not a 1:1 simulation, of course.

Soooo Part Clipping ? The ideal would be to get part clipped recalculated so that if some part is clipped into something and won't ever face the wind, then it only drag as much as the surface exposed. Sure, it'll definitely lead people to abuse this and stack tanks / air intake / engines / whatever in the same place, but you can't deal with that, and the majority will just consider a craft legit as long as... The clipping stay legit. Because, of course, there IS plenty legit clipping, it's just mandatory to get past the basic Lego look and improve in design, aesthetic. So yup, this "unwritten" rule regarding legit clipping will stay and get stronger and it's enough to deal with this debate, to me. It'll just be a written rule for competition, just like it's in most already, actually, since clipping is already an advantage even if mass and drag remain calculated : Part clipping will help having tiny bays / fairing. Engine clipping will help having as much thrust as you want on a tiny available surface. Tank clipping will help giving a dense and sturdy look to a craft with tremendous DeltaV while keeping little cute and chubby. Etc, etc.

But i'm afraid that the Devs won't take this route at all... Maybe for technical reasons. I don't know how FAR did work but it seems like it's doing a whole craft envelop pretty good, while keeping the ability to recalculate when something break, when a part is destroyed, etc ?

Edit : Actually, it will help with one of the most annoying non-legit part clipping, to me : the wing being placed whatever it's optimal to deal with COL/COM, even if it's a whole stack of wing inside a tank while keeping the lift capacity. Haha. Never understood how it's enjoyable to do so, but i'm quite severe when it comes to part clipping, while enjoying it very much as long as it's legit.

Since we are talking about KSP2, I dare to say that it's something that I feel should be totally revamped, with additional features. I'd love to see Tank Clipped to automatically adjust the available fuel capacity. Now that would be something new, clever, and game impacting ! I'd love to see Engine Clipped to automatically get a deep malus of thrust + temperature raising as hell. I'd love to see wing being clipped not giving any drag nor lift while still weighting. I mean, come on, what the point of KSP2 if there is no kind of ambition ? :p It's sound doable, actually, not something impossible for the sake of asking novelty.

Edited by Dakitess
Link to comment
Share on other sites

2 hours ago, Lyneira said:

I do not agree with this framing, it doesn't make sense to say that clipped parts are penalized by drag cubes. Under drag cubes, clipped parts are not linked to drag because the one has no influence on the other.

Yes they are linked, as developer himself said above.

2 hours ago, Lyneira said:

Rather, it's the whole-vessel approach that you propose that rewards clipped parts, because now part clipping affects drag. And it rewards unduly in the extreme case of clipping fuel tanks and engines, unless another system is created to prevent or discourage this.

No, it doesn't reward part-clipping, what it does reward is making designs aerodynamic. Which is what it should do. Part clipping is facilitated by VAB editor, not by aerodynamics.

2 hours ago, Lyneira said:

This will drastically affect the types of builds that players will be encouraged to use in atmosphere, encouraging spam of smaller clipped fuel tanks and engines to get unrealistic drag characteristics while inflating part count to boot. To avoid partclipped ascent vehicles becoming the norm, this means that next to implementing an entirely new whole-vessel (probably voxelization) approach to drag, you must also implement an anti-partclipping system. Even if this combination can be made performant and free of undesired artifacts, which I wouldn't consider a given, the cost of implementing it bug-free is going to be high and impact other engineering work on the game.

A couple of things:

1. FAR used approach functionally similar to the one I proposed for years, and I can't remember ever seeing anybody do a clipped fuel tanks spam.

2. Even if it would be true, why do you care how others play their game? I know I won't be doing this even if it would be beneficial. For example, asparagus staging has always been a preferred way of making launch vehicles, yet I've never built one in my entire 1000+ hrs of playing KSP because it's unrealistic. And most youtubers I've watched didn't do this either.

------

And my point still stands - it's not a job of aerodynamics to penalize players for abusing game bugs and glitches for fun and profit. Instead those bugs should be fixed, and - if part clipping spam is indeed considered undesired from gameplay point of view - then VAB editor should prevent such things from occuring, and/or adjust fuel capacity/loadout in case that happens.

Edited by asmi
Link to comment
Share on other sites

1 hour ago, Dakitess said:

I don't know how FAR did work but it seems like it's doing a whole craft envelop pretty good, while keeping the ability to recalculate when something break, when a part is destroyed, etc ?

From my discussions with Ferram, FAR uses voxelization to calculate an airfoil, what I proposed is a more simple model which doesn't account for a lot of aerodynamic effects. That said, it can be extended to do that should they ever decide to - for example they can add a part mask to figure out which part is exposed to airstream so that they can apply forces to it (this gives things like body lift and per-part vessel destruction due to aerodynamic loads) and/or add heating (gives us high-speed/reentry heating). This also "implements" air breaks essentially for free (since their deployments increases an area presented into the airstream, they generate additional drag). Finally coupled with slope calculations based on a depth map this system will also "implement" an actual aerodynamics, meaning not just drag, but also a lift, and allow removing yet another crurch, which is what current lift system is. And knowing slopes + depth you can calculate an airfoil, which leaves a door open for a full-blown FAR-like system with faithfully simulated advanced aerodynamic effects.

As far as performance goes, any more-or-less modern hardware should have no problems rendering a few 10K triangles into a texture, and compute shaders are no longer a serious performance drag like it was a some years ago.

Link to comment
Share on other sites

  • 2 months later...

I made an account just to post this. 

Everything @asmi says is 100% true. I'm a unity game developer myself and after reading this I have to say that this "system" is an unmaintainable mess. Anyone with a shred of programming experience could tell you that much. Trying to justify it by players being able to clip parts is ridiculous. The only real justification of keeping this system is that changing big parts of the game like that is a big risk. That is a valid concern, but in the long run trying to keep working on this mess will do this game's more harm than good. 

How to actually fix this?

To reiterate the point that was said before in the thread current gpus can easily render the craft to a render texture. The game I'm working on right now uses something similar on android. KSP2 minimal requirements require an RTX 2060. With that hardware you would be able to draw the craft from the direction of airstream to a texture with normals and depth, and process it to accurately determine drag and possibly even lift for any craft at every physics step. With a bit of trickery it could be ran in parallel to the game's logic and have almost negligible performance penalty.  It would also create a chance for emergent gameplay, because aerodynamics would actually make sense. Features such as airbrakes, decouplers or any sort of dynamically modified vehicles would just plain work. This could be a huge talking point and make the game really revolutionary.

Setting up something like this to approximate drag could be done in a single evening by a competent developer. I hope someone like that works on this game and decides to at least look into this option.

Link to comment
Share on other sites

  • 2 weeks later...
On 6/20/2023 at 11:30 PM, Nertea said:

There's no penalty for clipping, it gives me more fuel and better aerodynamics, and the most optimized design becomes the most clipped design, which to my mind is not ideal.

@Nertea... when parts are clipped together calculate the resulting mass and volume correctly (remove mass of intersecting walls, adjust volume of fuel in intersecting tanks etc.)

Link to comment
Share on other sites

Just now, Thomas_799 said:

Hello, how do you add pictures to your posts? I urgently need a way to add pictures to my posts.

Welcome to the forum @Thomas_799, the forum itself cannot host pictures so we use services like imgur or google drive to upload and then paste the link here. So long as the link ends with the file desriptor (.jpg, .png, etc) it should auto embed into your draft.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...