Jump to content

Wulfie Reanimator

Resident
  • Posts

    5,816
  • Joined

Everything posted by Wulfie Reanimator

  1. Here's a simpler version that I started from. The goal was to make the object spin a certain amount in a certain time. After that was working, everything else was easy to build upon it. float spin_time = 3; float spin_target = 2 * 360 * DEG_TO_RAD; float spin_per_second = spin_target / spin_time; float time; llResetTime(); while (time < spin_time) { time = llGetTime(); rotation now = llEuler2Rot(<0, 0, (spin_per_second * time)>); llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROTATION, now]); llSleep(0.022); } rotation now = llEuler2Rot(<0, 0, (spin_target)>); llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROTATION, now]);
  2. Am I doing it right? float get_relative_avatar_angle(key id) { vector this = llGetPos(); vector target = llList2Vector(llGetObjectDetails(id, [OBJECT_POS]), 0); target = llVecNorm(<target.x - this.x, target.y - this.y, 0>); // If the avatar is on the right (-Y) side, return negative value. float relative_angle; if (target.y < 0) relative_angle = -llRot2Angle(llRotBetween(<1,0,0>, target)); else relative_angle = llRot2Angle(llRotBetween(<1,0,0>, target)); return (relative_angle); } spin_to_avatar(key id, float duration, float damp, integer revolutions, float relative_angle) { float spin_time = duration; float spin_target = revolutions * TWO_PI + relative_angle; float spin_per_second = spin_target / spin_time; float spin_remaining = 0; // Calculate intermediate positions based on time. float time; llResetTime(); while (time < spin_time) { if (time < (spin_time - damp)) time = llGetTime(); else // Calculate a slower time { if (spin_remaining == 0) // Value not set, calculate it once. spin_remaining = (spin_target - (spin_per_second * time)); float damping = (spin_target - (spin_per_second * time)) / spin_remaining; float new_time = (llGetTime() - time) * damping; time += new_time; } rotation now = llEuler2Rot(<0, 0, (spin_per_second * time)>); llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROTATION, now]); llSleep(0.022); // Doesn't make sense to update position any faster than this. } // The loop will usually end before spin_target is reached, so we set it here. rotation now = llEuler2Rot(<0, 0, (spin_target)>); llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROTATION, now]); } default { state_entry() { llSetRot(ZERO_ROTATION); key target = llGetOwner(); float duration = 3; // Duration of the spin (including slowing down) float damp = 1; // Duration of the "slow down" period integer revolutions = 5; float relative_angle = get_relative_avatar_angle(target); spin_to_avatar(target, duration, damp, revolutions, relative_angle); } }
  3. This is the impossible part. Scripts can't get the "head angle" because it doesn't exist on the server. Every viewer handles its own animations, which means that every viewer is potentially seeing a different animation (or a different moment in the animation).
  4. It's tedious work but I've found a few "mostly static" standing poses. I'm also really annoyed when the avatar is taking steps or turning around. This one's my current favorite, it's from Body Language SLC: https://puu.sh/HxScy/917b1c64a7.mp4 I don't recall which set it comes from, though. Its name is "02/BLAOtriSt02_3"
  5. My tolerance for "uncanniness" is probably higher than yours because I've never felt that an avatar looks creepy because of their eyes, even when they're rotating more than humanly possible. I don't mind static eyes at all, either. But I will say that some eye textures have too much of that reddish vein detail around the eyes, which will look just really ugly when the eyes turn.
  6. Most mesh heads I've used come with mesh eyes that are rigged to the eyeball bones, which lets them work like the "real eyes" or unrigged attachments.
  7. Yup, what you're describing already exists in the viewer and is impossible to implement with scripts.
  8. Since we're still talking about the Kemono, I'd like to point out that the body itself (a fair comparison to other bodies like Maitreya -- excluding head or tail or anything else) is only 14'400 triangles, including two sets of legs from the knee-down. Since the body is modifiable, I can remove the animal legs so that we're comparing two "fully human" bodies. That makes 11'100 triangles vs 176'300. Granted, the Kemono body is not rigged to as many (read: most) shape-sliders as Maitreya, so it doesn't need to worry about not having enough topology to deal with the extremes. Edit: Here's "Avatar 2.0" from the same creator as the Kemono. Rigged more completely. 17'200 triangles after I removed extra boobs. The butt is kinda lacking, though. And while I am an advocate for better content, I don't think we need to set the bar around 10K. Making that amount look good does take a lot of skill, effort, and/or tradeoffs. 30-40K for a fully-rigged, human, headless bento body would be much more reasonable, especially for those who are already commercially successful in the current market. We'll never hit numbers like we see in "real games" because of the kind of environment SL is, and how we interact with it. Humans will always compromise efficiency for convenience.
  9. Personally I prefer the slightly more masculine (defined pecs) shape of the official Maitreya Male Chest addon, though it's not compatible with V-Tech clothes. It seems to have less problems with the boobs showing through. I haven't seen or heard of any dedicated male body that isn't noticably manly. Belleza Jake might be close to a "toned, but not overly so" body, but I wouldn't call it a "boy" body.
  10. Which demo was it? What version of the Maitreya body are you using? Your body is almost guaranteed to not be broken. The fix just depends on what's happening.
  11. Close! Do this at the same place instead: touch_start(integer total_number) { if (llDetectedKey(0) != llGetOwner()) return; integer i = 0; MENU1 = []; MENU2 = []; // count the textures in the prim to see if we need pages integer c = llGetInventoryNumber(INVENTORY_TEXTURE); if (c <= 12) { for (; i < c; ++i) MENU1 += llGetInventoryName(INVENTORY_TEXTURE, i); } In English, that would mean "If the detected key is not the owner, stop." What you had before was kind of an incomplete piece of code, and it was confusing the script.
  12. https://community.secondlife.com/blogs/entry/2504-marketplace-early-spring-cleaning/ If the Merchant hasn’t logged in in two years *and* a specific product hasn’t sold in 1 year, the product will be unlisted. If the Merchant hasn’t logged in in two years *and* the store hasn’t sold anything in 1 year, their store will be disabled. This means that if a product has sold within the past year, that product listing will remain available on the marketplace. If a store has products, which have sold within the past year, it will remain enabled.
  13. OAuth doesn't share any of "your information" with whatever third party your web service is using it for... That's the opposite of what it does.
  14. Calling it llOrd seems a little weird. Why that term? I've never heard it being used in ASCII/Unicode context besides here. I would prefer llInteger2Char and llChar2Integer to be more descriptive and in line with other conversion functions. You inspired me: string llOrdinal(integer n) { string number = (string)n; string last = llGetSubString(number, -1, -1); n = n % 100; if (10 < n && n < 20) // Exception: Teens end in -th. return (number + "th"); else if (last == "1") return (number + "st"); else if (last == "2") return (number + "nd"); else if (last == "3") return (number + "rd"); else return (number + "th"); } Regarding llHash -- while I don't dislike it, I don't really understand how it would be practically different from just choosing a random number (instead of a random string), or using already-existing data like the owner's key, the object's own key, group key, etc. It might at least benefit from being slightly clearer to read. integer channel = (integer)((string)llGetKey()); integer channel = llHash(llGetObjectName()); integer channel = (integer)((string)llGetOwner()); integer channel = llHash(llGetUsername(llGetOwner()));
  15. I want one of you to suffer my ear worm. Alternative listening:
  16. Input, physics, and rendering (along with many other things like audio/networking/file-handling/etc) can generally all be completely separated from each other, and often are, but there are still many modern games (from big triple-A developers for games like Need For Speed, Dark Souls, or Fallout) that do the dumbest things such as literally tie the physics to framerate. Try googling "tied to framerate" and you'll find lots of examples. It's not necessarily even that the engines used by game developers can't do it properly, it's just that people make mistakes and bad decisions. Here are a couple posts about specific games that have subframe inputs: Reflex Overwatch Microsoft Paint (from Edit 3 this thread) That said, the SL viewer doesn't do any kind of physics handling at all. It's happening completely on LL's servers, the viewer only asks "may I please move forward?" and the server will either grab its hand or respond with "no dummy, that's a wall."
  17. I wasn't explaining the difference in seeing a difference. It's going to be pretty difficult to talk about it in much depth because there's a ton of nuance to talk about, especially when you already missed what I was saying and I don't know how much prior knowledge you have about anything I've said so far or could say. I'll try though. When you're developing a game, the easiest thing to do is just have a one big infinite loop with a delay at the end. while (!quit) { frame_start = get_time(); process_input(); render_frame(); wait_until_next_frame(frame_start); } Here, your inputs are directly related to the current framerate. I think this (and simple variations of it) is the most common way games handle input, even big titles. Let's say you're playing a first-person game at 60 FPS, just walking and looking around. Your movement is based on velocity over time. Higher FPS means less time between updates from your keyboard, which means that higher FPS gives you more precise movement regardless of how often your monitor gets updated with a new image. Your movement feels (and is) more precise. The same applies to your mouse; the trajectory of your cursor changes as your FPS is increased. Imagine a curve that gets subdivided with more and more resolution. You get a smoother curve. Your camera-control feels (and is) more precise. But since the above code is pretty naive, there are of course more advanced ways to do it. You can do "subframe" processing, meaning that you might process input multiple times before drawing a new frame (for example, when the monitor refresh rate is 60 and the game can reach 240). while (!quit) { elapsed += get_time_since_last_time(); process_input(); if (should_draw_new_frame(elapsed)) { render_frame(); elapsed = 0; } } At that point it becomes a question of what you want to tell the player. You could report either the actual render-rate, or how often the whole logic is run. Which one is more important (and the side-effects of picking either) are subject to individual judgement.
  18. It's very hard to explain with words, but you can definitely feel the difference (in mouse movement) between a game that runs at 60 FPS vs 240 FPS, even if your monitor isn't displaying more than 60 FPS. You can also feel it when the game's framerate isn't consistent, even if it never drops below whatever your monitor can display. (That is one good reason to limit your FPS -- to keep it consistent.) I think you and @Ardy Lay are speaking past each other, though. If the game is running at 240 but your monitor at 60, you're not going to see things faster. But if you actually had a 240Hz monitor, then it would be true.
  19. This is true, but it's not a new change. That's how the LL/FS viewer has worked for many many years, longer than I can remember. I think it's called the "interest list," but I might be confusing that with something else.
  20. The rotation change is instant, as far as the sim is concerned. What you've noticed is the client-side interpolation, and it's done for all rotation/position/scale changes (for both, objects and avatars). You definitely can't adjust it through scripts at all.
  21. The "comments are bad" thing comes from a valid place, but that guy seems to have taken it to an extreme. You can find a lot of articles about it that explain it better than I have the energy for, but to put it simply: Comments need maintenance too. When you write code, add a comment, and then change the code again, the original comment may become outdated and misleading. Code is documentation. What is code anyway? A set of instructions. It tells you exactly what's being done, or at least that's the idea. When the code is so unclear that you need to write more to explain what it does or how it's done, you create two problems. To restate the obvious, comments aren't bad. Bad comments are bad. Bad code produces bad comments.
  22. What do you mean when you say "send the avatar key to the region?"
  23. list get_contents_except_self() { list contents; string self = llGetScriptName(); integer item = llGetInventoryNumber(INVENTORY_ALL); while (~--item) { string name = llGetInventoryName(INVENTORY_ALL, item); if (name != self) { contents += name; } } return (contents); } default { attach(key id) { if (id) llRequestPermissions(id, PERMISSION_ATTACH); } run_time_permissions(integer perm) { if (perm) { key avatar = llGetPermissionsKey(); llGiveInventoryList(avatar, "TEST CONTENT", get_contents_except_self()); llRegionSayTo(avatar, 0, "Got permission to detach, bye!"); llDetachFromAvatar(); } } }
  24. You need to assign more than one material to the model if you want to have more than one "face" in SL.
×
×
  • Create New...